From 5371e0a002e162443de34ab0a1a9f8e00a954e62 Mon Sep 17 00:00:00 2001 From: Evan Rittenhouse Date: Fri, 7 Jul 2023 21:46:39 -0500 Subject: [PATCH 1/2] Have CLI repsect rule applicability when fixing --- crates/ruff_cli/src/args.rs | 11 +- crates/ruff_cli/src/cache.rs | 6 +- crates/ruff_cli/src/commands/check.rs | 2 +- crates/ruff_cli/src/diagnostics.rs | 302 +++++++++++---------- crates/ruff_cli/src/lib.rs | 26 +- crates/ruff_cli/src/printer.rs | 118 +++++--- crates/ruff_cli/tests/integration_test.rs | 222 +++++++++++++++ crates/ruff_diagnostics/src/fix.rs | 30 +- crates/ruff_linter/src/autofix/mod.rs | 22 +- crates/ruff_linter/src/linter.rs | 4 +- crates/ruff_linter/src/settings/flags.rs | 20 +- crates/ruff_linter/src/test.rs | 7 +- crates/ruff_wasm/src/lib.rs | 1 + crates/ruff_workspace/src/configuration.rs | 4 + crates/ruff_workspace/src/options.rs | 10 +- crates/ruff_workspace/src/settings.rs | 3 + docs/configuration.md | 4 + ruff.schema.json | 9 +- 18 files changed, 594 insertions(+), 207 deletions(-) diff --git a/crates/ruff_cli/src/args.rs b/crates/ruff_cli/src/args.rs index b3159f4eb6ab5..1f3e50390a35f 100644 --- a/crates/ruff_cli/src/args.rs +++ b/crates/ruff_cli/src/args.rs @@ -72,7 +72,6 @@ pub enum Command { // The `Parser` derive is for ruff_dev, for ruff_cli `Args` would be sufficient #[derive(Clone, Debug, clap::Parser)] -#[allow(clippy::struct_excessive_bools)] pub struct CheckCommand { /// List of files or directories to check. pub files: Vec, @@ -80,7 +79,10 @@ pub struct CheckCommand { /// Use `--no-fix` to disable. #[arg(long, overrides_with("no_fix"))] fix: bool, - #[clap(long, overrides_with("fix"), hide = true)] + /// Attempt to automatically fix both automatic and suggested lint violations. + #[arg(long, overrides_with_all(["fix", "no_fix"]))] + fix_suggested: bool, + #[clap(long, overrides_with_all(["fix", "fix_suggested"]), hide = true)] no_fix: bool, /// Show violations with source code. /// Use `--no-show-source` to disable. @@ -497,6 +499,7 @@ impl CheckCommand { cache_dir: self.cache_dir, fix: resolve_bool_arg(self.fix, self.no_fix), fix_only: resolve_bool_arg(self.fix_only, self.no_fix_only), + fix_suggested: Some(self.fix_suggested), force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude), output_format: self.output_format.or(self.format), show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes), @@ -599,6 +602,7 @@ pub struct CliOverrides { pub cache_dir: Option, pub fix: Option, pub fix_only: Option, + pub fix_suggested: Option, pub force_exclude: Option, pub output_format: Option, pub show_fixes: Option, @@ -624,6 +628,9 @@ impl ConfigurationTransformer for CliOverrides { if let Some(fix_only) = &self.fix_only { config.fix_only = Some(*fix_only); } + if self.fix_suggested.is_some() { + config.fix_suggested = self.fix_suggested; + } config.rule_selections.push(RuleSelection { select: self.select.clone(), ignore: self diff --git a/crates/ruff_cli/src/cache.rs b/crates/ruff_cli/src/cache.rs index 3a2b851451f6e..b48593e52795d 100644 --- a/crates/ruff_cli/src/cache.rs +++ b/crates/ruff_cli/src/cache.rs @@ -409,7 +409,7 @@ mod tests { &settings.linter, Some(&cache), flags::Noqa::Enabled, - flags::FixMode::Generate, + flags::FixMode::Generate(flags::SuggestedFixes::Apply), ) .unwrap(); if diagnostics @@ -454,7 +454,7 @@ mod tests { &settings.linter, Some(&cache), flags::Noqa::Enabled, - flags::FixMode::Generate, + flags::FixMode::Generate(flags::SuggestedFixes::Apply), ) .unwrap(); } @@ -711,7 +711,7 @@ mod tests { &self.settings.linter, Some(cache), flags::Noqa::Enabled, - flags::FixMode::Generate, + flags::FixMode::Generate(flags::SuggestedFixes::Apply), ) } } diff --git a/crates/ruff_cli/src/commands/check.rs b/crates/ruff_cli/src/commands/check.rs index a16f9fef9ae5d..b4ef612ad8e13 100644 --- a/crates/ruff_cli/src/commands/check.rs +++ b/crates/ruff_cli/src/commands/check.rs @@ -284,7 +284,7 @@ mod test { &CliOverrides::default(), flags::Cache::Disabled, flags::Noqa::Disabled, - flags::FixMode::Generate, + flags::FixMode::Generate(flags::SuggestedFixes::Apply), ) .unwrap(); let mut output = Vec::new(); diff --git a/crates/ruff_cli/src/diagnostics.rs b/crates/ruff_cli/src/diagnostics.rs index 13de795a86b8b..246b3785f3323 100644 --- a/crates/ruff_cli/src/diagnostics.rs +++ b/crates/ruff_cli/src/diagnostics.rs @@ -220,97 +220,110 @@ pub(crate) fn lint_path( error: parse_error, }, fixed, - ) = if matches!(autofix, flags::FixMode::Apply | flags::FixMode::Diff) { - if let Ok(FixerResult { - result, - transformed, - fixed, - }) = lint_fix(path, package, noqa, settings, &source_kind, source_type) - { - if !fixed.is_empty() { - match autofix { - flags::FixMode::Apply => match transformed.as_ref() { - SourceKind::Python(transformed) => { - write(path, transformed.as_bytes())?; - } - SourceKind::IpyNotebook(notebook) => { - let mut writer = BufWriter::new(File::create(path)?); - notebook.write(&mut writer)?; - } - }, - flags::FixMode::Diff => { - match transformed.as_ref() { + ) = match autofix { + flags::FixMode::Apply(fix_suggested) | flags::FixMode::Diff(fix_suggested) => { + if let Ok(FixerResult { + result, + transformed, + fixed, + }) = lint_fix( + path, + package, + noqa, + settings, + &source_kind, + source_type, + fix_suggested, + ) { + if !fixed.is_empty() { + match autofix { + flags::FixMode::Apply(_) => match transformed.as_ref() { SourceKind::Python(transformed) => { - let mut stdout = io::stdout().lock(); - TextDiff::from_lines(source_kind.source_code(), transformed) - .unified_diff() - .header(&fs::relativize_path(path), &fs::relativize_path(path)) - .to_writer(&mut stdout)?; - stdout.write_all(b"\n")?; - stdout.flush()?; + write(path, transformed.as_bytes())?; } - SourceKind::IpyNotebook(dest_notebook) => { - // We need to load the notebook again, since we might've - // mutated it. - let src_notebook = source_kind.as_ipy_notebook().unwrap(); - let mut stdout = io::stdout().lock(); - for ((idx, src_cell), dest_cell) in src_notebook - .cells() - .iter() - .enumerate() - .zip(dest_notebook.cells().iter()) - { - let (Cell::Code(src_code_cell), Cell::Code(dest_code_cell)) = - (src_cell, dest_cell) - else { - continue; - }; - TextDiff::from_lines( - &src_code_cell.source.to_string(), - &dest_code_cell.source.to_string(), - ) - .unified_diff() - // Jupyter notebook cells don't necessarily have a newline - // at the end. For example, - // - // ```python - // print("hello") - // ``` - // - // For a cell containing the above code, there'll only be one line, - // and it won't have a newline at the end. If it did, there'd be - // two lines, and the second line would be empty: - // - // ```python - // print("hello") - // - // ``` - .missing_newline_hint(false) - .header( - &format!("{}:cell {}", &fs::relativize_path(path), idx), - &format!("{}:cell {}", &fs::relativize_path(path), idx), - ) - .to_writer(&mut stdout)?; + SourceKind::IpyNotebook(notebook) => { + let mut writer = BufWriter::new(File::create(path)?); + notebook.write(&mut writer)?; + } + }, + flags::FixMode::Diff(_) => { + match transformed.as_ref() { + SourceKind::Python(transformed) => { + let mut stdout = io::stdout().lock(); + TextDiff::from_lines(source_kind.source_code(), transformed) + .unified_diff() + .header( + &fs::relativize_path(path), + &fs::relativize_path(path), + ) + .to_writer(&mut stdout)?; + stdout.write_all(b"\n")?; + stdout.flush()?; + } + SourceKind::IpyNotebook(dest_notebook) => { + // We need to load the notebook again, since we might've + // mutated it. + let src_notebook = source_kind.as_ipy_notebook().unwrap(); + let mut stdout = io::stdout().lock(); + for ((idx, src_cell), dest_cell) in src_notebook + .cells() + .iter() + .enumerate() + .zip(dest_notebook.cells().iter()) + { + let (Cell::Code(src_code_cell), Cell::Code(dest_code_cell)) = + (src_cell, dest_cell) + else { + continue; + }; + TextDiff::from_lines( + &src_code_cell.source.to_string(), + &dest_code_cell.source.to_string(), + ) + .unified_diff() + // Jupyter notebook cells don't necessarily have a newline + // at the end. For example, + // + // ```python + // print("hello") + // ``` + // + // For a cell containing the above code, there'll only be one line, + // and it won't have a newline at the end. If it did, there'd be + // two lines, and the second line would be empty: + // + // ```python + // print("hello") + // + // ``` + .missing_newline_hint(false) + .header( + &format!("{}:cell {}", &fs::relativize_path(path), idx), + &format!("{}:cell {}", &fs::relativize_path(path), idx), + ) + .to_writer(&mut stdout)?; + } + stdout.write_all(b"\n")?; + stdout.flush()?; } - stdout.write_all(b"\n")?; - stdout.flush()?; } } + flags::FixMode::Generate(_) => {} } - flags::FixMode::Generate => {} } + (result, fixed) + } else { + // If we fail to autofix, lint the original source code. + let result = lint_only(path, package, settings, noqa, &source_kind, source_type); + let fixed = FxHashMap::default(); + (result, fixed) } - (result, fixed) - } else { - // If we fail to autofix, lint the original source code. + } + flags::FixMode::Generate(_) => { let result = lint_only(path, package, settings, noqa, &source_kind, source_type); let fixed = FxHashMap::default(); (result, fixed) } - } else { - let result = lint_only(path, package, settings, noqa, &source_kind, source_type); - let fixed = FxHashMap::default(); - (result, fixed) }; let imports = imports.unwrap_or_default(); @@ -378,13 +391,14 @@ pub(crate) fn lint_stdin( }; // Extract the sources from the file. - let LintSource(source_kind) = match LintSource::try_from_source_code(contents, source_type) { - Ok(Some(sources)) => sources, - Ok(None) => return Ok(Diagnostics::default()), - Err(err) => { - return Ok(Diagnostics::from_source_error(&err, path, &settings.linter)); - } - }; + let LintSource(source_kind) = + match LintSource::try_from_source_code(contents.clone(), source_type) { + Ok(Some(sources)) => sources, + Ok(None) => return Ok(Diagnostics::default()), + Err(err) => { + return Ok(Diagnostics::from_source_error(&err, path, &settings.linter)); + } + }; // Lint the inputs. let ( @@ -393,49 +407,70 @@ pub(crate) fn lint_stdin( error: parse_error, }, fixed, - ) = if matches!(autofix, flags::FixMode::Apply | flags::FixMode::Diff) { - if let Ok(FixerResult { - result, - transformed, - fixed, - }) = lint_fix( - path.unwrap_or_else(|| Path::new("-")), - package, - noqa, - &settings.linter, - &source_kind, - source_type, - ) { - match autofix { - flags::FixMode::Apply => { - // Write the contents to stdout, regardless of whether any errors were fixed. - io::stdout().write_all(transformed.source_code().as_bytes())?; - } - flags::FixMode::Diff => { - // But only write a diff if it's non-empty. - if !fixed.is_empty() { - let text_diff = TextDiff::from_lines( - source_kind.source_code(), - transformed.source_code(), - ); - let mut unified_diff = text_diff.unified_diff(); - if let Some(path) = path { - unified_diff - .header(&fs::relativize_path(path), &fs::relativize_path(path)); - } + ) = match autofix { + flags::FixMode::Apply(fix_suggested) | flags::FixMode::Diff(fix_suggested) => { + if let Ok(FixerResult { + result, + transformed, + fixed, + }) = lint_fix( + path.unwrap_or_else(|| Path::new("-")), + package, + noqa, + &settings.linter, + &source_kind, + source_type, + fix_suggested, + ) { + match autofix { + flags::FixMode::Apply(_) => { + // Write the contents to stdout, regardless of whether any errors were fixed. + io::stdout().write_all(transformed.source_code().as_bytes())?; + } + flags::FixMode::Diff(_) => { + // But only write a diff if it's non-empty. + if !fixed.is_empty() { + let text_diff = TextDiff::from_lines( + source_kind.source_code(), + transformed.source_code(), + ); + let mut unified_diff = text_diff.unified_diff(); + if let Some(path) = path { + unified_diff + .header(&fs::relativize_path(path), &fs::relativize_path(path)); + } - let mut stdout = io::stdout().lock(); - unified_diff.to_writer(&mut stdout)?; - stdout.write_all(b"\n")?; - stdout.flush()?; + let mut stdout = io::stdout().lock(); + unified_diff.to_writer(&mut stdout)?; + stdout.write_all(b"\n")?; + stdout.flush()?; + } } + flags::FixMode::Generate(_) => {} } - flags::FixMode::Generate => {} - } - (result, fixed) - } else { - // If we fail to autofix, lint the original source code. + (result, fixed) + } else { + // If we fail to autofix, lint the original source code. + let result = lint_only( + path.unwrap_or_else(|| Path::new("-")), + package, + &settings.linter, + noqa, + &source_kind, + source_type, + ); + let fixed = FxHashMap::default(); + + // Write the contents to stdout anyway. + if autofix.is_apply() { + io::stdout().write_all(contents.as_bytes())?; + } + + (result, fixed) + } + } + flags::FixMode::Generate(_) => { let result = lint_only( path.unwrap_or_else(|| Path::new("-")), package, @@ -445,25 +480,8 @@ pub(crate) fn lint_stdin( source_type, ); let fixed = FxHashMap::default(); - - // Write the contents to stdout anyway. - if autofix.is_apply() { - io::stdout().write_all(source_kind.source_code().as_bytes())?; - } - (result, fixed) } - } else { - let result = lint_only( - path.unwrap_or_else(|| Path::new("-")), - package, - &settings.linter, - noqa, - &source_kind, - source_type, - ); - let fixed = FxHashMap::default(); - (result, fixed) }; let imports = imports.unwrap_or_default(); diff --git a/crates/ruff_cli/src/lib.rs b/crates/ruff_cli/src/lib.rs index c7f0c34729322..b5f91e2103f6f 100644 --- a/crates/ruff_cli/src/lib.rs +++ b/crates/ruff_cli/src/lib.rs @@ -10,7 +10,7 @@ use log::warn; use notify::{recommended_watcher, RecursiveMode, Watcher}; use ruff_linter::logging::{set_up_logging, LogLevel}; -use ruff_linter::settings::flags; +use ruff_linter::settings::flags::{FixMode, SuggestedFixes}; use ruff_linter::settings::types::SerializationFormat; use ruff_linter::{fs, warn_user, warn_user_once}; use ruff_workspace::Settings; @@ -228,6 +228,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result { let Settings { fix, fix_only, + fix_suggested, output_format, show_fixes, show_source, @@ -237,15 +238,26 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result { // Autofix rules are as follows: // - By default, generate all fixes, but don't apply them to the filesystem. // - If `--fix` or `--fix-only` is set, always apply fixes to the filesystem (or - // print them to stdout, if we're reading from stdin). + // print them to stdout, if we're reading from stdin) for [`Applicability::Automatic`] rules + // only. // - If `--diff` or `--fix-only` are set, don't print any violations (only - // fixes). + // fixes) for [`Applicability::Automatic`] rules only. + // - If `--fix--suggested` is set, the above rules will apply to both [`Applicability::Suggested`] and + // [`Applicability::Automatic`] fixes. + // TODO: can't fix this until @zanieb changes the CLI + let fix_suggested = if fix_suggested { + SuggestedFixes::Apply + } else { + SuggestedFixes::Disable + }; + let autofix = if cli.diff { - flags::FixMode::Diff + FixMode::Diff(fix_suggested) } else if fix || fix_only { - flags::FixMode::Apply + FixMode::Apply(fix_suggested) } else { - flags::FixMode::Generate + // We'll always generate all fixes, regardless of [`Applicability`], in `generate` mode + FixMode::Generate(SuggestedFixes::Apply) }; let cache = !cli.no_cache; let noqa = !cli.ignore_noqa; @@ -382,7 +394,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result { // Always try to print violations (the printer itself may suppress output), // unless we're writing fixes via stdin (in which case, the transformed // source code goes to stdout). - if !(is_stdin && matches!(autofix, flags::FixMode::Apply | flags::FixMode::Diff)) { + if !(is_stdin && matches!(autofix, FixMode::Apply(_) | FixMode::Diff(_))) { if cli.statistics { printer.write_statistics(&diagnostics, &mut writer)?; } else { diff --git a/crates/ruff_cli/src/printer.rs b/crates/ruff_cli/src/printer.rs index 07757fed83860..8e2be4a1207e1 100644 --- a/crates/ruff_cli/src/printer.rs +++ b/crates/ruff_cli/src/printer.rs @@ -7,6 +7,7 @@ use anyhow::Result; use bitflags::bitflags; use colored::Colorize; use itertools::{iterate, Itertools}; +use ruff_diagnostics::Applicability; use rustc_hash::FxHashMap; use serde::Serialize; @@ -19,7 +20,7 @@ use ruff_linter::message::{ }; use ruff_linter::notify_user; use ruff_linter::registry::{AsRule, Rule}; -use ruff_linter::settings::flags; +use ruff_linter::settings::flags::{self, SuggestedFixes}; use ruff_linter::settings::types::SerializationFormat; use crate::diagnostics::Diagnostics; @@ -118,19 +119,11 @@ impl Printer { writeln!(writer, "Found {remaining} error{s}.")?; } - if show_fix_status(self.autofix_level) { - let num_fixable = diagnostics - .messages - .iter() - .filter(|message| message.fix.is_some()) - .count(); - if num_fixable > 0 { - writeln!( - writer, - "[{}] {num_fixable} potentially fixable with the --fix option.", - "*".cyan(), - )?; - } + let fixables = + FixableStatistics::new(diagnostics, self.autofix_level.suggested_fixes()); + + if !fixables.is_empty() { + writeln!(writer, "{}", fixables.violation_string())?; } } else { let fixed = diagnostics @@ -178,6 +171,7 @@ impl Printer { } let context = EmitterContext::new(&diagnostics.notebook_indexes); + let fixables = FixableStatistics::new(diagnostics, self.autofix_level.suggested_fixes()); match self.format { SerializationFormat::Json => { @@ -191,9 +185,9 @@ impl Printer { } SerializationFormat::Text => { TextEmitter::default() - .with_show_fix_status(show_fix_status(self.autofix_level)) - .with_show_fix_diff(self.flags.intersects(Flags::SHOW_FIX_DIFF)) - .with_show_source(self.flags.intersects(Flags::SHOW_SOURCE)) + .with_show_fix_status(fixables.fixes_are_applicable()) + .with_show_fix_diff(self.flags.contains(Flags::SHOW_FIX_DIFF)) + .with_show_source(self.flags.contains(Flags::SHOW_SOURCE)) .emit(writer, &diagnostics.messages, &context)?; if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) { @@ -208,8 +202,8 @@ impl Printer { } SerializationFormat::Grouped => { GroupedEmitter::default() - .with_show_source(self.flags.intersects(Flags::SHOW_SOURCE)) - .with_show_fix_status(show_fix_status(self.autofix_level)) + .with_show_source(self.flags.contains(Flags::SHOW_SOURCE)) + .with_show_fix_status(fixables.fixes_are_applicable()) .emit(writer, &diagnostics.messages, &context)?; if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) { @@ -359,6 +353,8 @@ impl Printer { ); } + let fixables = FixableStatistics::new(diagnostics, self.autofix_level.suggested_fixes()); + if !diagnostics.messages.is_empty() { if self.log_level >= LogLevel::Default { writeln!(writer)?; @@ -366,8 +362,8 @@ impl Printer { let context = EmitterContext::new(&diagnostics.notebook_indexes); TextEmitter::default() - .with_show_fix_status(show_fix_status(self.autofix_level)) - .with_show_source(self.flags.intersects(Flags::SHOW_SOURCE)) + .with_show_fix_status(fixables.fixes_are_applicable()) + .with_show_source(self.flags.contains(Flags::SHOW_SOURCE)) .emit(writer, &diagnostics.messages, &context)?; } writer.flush()?; @@ -389,16 +385,6 @@ fn num_digits(n: usize) -> usize { .max(1) } -/// Return `true` if the [`Printer`] should indicate that a rule is fixable. -const fn show_fix_status(autofix_level: flags::FixMode) -> bool { - // If we're in application mode, avoid indicating that a rule is fixable. - // If the specific violation were truly fixable, it would've been fixed in - // this pass! (We're occasionally unable to determine whether a specific - // violation is fixable without trying to fix it, so if autofix is not - // enabled, we may inadvertently indicate that a rule is fixable.) - !autofix_level.is_apply() -} - fn print_fix_summary(writer: &mut dyn Write, fixed: &FxHashMap) -> Result<()> { let total = fixed .values() @@ -439,3 +425,73 @@ fn print_fix_summary(writer: &mut dyn Write, fixed: &FxHashMap } Ok(()) } + +/// Contains the number of [`Applicability::Automatic`] and [`Applicability::Suggested`] fixes +struct FixableStatistics<'a> { + automatic: u32, + suggested: u32, + apply_suggested: &'a SuggestedFixes, +} + +impl<'a> FixableStatistics<'a> { + fn new(diagnostics: &Diagnostics, apply_suggested: &'a SuggestedFixes) -> Self { + let mut automatic = 0; + let mut suggested = 0; + + for message in &diagnostics.messages { + if let Some(fix) = &message.fix { + if fix.applicability() == Applicability::Suggested { + suggested += 1; + } else if fix.applicability() == Applicability::Automatic { + automatic += 1; + } + } + } + + Self { + automatic, + suggested, + apply_suggested, + } + } + + fn fixes_are_applicable(&self) -> bool { + match self.apply_suggested { + SuggestedFixes::Apply => self.automatic > 0 || self.suggested > 0, + SuggestedFixes::Disable => self.automatic > 0, + } + } + + /// Returns [`true`] if there aren't any fixes to be displayed + fn is_empty(&self) -> bool { + self.automatic == 0 && self.suggested == 0 + } + + /// Build the displayed fix status message depending on the types of the remaining fixes. + fn violation_string(&self) -> String { + let prefix = format!("[{}]", "*".cyan()); + let mut fix_status = prefix; + + if self.automatic > 0 { + fix_status = format!( + "{fix_status} {} potentially fixable with the --fix option.", + self.automatic + ); + } + + if self.suggested > 0 { + let (line_break, extra_prefix) = if self.automatic > 0 { + ("\n", format!("[{}]", "*".cyan())) + } else { + ("", String::new()) + }; + + let total = self.automatic + self.suggested; + fix_status = format!( + "{fix_status}{line_break}{extra_prefix} {total} potentially fixable with the --fix-suggested option." + ); + } + + fix_status + } +} diff --git a/crates/ruff_cli/tests/integration_test.rs b/crates/ruff_cli/tests/integration_test.rs index 1a4e538bd2ff8..1db755cc0c35a 100644 --- a/crates/ruff_cli/tests/integration_test.rs +++ b/crates/ruff_cli/tests/integration_test.rs @@ -574,3 +574,225 @@ fn check_input_from_argfile() -> Result<()> { Ok(()) } + +#[test] +fn display_different_safety_levels() -> Result<()> { + let mut cmd = Command::cargo_bin(BIN_NAME)?; + + // `--fix` should only apply safe fixes, but should tell the user about `--fix --unsafe` if + // there are remaining unsafe fixes. + let output = cmd + .args([ + "-", + "--format", + "text", + "--isolated", + "--select", + "F601,UP034", + ]) + .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .assert() + .failure(); + assert_eq!( + str::from_utf8(&output.get_output().stdout)?, + r#"-:1:14: F601 [*] Dictionary key literal `'a'` repeated +-:2:7: UP034 [*] Avoid extraneous parentheses +Found 2 errors. +[*] 1 potentially fixable with the --fix option. +[*] 2 potentially fixable with the --fix --unsafe options. +"# + ); + + Ok(()) +} + +#[test] +fn display_unsafe_fixes_remain() -> Result<()> { + let mut cmd = Command::cargo_bin(BIN_NAME)?; + + let output = cmd + .args(["-", "--format", "text", "--isolated", "--select", "F601"]) + .write_stdin("x = {'a': 1, 'a': 1}\n") + .assert() + .failure(); + assert_eq!( + str::from_utf8(&output.get_output().stdout)?, + r#"-:1:14: F601 [*] Dictionary key literal `'a'` repeated +Found 1 error. +[*] 1 potentially fixable with the --fix-suggested option. +"# + ); + + Ok(()) +} + +#[test] +fn fix_applies_safe_fixes_only() -> Result<()> { + let mut cmd = Command::cargo_bin(BIN_NAME)?; + + // `--fix` should only apply safe fixes. Since we're runnnig in `stdin` mode, output shouldn't + // be printed. + let output = cmd + .args([ + "-", + "--format", + "text", + "--isolated", + "--select", + "F601,UP034", + "--fix", + ]) + .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .assert() + .failure(); + assert_eq!( + str::from_utf8(&output.get_output().stdout)?, + "x = {'a': 1, 'a': 1}\nprint('foo')\n" + ); + + Ok(()) +} + +#[test] +fn fix_applies_all_fixes() -> Result<()> { + let mut cmd = Command::cargo_bin(BIN_NAME)?; + + // `--fix --unsafe` should apply both safe and unsafe fixes. + let output = cmd + .args([ + "-", + "--format", + "text", + "--isolated", + "--select", + "F601,UP034", + "--fix", + "--fix-suggested", + ]) + .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .assert() + .success(); + assert_eq!( + str::from_utf8(&output.get_output().stdout)?, + "x = {'a': 1}\nprint('foo')\n" + ); + + Ok(()) +} + +#[test] +fn diff_diffs_all_fixes() -> Result<()> { + let mut cmd = Command::cargo_bin(BIN_NAME)?; + + // `--fix --unsafe` should apply both safe and unsafe fixes. + let output = cmd + .args([ + "-", + "--format", + "text", + "--isolated", + "--select", + "F601,UP034", + "--diff", + "--fix-suggested", + ]) + .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .assert() + .failure(); + assert_eq!( + str::from_utf8(&output.get_output().stdout)?, + r#"@@ -1,2 +1,2 @@ +-x = {'a': 1, 'a': 1} +-print(('foo')) ++x = {'a': 1} ++print('foo') + +"# + ); + + Ok(()) +} + +#[test] +fn diff_diffs_safe_fixes_only() -> Result<()> { + let mut cmd = Command::cargo_bin(BIN_NAME)?; + + // `--fix --unsafe` should apply both safe and unsafe fixes. + let output = cmd + .args([ + "-", + "--format", + "text", + "--isolated", + "--select", + "F601,UP034", + "--diff", + ]) + .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .assert() + .failure(); + assert_eq!( + str::from_utf8(&output.get_output().stdout)?, + r#"@@ -1,2 +1,2 @@ + x = {'a': 1, 'a': 1} +-print(('foo')) ++print('foo') + +"# + ); + + Ok(()) +} + +#[test] +fn fix_only_applies_all_fixes() -> Result<()> { + let mut cmd = Command::cargo_bin(BIN_NAME)?; + + // `--fix --unsafe` should apply both safe and unsafe fixes. + let output = cmd + .args([ + "-", + "--format", + "text", + "--isolated", + "--select", + "F601,UP034", + "--fix-only", + "--fix-suggested", + ]) + .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .assert() + .success(); + assert_eq!( + str::from_utf8(&output.get_output().stdout)?, + "x = {'a': 1}\nprint('foo')\n" + ); + + Ok(()) +} + +#[test] +fn fix_only_applies_safe_fixes_only() -> Result<()> { + let mut cmd = Command::cargo_bin(BIN_NAME)?; + + // `--fix --unsafe` should apply both safe and unsafe fixes. + let output = cmd + .args([ + "-", + "--format", + "text", + "--isolated", + "--select", + "F601,UP034", + "--fix-only", + ]) + .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .assert() + .success(); + assert_eq!( + str::from_utf8(&output.get_output().stdout)?, + "x = {'a': 1, 'a': 1}\nprint('foo')\n" + ); + + Ok(()) +} diff --git a/crates/ruff_diagnostics/src/fix.rs b/crates/ruff_diagnostics/src/fix.rs index d553e83d6696f..e04b76e6c308f 100644 --- a/crates/ruff_diagnostics/src/fix.rs +++ b/crates/ruff_diagnostics/src/fix.rs @@ -5,27 +5,30 @@ use ruff_text_size::{Ranged, TextSize}; use crate::edit::Edit; -/// Indicates confidence in the correctness of a suggested fix. +/// Indicates confidence in the correctness of a suggested fix. Rust internally allows comparison +/// of enum values based on their order (see Rust's [enum +/// documentation](https://doc.rust-lang.org/reference/items/enumerations.html)). This allows us to +/// apply [`Fix`]es based on their [`Applicability`]. #[derive(Default, Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Applicability { - /// The fix is definitely what the user intended, or maintains the exact meaning of the code. - /// This fix should be automatically applied. - Automatic, - - /// The fix may be what the user intended, but it is uncertain. - /// The fix should result in valid code if it is applied. - /// The fix can be applied with user opt-in. - Suggested, - /// The fix has a good chance of being incorrect or the code be incomplete. /// The fix may result in invalid code if it is applied. /// The fix should only be manually applied by the user. Manual, - /// The applicability of the fix is unknown. + /// The applicability of the fix is unknown or not relevant. #[default] Unspecified, + + /// The fix may be what the user intended, but it is uncertain. + /// The fix should result in valid code if it is applied. + /// The fix can be applied with user opt-in. + Suggested, + + /// The fix is definitely what the user intended, or maintains the exact meaning of the code. + /// This fix should be automatically applied. + Automatic, } /// Indicates the level of isolation required to apply a fix. @@ -162,4 +165,9 @@ impl Fix { self.isolation_level = isolation; self } + + /// Return [`true`] if this [`Fix`] should be applied with a given [`Applicability`]. + pub fn is_applied(&self, applicability: Applicability) -> bool { + self.applicability >= applicability + } } diff --git a/crates/ruff_linter/src/autofix/mod.rs b/crates/ruff_linter/src/autofix/mod.rs index 6e33cb99a1ed5..9a885a5db44df 100644 --- a/crates/ruff_linter/src/autofix/mod.rs +++ b/crates/ruff_linter/src/autofix/mod.rs @@ -4,11 +4,12 @@ use std::collections::BTreeSet; use ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; use rustc_hash::{FxHashMap, FxHashSet}; -use ruff_diagnostics::{Diagnostic, Edit, Fix, IsolationLevel, SourceMap}; +use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, IsolationLevel, SourceMap}; use ruff_source_file::Locator; use crate::linter::FixTable; use crate::registry::{AsRule, Rule}; +use crate::settings::flags::SuggestedFixes; pub(crate) mod codemods; pub(crate) mod edits; @@ -24,10 +25,25 @@ pub(crate) struct FixResult { } /// Auto-fix errors in a file, and write the fixed source code to disk. -pub(crate) fn fix_file(diagnostics: &[Diagnostic], locator: &Locator) -> Option { +pub(crate) fn fix_file( + diagnostics: &[Diagnostic], + locator: &Locator, + fix_suggested: SuggestedFixes, +) -> Option { let mut with_fixes = diagnostics .iter() - .filter(|diag| diag.fix.is_some()) + .filter(|diag| { + let Some(ref fix) = diag.fix else { + return false; + }; + + // change this + if matches!(fix_suggested, SuggestedFixes::Apply) { + fix.applicability() >= Applicability::Suggested + } else { + fix.applicability() == Applicability::Automatic + } + }) .peekable(); if with_fixes.peek().is_none() { diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index f07e3d3f0781a..84596343edb02 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -414,6 +414,7 @@ fn diagnostics_to_messages( /// Generate `Diagnostic`s from source code content, iteratively autofixing /// until stable. +#[allow(clippy::too_many_arguments)] pub fn lint_fix<'a>( path: &Path, package: Option<&Path>, @@ -421,6 +422,7 @@ pub fn lint_fix<'a>( settings: &LinterSettings, source_kind: &'a SourceKind, source_type: PySourceType, + fix_suggested: flags::SuggestedFixes, ) -> Result> { let mut transformed = Cow::Borrowed(source_kind); @@ -493,7 +495,7 @@ pub fn lint_fix<'a>( code: fixed_contents, fixes: applied, source_map, - }) = fix_file(&result.data.0, &locator) + }) = fix_file(&result.data.0, &locator, fix_suggested) { if iterations < MAX_ITERATIONS { // Count the number of fixed errors. diff --git a/crates/ruff_linter/src/settings/flags.rs b/crates/ruff_linter/src/settings/flags.rs index a1ce403194c83..244db27d9d463 100644 --- a/crates/ruff_linter/src/settings/flags.rs +++ b/crates/ruff_linter/src/settings/flags.rs @@ -1,8 +1,24 @@ #[derive(Debug, Copy, Clone, Hash, is_macro::Is)] pub enum FixMode { - Generate, + Generate(SuggestedFixes), + Apply(SuggestedFixes), + Diff(SuggestedFixes), +} + +impl FixMode { + pub fn suggested_fixes(&self) -> &SuggestedFixes { + match self { + FixMode::Generate(suggested) => suggested, + FixMode::Apply(suggested) => suggested, + FixMode::Diff(suggested) => suggested, + } + } +} + +#[derive(Debug, Copy, Clone, Hash, is_macro::Is)] +pub enum SuggestedFixes { Apply, - Diff, + Disable, } #[derive(Debug, Copy, Clone, Hash, result_like::BoolLike)] diff --git a/crates/ruff_linter/src/test.rs b/crates/ruff_linter/src/test.rs index 2924814c08542..96ba5625abc11 100644 --- a/crates/ruff_linter/src/test.rs +++ b/crates/ruff_linter/src/test.rs @@ -155,8 +155,11 @@ pub(crate) fn test_contents<'a>( code: fixed_contents, source_map, .. - }) = fix_file(&diagnostics, &Locator::new(transformed.source_code())) - { + }) = fix_file( + &diagnostics, + &Locator::new(transformed.source_code()), + flags::SuggestedFixes::Apply, + ) { if iterations < max_iterations() { iterations += 1; } else { diff --git a/crates/ruff_wasm/src/lib.rs b/crates/ruff_wasm/src/lib.rs index e3a6974e6b28c..dff58f56cb261 100644 --- a/crates/ruff_wasm/src/lib.rs +++ b/crates/ruff_wasm/src/lib.rs @@ -143,6 +143,7 @@ impl Workspace { extend_per_file_ignores: None, fix: None, fix_only: None, + fix_suggested: None, fixable: None, force_exclude: None, output_format: None, diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index afc47401c84db..3cd151766e198 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -73,6 +73,7 @@ pub struct Configuration { pub extend_per_file_ignores: Vec, pub external: Option>, pub fix: Option, + pub fix_suggested: Option, pub fix_only: Option, pub force_exclude: Option, pub output_format: Option, @@ -163,6 +164,7 @@ impl Configuration { .clone() .unwrap_or_else(|| cache_dir(project_root)), fix: self.fix.unwrap_or(false), + fix_suggested: self.fix_suggested.unwrap_or(false), fix_only: self.fix_only.unwrap_or(false), output_format: self.output_format.unwrap_or_default(), show_fixes: self.show_fixes.unwrap_or(false), @@ -410,6 +412,7 @@ impl Configuration { .unwrap_or_default(), external: options.external, fix: options.fix, + fix_suggested: options.fix_suggested, fix_only: options.fix_only, output_format: options.output_format.or_else(|| { options @@ -753,6 +756,7 @@ impl Configuration { .collect(), external: self.external.or(config.external), fix: self.fix.or(config.fix), + fix_suggested: self.fix_suggested.or(config.fix_suggested), fix_only: self.fix_only.or(config.fix_only), output_format: self.output_format.or(config.output_format), force_exclude: self.force_exclude.or(config.force_exclude), diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index 7963ef48f2346..902b3296fb589 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -239,10 +239,18 @@ pub struct Options { pub external: Option>, /// Enable autofix behavior by-default when running `ruff` (overridden - /// by the `--fix` and `--no-fix` command-line flags). + /// by the `--fix` and `--no-fix` command-line flags). Only works on Automatic fixes. #[option(default = "false", value_type = "bool", example = "fix = true")] pub fix: Option, + /// Enable autofix behavior for Automatic and Suggested fixes. + #[option( + default = "false", + value_type = "bool", + example = "fix-suggested = true" + )] + pub fix_suggested: Option, + /// Like `fix`, but disables reporting on leftover violation. Implies `fix`. #[option(default = "false", value_type = "bool", example = "fix-only = true")] pub fix_only: Option, diff --git a/crates/ruff_workspace/src/settings.rs b/crates/ruff_workspace/src/settings.rs index 0e069a58c01dd..80a7538b0021c 100644 --- a/crates/ruff_workspace/src/settings.rs +++ b/crates/ruff_workspace/src/settings.rs @@ -17,6 +17,8 @@ pub struct Settings { #[cache_key(ignore)] pub fix: bool, #[cache_key(ignore)] + pub fix_suggested: bool, + #[cache_key(ignore)] pub fix_only: bool, #[cache_key(ignore)] pub output_format: SerializationFormat, @@ -36,6 +38,7 @@ impl Default for Settings { Self { cache_dir: cache_dir(project_root), fix: false, + fix_suggested: false, fix_only: false, output_format: SerializationFormat::default(), show_fixes: false, diff --git a/docs/configuration.md b/docs/configuration.md index 8cce70bcc6c11..cfe28c88be395 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -194,6 +194,8 @@ Arguments: Options: --fix Attempt to automatically fix lint violations. Use `--no-fix` to disable + --fix-suggested + Attempt to fix automatic and suggested lint violations. Implies `--fix` --show-source Show violations with source code. Use `--no-show-source` to disable --show-fixes @@ -216,6 +218,8 @@ Options: Enable preview mode; checks will include unstable rules and fixes. Use `--no-preview` to disable --config Path to the `pyproject.toml` or `ruff.toml` file to use for configuration + --fix--suggested + Attempt to automatically fix both automatic and suggested lint violations. Implies `--fix` --statistics Show counts for every rule with at least one violation --add-noqa diff --git a/ruff.schema.json b/ruff.schema.json index 07d35210b27b5..7dc122e69abe1 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -120,7 +120,7 @@ } }, "fix": { - "description": "Enable autofix behavior by-default when running `ruff` (overridden by the `--fix` and `--no-fix` command-line flags).", + "description": "Enable autofix behavior by-default when running `ruff` (overridden by the `--fix` and `--no-fix` command-line flags). This will only implement \"safe\" fixes Safe fixes are exactly what the developer wants and/or maintain the exact meaning of the existing code).", "type": [ "boolean", "null" @@ -619,6 +619,13 @@ "items": { "$ref": "#/definitions/RuleSelector" } + }, + "unsafe": { + "description": "Like `fix`, but will also implement \"unsafe\" fixes. Unsafe fixes are those which may not be exactly what you intend, but should still result in valid code. Implies `fix`.", + "type": [ + "boolean", + "null" + ] } }, "additionalProperties": false, From 05462cec94b177b28b0a1878352a93f600c33fe1 Mon Sep 17 00:00:00 2001 From: Evan Rittenhouse Date: Sun, 24 Sep 2023 12:52:00 -0500 Subject: [PATCH 2/2] WIP: Migrating integration tests to new structure --- .../tests/.integration_test.rs.pending-snap | 201 ++++++++++++++++++ crates/ruff_cli/tests/integration_test.rs | 114 +++++----- docs/configuration.md | 4 +- ruff.schema.json | 16 +- 4 files changed, 265 insertions(+), 70 deletions(-) create mode 100644 crates/ruff_cli/tests/.integration_test.rs.pending-snap diff --git a/crates/ruff_cli/tests/.integration_test.rs.pending-snap b/crates/ruff_cli/tests/.integration_test.rs.pending-snap new file mode 100644 index 0000000000000..c999398131afb --- /dev/null +++ b/crates/ruff_cli/tests/.integration_test.rs.pending-snap @@ -0,0 +1,201 @@ +{"run_id":"1695576107-316253000","line":585,"new":{"module_name":"integration_test","snapshot_name":"display_different_safety_levels","metadata":{"source":"crates/ruff_cli/tests/integration_test.rs","assertion_line":585,"info":{"program":"ruff","args":["-","--format","text","--isolated","--select","F601,UP034"],"stdin":"x = {'a': 1, 'a': 1}\nprint(('foo'))\n"}},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\n\n----- stderr -----\nwarning: The argument `--format=` is deprecated. Use `--output-format=` instead.\nwarning: Detected debug build without --no-cache.\n"},"old":{"module_name":"integration_test","metadata":{},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 Dictionary key literal `'a'` repeated\n-:2:7: UP034 Avoid extraneous parentheses\nFound 2 errors."}} +{"run_id":"1695576107-316253000","line":561,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":445,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":464,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":352,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":237,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":393,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":197,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":179,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":370,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":142,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":160,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":428,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":48,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":101,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":64,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":82,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":35,"new":null,"old":null} +{"run_id":"1695576107-316253000","line":518,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":272,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":254,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":222,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":411,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":332,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":312,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":293,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":585,"new":{"module_name":"integration_test","snapshot_name":"display_different_safety_levels","metadata":{"source":"crates/ruff_cli/tests/integration_test.rs","assertion_line":585,"info":{"program":"ruff","args":["-","--format","text","--isolated","--select","F601,UP034"],"stdin":"x = {'a': 1, 'a': 1}\nprint(('foo'))\n"}},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\n\n----- stderr -----\nwarning: The argument `--format=` is deprecated. Use `--output-format=` instead.\nwarning: Detected debug build without --no-cache.\n"},"old":{"module_name":"integration_test","metadata":{},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix --unsafe options.\nFound 2 errors."}} +{"run_id":"1695576194-876745000","line":561,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":464,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":445,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":197,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":393,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":352,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":237,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":179,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":428,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":142,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":370,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":48,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":64,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":160,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":35,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":82,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":101,"new":null,"old":null} +{"run_id":"1695576194-876745000","line":518,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":222,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":272,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":293,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":411,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":332,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":254,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":585,"new":{"module_name":"integration_test","snapshot_name":"display_different_safety_levels","metadata":{"source":"crates/ruff_cli/tests/integration_test.rs","assertion_line":585,"info":{"program":"ruff","args":["-","--format","text","--isolated","--select","F601,UP034"],"stdin":"x = {'a': 1, 'a': 1}\nprint(('foo'))\n"}},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\n\n----- stderr -----\nwarning: The argument `--format=` is deprecated. Use `--output-format=` instead.\nwarning: Detected debug build without --no-cache.\n"},"old":{"module_name":"integration_test","metadata":{},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\nFound 2 errors."}} +{"run_id":"1695576349-593807000","line":312,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":561,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":464,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":445,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":179,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":352,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":428,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":197,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":393,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":370,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":142,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":48,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":237,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":160,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":64,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":101,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":35,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":82,"new":null,"old":null} +{"run_id":"1695576349-593807000","line":518,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":312,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":222,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":332,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":411,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":293,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":254,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":272,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":585,"new":{"module_name":"integration_test","snapshot_name":"display_different_safety_levels","metadata":{"source":"crates/ruff_cli/tests/integration_test.rs","assertion_line":585,"info":{"program":"ruff","args":["-","--format","text","--isolated","--select","F601,UP034","--no-cache"],"stdin":"x = {'a': 1, 'a': 1}\nprint(('foo'))\n"}},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\n\n----- stderr -----\nwarning: The argument `--format=` is deprecated. Use `--output-format=` instead.\n"},"old":{"module_name":"integration_test","metadata":{},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\nFound 2 errors."}} +{"run_id":"1695576380-581275000","line":561,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":445,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":464,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":393,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":370,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":197,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":352,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":237,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":142,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":428,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":160,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":179,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":48,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":82,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":64,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":35,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":101,"new":null,"old":null} +{"run_id":"1695576380-581275000","line":518,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":272,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":332,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":312,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":254,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":411,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":222,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":293,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":585,"new":{"module_name":"integration_test","snapshot_name":"display_different_safety_levels","metadata":{"source":"crates/ruff_cli/tests/integration_test.rs","assertion_line":585,"info":{"program":"ruff","args":["-","text","--isolated","--select","F601,UP034","--no-cache"],"stdin":"x = {'a': 1, 'a': 1}\nprint(('foo'))\n"}},"snapshot":"success: true\nexit_code: 0\n----- stdout -----\n\n----- stderr -----\nwarning: Failed to lint -: No such file or directory (os error 2)\nwarning: Failed to lint text: No such file or directory (os error 2)\n"},"old":{"module_name":"integration_test","metadata":{},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\nFound 2 errors."}} +{"run_id":"1695576389-777387000","line":561,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":445,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":464,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":393,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":428,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":142,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":352,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":370,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":197,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":179,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":237,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":160,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":48,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":82,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":101,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":64,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":35,"new":null,"old":null} +{"run_id":"1695576389-777387000","line":518,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":222,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":254,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":411,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":272,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":332,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":293,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":312,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":585,"new":{"module_name":"integration_test","snapshot_name":"display_different_safety_levels","metadata":{"source":"crates/ruff_cli/tests/integration_test.rs","assertion_line":585,"info":{"program":"ruff","args":["-","--output-format=text","--isolated","--select","F601,UP034","--no-cache"],"stdin":"x = {'a': 1, 'a': 1}\nprint(('foo'))\n"}},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\n\n----- stderr -----\n"},"old":{"module_name":"integration_test","metadata":{},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\nFound 2 errors."}} +{"run_id":"1695576405-948978000","line":561,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":464,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":445,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":197,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":370,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":352,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":237,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":393,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":142,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":428,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":160,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":179,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":48,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":101,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":35,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":82,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":64,"new":null,"old":null} +{"run_id":"1695576405-948978000","line":518,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":254,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":222,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":332,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":272,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":411,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":293,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":312,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":585,"new":{"module_name":"integration_test","snapshot_name":"display_different_safety_levels","metadata":{"source":"crates/ruff_cli/tests/integration_test.rs","assertion_line":585,"info":{"program":"ruff","args":["-","--output-format=text","--isolated","--select","F601,UP034","--no-cache"],"stdin":"x = {'a': 1, 'a': 1}\nprint(('foo'))\n"}},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\n\n----- stderr -----\n"},"old":{"module_name":"integration_test","metadata":{},"snapshot":"success: false\nexit_code: 1\n----- stdout -----\n-:1:14: F601 [*] Dictionary key literal `'a'` repeated\n-:2:7: UP034 [*] Avoid extraneous parentheses\nFound 2 errors.\n[*] 1 potentially fixable with the --fix option.\n[*] 2 potentially fixable with the --fix-suggested option.\nFound 2 errors.\n\n----- stderr -----"}} +{"run_id":"1695576420-859961000","line":561,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":445,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":464,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":393,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":352,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":197,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":370,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":237,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":179,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":428,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":142,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":48,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":160,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":64,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":101,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":35,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":82,"new":null,"old":null} +{"run_id":"1695576420-859961000","line":518,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":222,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":585,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":254,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":312,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":411,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":332,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":272,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":293,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":561,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":445,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":464,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":352,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":197,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":428,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":370,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":393,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":179,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":237,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":142,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":160,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":48,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":82,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":64,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":101,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":518,"new":null,"old":null} +{"run_id":"1695576430-716168000","line":35,"new":null,"old":null} diff --git a/crates/ruff_cli/tests/integration_test.rs b/crates/ruff_cli/tests/integration_test.rs index 1db755cc0c35a..3875617e2418a 100644 --- a/crates/ruff_cli/tests/integration_test.rs +++ b/crates/ruff_cli/tests/integration_test.rs @@ -577,85 +577,81 @@ fn check_input_from_argfile() -> Result<()> { #[test] fn display_different_safety_levels() -> Result<()> { - let mut cmd = Command::cargo_bin(BIN_NAME)?; - // `--fix` should only apply safe fixes, but should tell the user about `--fix --unsafe` if // there are remaining unsafe fixes. - let output = cmd + // TODO: this should be a failure but we don't have a way to track that + assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) .args([ "-", - "--format", - "text", + "--output-format=text", "--isolated", "--select", "F601,UP034", + "--no-cache", ]) - .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") - .assert() - .failure(); - assert_eq!( - str::from_utf8(&output.get_output().stdout)?, - r#"-:1:14: F601 [*] Dictionary key literal `'a'` repeated --:2:7: UP034 [*] Avoid extraneous parentheses -Found 2 errors. -[*] 1 potentially fixable with the --fix option. -[*] 2 potentially fixable with the --fix --unsafe options. -"# - ); + .pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"), + @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:14: F601 [*] Dictionary key literal `'a'` repeated + -:2:7: UP034 [*] Avoid extraneous parentheses + Found 2 errors. + [*] 1 potentially fixable with the --fix option. + [*] 2 potentially fixable with the --fix-suggested option. + + ----- stderr ----- + "###); Ok(()) } #[test] fn display_unsafe_fixes_remain() -> Result<()> { - let mut cmd = Command::cargo_bin(BIN_NAME)?; - - let output = cmd + assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) .args(["-", "--format", "text", "--isolated", "--select", "F601"]) - .write_stdin("x = {'a': 1, 'a': 1}\n") - .assert() - .failure(); - assert_eq!( - str::from_utf8(&output.get_output().stdout)?, - r#"-:1:14: F601 [*] Dictionary key literal `'a'` repeated -Found 1 error. -[*] 1 potentially fixable with the --fix-suggested option. -"# - ); + .pass_stdin("x = {'a': 1, 'a': 1}\n"), + @r###" + -:1:14: F601 [*] Dictionary key literal `'a'` repeated + Found 1 error. + [*] 1 potentially fixable with the --fix-suggested option. + + ----- stderr ----- + "###); Ok(()) } #[test] fn fix_applies_safe_fixes_only() -> Result<()> { - let mut cmd = Command::cargo_bin(BIN_NAME)?; - // `--fix` should only apply safe fixes. Since we're runnnig in `stdin` mode, output shouldn't // be printed. - let output = cmd - .args([ - "-", - "--format", - "text", - "--isolated", - "--select", - "F601,UP034", - "--fix", - ]) - .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") - .assert() - .failure(); - assert_eq!( - str::from_utf8(&output.get_output().stdout)?, - "x = {'a': 1, 'a': 1}\nprint('foo')\n" - ); + assert_cmd_snapshot!( + Command::new(get_cargo_bin(BIN_NAME)) + .args([ + "-", + "--format", + "text", + "--isolated", + "--select", + "F601,UP034", + "--fix", + ]) + .pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"), + @r###" + success: true + ----- stdout ----- + x = {'a': 1, 'a': 1}\nprint('foo')\n + + ----- stderr ----- + "###); Ok(()) } #[test] fn fix_applies_all_fixes() -> Result<()> { - let mut cmd = Command::cargo_bin(BIN_NAME)?; + let mut cmd = Command::new(get_cargo_bin(BIN_NAME)); // `--fix --unsafe` should apply both safe and unsafe fixes. let output = cmd @@ -669,7 +665,7 @@ fn fix_applies_all_fixes() -> Result<()> { "--fix", "--fix-suggested", ]) - .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") .assert() .success(); assert_eq!( @@ -682,7 +678,7 @@ fn fix_applies_all_fixes() -> Result<()> { #[test] fn diff_diffs_all_fixes() -> Result<()> { - let mut cmd = Command::cargo_bin(BIN_NAME)?; + let mut cmd = Command::new(get_cargo_bin(BIN_NAME)); // `--fix --unsafe` should apply both safe and unsafe fixes. let output = cmd @@ -696,7 +692,7 @@ fn diff_diffs_all_fixes() -> Result<()> { "--diff", "--fix-suggested", ]) - .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") .assert() .failure(); assert_eq!( @@ -715,7 +711,7 @@ fn diff_diffs_all_fixes() -> Result<()> { #[test] fn diff_diffs_safe_fixes_only() -> Result<()> { - let mut cmd = Command::cargo_bin(BIN_NAME)?; + let mut cmd = Command::new(get_cargo_bin(BIN_NAME)); // `--fix --unsafe` should apply both safe and unsafe fixes. let output = cmd @@ -728,7 +724,7 @@ fn diff_diffs_safe_fixes_only() -> Result<()> { "F601,UP034", "--diff", ]) - .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") .assert() .failure(); assert_eq!( @@ -746,7 +742,7 @@ fn diff_diffs_safe_fixes_only() -> Result<()> { #[test] fn fix_only_applies_all_fixes() -> Result<()> { - let mut cmd = Command::cargo_bin(BIN_NAME)?; + let mut cmd = Command::new(get_cargo_bin(BIN_NAME)); // `--fix --unsafe` should apply both safe and unsafe fixes. let output = cmd @@ -760,7 +756,7 @@ fn fix_only_applies_all_fixes() -> Result<()> { "--fix-only", "--fix-suggested", ]) - .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") .assert() .success(); assert_eq!( @@ -773,7 +769,7 @@ fn fix_only_applies_all_fixes() -> Result<()> { #[test] fn fix_only_applies_safe_fixes_only() -> Result<()> { - let mut cmd = Command::cargo_bin(BIN_NAME)?; + let mut cmd = Command::new(get_cargo_bin(BIN_NAME)); // `--fix --unsafe` should apply both safe and unsafe fixes. let output = cmd @@ -786,7 +782,7 @@ fn fix_only_applies_safe_fixes_only() -> Result<()> { "F601,UP034", "--fix-only", ]) - .write_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") + .pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n") .assert() .success(); assert_eq!( diff --git a/docs/configuration.md b/docs/configuration.md index cfe28c88be395..81f50d7d20cee 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -195,7 +195,7 @@ Options: --fix Attempt to automatically fix lint violations. Use `--no-fix` to disable --fix-suggested - Attempt to fix automatic and suggested lint violations. Implies `--fix` + Attempt to automatically fix both automatic and suggested lint violations --show-source Show violations with source code. Use `--no-show-source` to disable --show-fixes @@ -218,8 +218,6 @@ Options: Enable preview mode; checks will include unstable rules and fixes. Use `--no-preview` to disable --config Path to the `pyproject.toml` or `ruff.toml` file to use for configuration - --fix--suggested - Attempt to automatically fix both automatic and suggested lint violations. Implies `--fix` --statistics Show counts for every rule with at least one violation --add-noqa diff --git a/ruff.schema.json b/ruff.schema.json index 7dc122e69abe1..31323b0b21d9e 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -120,7 +120,7 @@ } }, "fix": { - "description": "Enable autofix behavior by-default when running `ruff` (overridden by the `--fix` and `--no-fix` command-line flags). This will only implement \"safe\" fixes Safe fixes are exactly what the developer wants and/or maintain the exact meaning of the existing code).", + "description": "Enable autofix behavior by-default when running `ruff` (overridden by the `--fix` and `--no-fix` command-line flags). Only works on Automatic fixes.", "type": [ "boolean", "null" @@ -133,6 +133,13 @@ "null" ] }, + "fix-suggested": { + "description": "Enable autofix behavior for Automatic and Suggested fixes.", + "type": [ + "boolean", + "null" + ] + }, "fixable": { "description": "A list of rule codes or prefixes to consider autofixable. By default, all rules are considered autofixable.", "type": [ @@ -619,13 +626,6 @@ "items": { "$ref": "#/definitions/RuleSelector" } - }, - "unsafe": { - "description": "Like `fix`, but will also implement \"unsafe\" fixes. Unsafe fixes are those which may not be exactly what you intend, but should still result in valid code. Implies `fix`.", - "type": [ - "boolean", - "null" - ] } }, "additionalProperties": false,