mirror of
https://github.com/astral-sh/ruff.git
synced 2026-10-28 10:45:37 +08:00
[ty] Fix anchoring of include and exclude patterns after the project's root changed (#28995)
This commit is contained in:
1 parent
55c48b1e76
commit
dab6370667
4 files changed
+83
-26
No files matched your search
@@ -20,7 +20,7 @@ use crate::glob::portable::AbsolutePortableGlobPattern;
|
||||
///
|
||||
/// # Equality
|
||||
///
|
||||
/// Two filters are equal if they're constructed from the same patterns (including order).
|
||||
/// Two filters are equal if they're constructed from the same absolute globs (including order).
|
||||
/// Two filters that exclude the exact same files but were constructed from different patterns aren't considered
|
||||
/// equal.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
|
||||
@@ -112,7 +112,7 @@ impl ExcludeFilterBuilder {
|
||||
///
|
||||
/// # Equality
|
||||
///
|
||||
/// Two ignore matches are only equal if they're constructed from the same patterns (including order).
|
||||
/// Two ignore matchers are only equal if they're constructed from the same absolute globs (including order).
|
||||
/// Two matchers that were constructed from different patterns but result in
|
||||
/// including the same files don't compare equal.
|
||||
#[derive(Clone, get_size2::GetSize)]
|
||||
@@ -192,7 +192,7 @@ impl Match {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
|
||||
struct IgnoreGlob {
|
||||
/// The pattern that was originally parsed.
|
||||
/// The original absolute glob, before removing negation or a trailing slash.
|
||||
original: String,
|
||||
|
||||
/// This is a pattern allowing a path (it starts with a `!`, possibly undoing a previous ignore)
|
||||
@@ -247,7 +247,7 @@ impl GitignoreBuilder {
|
||||
pattern: &AbsolutePortableGlobPattern,
|
||||
) -> Result<&mut GitignoreBuilder, globset::Error> {
|
||||
let mut glob = IgnoreGlob {
|
||||
original: pattern.relative().to_string(),
|
||||
original: pattern.absolute().to_string(),
|
||||
is_allow: false,
|
||||
is_only_dir: false,
|
||||
};
|
||||
@@ -291,3 +291,32 @@ impl GitignoreBuilder {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ExcludeFilter, ExcludeFilterBuilder};
|
||||
use crate::GlobFilterCheckMode;
|
||||
use crate::glob::{PortableGlobKind, PortableGlobPattern};
|
||||
use ruff_db::system::SystemPath;
|
||||
|
||||
#[test]
|
||||
fn equality_accounts_for_pattern_root() -> anyhow::Result<()> {
|
||||
let build = |root| -> anyhow::Result<ExcludeFilter> {
|
||||
let mut builder = ExcludeFilterBuilder::new();
|
||||
builder.add(
|
||||
&PortableGlobPattern::parse("**/.venv/", PortableGlobKind::Exclude)?
|
||||
.into_absolute(root),
|
||||
)?;
|
||||
Ok(builder.build()?)
|
||||
};
|
||||
let member = build("/workspace/project")?;
|
||||
let workspace = build("/workspace")?;
|
||||
let environment = SystemPath::new("/workspace/.venv");
|
||||
|
||||
assert!(!member.match_directory(environment, GlobFilterCheckMode::Adhoc));
|
||||
assert!(workspace.match_directory(environment, GlobFilterCheckMode::Adhoc));
|
||||
assert_ne!(member, workspace);
|
||||
assert_eq!(workspace, build("/workspace")?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ const DFA_SIZE_LIMIT: usize = 1_000_000;
|
||||
/// regex allows to check for prefix matches.
|
||||
///
|
||||
/// ## Equality
|
||||
/// Equality is based on the patterns from which a filter was constructed.
|
||||
/// Equality is based on the original absolute globs.
|
||||
///
|
||||
/// Because of that, two filters that include the exact same files but were
|
||||
/// constructed from different patterns (or even just order) compare unequal.
|
||||
@@ -37,6 +37,7 @@ const DFA_SIZE_LIMIT: usize = 1_000_000;
|
||||
pub(crate) struct IncludeFilter {
|
||||
#[get_size(ignore)]
|
||||
glob_set: GlobSet,
|
||||
/// The original absolute globs, before adding patterns for descendants.
|
||||
original_patterns: Box<[Box<str>]>,
|
||||
#[get_size(size_fn = bit_box_size)]
|
||||
literal_pattern_indices: BitBox,
|
||||
@@ -216,7 +217,7 @@ impl IncludeFilterBuilder {
|
||||
.backslash_escape(true)
|
||||
.build()?;
|
||||
|
||||
self.original_patterns.push(input.relative().into());
|
||||
self.original_patterns.push(input.absolute().into());
|
||||
|
||||
// `lib` is the same as `lib/**`
|
||||
// Add a glob that matches `lib` exactly, change the glob to `lib/**`.
|
||||
@@ -355,6 +356,28 @@ mod tests {
|
||||
assert!(!filter.match_directory(path.replace('/', MAIN_SEPARATOR_STR)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equality_accounts_for_pattern_root() -> anyhow::Result<()> {
|
||||
let build = |root| -> anyhow::Result<IncludeFilter> {
|
||||
let mut builder = IncludeFilterBuilder::new();
|
||||
builder.add(
|
||||
&PortableGlobPattern::parse("src", PortableGlobKind::Include)?.into_absolute(root),
|
||||
)?;
|
||||
Ok(builder.build()?)
|
||||
};
|
||||
let member = build("/workspace/project")?;
|
||||
let workspace = build("/workspace")?;
|
||||
|
||||
assert_eq!(member.match_file("/workspace/src/main.py"), MatchFile::No);
|
||||
assert_eq!(
|
||||
workspace.match_file("/workspace/src/main.py"),
|
||||
MatchFile::Pattern
|
||||
);
|
||||
assert_ne!(member, workspace);
|
||||
assert_eq!(workspace, build("/workspace")?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_directory() {
|
||||
// `lib` is the same as `src/**`. It includes a file or directory (including its contents)
|
||||
|
||||
@@ -1694,6 +1694,11 @@ impl TestContext {
|
||||
.map_err(|()| anyhow!("Failed to convert root directory to uri"))?;
|
||||
settings.add_filter(&tempdir_filter(project_dir.as_str()), "<temp_dir>/");
|
||||
settings.add_filter(&tempdir_filter(project_dir_uri.path()), "<temp_dir>/");
|
||||
// Absolute globs use forward slashes on every platform.
|
||||
settings.add_filter(
|
||||
&tempdir_filter(project_dir.as_str().replace('\\', "/")),
|
||||
"<temp_dir>/",
|
||||
);
|
||||
settings.add_filter(r#"\\\\"#, "/");
|
||||
settings.add_filter(
|
||||
r#"The system cannot find the file specified."#,
|
||||
|
||||
@@ -58,102 +58,102 @@ Settings: Settings {
|
||||
ignore: Gitignore(
|
||||
[
|
||||
IgnoreGlob {
|
||||
original: "**/.bzr/",
|
||||
original: "<temp_dir>/**/.bzr/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.direnv/",
|
||||
original: "<temp_dir>/**/.direnv/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.eggs/",
|
||||
original: "<temp_dir>/**/.eggs/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.git/",
|
||||
original: "<temp_dir>/**/.git/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.git-rewrite/",
|
||||
original: "<temp_dir>/**/.git-rewrite/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.hg/",
|
||||
original: "<temp_dir>/**/.hg/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.mypy_cache/",
|
||||
original: "<temp_dir>/**/.mypy_cache/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.nox/",
|
||||
original: "<temp_dir>/**/.nox/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.pants.d/",
|
||||
original: "<temp_dir>/**/.pants.d/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.pytype/",
|
||||
original: "<temp_dir>/**/.pytype/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.ruff_cache/",
|
||||
original: "<temp_dir>/**/.ruff_cache/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.svn/",
|
||||
original: "<temp_dir>/**/.svn/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.tox/",
|
||||
original: "<temp_dir>/**/.tox/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/.venv/",
|
||||
original: "<temp_dir>/**/.venv/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/__pypackages__/",
|
||||
original: "<temp_dir>/**/__pypackages__/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/_build/",
|
||||
original: "<temp_dir>/**/_build/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/buck-out/",
|
||||
original: "<temp_dir>/**/buck-out/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/dist/",
|
||||
original: "<temp_dir>/**/dist/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/node_modules/",
|
||||
original: "<temp_dir>/**/node_modules/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
IgnoreGlob {
|
||||
original: "**/venv/",
|
||||
original: "<temp_dir>/**/venv/",
|
||||
is_allow: false,
|
||||
is_only_dir: true,
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user