Compare commits
9 Commits
zanie/ecos
...
options-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ca7407868 | ||
|
|
f1b00cafd4 | ||
|
|
dda4ceda71 | ||
|
|
195c000f5a | ||
|
|
a62c735f9e | ||
|
|
94b4bb0f57 | ||
|
|
fe485d791c | ||
|
|
d685107638 | ||
|
|
d85950ce5a |
22
Cargo.lock
generated
22
Cargo.lock
generated
@@ -2259,6 +2259,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruff_python_trivia",
|
||||
"serde_derive_internals 0.29.0",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
@@ -2624,7 +2625,7 @@ checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"serde_derive_internals 0.26.0",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
@@ -2664,9 +2665,9 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.188"
|
||||
version = "1.0.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -2684,9 +2685,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.188"
|
||||
version = "1.0.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2704,6 +2705,17 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.107"
|
||||
|
||||
@@ -34,7 +34,7 @@ quote = { version = "1.0.23" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
schemars = { version = "0.8.15" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde = { version = "1.0.189", features = ["derive"] }
|
||||
serde_json = { version = "1.0.107" }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.3.0", features = ["inline"] }
|
||||
|
||||
@@ -17,8 +17,8 @@ use ruff_linter::settings::DEFAULT_SELECTORS;
|
||||
use ruff_linter::warn_user;
|
||||
use ruff_workspace::options::{
|
||||
Flake8AnnotationsOptions, Flake8BugbearOptions, Flake8BuiltinsOptions, Flake8ErrMsgOptions,
|
||||
Flake8PytestStyleOptions, Flake8QuotesOptions, Flake8TidyImportsOptions, LintOptions,
|
||||
McCabeOptions, Options, Pep8NamingOptions, PydocstyleOptions,
|
||||
Flake8PytestStyleOptions, Flake8QuotesOptions, Flake8TidyImportsOptions, LintCommonOptions,
|
||||
LintOptions, McCabeOptions, Options, Pep8NamingOptions, PydocstyleOptions,
|
||||
};
|
||||
use ruff_workspace::pyproject::Pyproject;
|
||||
|
||||
@@ -99,7 +99,7 @@ pub(crate) fn convert(
|
||||
|
||||
// Parse each supported option.
|
||||
let mut options = Options::default();
|
||||
let mut lint_options = LintOptions::default();
|
||||
let mut lint_options = LintCommonOptions::default();
|
||||
let mut flake8_annotations = Flake8AnnotationsOptions::default();
|
||||
let mut flake8_bugbear = Flake8BugbearOptions::default();
|
||||
let mut flake8_builtins = Flake8BuiltinsOptions::default();
|
||||
@@ -433,8 +433,11 @@ pub(crate) fn convert(
|
||||
}
|
||||
}
|
||||
|
||||
if lint_options != LintOptions::default() {
|
||||
options.lint = Some(lint_options);
|
||||
if lint_options != LintCommonOptions::default() {
|
||||
options.lint = Some(LintOptions {
|
||||
common: lint_options,
|
||||
..LintOptions::default()
|
||||
});
|
||||
}
|
||||
|
||||
// Create the pyproject.toml.
|
||||
@@ -465,7 +468,9 @@ mod tests {
|
||||
use ruff_linter::rules::flake8_quotes;
|
||||
use ruff_linter::rules::pydocstyle::settings::Convention;
|
||||
use ruff_linter::settings::types::PythonVersion;
|
||||
use ruff_workspace::options::{Flake8QuotesOptions, LintOptions, Options, PydocstyleOptions};
|
||||
use ruff_workspace::options::{
|
||||
Flake8QuotesOptions, LintCommonOptions, LintOptions, Options, PydocstyleOptions,
|
||||
};
|
||||
use ruff_workspace::pyproject::Pyproject;
|
||||
|
||||
use crate::converter::DEFAULT_SELECTORS;
|
||||
@@ -475,8 +480,8 @@ mod tests {
|
||||
use super::super::plugin::Plugin;
|
||||
use super::convert;
|
||||
|
||||
fn lint_default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> LintOptions {
|
||||
LintOptions {
|
||||
fn lint_default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> LintCommonOptions {
|
||||
LintCommonOptions {
|
||||
ignore: Some(vec![]),
|
||||
select: Some(
|
||||
DEFAULT_SELECTORS
|
||||
@@ -486,7 +491,7 @@ mod tests {
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
),
|
||||
..LintOptions::default()
|
||||
..LintCommonOptions::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,7 +503,10 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(lint_default_options([])),
|
||||
lint: Some(LintOptions {
|
||||
common: lint_default_options([]),
|
||||
..LintOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -516,7 +524,10 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||
lint: Some(lint_default_options([])),
|
||||
lint: Some(LintOptions {
|
||||
common: lint_default_options([]),
|
||||
..LintOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -534,7 +545,10 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||
lint: Some(lint_default_options([])),
|
||||
lint: Some(LintOptions {
|
||||
common: lint_default_options([]),
|
||||
..LintOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -551,7 +565,10 @@ mod tests {
|
||||
Some(vec![]),
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(lint_default_options([])),
|
||||
lint: Some(LintOptions {
|
||||
common: lint_default_options([]),
|
||||
..LintOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
@@ -569,13 +586,16 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(LintOptions {
|
||||
flake8_quotes: Some(Flake8QuotesOptions {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..lint_default_options([])
|
||||
common: LintCommonOptions {
|
||||
flake8_quotes: Some(Flake8QuotesOptions {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..lint_default_options([])
|
||||
},
|
||||
..LintOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
});
|
||||
@@ -597,12 +617,15 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(LintOptions {
|
||||
pydocstyle: Some(PydocstyleOptions {
|
||||
convention: Some(Convention::Numpy),
|
||||
ignore_decorators: None,
|
||||
property_decorators: None,
|
||||
}),
|
||||
..lint_default_options([Linter::Pydocstyle.into()])
|
||||
common: LintCommonOptions {
|
||||
pydocstyle: Some(PydocstyleOptions {
|
||||
convention: Some(Convention::Numpy),
|
||||
ignore_decorators: None,
|
||||
property_decorators: None,
|
||||
}),
|
||||
..lint_default_options([Linter::Pydocstyle.into()])
|
||||
},
|
||||
..LintOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
});
|
||||
@@ -621,13 +644,16 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
lint: Some(LintOptions {
|
||||
flake8_quotes: Some(Flake8QuotesOptions {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..lint_default_options([Linter::Flake8Quotes.into()])
|
||||
common: LintCommonOptions {
|
||||
flake8_quotes: Some(Flake8QuotesOptions {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..lint_default_options([Linter::Flake8Quotes.into()])
|
||||
},
|
||||
..LintOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
});
|
||||
@@ -648,7 +674,10 @@ mod tests {
|
||||
);
|
||||
let expected = Pyproject::new(Options {
|
||||
target_version: Some(PythonVersion::Py38),
|
||||
lint: Some(lint_default_options([])),
|
||||
lint: Some(LintOptions {
|
||||
common: lint_default_options([]),
|
||||
..LintOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
@@ -366,6 +366,15 @@ pub struct FormatCommand {
|
||||
respect_gitignore: bool,
|
||||
#[clap(long, overrides_with("respect_gitignore"), hide = true)]
|
||||
no_respect_gitignore: bool,
|
||||
/// List of paths, used to omit files and/or directories from analysis.
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "FILE_PATTERN",
|
||||
help_heading = "File selection"
|
||||
)]
|
||||
pub exclude: Option<Vec<FilePattern>>,
|
||||
|
||||
/// Enforce exclusions, even for paths passed to Ruff directly on the command-line.
|
||||
/// Use `--no-force-exclude` to disable.
|
||||
#[arg(
|
||||
@@ -386,9 +395,9 @@ pub struct FormatCommand {
|
||||
#[arg(long, help_heading = "Miscellaneous")]
|
||||
pub stdin_filename: Option<PathBuf>,
|
||||
|
||||
/// Enable preview mode; checks will include unstable rules and fixes.
|
||||
/// Enable preview mode; enables unstable formatting.
|
||||
/// Use `--no-preview` to disable.
|
||||
#[arg(long, overrides_with("no_preview"), hide = true)]
|
||||
#[arg(long, overrides_with("no_preview"))]
|
||||
preview: bool,
|
||||
#[clap(long, overrides_with("preview"), hide = true)]
|
||||
no_preview: bool,
|
||||
@@ -522,6 +531,7 @@ impl FormatCommand {
|
||||
self.respect_gitignore,
|
||||
self.no_respect_gitignore,
|
||||
),
|
||||
exclude: self.exclude,
|
||||
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
|
||||
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
||||
// Unsupported on the formatter CLI, but required on `Overrides`.
|
||||
@@ -658,6 +668,8 @@ impl ConfigurationTransformer for CliOverrides {
|
||||
}
|
||||
if let Some(preview) = &self.preview {
|
||||
config.preview = Some(*preview);
|
||||
config.lint.preview = Some(*preview);
|
||||
config.format.preview = Some(*preview);
|
||||
}
|
||||
if let Some(per_file_ignores) = &self.per_file_ignores {
|
||||
config.lint.per_file_ignores = Some(collect_per_file_ignores(per_file_ignores.clone()));
|
||||
|
||||
@@ -10,7 +10,7 @@ use ruff_linter::linter::add_noqa_to_path;
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_linter::warn_user_once;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
||||
|
||||
use crate::args::CliOverrides;
|
||||
|
||||
@@ -36,7 +36,7 @@ pub(crate) fn add_noqa(
|
||||
&paths
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(ignore::DirEntry::path)
|
||||
.map(ResolvedFile::path)
|
||||
.collect::<Vec<_>>(),
|
||||
pyproject_config,
|
||||
);
|
||||
@@ -45,14 +45,15 @@ pub(crate) fn add_noqa(
|
||||
let modifications: usize = paths
|
||||
.par_iter()
|
||||
.flatten()
|
||||
.filter_map(|entry| {
|
||||
let path = entry.path();
|
||||
.filter_map(|resolved_file| {
|
||||
let SourceType::Python(source_type @ (PySourceType::Python | PySourceType::Stub)) =
|
||||
SourceType::from(path)
|
||||
SourceType::from(resolved_file.path())
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let package = path
|
||||
let path = resolved_file.path();
|
||||
let package = resolved_file
|
||||
.path()
|
||||
.parent()
|
||||
.and_then(|parent| package_roots.get(parent))
|
||||
.and_then(|package| *package);
|
||||
|
||||
@@ -22,7 +22,10 @@ use ruff_linter::{fs, warn_user_once, IOError};
|
||||
use ruff_python_ast::imports::ImportMap;
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, PyprojectDiscoveryStrategy};
|
||||
use ruff_workspace::resolver::{
|
||||
match_exclusion, python_files_in_path, PyprojectConfig, PyprojectDiscoveryStrategy,
|
||||
ResolvedFile,
|
||||
};
|
||||
|
||||
use crate::args::CliOverrides;
|
||||
use crate::cache::{self, Cache};
|
||||
@@ -42,8 +45,7 @@ pub(crate) fn check(
|
||||
// Collect all the Python files to check.
|
||||
let start = Instant::now();
|
||||
let (paths, resolver) = python_files_in_path(files, pyproject_config, overrides)?;
|
||||
let duration = start.elapsed();
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
debug!("Identified files to lint in: {:?}", start.elapsed());
|
||||
|
||||
if paths.is_empty() {
|
||||
warn_user_once!("No Python files found under the given path(s)");
|
||||
@@ -77,7 +79,7 @@ pub(crate) fn check(
|
||||
&paths
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(ignore::DirEntry::path)
|
||||
.map(ResolvedFile::path)
|
||||
.collect::<Vec<_>>(),
|
||||
pyproject_config,
|
||||
);
|
||||
@@ -98,95 +100,114 @@ pub(crate) fn check(
|
||||
});
|
||||
|
||||
let start = Instant::now();
|
||||
let mut diagnostics: Diagnostics = paths
|
||||
.par_iter()
|
||||
.map(|entry| {
|
||||
match entry {
|
||||
Ok(entry) => {
|
||||
let path = entry.path();
|
||||
let package = path
|
||||
.parent()
|
||||
.and_then(|parent| package_roots.get(parent))
|
||||
.and_then(|package| *package);
|
||||
let diagnostics_per_file = paths.par_iter().filter_map(|resolved_file| {
|
||||
let result = match resolved_file {
|
||||
Ok(resolved_file) => {
|
||||
let path = resolved_file.path();
|
||||
let package = path
|
||||
.parent()
|
||||
.and_then(|parent| package_roots.get(parent))
|
||||
.and_then(|package| *package);
|
||||
|
||||
let settings = resolver.resolve(path, pyproject_config);
|
||||
let settings = resolver.resolve(path, pyproject_config);
|
||||
|
||||
let cache_root = package.unwrap_or_else(|| path.parent().unwrap_or(path));
|
||||
let cache = caches.as_ref().and_then(|caches| {
|
||||
if let Some(cache) = caches.get(&cache_root) {
|
||||
Some(cache)
|
||||
} else {
|
||||
debug!("No cache found for {}", cache_root.display());
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
lint_path(
|
||||
path,
|
||||
package,
|
||||
&settings.linter,
|
||||
cache,
|
||||
noqa,
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
if !resolved_file.is_root()
|
||||
&& match_exclusion(
|
||||
resolved_file.path(),
|
||||
resolved_file.file_name(),
|
||||
&settings.linter.exclude,
|
||||
)
|
||||
.map_err(|e| {
|
||||
(Some(path.to_owned()), {
|
||||
let mut error = e.to_string();
|
||||
for cause in e.chain() {
|
||||
write!(&mut error, "\n Cause: {cause}").unwrap();
|
||||
}
|
||||
error
|
||||
})
|
||||
})
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Err(e) => Err((
|
||||
if let Error::WithPath { path, .. } = e {
|
||||
Some(path.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
e.io_error()
|
||||
.map_or_else(|| e.to_string(), io::Error::to_string),
|
||||
)),
|
||||
}
|
||||
.unwrap_or_else(|(path, message)| {
|
||||
if let Some(path) = &path {
|
||||
let settings = resolver.resolve(path, pyproject_config);
|
||||
if settings.linter.rules.enabled(Rule::IOError) {
|
||||
let dummy =
|
||||
SourceFileBuilder::new(path.to_string_lossy().as_ref(), "").finish();
|
||||
|
||||
Diagnostics::new(
|
||||
vec![Message::from_diagnostic(
|
||||
Diagnostic::new(IOError { message }, TextRange::default()),
|
||||
dummy,
|
||||
TextSize::default(),
|
||||
)],
|
||||
ImportMap::default(),
|
||||
FxHashMap::default(),
|
||||
)
|
||||
let cache_root = package.unwrap_or_else(|| path.parent().unwrap_or(path));
|
||||
let cache = caches.as_ref().and_then(|caches| {
|
||||
if let Some(cache) = caches.get(&cache_root) {
|
||||
Some(cache)
|
||||
} else {
|
||||
warn!(
|
||||
"{}{}{} {message}",
|
||||
"Failed to lint ".bold(),
|
||||
fs::relativize_path(path).bold(),
|
||||
":".bold()
|
||||
);
|
||||
Diagnostics::default()
|
||||
debug!("No cache found for {}", cache_root.display());
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
lint_path(
|
||||
path,
|
||||
package,
|
||||
&settings.linter,
|
||||
cache,
|
||||
noqa,
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
)
|
||||
.map_err(|e| {
|
||||
(Some(path.to_path_buf()), {
|
||||
let mut error = e.to_string();
|
||||
for cause in e.chain() {
|
||||
write!(&mut error, "\n Cause: {cause}").unwrap();
|
||||
}
|
||||
error
|
||||
})
|
||||
})
|
||||
}
|
||||
Err(e) => Err((
|
||||
if let Error::WithPath { path, .. } = e {
|
||||
Some(path.clone())
|
||||
} else {
|
||||
warn!("{} {message}", "Encountered error:".bold());
|
||||
None
|
||||
},
|
||||
e.io_error()
|
||||
.map_or_else(|| e.to_string(), io::Error::to_string),
|
||||
)),
|
||||
};
|
||||
|
||||
Some(result.unwrap_or_else(|(path, message)| {
|
||||
if let Some(path) = &path {
|
||||
let settings = resolver.resolve(path, pyproject_config);
|
||||
if settings.linter.rules.enabled(Rule::IOError) {
|
||||
let dummy =
|
||||
SourceFileBuilder::new(path.to_string_lossy().as_ref(), "").finish();
|
||||
|
||||
Diagnostics::new(
|
||||
vec![Message::from_diagnostic(
|
||||
Diagnostic::new(IOError { message }, TextRange::default()),
|
||||
dummy,
|
||||
TextSize::default(),
|
||||
)],
|
||||
ImportMap::default(),
|
||||
FxHashMap::default(),
|
||||
)
|
||||
} else {
|
||||
warn!(
|
||||
"{}{}{} {message}",
|
||||
"Failed to lint ".bold(),
|
||||
fs::relativize_path(path).bold(),
|
||||
":".bold()
|
||||
);
|
||||
Diagnostics::default()
|
||||
}
|
||||
})
|
||||
})
|
||||
.reduce(Diagnostics::default, |mut acc, item| {
|
||||
acc += item;
|
||||
acc
|
||||
});
|
||||
} else {
|
||||
warn!("{} {message}", "Encountered error:".bold());
|
||||
Diagnostics::default()
|
||||
}
|
||||
}))
|
||||
});
|
||||
|
||||
diagnostics.messages.sort();
|
||||
// Aggregate the diagnostics of all checked files and count the checked files.
|
||||
// This can't be a regular for loop because we use `par_iter`.
|
||||
let (mut all_diagnostics, checked_files) = diagnostics_per_file
|
||||
.fold(
|
||||
|| (Diagnostics::default(), 0u64),
|
||||
|(all_diagnostics, checked_files), file_diagnostics| {
|
||||
(all_diagnostics + file_diagnostics, checked_files + 1)
|
||||
},
|
||||
)
|
||||
.reduce(
|
||||
|| (Diagnostics::default(), 0u64),
|
||||
|a, b| (a.0 + b.0, a.1 + b.1),
|
||||
);
|
||||
|
||||
all_diagnostics.messages.sort();
|
||||
|
||||
// Store the caches.
|
||||
if let Some(caches) = caches {
|
||||
@@ -196,9 +217,9 @@ pub(crate) fn check(
|
||||
}
|
||||
|
||||
let duration = start.elapsed();
|
||||
debug!("Checked {:?} files in: {:?}", paths.len(), duration);
|
||||
debug!("Checked {:?} files in: {:?}", checked_files, duration);
|
||||
|
||||
Ok(diagnostics)
|
||||
Ok(all_diagnostics)
|
||||
}
|
||||
|
||||
/// Wraps [`lint_path`](crate::diagnostics::lint_path) in a [`catch_unwind`](std::panic::catch_unwind) and emits
|
||||
|
||||
@@ -4,7 +4,7 @@ use anyhow::Result;
|
||||
|
||||
use ruff_linter::packaging;
|
||||
use ruff_linter::settings::flags;
|
||||
use ruff_workspace::resolver::{python_file_at_path, PyprojectConfig};
|
||||
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, PyprojectConfig};
|
||||
|
||||
use crate::args::CliOverrides;
|
||||
use crate::diagnostics::{lint_stdin, Diagnostics};
|
||||
@@ -22,6 +22,14 @@ pub(crate) fn check_stdin(
|
||||
if !python_file_at_path(filename, pyproject_config, overrides)? {
|
||||
return Ok(Diagnostics::default());
|
||||
}
|
||||
|
||||
let lint_settings = &pyproject_config.settings.linter;
|
||||
if filename
|
||||
.file_name()
|
||||
.is_some_and(|name| match_exclusion(filename, name, &lint_settings.exclude))
|
||||
{
|
||||
return Ok(Diagnostics::default());
|
||||
}
|
||||
}
|
||||
let package_root = filename.and_then(Path::parent).and_then(|path| {
|
||||
packaging::detect_package_root(path, &pyproject_config.settings.linter.namespace_packages)
|
||||
|
||||
@@ -20,7 +20,7 @@ use ruff_linter::warn_user_once;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_python_formatter::{format_module_source, FormatModuleError};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use ruff_workspace::resolver::python_files_in_path;
|
||||
use ruff_workspace::resolver::{match_exclusion, python_files_in_path};
|
||||
use ruff_workspace::FormatterSettings;
|
||||
|
||||
use crate::args::{CliOverrides, FormatArguments};
|
||||
@@ -61,26 +61,42 @@ pub(crate) fn format(
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let (results, errors): (Vec<_>, Vec<_>) = paths
|
||||
let (mut results, errors): (Vec<_>, Vec<_>) = paths
|
||||
.into_par_iter()
|
||||
.filter_map(|entry| {
|
||||
match entry {
|
||||
Ok(entry) => {
|
||||
let path = entry.into_path();
|
||||
|
||||
Ok(resolved_file) => {
|
||||
let path = resolved_file.path();
|
||||
let SourceType::Python(source_type) = SourceType::from(&path) else {
|
||||
// Ignore any non-Python files.
|
||||
return None;
|
||||
};
|
||||
|
||||
let resolved_settings = resolver.resolve(&path, &pyproject_config);
|
||||
let resolved_settings = resolver.resolve(path, &pyproject_config);
|
||||
|
||||
// Ignore files that are excluded from formatting
|
||||
if !resolved_file.is_root()
|
||||
&& match_exclusion(
|
||||
path,
|
||||
resolved_file.file_name(),
|
||||
&resolved_settings.formatter.exclude,
|
||||
)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(
|
||||
match catch_unwind(|| {
|
||||
format_path(&path, &resolved_settings.formatter, source_type, mode)
|
||||
format_path(path, &resolved_settings.formatter, source_type, mode)
|
||||
}) {
|
||||
Ok(inner) => inner.map(|result| FormatPathResult { path, result }),
|
||||
Err(error) => Err(FormatCommandError::Panic(Some(path), error)),
|
||||
Ok(inner) => inner.map(|result| FormatPathResult {
|
||||
path: resolved_file.into_path(),
|
||||
result,
|
||||
}),
|
||||
Err(error) => Err(FormatCommandError::Panic(
|
||||
Some(resolved_file.into_path()),
|
||||
error,
|
||||
)),
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -104,6 +120,8 @@ pub(crate) fn format(
|
||||
error!("{error}");
|
||||
}
|
||||
|
||||
results.sort_unstable_by(|a, b| a.path.cmp(&b.path));
|
||||
|
||||
let summary = FormatSummary::new(results.as_slice(), mode);
|
||||
|
||||
// Report on the formatting changes.
|
||||
@@ -137,7 +155,7 @@ pub(crate) fn format(
|
||||
}
|
||||
|
||||
/// Format the file at the given [`Path`].
|
||||
#[tracing::instrument(skip_all, fields(path = %path.display()))]
|
||||
#[tracing::instrument(level="debug", skip_all, fields(path = %path.display()))]
|
||||
fn format_path(
|
||||
path: &Path,
|
||||
settings: &FormatterSettings,
|
||||
|
||||
@@ -6,7 +6,7 @@ use log::error;
|
||||
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_workspace::resolver::python_file_at_path;
|
||||
use ruff_workspace::resolver::{match_exclusion, python_file_at_path};
|
||||
use ruff_workspace::FormatterSettings;
|
||||
|
||||
use crate::args::{CliOverrides, FormatArguments};
|
||||
@@ -33,6 +33,14 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||
if !python_file_at_path(filename, &pyproject_config, overrides)? {
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
|
||||
let format_settings = &pyproject_config.settings.formatter;
|
||||
if filename
|
||||
.file_name()
|
||||
.is_some_and(|name| match_exclusion(filename, name, &format_settings.exclude))
|
||||
{
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
}
|
||||
|
||||
let path = cli.stdin_filename.as_deref();
|
||||
|
||||
@@ -5,7 +5,7 @@ use anyhow::Result;
|
||||
use itertools::Itertools;
|
||||
|
||||
use ruff_linter::warn_user_once;
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
||||
|
||||
use crate::args::CliOverrides;
|
||||
|
||||
@@ -25,12 +25,13 @@ pub(crate) fn show_files(
|
||||
}
|
||||
|
||||
// Print the list of files.
|
||||
for entry in paths
|
||||
.iter()
|
||||
for path in paths
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.sorted_by(|a, b| a.path().cmp(b.path()))
|
||||
.map(ResolvedFile::into_path)
|
||||
.sorted_unstable()
|
||||
{
|
||||
writeln!(writer, "{}", entry.path().to_string_lossy())?;
|
||||
writeln!(writer, "{}", path.to_string_lossy())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::path::PathBuf;
|
||||
use anyhow::{bail, Result};
|
||||
use itertools::Itertools;
|
||||
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
||||
|
||||
use crate::args::CliOverrides;
|
||||
|
||||
@@ -19,16 +19,17 @@ pub(crate) fn show_settings(
|
||||
let (paths, resolver) = python_files_in_path(files, pyproject_config, overrides)?;
|
||||
|
||||
// Print the list of files.
|
||||
let Some(entry) = paths
|
||||
.iter()
|
||||
let Some(path) = paths
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.sorted_by(|a, b| a.path().cmp(b.path()))
|
||||
.map(ResolvedFile::into_path)
|
||||
.sorted_unstable()
|
||||
.next()
|
||||
else {
|
||||
bail!("No files found under the given path");
|
||||
};
|
||||
let path = entry.path();
|
||||
let settings = resolver.resolve(path, pyproject_config);
|
||||
|
||||
let settings = resolver.resolve(&path, pyproject_config);
|
||||
|
||||
writeln!(writer, "Resolved settings for: {path:?}")?;
|
||||
if let Some(settings_path) = pyproject_config.path.as_ref() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::ops::AddAssign;
|
||||
use std::ops::{Add, AddAssign};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
@@ -11,7 +11,6 @@ use anyhow::{Context, Result};
|
||||
use colored::Colorize;
|
||||
use filetime::FileTime;
|
||||
use log::{debug, error, warn};
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
@@ -20,6 +19,7 @@ use ruff_linter::logging::DisplayParseError;
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::pyproject_toml::lint_pyproject_toml;
|
||||
use ruff_linter::registry::AsRule;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use ruff_linter::settings::{flags, LinterSettings};
|
||||
use ruff_linter::source_kind::{SourceError, SourceKind};
|
||||
use ruff_linter::{fs, IOError, SyntaxError};
|
||||
@@ -61,7 +61,7 @@ impl FileCacheKey {
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub(crate) struct Diagnostics {
|
||||
pub(crate) messages: Vec<Message>,
|
||||
pub(crate) fixed: FxHashMap<String, FixTable>,
|
||||
pub(crate) fixed: FixMap,
|
||||
pub(crate) imports: ImportMap,
|
||||
pub(crate) notebook_indexes: FxHashMap<String, NotebookIndex>,
|
||||
}
|
||||
@@ -74,7 +74,7 @@ impl Diagnostics {
|
||||
) -> Self {
|
||||
Self {
|
||||
messages,
|
||||
fixed: FxHashMap::default(),
|
||||
fixed: FixMap::default(),
|
||||
imports,
|
||||
notebook_indexes,
|
||||
}
|
||||
@@ -142,22 +142,68 @@ impl Diagnostics {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Diagnostics {
|
||||
type Output = Diagnostics;
|
||||
|
||||
fn add(mut self, other: Self) -> Self::Output {
|
||||
self += other;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Diagnostics {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
self.messages.extend(other.messages);
|
||||
self.imports.extend(other.imports);
|
||||
for (filename, fixed) in other.fixed {
|
||||
self.fixed += other.fixed;
|
||||
self.notebook_indexes.extend(other.notebook_indexes);
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of fixes indexed by file path.
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub(crate) struct FixMap(FxHashMap<String, FixTable>);
|
||||
|
||||
impl FixMap {
|
||||
/// Returns `true` if there are no fixes in the map.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Returns an iterator over the fixes in the map, along with the file path.
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = (&String, &FixTable)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Returns an iterator over the fixes in the map.
|
||||
pub(crate) fn values(&self) -> impl Iterator<Item = &FixTable> {
|
||||
self.0.values()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(String, FixTable)> for FixMap {
|
||||
fn from_iter<T: IntoIterator<Item = (String, FixTable)>>(iter: T) -> Self {
|
||||
Self(
|
||||
iter.into_iter()
|
||||
.filter(|(_, fixes)| !fixes.is_empty())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for FixMap {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
for (filename, fixed) in rhs.0 {
|
||||
if fixed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let fixed_in_file = self.fixed.entry(filename).or_default();
|
||||
let fixed_in_file = self.0.entry(filename).or_default();
|
||||
for (rule, count) in fixed {
|
||||
if count > 0 {
|
||||
*fixed_in_file.entry(rule).or_default() += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.notebook_indexes.extend(other.notebook_indexes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +364,7 @@ pub(crate) fn lint_path(
|
||||
|
||||
Ok(Diagnostics {
|
||||
messages,
|
||||
fixed: FxHashMap::from_iter([(fs::relativize_path(path), fixed)]),
|
||||
fixed: FixMap::from_iter([(fs::relativize_path(path), fixed)]),
|
||||
imports,
|
||||
notebook_indexes,
|
||||
})
|
||||
@@ -436,7 +482,7 @@ pub(crate) fn lint_stdin(
|
||||
|
||||
Ok(Diagnostics {
|
||||
messages,
|
||||
fixed: FxHashMap::from_iter([(
|
||||
fixed: FixMap::from_iter([(
|
||||
fs::relativize_path(path.unwrap_or_else(|| Path::new("-"))),
|
||||
fixed,
|
||||
)]),
|
||||
|
||||
@@ -7,11 +7,9 @@ use anyhow::Result;
|
||||
use bitflags::bitflags;
|
||||
use colored::Colorize;
|
||||
use itertools::{iterate, Itertools};
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::Serialize;
|
||||
|
||||
use ruff_linter::fs::relativize_path;
|
||||
use ruff_linter::linter::FixTable;
|
||||
use ruff_linter::logging::LogLevel;
|
||||
use ruff_linter::message::{
|
||||
AzureEmitter, Emitter, EmitterContext, GithubEmitter, GitlabEmitter, GroupedEmitter,
|
||||
@@ -22,7 +20,7 @@ use ruff_linter::registry::{AsRule, Rule};
|
||||
use ruff_linter::settings::flags::{self};
|
||||
use ruff_linter::settings::types::{SerializationFormat, UnsafeFixes};
|
||||
|
||||
use crate::diagnostics::Diagnostics;
|
||||
use crate::diagnostics::{Diagnostics, FixMap};
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
@@ -462,7 +460,7 @@ fn show_fix_status(fix_mode: flags::FixMode, fixables: Option<&FixableStatistics
|
||||
(!fix_mode.is_apply()) && fixables.is_some_and(FixableStatistics::any_applicable_fixes)
|
||||
}
|
||||
|
||||
fn print_fix_summary(writer: &mut dyn Write, fixed: &FxHashMap<String, FixTable>) -> Result<()> {
|
||||
fn print_fix_summary(writer: &mut dyn Write, fixed: &FixMap) -> Result<()> {
|
||||
let total = fixed
|
||||
.values()
|
||||
.map(|table| table.values().sum::<usize>())
|
||||
|
||||
@@ -13,7 +13,7 @@ const BIN_NAME: &str = "ruff";
|
||||
#[test]
|
||||
fn default_options() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["format", "--isolated"])
|
||||
.args(["format", "--isolated", "--stdin-filename", "test.py"])
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
def foo(arg1, arg2,):
|
||||
@@ -50,6 +50,9 @@ fn format_options() -> Result<()> {
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
tab-size = 8
|
||||
line-length = 84
|
||||
|
||||
[format]
|
||||
indent-style = "tab"
|
||||
quote-style = "single"
|
||||
@@ -64,7 +67,7 @@ line-ending = "cr-lf"
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
def foo(arg1, arg2,):
|
||||
print("Shouldn't change quotes")
|
||||
print("Shouldn't change quotes. It exceeds the line width with the tab size 8")
|
||||
|
||||
|
||||
if condition:
|
||||
@@ -76,7 +79,9 @@ if condition:
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
def foo(arg1, arg2):
|
||||
print("Shouldn't change quotes")
|
||||
print(
|
||||
"Shouldn't change quotes. It exceeds the line width with the tab size 8"
|
||||
)
|
||||
|
||||
|
||||
if condition:
|
||||
@@ -88,6 +93,108 @@ if condition:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclude() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
extend-exclude = ["out"]
|
||||
|
||||
[format]
|
||||
exclude = ["test.py", "generated.py"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
fs::write(
|
||||
tempdir.path().join("main.py"),
|
||||
r#"
|
||||
from test import say_hy
|
||||
|
||||
if __name__ == "__main__":
|
||||
say_hy("dear Ruff contributor")
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Excluded file but passed to the CLI directly, should be formatted
|
||||
let test_path = tempdir.path().join("test.py");
|
||||
fs::write(
|
||||
&test_path,
|
||||
r#"
|
||||
def say_hy(name: str):
|
||||
print(f"Hy {name}")"#,
|
||||
)?;
|
||||
|
||||
fs::write(
|
||||
tempdir.path().join("generated.py"),
|
||||
r#"NUMBERS = [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19
|
||||
]
|
||||
OTHER = "OTHER"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let out_dir = tempdir.path().join("out");
|
||||
fs::create_dir(&out_dir)?;
|
||||
|
||||
fs::write(out_dir.join("a.py"), "a = a")?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.current_dir(tempdir.path())
|
||||
.args(["format", "--check", "--config"])
|
||||
.arg(ruff_toml.file_name().unwrap())
|
||||
// Explicitly pass test.py, should be formatted regardless of it being excluded by format.exclude
|
||||
.arg(test_path.file_name().unwrap())
|
||||
// Format all other files in the directory, should respect the `exclude` and `format.exclude` options
|
||||
.arg("."), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
Would reformat: main.py
|
||||
Would reformat: test.py
|
||||
2 files would be reformatted
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclude_stdin() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
extend-select = ["B", "Q"]
|
||||
|
||||
[format]
|
||||
exclude = ["generated.py"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.current_dir(tempdir.path())
|
||||
.args(["format", "--config", &ruff_toml.file_name().unwrap().to_string_lossy(), "--stdin-filename", "generated.py", "-"])
|
||||
.pass_stdin(r#"
|
||||
from test import say_hy
|
||||
|
||||
if __name__ == '__main__':
|
||||
say_hy("dear Ruff contributor")
|
||||
"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_option_inheritance() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
|
||||
@@ -1252,8 +1252,8 @@ fn diff_does_not_show_display_only_fixes_with_unsafe_fixes_enabled() {
|
||||
])
|
||||
.pass_stdin("def add_to_list(item, some_list=[]): ..."),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
@@ -1276,8 +1276,8 @@ fn diff_only_unsafe_fixes_available() {
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
|
||||
@@ -31,14 +31,15 @@ inline-quotes = "single"
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(&ruff_toml)
|
||||
.args(["--stdin-filename", "test.py"])
|
||||
.arg("-")
|
||||
.pass_stdin(r#"a = "abcba".strip("aba")"#), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||
-:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||
test.py:1:5: Q000 [*] Double quotes found but single quotes preferred
|
||||
test.py:1:5: B005 Using `.strip()` with multi-character strings is misleading
|
||||
test.py:1:19: Q000 [*] Double quotes found but single quotes preferred
|
||||
Found 3 errors.
|
||||
[*] 2 fixable with the `--fix` option.
|
||||
|
||||
@@ -155,3 +156,117 @@ inline-quotes = "single"
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclude() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
extend-select = ["B", "Q"]
|
||||
extend-exclude = ["out"]
|
||||
|
||||
[lint]
|
||||
exclude = ["test.py", "generated.py"]
|
||||
|
||||
[lint.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
fs::write(
|
||||
tempdir.path().join("main.py"),
|
||||
r#"
|
||||
from test import say_hy
|
||||
|
||||
if __name__ == "__main__":
|
||||
say_hy("dear Ruff contributor")
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Excluded file but passed to the CLI directly, should be linted
|
||||
let test_path = tempdir.path().join("test.py");
|
||||
fs::write(
|
||||
&test_path,
|
||||
r#"
|
||||
def say_hy(name: str):
|
||||
print(f"Hy {name}")"#,
|
||||
)?;
|
||||
|
||||
fs::write(
|
||||
tempdir.path().join("generated.py"),
|
||||
r#"NUMBERS = [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19
|
||||
]
|
||||
OTHER = "OTHER"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let out_dir = tempdir.path().join("out");
|
||||
fs::create_dir(&out_dir)?;
|
||||
|
||||
fs::write(out_dir.join("a.py"), r#"a = "a""#)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.current_dir(tempdir.path())
|
||||
.arg("check")
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(["--config", &ruff_toml.file_name().unwrap().to_string_lossy()])
|
||||
// Explicitly pass test.py, should be linted regardless of it being excluded by lint.exclude
|
||||
.arg(test_path.file_name().unwrap())
|
||||
// Lint all other files in the directory, should respect the `exclude` and `lint.exclude` options
|
||||
.arg("."), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
main.py:4:16: Q000 [*] Double quotes found but single quotes preferred
|
||||
main.py:5:12: Q000 [*] Double quotes found but single quotes preferred
|
||||
test.py:3:15: Q000 [*] Double quotes found but single quotes preferred
|
||||
Found 3 errors.
|
||||
[*] 3 fixable with the `--fix` option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclude_stdin() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
extend-select = ["B", "Q"]
|
||||
|
||||
[lint]
|
||||
exclude = ["generated.py"]
|
||||
|
||||
[lint.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.current_dir(tempdir.path())
|
||||
.arg("check")
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(["--config", &ruff_toml.file_name().unwrap().to_string_lossy()])
|
||||
.args(["--stdin-filename", "generated.py"])
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
from test import say_hy
|
||||
|
||||
if __name__ == "__main__":
|
||||
say_hy("dear Ruff contributor")
|
||||
"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use std::{fmt, fs, io, iter};
|
||||
|
||||
use anyhow::{bail, format_err, Context, Error};
|
||||
use clap::{CommandFactory, FromArgMatches};
|
||||
use ignore::DirEntry;
|
||||
use imara_diff::intern::InternedInput;
|
||||
use imara_diff::sink::Counter;
|
||||
use imara_diff::{diff, Algorithm};
|
||||
@@ -36,14 +35,14 @@ use ruff_linter::settings::types::{FilePattern, FilePatternSet};
|
||||
use ruff_python_formatter::{
|
||||
format_module_source, FormatModuleError, MagicTrailingComma, PyFormatOptions,
|
||||
};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, Resolver};
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile, Resolver};
|
||||
|
||||
/// Find files that ruff would check so we can format them. Adapted from `ruff_cli`.
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn ruff_check_paths(
|
||||
dirs: &[PathBuf],
|
||||
) -> anyhow::Result<(
|
||||
Vec<Result<DirEntry, ignore::Error>>,
|
||||
Vec<Result<ResolvedFile, ignore::Error>>,
|
||||
Resolver,
|
||||
PyprojectConfig,
|
||||
)> {
|
||||
@@ -467,9 +466,9 @@ fn format_dev_project(
|
||||
let iter = { paths.into_par_iter() };
|
||||
#[cfg(feature = "singlethreaded")]
|
||||
let iter = { paths.into_iter() };
|
||||
iter.map(|dir_entry| {
|
||||
iter.map(|path| {
|
||||
let result = format_dir_entry(
|
||||
dir_entry,
|
||||
path,
|
||||
stability_check,
|
||||
write,
|
||||
&black_options,
|
||||
@@ -527,24 +526,20 @@ fn format_dev_project(
|
||||
|
||||
/// Error handling in between walkdir and `format_dev_file`
|
||||
fn format_dir_entry(
|
||||
dir_entry: Result<DirEntry, ignore::Error>,
|
||||
resolved_file: Result<ResolvedFile, ignore::Error>,
|
||||
stability_check: bool,
|
||||
write: bool,
|
||||
options: &BlackOptions,
|
||||
resolver: &Resolver,
|
||||
pyproject_config: &PyprojectConfig,
|
||||
) -> anyhow::Result<(Result<Statistics, CheckFileError>, PathBuf), Error> {
|
||||
let dir_entry = match dir_entry.context("Iterating the files in the repository failed") {
|
||||
Ok(dir_entry) => dir_entry,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let file = dir_entry.path().to_path_buf();
|
||||
let resolved_file = resolved_file.context("Iterating the files in the repository failed")?;
|
||||
// For some reason it does not filter in the beginning
|
||||
if dir_entry.file_name() == "pyproject.toml" {
|
||||
return Ok((Ok(Statistics::default()), file));
|
||||
if resolved_file.file_name() == "pyproject.toml" {
|
||||
return Ok((Ok(Statistics::default()), resolved_file.into_path()));
|
||||
}
|
||||
|
||||
let path = dir_entry.path().to_path_buf();
|
||||
let path = resolved_file.into_path();
|
||||
let mut options = options.to_py_format_options(&path);
|
||||
|
||||
let settings = resolver.resolve(&path, pyproject_config);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`.
|
||||
//!
|
||||
//! Used for <https://docs.astral.sh/ruff/settings/>.
|
||||
use itertools::Itertools;
|
||||
use std::fmt::Write;
|
||||
|
||||
use ruff_workspace::options::Options;
|
||||
@@ -107,6 +108,24 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set:
|
||||
output.push('\n');
|
||||
output.push_str(&format!("**Type**: `{}`\n", field.value_type));
|
||||
output.push('\n');
|
||||
|
||||
if !field.aliases.is_empty() {
|
||||
let title = if field.aliases.len() == 1 {
|
||||
"Alias"
|
||||
} else {
|
||||
"Aliases"
|
||||
};
|
||||
output.push_str(&format!(
|
||||
"**{title}**: {}\n",
|
||||
field
|
||||
.aliases
|
||||
.iter()
|
||||
.map(|alias| format!("`{alias}`"))
|
||||
.join(", ")
|
||||
));
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
output.push_str(&format!(
|
||||
"**Example usage**:\n\n```toml\n[tool.ruff{}]\n{}\n```\n",
|
||||
if let Some(set_name) = parent_set.name() {
|
||||
|
||||
@@ -95,7 +95,7 @@ impl std::fmt::Display for IndentStyle {
|
||||
///
|
||||
/// Determines the visual width of a tab character (`\t`) and the number of
|
||||
/// spaces per indent when using [`IndentStyle::Space`].
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, CacheKey)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct IndentWidth(NonZeroU8);
|
||||
|
||||
@@ -10,6 +10,10 @@ def double_quotes_backslash_uppercase():
|
||||
R"""Sum\\mary."""
|
||||
|
||||
|
||||
def shouldnt_add_raw_here():
|
||||
"Ruff \U000026a1"
|
||||
|
||||
|
||||
def make_unique_pod_id(pod_id: str) -> str | None:
|
||||
r"""
|
||||
Generate a unique Pod name.
|
||||
|
||||
17
crates/ruff_linter/resources/test/fixtures/pyflakes/F401_19.py
vendored
Normal file
17
crates/ruff_linter/resources/test/fixtures/pyflakes/F401_19.py
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
"""Test that type parameters are considered used."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
from .foo import Record as Record1
|
||||
from .bar import Record as Record2
|
||||
|
||||
type RecordCallback[R: Record1] = Callable[[R], None]
|
||||
|
||||
|
||||
def process_record[R: Record2](record: R) -> None:
|
||||
...
|
||||
5
crates/ruff_linter/resources/test/fixtures/pyflakes/F821_20.py
vendored
Normal file
5
crates/ruff_linter/resources/test/fixtures/pyflakes/F821_20.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Test lazy evaluation of type alias values."""
|
||||
|
||||
type RecordCallback[R: Record] = Callable[[R], None]
|
||||
|
||||
from collections.abc import Callable
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::{Expr, TypeParam};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_python_semantic::{ScopeId, Snapshot};
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
@@ -10,7 +10,7 @@ pub(crate) struct Deferred<'a> {
|
||||
pub(crate) scopes: Vec<ScopeId>,
|
||||
pub(crate) string_type_definitions: Vec<(TextRange, &'a str, Snapshot)>,
|
||||
pub(crate) future_type_definitions: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) type_param_definitions: Vec<(&'a TypeParam, Snapshot)>,
|
||||
pub(crate) type_param_definitions: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) functions: Vec<Snapshot>,
|
||||
pub(crate) lambdas: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) for_loops: Vec<Snapshot>,
|
||||
|
||||
@@ -582,9 +582,9 @@ where
|
||||
if let Some(type_params) = type_params {
|
||||
self.visit_type_params(type_params);
|
||||
}
|
||||
// The value in a `type` alias has annotation semantics, in that it's never
|
||||
// evaluated at runtime.
|
||||
self.visit_annotation(value);
|
||||
self.deferred
|
||||
.type_param_definitions
|
||||
.push((value, self.semantic.snapshot()));
|
||||
self.semantic.pop_scope();
|
||||
self.visit_expr(name);
|
||||
}
|
||||
@@ -1389,9 +1389,14 @@ where
|
||||
}
|
||||
}
|
||||
// Step 2: Traversal
|
||||
self.deferred
|
||||
.type_param_definitions
|
||||
.push((type_param, self.semantic.snapshot()));
|
||||
if let ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
|
||||
bound: Some(bound), ..
|
||||
}) = type_param
|
||||
{
|
||||
self.deferred
|
||||
.type_param_definitions
|
||||
.push((bound, self.semantic.snapshot()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1766,12 +1771,9 @@ impl<'a> Checker<'a> {
|
||||
for (type_param, snapshot) in type_params {
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if let ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
|
||||
bound: Some(bound), ..
|
||||
}) = type_param
|
||||
{
|
||||
self.visit_annotation(bound);
|
||||
}
|
||||
self.semantic.flags |=
|
||||
SemanticModelFlags::TYPE_PARAM_DEFINITION | SemanticModelFlags::TYPE_DEFINITION;
|
||||
self.visit_expr(type_param);
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Stmt};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
|
||||
@@ -253,3 +253,9 @@ impl From<NonZeroU8> for TabSize {
|
||||
Self(tab_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TabSize> for NonZeroU8 {
|
||||
fn from(value: TabSize) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ use crate::checkers::ast::Checker;
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// "text.txt".strip(".txt") # "ex"
|
||||
/// "text.txt".strip(".txt") # "e"
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
|
||||
@@ -5,8 +5,8 @@ use rustc_hash::FxHashMap;
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::AstNode;
|
||||
use ruff_python_ast::{self as ast, Arguments, Constant, Decorator, Expr, ExprContext};
|
||||
use ruff_python_codegen::Generator;
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
|
||||
@@ -6,7 +6,7 @@ use log::error;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{self as ast, whitespace, Constant, ElifElseClause, Expr, Stmt};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{self as ast, Arguments, CmpOp, Comprehension, Expr};
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use memchr::memchr_iter;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
@@ -46,18 +46,21 @@ use crate::docstrings::Docstring;
|
||||
#[violation]
|
||||
pub struct EscapeSequenceInDocstring;
|
||||
|
||||
impl Violation for EscapeSequenceInDocstring {
|
||||
impl AlwaysFixableViolation for EscapeSequenceInDocstring {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!(r#"Use `r"""` if any backslashes in a docstring"#)
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
format!(r#"Add `r` prefix"#)
|
||||
}
|
||||
}
|
||||
|
||||
/// D301
|
||||
pub(crate) fn backslashes(checker: &mut Checker, docstring: &Docstring) {
|
||||
// Docstring is already raw.
|
||||
let contents = docstring.contents;
|
||||
if contents.starts_with('r') || contents.starts_with("ur") {
|
||||
if docstring.leading_quote().contains(['r', 'R']) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,11 +70,15 @@ pub(crate) fn backslashes(checker: &mut Checker, docstring: &Docstring) {
|
||||
if memchr_iter(b'\\', bytes).any(|position| {
|
||||
let escaped_char = bytes.get(position.saturating_add(1));
|
||||
// Allow continuations (backslashes followed by newlines) and Unicode escapes.
|
||||
!matches!(escaped_char, Some(b'\r' | b'\n' | b'u' | b'N'))
|
||||
!matches!(escaped_char, Some(b'\r' | b'\n' | b'u' | b'U' | b'N'))
|
||||
}) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
EscapeSequenceInDocstring,
|
||||
let mut diagnostic = Diagnostic::new(EscapeSequenceInDocstring, docstring.range());
|
||||
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"r".to_owned() + docstring.contents,
|
||||
docstring.range(),
|
||||
));
|
||||
)));
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,23 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
|
||||
---
|
||||
D.py:328:5: D301 Use `r"""` if any backslashes in a docstring
|
||||
|
|
||||
326 | @expect('D301: Use r""" if any backslashes in a docstring')
|
||||
327 | def single_quotes_raw_uppercase_backslash():
|
||||
328 | R'Sum\mary.'
|
||||
| ^^^^^^^^^^^^ D301
|
||||
|
|
||||
|
||||
D.py:333:5: D301 Use `r"""` if any backslashes in a docstring
|
||||
D.py:333:5: D301 [*] Use `r"""` if any backslashes in a docstring
|
||||
|
|
||||
331 | @expect('D301: Use r""" if any backslashes in a docstring')
|
||||
332 | def double_quotes_backslash():
|
||||
333 | """Sum\\mary."""
|
||||
| ^^^^^^^^^^^^^^^^ D301
|
||||
|
|
||||
= help: Add `r` prefix
|
||||
|
||||
D.py:338:5: D301 Use `r"""` if any backslashes in a docstring
|
||||
|
|
||||
336 | @expect('D301: Use r""" if any backslashes in a docstring')
|
||||
337 | def double_quotes_backslash_uppercase():
|
||||
338 | R"""Sum\\mary."""
|
||||
| ^^^^^^^^^^^^^^^^^ D301
|
||||
|
|
||||
ℹ Suggested fix
|
||||
330 330 |
|
||||
331 331 | @expect('D301: Use r""" if any backslashes in a docstring')
|
||||
332 332 | def double_quotes_backslash():
|
||||
333 |- """Sum\\mary."""
|
||||
333 |+ r"""Sum\\mary."""
|
||||
334 334 |
|
||||
335 335 |
|
||||
336 336 | @expect('D301: Use r""" if any backslashes in a docstring')
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
|
||||
---
|
||||
D301.py:2:5: D301 Use `r"""` if any backslashes in a docstring
|
||||
D301.py:2:5: D301 [*] Use `r"""` if any backslashes in a docstring
|
||||
|
|
||||
1 | def double_quotes_backslash():
|
||||
2 | """Sum\\mary."""
|
||||
| ^^^^^^^^^^^^^^^^ D301
|
||||
|
|
||||
= help: Add `r` prefix
|
||||
|
||||
D301.py:10:5: D301 Use `r"""` if any backslashes in a docstring
|
||||
|
|
||||
9 | def double_quotes_backslash_uppercase():
|
||||
10 | R"""Sum\\mary."""
|
||||
| ^^^^^^^^^^^^^^^^^ D301
|
||||
|
|
||||
ℹ Suggested fix
|
||||
1 1 | def double_quotes_backslash():
|
||||
2 |- """Sum\\mary."""
|
||||
2 |+ r"""Sum\\mary."""
|
||||
3 3 |
|
||||
4 4 |
|
||||
5 5 | def double_quotes_backslash_raw():
|
||||
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ mod tests {
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_16.py"))]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_17.py"))]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_18.py"))]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_19.py"))]
|
||||
#[test_case(Rule::ImportShadowedByLoopVar, Path::new("F402.py"))]
|
||||
#[test_case(Rule::UndefinedLocalWithImportStar, Path::new("F403.py"))]
|
||||
#[test_case(Rule::LateFutureImport, Path::new("F404.py"))]
|
||||
@@ -135,6 +136,7 @@ mod tests {
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_17.py"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_18.py"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_19.py"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_20.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_0.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_1.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_2.py"))]
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
F821_20.py:3:24: F821 Undefined name `Record`
|
||||
|
|
||||
1 | """Test lazy evaluation of type alias values."""
|
||||
2 |
|
||||
3 | type RecordCallback[R: Record] = Callable[[R], None]
|
||||
| ^^^^^^ F821
|
||||
4 |
|
||||
5 | from collections.abc import Callable
|
||||
|
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ use itertools::Itertools;
|
||||
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::AstNode;
|
||||
use ruff_python_ast::{self as ast, Arguments, Expr};
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::rules::{
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming,
|
||||
pycodestyle, pydocstyle, pyflakes, pylint, pyupgrade,
|
||||
};
|
||||
use crate::settings::types::{PerFileIgnore, PythonVersion};
|
||||
use crate::settings::types::{FilePatternSet, PerFileIgnore, PythonVersion};
|
||||
use crate::{codes, RuleSelector};
|
||||
|
||||
use super::line_width::{LineLength, TabSize};
|
||||
@@ -38,6 +38,7 @@ pub mod types;
|
||||
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct LinterSettings {
|
||||
pub exclude: FilePatternSet,
|
||||
pub project_root: PathBuf,
|
||||
|
||||
pub rules: RuleTable,
|
||||
@@ -131,6 +132,7 @@ impl LinterSettings {
|
||||
|
||||
pub fn new(project_root: &Path) -> Self {
|
||||
Self {
|
||||
exclude: FilePatternSet::default(),
|
||||
target_version: PythonVersion::default(),
|
||||
project_root: project_root.to_path_buf(),
|
||||
rules: DEFAULT_SELECTORS
|
||||
|
||||
@@ -16,6 +16,7 @@ doctest = false
|
||||
|
||||
[dependencies]
|
||||
ruff_python_trivia = { path = "../ruff_python_trivia" }
|
||||
serde_derive_internals = "0.29.0"
|
||||
|
||||
proc-macro2 = { workspace = true }
|
||||
quote = { workspace = true }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use proc_macro2::TokenTree;
|
||||
use quote::{quote, quote_spanned};
|
||||
use serde_derive_internals::Ctxt;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::token::Comma;
|
||||
use syn::{
|
||||
AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput, ExprLit, Field,
|
||||
Fields, Lit, LitStr, Meta, Path, PathArguments, PathSegment, Token, Type, TypePath,
|
||||
Fields, Lit, LitStr, Path, PathArguments, PathSegment, Token, Type, TypePath,
|
||||
};
|
||||
|
||||
use ruff_python_trivia::textwrap::dedent;
|
||||
@@ -38,25 +38,14 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
|
||||
.any(|attr| attr.path().is_ident("option_group"))
|
||||
{
|
||||
output.push(handle_option_group(field)?);
|
||||
} else if let Some(serde) = field
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|attr| attr.path().is_ident("serde"))
|
||||
{
|
||||
} else if let Type::Path(ty) = &field.ty {
|
||||
let serde_field = serde_field_metadata(field)?;
|
||||
|
||||
// If a field has the `serde(flatten)` attribute, flatten the options into the parent
|
||||
// by calling `Type::record` instead of `visitor.visit_set`
|
||||
if let (Type::Path(ty), Meta::List(list)) = (&field.ty, &serde.meta) {
|
||||
for token in list.tokens.clone() {
|
||||
if let TokenTree::Ident(ident) = token {
|
||||
if ident == "flatten" {
|
||||
let ty_name = ty.path.require_ident()?;
|
||||
output.push(quote_spanned!(
|
||||
ident.span() => (#ty_name::record(visit))
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if serde_field.flatten() {
|
||||
let ty_name = ty.path.require_ident()?;
|
||||
output.push(quote_spanned!(ident.span() => (#ty_name::record(visit))));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,6 +182,10 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<proc_macro2::To
|
||||
} = attr.parse_args::<FieldAttributes>()?;
|
||||
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
|
||||
|
||||
let serde_field = serde_field_metadata(field)?;
|
||||
let attributed_aliases = serde_field.aliases();
|
||||
let aliases = quote!(BTreeSet::from_iter([#(#attributed_aliases),*]));
|
||||
|
||||
Ok(quote_spanned!(
|
||||
ident.span() => {
|
||||
visit.record_field(#kebab_name, crate::options_base::OptionField{
|
||||
@@ -200,6 +193,7 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<proc_macro2::To
|
||||
default: &#default,
|
||||
value_type: &#value_type,
|
||||
example: &#example,
|
||||
aliases: #aliases
|
||||
})
|
||||
}
|
||||
))
|
||||
@@ -248,3 +242,17 @@ fn _parse_key_value(input: ParseStream, name: &str) -> syn::Result<String> {
|
||||
_ => Err(syn::Error::new(value.span(), "Expected literal string")),
|
||||
}
|
||||
}
|
||||
|
||||
fn serde_field_metadata(field: &Field) -> syn::Result<serde_derive_internals::attr::Field> {
|
||||
let context = Ctxt::new();
|
||||
let field = serde_derive_internals::attr::Field::from_ast(
|
||||
&context,
|
||||
0,
|
||||
field,
|
||||
None,
|
||||
&serde_derive_internals::attr::Default::Default,
|
||||
);
|
||||
context.check()?;
|
||||
|
||||
Ok(field)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::node::AnyNodeRef;
|
||||
use crate::AnyNodeRef;
|
||||
use crate::{self as ast, Expr};
|
||||
|
||||
/// Unowned pendant to [`ast::Expr`] that stores a reference instead of a owned value.
|
||||
|
||||
@@ -8,9 +8,9 @@ use smallvec::SmallVec;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::call_path::CallPath;
|
||||
use crate::node::AnyNodeRef;
|
||||
use crate::parenthesize::parenthesized_range;
|
||||
use crate::statement_visitor::{walk_body, walk_stmt, StatementVisitor};
|
||||
use crate::AnyNodeRef;
|
||||
use crate::{
|
||||
self as ast, Arguments, CmpOp, Constant, ExceptHandler, Expr, MatchCase, Pattern, Stmt,
|
||||
TypeParam,
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::path::Path;
|
||||
|
||||
pub use expression::*;
|
||||
pub use int::*;
|
||||
pub use node::{AnyNode, AnyNodeRef, AstNode, NodeKind};
|
||||
pub use nodes::*;
|
||||
|
||||
pub mod all;
|
||||
@@ -14,7 +15,7 @@ pub mod helpers;
|
||||
pub mod identifier;
|
||||
pub mod imports;
|
||||
mod int;
|
||||
pub mod node;
|
||||
mod node;
|
||||
mod nodes;
|
||||
pub mod parenthesize;
|
||||
pub mod relocate;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruff_python_trivia::{BackwardsTokenizer, CommentRanges, SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||
|
||||
use crate::node::AnyNodeRef;
|
||||
use crate::AnyNodeRef;
|
||||
use crate::ExpressionRef;
|
||||
|
||||
/// Returns the [`TextRange`] of a given expression including parentheses, if the expression is
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::node::{AnyNodeRef, AstNode};
|
||||
use crate::{
|
||||
Alias, Arguments, BoolOp, CmpOp, Comprehension, Constant, Decorator, ElifElseClause,
|
||||
ExceptHandler, Expr, Keyword, MatchCase, Mod, Operator, Parameter, ParameterWithDefault,
|
||||
Parameters, Pattern, PatternArguments, PatternKeyword, Stmt, TypeParam, TypeParams, UnaryOp,
|
||||
WithItem,
|
||||
};
|
||||
use crate::{AnyNodeRef, AstNode};
|
||||
|
||||
/// Visitor that traverses all nodes recursively in pre-order.
|
||||
pub trait PreorderVisitor<'a> {
|
||||
|
||||
@@ -2,12 +2,12 @@ use std::fmt::{Debug, Write};
|
||||
|
||||
use insta::assert_snapshot;
|
||||
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::visitor::preorder::{
|
||||
walk_alias, walk_comprehension, walk_except_handler, walk_expr, walk_keyword, walk_match_case,
|
||||
walk_module, walk_parameter, walk_parameters, walk_pattern, walk_stmt, walk_type_param,
|
||||
walk_with_item, PreorderVisitor,
|
||||
};
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{
|
||||
Alias, BoolOp, CmpOp, Comprehension, Constant, ExceptHandler, Expr, Keyword, MatchCase, Mod,
|
||||
Operator, Parameter, Parameters, Pattern, Stmt, TypeParam, UnaryOp, WithItem,
|
||||
|
||||
@@ -5,12 +5,12 @@ use ruff_python_ast as ast;
|
||||
use ruff_python_parser::lexer::lex;
|
||||
use ruff_python_parser::{parse_tokens, Mode};
|
||||
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::visitor::{
|
||||
walk_alias, walk_comprehension, walk_except_handler, walk_expr, walk_keyword, walk_match_case,
|
||||
walk_parameter, walk_parameters, walk_pattern, walk_stmt, walk_type_param, walk_with_item,
|
||||
Visitor,
|
||||
};
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{
|
||||
Alias, BoolOp, CmpOp, Comprehension, ExceptHandler, Expr, Keyword, MatchCase, Operator,
|
||||
Parameter, Parameters, Pattern, Stmt, TypeParam, UnaryOp, WithItem,
|
||||
|
||||
@@ -150,7 +150,7 @@ impl<'fmt, 'ast, 'buf> JoinCommaSeparatedBuilder<'fmt, 'ast, 'buf> {
|
||||
N: Ranged,
|
||||
Separator: Format<PyFormatContext<'ast>>,
|
||||
{
|
||||
self.result = self.result.and_then(|_| {
|
||||
self.result = self.result.and_then(|()| {
|
||||
if self.entries.is_one_or_more() {
|
||||
write!(self.fmt, [token(","), separator])?;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ impl<'fmt, 'ast, 'buf> JoinCommaSeparatedBuilder<'fmt, 'ast, 'buf> {
|
||||
}
|
||||
|
||||
pub(crate) fn finish(&mut self) -> FormatResult<()> {
|
||||
self.result.and_then(|_| {
|
||||
self.result.and_then(|()| {
|
||||
if let Some(last_end) = self.entries.position() {
|
||||
let magic_trailing_comma = has_magic_trailing_comma(
|
||||
TextRange::new(last_end, self.sequence_end),
|
||||
|
||||
@@ -180,7 +180,7 @@ mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
|
||||
use ruff_formatter::SourceCode;
|
||||
use ruff_python_ast::node::AnyNode;
|
||||
use ruff_python_ast::AnyNode;
|
||||
use ruff_python_ast::{StmtBreak, StmtContinue};
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use ruff_formatter::{format_args, write, FormatError, FormatOptions, SourceCode};
|
||||
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_ast::{AnyNodeRef, AstNode};
|
||||
use ruff_python_trivia::{
|
||||
is_pragma_comment, lines_after, lines_after_ignoring_trivia, lines_before,
|
||||
};
|
||||
|
||||
@@ -96,8 +96,8 @@ pub(crate) use format::{
|
||||
leading_alternate_branch_comments, leading_comments, leading_node_comments, trailing_comments,
|
||||
};
|
||||
use ruff_formatter::{SourceCode, SourceCodeSlice};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::visitor::preorder::{PreorderVisitor, TraversalSignal};
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::Mod;
|
||||
use ruff_python_trivia::{CommentRanges, PythonWhitespace};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
@@ -52,7 +52,7 @@ impl<'a> From<AnyNodeRef<'a>> for NodeRefEqualityKey<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::comments::node_key::NodeRefEqualityKey;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::StmtContinue;
|
||||
use ruff_text_size::TextRange;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::whitespace::indentation;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{self as ast, Comprehension, Expr, MatchCase, ModModule, Parameters};
|
||||
use ruff_python_trivia::{
|
||||
find_only_token_in_range, indentation_at_offset, BackwardsTokenizer, CommentRanges,
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::fmt::Debug;
|
||||
use std::iter::Peekable;
|
||||
|
||||
use ruff_formatter::{SourceCode, SourceCodeSlice};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Mod, Stmt};
|
||||
// The interface is designed to only export the members relevant for iterating nodes in
|
||||
// pre-order.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Constant, Expr, ExprAttribute, ExprConstant};
|
||||
use ruff_python_trivia::{find_only_token_in_range, SimpleTokenKind};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprAwait;
|
||||
|
||||
use crate::expression::maybe_parenthesize_expression;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Expr, ExprBinOp};
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{BoolOp, ExprBoolOp};
|
||||
|
||||
use crate::expression::binary_like::BinaryLike;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::FormatRuleWithOptions;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Expr, ExprCall};
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{CmpOp, Expr, ExprCompare};
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::FormatRuleWithOptions;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Constant, ExprConstant};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Expr, ExprDict};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprDictComp;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use memchr::memchr2;
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
use ruff_formatter::FormatResult;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprFString;
|
||||
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprFormattedValue;
|
||||
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprGeneratorExp;
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Expr, ExprIfExp};
|
||||
|
||||
use crate::comments::leading_comments;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprLambda;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::prelude::format_with;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprList;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write, FormatResult};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprListComp;
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{write, FormatContext};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprName;
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprNamedExpr;
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprSet;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write, Buffer, FormatResult};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprSetComp;
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{write, FormatError};
|
||||
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||
use ruff_python_ast::{AnyNodeRef, AstNode};
|
||||
use ruff_python_ast::{Expr, ExprSlice, ExprUnaryOp, UnaryOp};
|
||||
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprStarred;
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||
use ruff_python_ast::{AnyNodeRef, AstNode};
|
||||
use ruff_python_ast::{Expr, ExprSubscript};
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprTuple;
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprUnaryOp;
|
||||
use ruff_python_ast::UnaryOp;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Expr, ExprYield, ExprYieldFrom};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprYieldFrom;
|
||||
|
||||
use crate::expression::expr_yield::AnyExpressionYield;
|
||||
|
||||
@@ -6,8 +6,8 @@ use ruff_formatter::{
|
||||
write, FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions,
|
||||
};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::visitor::preorder::{walk_expr, PreorderVisitor};
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Constant, Expr, ExpressionRef, Operator};
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_formatter::prelude::tag::Condition;
|
||||
use ruff_formatter::{format_args, write, Argument, Arguments};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExpressionRef;
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_python_trivia::{
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::borrow::Cow;
|
||||
use bitflags::bitflags;
|
||||
|
||||
use ruff_formatter::{format_args, write, FormatError};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{self as ast, Constant, ExprConstant, ExprFString, ExpressionRef};
|
||||
use ruff_python_parser::lexer::{lex_starts_at, LexicalError, LexicalErrorType};
|
||||
use ruff_python_parser::{Mode, Tok};
|
||||
|
||||
@@ -3,7 +3,7 @@ use tracing::Level;
|
||||
|
||||
use ruff_formatter::prelude::*;
|
||||
use ruff_formatter::{format, FormatError, Formatted, PrintError, Printed, SourceCode};
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_python_ast::AstNode;
|
||||
use ruff_python_ast::Mod;
|
||||
use ruff_python_index::tokens_and_ranges;
|
||||
use ruff_python_parser::lexer::LexicalError;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_python_ast::AstNode;
|
||||
use ruff_python_ast::MatchCase;
|
||||
|
||||
use crate::builders::parenthesize_if_expands;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::usize;
|
||||
|
||||
use ruff_formatter::{format_args, write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||
use ruff_python_ast::Parameters;
|
||||
use ruff_python_ast::{AnyNodeRef, AstNode};
|
||||
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::Pattern;
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_python_trivia::{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_python_ast::AstNode;
|
||||
use ruff_python_ast::{Pattern, PatternArguments};
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::PatternMatchAs;
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::PatternMatchClass;
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::PatternMatchMapping;
|
||||
use ruff_python_ast::{Expr, Identifier, Pattern};
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::PatternMatchOr;
|
||||
|
||||
use crate::comments::leading_comments;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::comments::SourceComment;
|
||||
use ruff_formatter::{format_args, Format, FormatResult};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::PatternMatchSequence;
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{Constant, PatternMatchSingleton};
|
||||
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::PatternMatchStar;
|
||||
|
||||
use crate::comments::{dangling_comments, SourceComment};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::PatternMatchValue;
|
||||
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses, Parentheses};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{write, Argument, Arguments, FormatError};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{
|
||||
ElifElseClause, ExceptHandlerExceptHandler, MatchCase, StmtClassDef, StmtFor, StmtFunctionDef,
|
||||
StmtIf, StmtMatch, StmtTry, StmtWhile, StmtWith, Suite,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_python_ast::node::AstNode;
|
||||
use ruff_python_ast::AstNode;
|
||||
use ruff_python_ast::StmtGlobal;
|
||||
|
||||
use crate::comments::{SourceComment, SuppressionKind};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::{ElifElseClause, StmtIf};
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user