diff --git a/crates/ruff/src/args.rs b/crates/ruff/src/args.rs index 71890a0342c55..5742ed4fc176b 100644 --- a/crates/ruff/src/args.rs +++ b/crates/ruff/src/args.rs @@ -11,7 +11,7 @@ use ruff_linter::settings::types::{ ExtensionPair, FilePattern, PatternPrefixPair, PerFileIgnore, PreviewMode, PythonVersion, SerializationFormat, UnsafeFixes, }; -use ruff_linter::{RuleParser, RuleSelector, RuleSelectorParser}; +use ruff_linter::{warn_user, RuleParser, RuleSelector, RuleSelectorParser}; use ruff_workspace::configuration::{Configuration, RuleSelection}; use ruff_workspace::options::PycodestyleOptions; use ruff_workspace::resolver::ConfigurationTransformer; @@ -104,6 +104,7 @@ pub struct CheckCommand { no_unsafe_fixes: bool, /// Show violations with source code. /// Use `--no-show-source` to disable. + /// (Deprecated: use `--output-format=full` or `--output-format=concise` instead of `--show-source` and `--no-show-source`, respectively) #[arg(long, overrides_with("no_show_source"))] show_source: bool, #[clap(long, overrides_with("show_source"), hide = true)] @@ -131,6 +132,8 @@ pub struct CheckCommand { ignore_noqa: bool, /// Output serialization format for violations. + /// The default serialization format is "concise". + /// In preview mode, the default serialization format is "full". #[arg(long, value_enum, env = "RUFF_OUTPUT_FORMAT")] pub output_format: Option, @@ -533,7 +536,6 @@ impl CheckCommand { self.no_respect_gitignore, ), select: self.select, - show_source: resolve_bool_arg(self.show_source, self.no_show_source), target_version: self.target_version, unfixable: self.unfixable, // TODO(charlie): Included in `pyproject.toml`, but not inherited. @@ -543,7 +545,11 @@ impl CheckCommand { unsafe_fixes: resolve_bool_arg(self.unsafe_fixes, self.no_unsafe_fixes) .map(UnsafeFixes::from), force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude), - output_format: self.output_format, + output_format: resolve_output_format( + self.output_format, + resolve_bool_arg(self.show_source, self.no_show_source), + resolve_bool_arg(self.preview, self.no_preview).unwrap_or_default(), + ), show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes), extension: self.extension, }, @@ -594,6 +600,43 @@ fn resolve_bool_arg(yes: bool, no: bool) -> Option { } } +fn resolve_output_format( + output_format: Option, + show_sources: Option, + preview: bool, +) -> Option { + Some(match (output_format, show_sources) { + (Some(o), None) => o, + (Some(SerializationFormat::Grouped), Some(true)) => { + warn_user!("`--show-source` with `--output-format=grouped` is deprecated, and will not show source files. Use `--output-format=full` to show source information."); + SerializationFormat::Grouped + } + (Some(fmt), Some(true)) => { + warn_user!("The `--show-source` argument is deprecated and has been ignored in favor of `--output-format={fmt}`."); + fmt + } + (Some(fmt), Some(false)) => { + warn_user!("The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format={fmt}`."); + fmt + } + (None, Some(true)) => { + warn_user!("The `--show-source` argument is deprecated. Use `--output-format=full` instead."); + SerializationFormat::Full + } + (None, Some(false)) => { + warn_user!("The `--no-show-source` argument is deprecated. Use `--output-format=concise` instead."); + SerializationFormat::Concise + } + (None, None) => return None + }).map(|format| match format { + SerializationFormat::Text => { + warn_user!("`--output-format=text` is deprecated. Use `--output-format=full` or `--output-format=concise` instead. `text` will be treated as `{}`.", SerializationFormat::default(preview)); + SerializationFormat::default(preview) + }, + other => other + }) +} + /// CLI settings that are distinct from configuration (commands, lists of files, /// etc.). #[allow(clippy::struct_excessive_bools)] @@ -648,7 +691,6 @@ pub struct CliOverrides { pub preview: Option, pub respect_gitignore: Option, pub select: Option>, - pub show_source: Option, pub target_version: Option, pub unfixable: Option>, // TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`. @@ -735,9 +777,6 @@ impl ConfigurationTransformer for CliOverrides { if let Some(respect_gitignore) = &self.respect_gitignore { config.respect_gitignore = Some(*respect_gitignore); } - if let Some(show_source) = &self.show_source { - config.show_source = Some(*show_source); - } if let Some(show_fixes) = &self.show_fixes { config.show_fixes = Some(*show_fixes); } diff --git a/crates/ruff/src/lib.rs b/crates/ruff/src/lib.rs index f8f99505b3c91..38fdcb8c4eea7 100644 --- a/crates/ruff/src/lib.rs +++ b/crates/ruff/src/lib.rs @@ -255,7 +255,6 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result { unsafe_fixes, output_format, show_fixes, - show_source, .. } = pyproject_config.settings; @@ -284,9 +283,6 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result { if show_fixes { printer_flags |= PrinterFlags::SHOW_FIX_SUMMARY; } - if show_source { - printer_flags |= PrinterFlags::SHOW_SOURCE; - } if cli.ecosystem_ci { warn_user!( "The formatting of fixes emitted by this option is a work-in-progress, subject to \ @@ -325,9 +321,14 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result { printer_flags, ); + let preview = overrides.preview.unwrap_or_default().is_enabled(); + if cli.watch { - if output_format != SerializationFormat::Text { - warn_user!("`--output-format text` is always used in watch mode."); + if output_format != SerializationFormat::default(preview) { + warn_user!( + "`--output-format {}` is always used in watch mode.", + SerializationFormat::default(preview) + ); } // Configure the file watcher. @@ -353,7 +354,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result { fix_mode, unsafe_fixes, )?; - printer.write_continuously(&mut writer, &messages)?; + printer.write_continuously(&mut writer, &messages, preview)?; // In watch mode, we may need to re-resolve the configuration. // TODO(charlie): Re-compute other derivative values, like the `printer`. @@ -386,7 +387,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result { fix_mode, unsafe_fixes, )?; - printer.write_continuously(&mut writer, &messages)?; + printer.write_continuously(&mut writer, &messages, preview)?; } Err(err) => return Err(err.into()), } diff --git a/crates/ruff/src/printer.rs b/crates/ruff/src/printer.rs index 5bfdece63beaa..13a0d966dfe3c 100644 --- a/crates/ruff/src/printer.rs +++ b/crates/ruff/src/printer.rs @@ -27,8 +27,6 @@ bitflags! { pub(crate) struct Flags: u8 { /// Whether to show violations when emitting diagnostics. const SHOW_VIOLATIONS = 0b0000_0001; - /// Whether to show the source code when emitting diagnostics. - const SHOW_SOURCE = 0b000_0010; /// Whether to show a summary of the fixed violations when emitting diagnostics. const SHOW_FIX_SUMMARY = 0b0000_0100; /// Whether to show a diff of each fixed violation when emitting diagnostics. @@ -218,7 +216,10 @@ impl Printer { if !self.flags.intersects(Flags::SHOW_VIOLATIONS) { if matches!( self.format, - SerializationFormat::Text | SerializationFormat::Grouped + SerializationFormat::Text + | SerializationFormat::Full + | SerializationFormat::Concise + | SerializationFormat::Grouped ) { if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) { if !diagnostics.fixed.is_empty() { @@ -245,11 +246,12 @@ impl Printer { SerializationFormat::Junit => { JunitEmitter.emit(writer, &diagnostics.messages, &context)?; } - SerializationFormat::Text => { + SerializationFormat::Concise + | SerializationFormat::Full => { TextEmitter::default() .with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref())) .with_show_fix_diff(self.flags.intersects(Flags::SHOW_FIX_DIFF)) - .with_show_source(self.flags.intersects(Flags::SHOW_SOURCE)) + .with_show_source(self.format == SerializationFormat::Full) .with_unsafe_fixes(self.unsafe_fixes) .emit(writer, &diagnostics.messages, &context)?; @@ -265,7 +267,6 @@ impl Printer { } SerializationFormat::Grouped => { GroupedEmitter::default() - .with_show_source(self.flags.intersects(Flags::SHOW_SOURCE)) .with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref())) .with_unsafe_fixes(self.unsafe_fixes) .emit(writer, &diagnostics.messages, &context)?; @@ -294,6 +295,7 @@ impl Printer { SerializationFormat::Sarif => { SarifEmitter.emit(writer, &diagnostics.messages, &context)?; } + SerializationFormat::Text => unreachable!("Text is deprecated and should have been automatically converted to the default serialization format") } writer.flush()?; @@ -342,7 +344,9 @@ impl Printer { } match self.format { - SerializationFormat::Text => { + SerializationFormat::Text + | SerializationFormat::Full + | SerializationFormat::Concise => { // Compute the maximum number of digits in the count and code, for all messages, // to enable pretty-printing. let count_width = num_digits( @@ -403,6 +407,7 @@ impl Printer { &self, writer: &mut dyn Write, diagnostics: &Diagnostics, + preview: bool, ) -> Result<()> { if matches!(self.log_level, LogLevel::Silent) { return Ok(()); @@ -430,7 +435,7 @@ impl Printer { let context = EmitterContext::new(&diagnostics.notebook_indexes); TextEmitter::default() .with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref())) - .with_show_source(self.flags.intersects(Flags::SHOW_SOURCE)) + .with_show_source(preview) .with_unsafe_fixes(self.unsafe_fixes) .emit(writer, &diagnostics.messages, &context)?; } diff --git a/crates/ruff/tests/deprecation.rs b/crates/ruff/tests/deprecation.rs new file mode 100644 index 0000000000000..0d3215d3bdeb3 --- /dev/null +++ b/crates/ruff/tests/deprecation.rs @@ -0,0 +1,149 @@ +//! A test suite that ensures deprecated command line options have appropriate warnings / behaviors + +use ruff_linter::settings::types::SerializationFormat; +use std::process::Command; + +use insta_cmd::{assert_cmd_snapshot, get_cargo_bin}; + +const BIN_NAME: &str = "ruff"; + +const STDIN: &str = "l = 1"; + +fn ruff_check(show_source: Option, output_format: Option) -> Command { + let mut cmd = Command::new(get_cargo_bin(BIN_NAME)); + let output_format = output_format.unwrap_or(format!("{}", SerializationFormat::default(false))); + cmd.arg("--output-format"); + cmd.arg(output_format); + cmd.arg("--no-cache"); + match show_source { + Some(true) => { + cmd.arg("--show-source"); + } + Some(false) => { + cmd.arg("--no-show-source"); + } + None => {} + } + cmd.arg("-"); + + cmd +} + +#[test] +fn ensure_show_source_is_deprecated() { + assert_cmd_snapshot!(ruff_check(Some(true), None).pass_stdin(STDIN), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + Found 1 error. + + ----- stderr ----- + warning: The `--show-source` argument is deprecated and has been ignored in favor of `--output-format=concise`. + "###); +} + +#[test] +fn ensure_no_show_source_is_deprecated() { + assert_cmd_snapshot!(ruff_check(Some(false), None).pass_stdin(STDIN), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + Found 1 error. + + ----- stderr ----- + warning: The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format=concise`. + "###); +} + +#[test] +fn ensure_output_format_is_deprecated() { + assert_cmd_snapshot!(ruff_check(None, Some("text".into())).pass_stdin(STDIN), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + Found 1 error. + + ----- stderr ----- + warning: `--output-format=text` is deprecated. Use `--output-format=full` or `--output-format=concise` instead. `text` will be treated as `concise`. + "###); +} + +#[test] +fn ensure_output_format_overrides_show_source() { + assert_cmd_snapshot!(ruff_check(Some(true), Some("concise".into())).pass_stdin(STDIN), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + Found 1 error. + + ----- stderr ----- + warning: The `--show-source` argument is deprecated and has been ignored in favor of `--output-format=concise`. + "###); +} + +#[test] +fn ensure_full_output_format_overrides_no_show_source() { + assert_cmd_snapshot!(ruff_check(Some(false), Some("full".into())).pass_stdin(STDIN), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + | + 1 | l = 1 + | ^ E741 + | + + Found 1 error. + + ----- stderr ----- + warning: The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format=full`. + "###); +} + +#[test] +fn ensure_output_format_uses_concise_over_no_show_source() { + assert_cmd_snapshot!(ruff_check(Some(false), Some("concise".into())).pass_stdin(STDIN), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + Found 1 error. + + ----- stderr ----- + warning: The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format=concise`. + "###); +} + +#[test] +fn ensure_deprecated_output_format_overrides_show_source() { + assert_cmd_snapshot!(ruff_check(Some(true), Some("text".into())).pass_stdin(STDIN), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + Found 1 error. + + ----- stderr ----- + warning: The `--show-source` argument is deprecated and has been ignored in favor of `--output-format=text`. + warning: `--output-format=text` is deprecated. Use `--output-format=full` or `--output-format=concise` instead. `text` will be treated as `concise`. + "###); +} + +#[test] +fn ensure_deprecated_output_format_overrides_no_show_source() { + assert_cmd_snapshot!(ruff_check(Some(false), Some("text".into())).pass_stdin(STDIN), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + Found 1 error. + + ----- stderr ----- + warning: The `--no-show-source` argument is deprecated and has been ignored in favor of `--output-format=text`. + warning: `--output-format=text` is deprecated. Use `--output-format=full` or `--output-format=concise` instead. `text` will be treated as `concise`. + "###); +} diff --git a/crates/ruff/tests/format.rs b/crates/ruff/tests/format.rs index d7a255fb555ac..b834014146c64 100644 --- a/crates/ruff/tests/format.rs +++ b/crates/ruff/tests/format.rs @@ -508,6 +508,11 @@ if __name__ == '__main__': say_hy("dear Ruff contributor") ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'extend-select' -> 'lint.extend-select' + - 'ignore' -> 'lint.ignore' + + "###); Ok(()) } @@ -546,6 +551,11 @@ if __name__ == '__main__': say_hy("dear Ruff contributor") ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'extend-select' -> 'lint.extend-select' + - 'ignore' -> 'lint.ignore' + + "###); Ok(()) } diff --git a/crates/ruff/tests/integration_test.rs b/crates/ruff/tests/integration_test.rs index d21b44aa470f2..b794495d1fd80 100644 --- a/crates/ruff/tests/integration_test.rs +++ b/crates/ruff/tests/integration_test.rs @@ -31,25 +31,14 @@ fn ruff_cmd() -> Command { } /// Builder for `ruff check` commands. -#[derive(Debug)] +#[derive(Debug, Default)] struct RuffCheck<'a> { - output_format: &'a str, + output_format: Option<&'a str>, config: Option<&'a Path>, filename: Option<&'a str>, args: Vec<&'a str>, } -impl<'a> Default for RuffCheck<'a> { - fn default() -> RuffCheck<'a> { - RuffCheck { - output_format: "text", - config: None, - filename: None, - args: vec![], - } - } -} - impl<'a> RuffCheck<'a> { /// Set the `--config` option. #[must_use] @@ -61,7 +50,7 @@ impl<'a> RuffCheck<'a> { /// Set the `--output-format` option. #[must_use] fn output_format(mut self, format: &'a str) -> Self { - self.output_format = format; + self.output_format = Some(format); self } @@ -82,7 +71,11 @@ impl<'a> RuffCheck<'a> { /// Generate a [`Command`] for the `ruff check` command. fn build(self) -> Command { let mut cmd = ruff_cmd(); - cmd.args(["--output-format", self.output_format, "--no-cache"]); + if let Some(output_format) = self.output_format { + cmd.args(["--output-format", output_format]); + } + cmd.arg("--no-cache"); + if let Some(path) = self.config { cmd.arg("--config"); cmd.arg(path); @@ -743,8 +736,28 @@ fn stdin_parse_error() { } #[test] -fn show_source() { - let mut cmd = RuffCheck::default().args(["--show-source"]).build(); +fn full_output_preview() { + let mut cmd = RuffCheck::default().args(["--preview"]).build(); + assert_cmd_snapshot!(cmd + .pass_stdin("l = 1"), @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: E741 Ambiguous variable name: `l` + | + 1 | l = 1 + | ^ E741 + | + + Found 1 error. + + ----- stderr ----- + "###); +} + +#[test] +fn full_output_format() { + let mut cmd = RuffCheck::default().output_format("full").build(); assert_cmd_snapshot!(cmd .pass_stdin("l = 1"), @r###" success: false @@ -800,7 +813,9 @@ fn show_statistics() { #[test] fn nursery_prefix() { // Should only detect RUF90X, but not the unstable test rules - let mut cmd = RuffCheck::default().args(["--select", "RUF9"]).build(); + let mut cmd = RuffCheck::default() + .args(["--select", "RUF9", "--output-format=concise"]) + .build(); assert_cmd_snapshot!(cmd, @r###" success: false exit_code: 1 @@ -809,7 +824,10 @@ fn nursery_prefix() { -:1:1: RUF901 [*] Hey this is a stable test rule with a safe fix. -:1:1: RUF902 Hey this is a stable test rule with an unsafe fix. -:1:1: RUF903 Hey this is a stable test rule with a display only fix. - Found 4 errors. + -:1:1: RUF920 Hey this is a deprecated test rule. + -:1:1: RUF921 Hey this is another deprecated test rule. + -:1:1: RUF950 Hey this is a test rule that was redirected from another. + Found 7 errors. [*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option). ----- stderr ----- @@ -819,7 +837,9 @@ fn nursery_prefix() { #[test] fn nursery_all() { // Should detect RUF90X, but not the unstable test rules - let mut cmd = RuffCheck::default().args(["--select", "ALL"]).build(); + let mut cmd = RuffCheck::default() + .args(["--select", "ALL", "--output-format=concise"]) + .build(); assert_cmd_snapshot!(cmd, @r###" success: false exit_code: 1 @@ -829,7 +849,10 @@ fn nursery_all() { -:1:1: RUF901 [*] Hey this is a stable test rule with a safe fix. -:1:1: RUF902 Hey this is a stable test rule with an unsafe fix. -:1:1: RUF903 Hey this is a stable test rule with a display only fix. - Found 5 errors. + -:1:1: RUF920 Hey this is a deprecated test rule. + -:1:1: RUF921 Hey this is another deprecated test rule. + -:1:1: RUF950 Hey this is a test rule that was redirected from another. + Found 8 errors. [*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option). ----- stderr ----- @@ -840,41 +863,44 @@ fn nursery_all() { #[test] fn nursery_direct() { - // Should warn that the nursery rule is selected without preview flag but still - // include the diagnostic - let mut cmd = RuffCheck::default().args(["--select", "RUF912"]).build(); + // Should fail when a nursery rule is selected without the preview flag + // Before Ruff v0.2.0 this would warn + let mut cmd = RuffCheck::default() + .args(["--select", "RUF912", "--output-format=concise"]) + .build(); assert_cmd_snapshot!(cmd, @r###" success: false - exit_code: 1 + exit_code: 2 ----- stdout ----- - -:1:1: RUF912 Hey this is a nursery test rule. - Found 1 error. ----- stderr ----- - warning: Selection of nursery rule `RUF912` without the `--preview` flag is deprecated. + ruff failed + Cause: Selection of unstable rule `RUF912` without the `--preview` flag is not allowed. "###); } #[test] fn nursery_group_selector() { - // Only nursery rules should be detected e.g. RUF912 - let mut cmd = RuffCheck::default().args(["--select", "NURSERY"]).build(); + // The NURSERY selector is removed but parses in the CLI for a nicer error message + // Before Ruff v0.2.0 this would warn + let mut cmd = RuffCheck::default() + .args(["--select", "NURSERY", "--output-format=concise"]) + .build(); assert_cmd_snapshot!(cmd, @r###" success: false - exit_code: 1 + exit_code: 2 ----- stdout ----- - -:1:1: CPY001 Missing copyright notice at top of file - -:1:1: RUF912 Hey this is a nursery test rule. - Found 2 errors. ----- stderr ----- - warning: The `NURSERY` selector has been deprecated. Use the `--preview` flag instead. + ruff failed + Cause: The `NURSERY` selector was removed. Use the `--preview` flag instead. "###); } #[test] fn nursery_group_selector_preview_enabled() { - // A warning should be displayed due to deprecated selector usage + // When preview mode is enabled, we shouldn't suggest using the `--preview` flag. + // Before Ruff v0.2.0 this would warn let mut cmd = RuffCheck::default() .args(["--select", "NURSERY", "--preview"]) .build(); @@ -885,7 +911,7 @@ fn nursery_group_selector_preview_enabled() { ----- stderr ----- ruff failed - Cause: The `NURSERY` selector is deprecated and cannot be used with preview mode enabled. + Cause: The `NURSERY` selector was removed. Unstable rules should be selected individually or by their respective groups. "###); } @@ -893,7 +919,7 @@ fn nursery_group_selector_preview_enabled() { fn preview_enabled_prefix() { // All the RUF9XX test rules should be triggered let mut cmd = RuffCheck::default() - .args(["--select", "RUF9", "--preview"]) + .args(["--select", "RUF9", "--output-format=concise", "--preview"]) .build(); assert_cmd_snapshot!(cmd, @r###" success: false @@ -905,7 +931,8 @@ fn preview_enabled_prefix() { -:1:1: RUF903 Hey this is a stable test rule with a display only fix. -:1:1: RUF911 Hey this is a preview test rule. -:1:1: RUF912 Hey this is a nursery test rule. - Found 6 errors. + -:1:1: RUF950 Hey this is a test rule that was redirected from another. + Found 7 errors. [*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option). ----- stderr ----- @@ -915,7 +942,7 @@ fn preview_enabled_prefix() { #[test] fn preview_enabled_all() { let mut cmd = RuffCheck::default() - .args(["--select", "ALL", "--preview"]) + .args(["--select", "ALL", "--output-format=concise", "--preview"]) .build(); assert_cmd_snapshot!(cmd, @r###" success: false @@ -929,7 +956,8 @@ fn preview_enabled_all() { -:1:1: RUF903 Hey this is a stable test rule with a display only fix. -:1:1: RUF911 Hey this is a preview test rule. -:1:1: RUF912 Hey this is a nursery test rule. - Found 8 errors. + -:1:1: RUF950 Hey this is a test rule that was redirected from another. + Found 9 errors. [*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option). ----- stderr ----- @@ -942,7 +970,7 @@ fn preview_enabled_all() { fn preview_enabled_direct() { // Should be enabled without warning let mut cmd = RuffCheck::default() - .args(["--select", "RUF911", "--preview"]) + .args(["--select", "RUF911", "--output-format=concise", "--preview"]) .build(); assert_cmd_snapshot!(cmd, @r###" success: false @@ -957,36 +985,42 @@ fn preview_enabled_direct() { #[test] fn preview_disabled_direct() { - // RUFF911 is preview not nursery so the selection should be empty - let mut cmd = RuffCheck::default().args(["--select", "RUF911"]).build(); + // RUFF911 is preview so we should warn without selecting + let mut cmd = RuffCheck::default() + .args(["--select", "RUF911", "--output-format=concise"]) + .build(); assert_cmd_snapshot!(cmd, @r###" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: Selection `RUF911` has no effect because the `--preview` flag was not included. + warning: Selection `RUF911` has no effect because preview is not enabled. "###); } #[test] fn preview_disabled_prefix_empty() { // Warns that the selection is empty since all of the RUF91 rules are in preview - let mut cmd = RuffCheck::default().args(["--select", "RUF91"]).build(); + let mut cmd = RuffCheck::default() + .args(["--select", "RUF91", "--output-format=concise"]) + .build(); assert_cmd_snapshot!(cmd, @r###" success: true exit_code: 0 ----- stdout ----- ----- stderr ----- - warning: Selection `RUF91` has no effect because the `--preview` flag was not included. + warning: Selection `RUF91` has no effect because preview is not enabled. "###); } #[test] fn preview_disabled_does_not_warn_for_empty_ignore_selections() { // Does not warn that the selection is empty since the user is not trying to enable the rule - let mut cmd = RuffCheck::default().args(["--ignore", "RUF9"]).build(); + let mut cmd = RuffCheck::default() + .args(["--ignore", "RUF9", "--output-format=concise"]) + .build(); assert_cmd_snapshot!(cmd, @r###" success: true exit_code: 0 @@ -999,7 +1033,9 @@ fn preview_disabled_does_not_warn_for_empty_ignore_selections() { #[test] fn preview_disabled_does_not_warn_for_empty_fixable_selections() { // Does not warn that the selection is empty since the user is not trying to enable the rule - let mut cmd = RuffCheck::default().args(["--fixable", "RUF9"]).build(); + let mut cmd = RuffCheck::default() + .args(["--fixable", "RUF9", "--output-format=concise"]) + .build(); assert_cmd_snapshot!(cmd, @r###" success: true exit_code: 0 @@ -1013,7 +1049,12 @@ fn preview_disabled_does_not_warn_for_empty_fixable_selections() { fn preview_group_selector() { // `--select PREVIEW` should error (selector was removed) let mut cmd = RuffCheck::default() - .args(["--select", "PREVIEW", "--preview"]) + .args([ + "--select", + "PREVIEW", + "--preview", + "--output-format=concise", + ]) .build(); assert_cmd_snapshot!(cmd .pass_stdin("I=42\n"), @r###" @@ -1032,10 +1073,16 @@ fn preview_group_selector() { fn preview_enabled_group_ignore() { // Should detect stable and unstable rules, RUF9 is more specific than RUF so ignore has no effect let mut cmd = RuffCheck::default() - .args(["--select", "RUF9", "--ignore", "RUF", "--preview"]) + .args([ + "--select", + "RUF9", + "--ignore", + "RUF", + "--preview", + "--output-format=concise", + ]) .build(); - assert_cmd_snapshot!(cmd - .pass_stdin("I=42\n"), @r###" + assert_cmd_snapshot!(cmd, @r###" success: false exit_code: 1 ----- stdout ----- @@ -1045,13 +1092,217 @@ fn preview_enabled_group_ignore() { -:1:1: RUF903 Hey this is a stable test rule with a display only fix. -:1:1: RUF911 Hey this is a preview test rule. -:1:1: RUF912 Hey this is a nursery test rule. - Found 6 errors. + -:1:1: RUF950 Hey this is a test rule that was redirected from another. + Found 7 errors. [*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option). ----- stderr ----- "###); } +#[test] +fn removed_direct() { + // Selection of a removed rule should fail + let mut cmd = RuffCheck::default().args(["--select", "RUF931"]).build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + ruff failed + Cause: Rule `RUF931` was removed and cannot be selected. + "###); +} + +#[test] +fn removed_direct_multiple() { + // Selection of multiple removed rule should fail with a message + // including all the rules + let mut cmd = RuffCheck::default() + .args(["--select", "RUF930", "--select", "RUF931"]) + .build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + ruff failed + Cause: The following rules have been removed and cannot be selected: + - RUF930 + - RUF931 + + "###); +} + +#[test] +fn removed_indirect() { + // Selection _including_ a removed rule without matching should not fail + // nor should the rule be used + let mut cmd = RuffCheck::default().args(["--select", "RUF93"]).build(); + assert_cmd_snapshot!(cmd, @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + "###); +} + +#[test] +fn redirect_direct() { + // Selection of a redirected rule directly should use the new rule and warn + let mut cmd = RuffCheck::default().args(["--select", "RUF940"]).build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: RUF950 Hey this is a test rule that was redirected from another. + Found 1 error. + + ----- stderr ----- + warning: `RUF940` has been remapped to `RUF950`. + "###); +} + +#[test] +fn redirect_indirect() { + // Selection _including_ a redirected rule without matching should not fail + // nor should the rule be used + let mut cmd = RuffCheck::default().args(["--select", "RUF94"]).build(); + assert_cmd_snapshot!(cmd, @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + "###); +} + +#[test] +fn redirect_prefix() { + // Selection using a redirected prefix should switch to all rules in the + // new prefix + let mut cmd = RuffCheck::default().args(["--select", "RUF96"]).build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: RUF950 Hey this is a test rule that was redirected from another. + Found 1 error. + + ----- stderr ----- + warning: `RUF96` has been remapped to `RUF95`. + "###); +} + +#[test] +fn deprecated_direct() { + // Selection of a deprecated rule without preview enabled should still work + // but a warning should be displayed + let mut cmd = RuffCheck::default().args(["--select", "RUF920"]).build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: RUF920 Hey this is a deprecated test rule. + Found 1 error. + + ----- stderr ----- + warning: Rule `RUF920` is deprecated and will be removed in a future release. + "###); +} + +#[test] +fn deprecated_multiple_direct() { + let mut cmd = RuffCheck::default() + .args(["--select", "RUF920", "--select", "RUF921"]) + .build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: RUF920 Hey this is a deprecated test rule. + -:1:1: RUF921 Hey this is another deprecated test rule. + Found 2 errors. + + ----- stderr ----- + warning: Rule `RUF920` is deprecated and will be removed in a future release. + warning: Rule `RUF921` is deprecated and will be removed in a future release. + "###); +} + +#[test] +fn deprecated_indirect() { + // `RUF92` includes deprecated rules but should not warn + // since it is not a "direct" selection + let mut cmd = RuffCheck::default().args(["--select", "RUF92"]).build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 1 + ----- stdout ----- + -:1:1: RUF920 Hey this is a deprecated test rule. + -:1:1: RUF921 Hey this is another deprecated test rule. + Found 2 errors. + + ----- stderr ----- + "###); +} + +#[test] +fn deprecated_direct_preview_enabled() { + // Direct selection of a deprecated rule in preview should fail + let mut cmd = RuffCheck::default() + .args(["--select", "RUF920", "--preview"]) + .build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + ruff failed + Cause: Selection of deprecated rule `RUF920` is not allowed when preview is enabled. + "###); +} + +#[test] +fn deprecated_indirect_preview_enabled() { + // `RUF920` is deprecated and should be off by default in preview. + let mut cmd = RuffCheck::default() + .args(["--select", "RUF92", "--preview"]) + .build(); + assert_cmd_snapshot!(cmd, @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + "###); +} + +#[test] +fn deprecated_multiple_direct_preview_enabled() { + // Direct selection of the deprecated rules in preview should fail with + // a message listing all of the rule codes + let mut cmd = RuffCheck::default() + .args(["--select", "RUF920", "--select", "RUF921", "--preview"]) + .build(); + assert_cmd_snapshot!(cmd, @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + ruff failed + Cause: Selection of deprecated rules is not allowed when preview is enabled. Remove selection of: + - RUF920 + - RUF921 + + "###); +} + /// An unreadable pyproject.toml in non-isolated mode causes ruff to hard-error trying to build up /// configuration globs #[cfg(unix)] @@ -1569,7 +1820,10 @@ extend-safe-fixes = ["RUF9"] -:1:1: RUF901 Hey this is a stable test rule with a safe fix. -:1:1: RUF902 [*] Hey this is a stable test rule with an unsafe fix. -:1:1: RUF903 Hey this is a stable test rule with a display only fix. - Found 4 errors. + -:1:1: RUF920 Hey this is a deprecated test rule. + -:1:1: RUF921 Hey this is another deprecated test rule. + -:1:1: RUF950 Hey this is a test rule that was redirected from another. + Found 7 errors. [*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option). ----- stderr ----- diff --git a/crates/ruff/tests/lint.rs b/crates/ruff/tests/lint.rs index 7787c9a234ec6..7cc55eab8ccd9 100644 --- a/crates/ruff/tests/lint.rs +++ b/crates/ruff/tests/lint.rs @@ -11,7 +11,7 @@ use insta_cmd::{assert_cmd_snapshot, get_cargo_bin}; use tempfile::TempDir; const BIN_NAME: &str = "ruff"; -const STDIN_BASE_OPTIONS: &[&str] = &["--no-cache", "--output-format", "text"]; +const STDIN_BASE_OPTIONS: &[&str] = &["--no-cache", "--output-format", "concise"]; #[test] fn top_level_options() -> Result<()> { @@ -44,6 +44,11 @@ inline-quotes = "single" [*] 2 fixable with the `--fix` option. ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'extend-select' -> 'lint.extend-select' + - 'flake8-quotes' -> 'lint.flake8-quotes' + + "###); Ok(()) } @@ -114,6 +119,10 @@ inline-quotes = "single" [*] 2 fixable with the `--fix` option. ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'extend-select' -> 'lint.extend-select' + + "###); Ok(()) } @@ -153,6 +162,10 @@ inline-quotes = "single" [*] 2 fixable with the `--fix` option. ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'flake8-quotes' -> 'lint.flake8-quotes' + + "###); Ok(()) } @@ -228,6 +241,10 @@ OTHER = "OTHER" [*] 3 fixable with the `--fix` option. ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'extend-select' -> 'lint.extend-select' + + "###); Ok(()) } @@ -271,6 +288,10 @@ if __name__ == "__main__": [*] 2 fixable with the `--fix` option. ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'extend-select' -> 'lint.extend-select' + + "###); Ok(()) } @@ -309,6 +330,11 @@ _ = "--------------------------------------------------------------------------- Found 1 error. ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'select' -> 'lint.select' + - 'pycodestyle' -> 'lint.pycodestyle' + + "###); Ok(()) } @@ -351,6 +377,10 @@ if __name__ == "__main__": [*] 1 fixable with the `--fix` option. ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'extend-select' -> 'lint.extend-select' + + "###); Ok(()) } @@ -393,6 +423,10 @@ if __name__ == "__main__": [*] 1 fixable with the `--fix` option. ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'extend-select' -> 'lint.extend-select' + + "###); Ok(()) } diff --git a/crates/ruff/tests/resolve_files.rs b/crates/ruff/tests/resolve_files.rs index b9f75a8760b2a..6b8ea0044cbfd 100644 --- a/crates/ruff/tests/resolve_files.rs +++ b/crates/ruff/tests/resolve_files.rs @@ -39,6 +39,10 @@ fn check_project_include_defaults() { [BASEPATH]/include-test/subdirectory/c.py ----- stderr ----- + warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration: + - 'select' -> 'lint.select' + + "###); }); } diff --git a/crates/ruff/tests/snapshots/integration_test__explain_status_codes_f401.snap b/crates/ruff/tests/snapshots/integration_test__explain_status_codes_f401.snap index f2383eed68ef3..b3ed5111bae35 100644 --- a/crates/ruff/tests/snapshots/integration_test__explain_status_codes_f401.snap +++ b/crates/ruff/tests/snapshots/integration_test__explain_status_codes_f401.snap @@ -51,7 +51,7 @@ else: ``` ## Options -- `pyflakes.extend-generics` +- `lint.pyflakes.extend-generics` ## References - [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement) diff --git a/crates/ruff/tests/snapshots/show_settings__display_default_settings.snap b/crates/ruff/tests/snapshots/show_settings__display_default_settings.snap index ae92e615f048f..37b5699286474 100644 --- a/crates/ruff/tests/snapshots/show_settings__display_default_settings.snap +++ b/crates/ruff/tests/snapshots/show_settings__display_default_settings.snap @@ -17,9 +17,8 @@ Settings path: "[BASEPATH]/pyproject.toml" cache_dir = "[BASEPATH]/.ruff_cache" fix = false fix_only = false -output_format = text +output_format = concise show_fixes = false -show_source = false unsafe_fixes = hint # File Resolver Settings diff --git a/crates/ruff_dev/src/generate_docs.rs b/crates/ruff_dev/src/generate_docs.rs index db9402b14699d..309a61a459fcb 100644 --- a/crates/ruff_dev/src/generate_docs.rs +++ b/crates/ruff_dev/src/generate_docs.rs @@ -26,9 +26,9 @@ pub(crate) fn main(args: &Args) -> Result<()> { for rule in Rule::iter() { if let Some(explanation) = rule.explanation() { let mut output = String::new(); + output.push_str(&format!("# {} ({})", rule.as_ref(), rule.noqa_code())); output.push('\n'); - output.push('\n'); let (linter, _) = Linter::parse_code(&rule.noqa_code().to_string()).unwrap(); if linter.url().is_some() { @@ -37,6 +37,22 @@ pub(crate) fn main(args: &Args) -> Result<()> { output.push('\n'); } + if rule.is_deprecated() { + output.push_str( + r"**Warning: This rule is deprecated and will be removed in a future release.**", + ); + output.push('\n'); + output.push('\n'); + } + + if rule.is_removed() { + output.push_str( + r"**Warning: This rule has been removed and its documentation is only available for historical reasons.**", + ); + output.push('\n'); + output.push('\n'); + } + let fix_availability = rule.fixable(); if matches!( fix_availability, @@ -116,7 +132,7 @@ fn process_documentation(documentation: &str, out: &mut String, rule_name: &str) } } - let anchor = option.replace('.', "-"); + let anchor = option.replace('.', "_"); out.push_str(&format!("- [`{option}`][{option}]\n")); after.push_str(&format!("[{option}]: ../settings.md#{anchor}\n")); @@ -142,13 +158,13 @@ mod tests { let mut output = String::new(); process_documentation( " -See also [`mccabe.max-complexity`] and [`task-tags`]. +See also [`lint.mccabe.max-complexity`] and [`lint.task-tags`]. Something [`else`][other]. ## Options -- `task-tags` -- `mccabe.max-complexity` +- `lint.task-tags` +- `lint.mccabe.max-complexity` [other]: http://example.com.", &mut output, @@ -157,18 +173,18 @@ Something [`else`][other]. assert_eq!( output, " -See also [`mccabe.max-complexity`][mccabe.max-complexity] and [`task-tags`][task-tags]. +See also [`lint.mccabe.max-complexity`][lint.mccabe.max-complexity] and [`lint.task-tags`][lint.task-tags]. Something [`else`][other]. ## Options -- [`task-tags`][task-tags] -- [`mccabe.max-complexity`][mccabe.max-complexity] +- [`lint.task-tags`][lint.task-tags] +- [`lint.mccabe.max-complexity`][lint.mccabe.max-complexity] [other]: http://example.com. -[task-tags]: ../settings.md#task-tags -[mccabe.max-complexity]: ../settings.md#mccabe-max-complexity +[lint.task-tags]: ../settings.md#lint_task-tags +[lint.mccabe.max-complexity]: ../settings.md#lint_mccabe_max-complexity " ); } diff --git a/crates/ruff_dev/src/generate_options.rs b/crates/ruff_dev/src/generate_options.rs index 8e3f21f72985f..652cc4d2ee756 100644 --- a/crates/ruff_dev/src/generate_options.rs +++ b/crates/ruff_dev/src/generate_options.rs @@ -1,6 +1,7 @@ //! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`. //! //! Used for . +use itertools::Itertools; use std::fmt::Write; use ruff_python_trivia::textwrap; @@ -9,16 +10,29 @@ use ruff_workspace::options_base::{OptionField, OptionSet, OptionsMetadata, Visi pub(crate) fn generate() -> String { let mut output = String::new(); - generate_set(&mut output, &Set::Toplevel(Options::metadata())); + + generate_set( + &mut output, + Set::Toplevel(Options::metadata()), + &mut Vec::new(), + ); output } -fn generate_set(output: &mut String, set: &Set) { - if set.level() < 2 { - writeln!(output, "### {title}\n", title = set.title()).unwrap(); - } else { - writeln!(output, "#### {title}\n", title = set.title()).unwrap(); +fn generate_set(output: &mut String, set: Set, parents: &mut Vec) { + match &set { + Set::Toplevel(_) => { + output.push_str("### Top-level\n"); + } + Set::Named { name, .. } => { + let title = parents + .iter() + .filter_map(|set| set.name()) + .chain(std::iter::once(name.as_str())) + .join("."); + writeln!(output, "#### `{title}`\n",).unwrap(); + } } if let Some(documentation) = set.metadata().documentation() { @@ -35,72 +49,68 @@ fn generate_set(output: &mut String, set: &Set) { fields.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2)); sets.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2)); + parents.push(set); + // Generate the fields. for (name, field) in &fields { - emit_field(output, name, field, set); + emit_field(output, name, field, parents.as_slice()); output.push_str("---\n\n"); } // Generate all the sub-sets. for (set_name, sub_set) in &sets { - generate_set(output, &Set::Named(set_name, *sub_set, set.level() + 1)); + generate_set( + output, + Set::Named { + name: set_name.to_string(), + set: *sub_set, + }, + parents, + ); } + + parents.pop(); } -enum Set<'a> { +enum Set { Toplevel(OptionSet), - Named(&'a str, OptionSet, u32), + Named { name: String, set: OptionSet }, } -impl<'a> Set<'a> { - fn name(&self) -> Option<&'a str> { +impl Set { + fn name(&self) -> Option<&str> { match self { Set::Toplevel(_) => None, - Set::Named(name, _, _) => Some(name), - } - } - - fn title(&self) -> &'a str { - match self { - Set::Toplevel(_) => "Top-level", - Set::Named(name, _, _) => name, + Set::Named { name, .. } => Some(name), } } fn metadata(&self) -> &OptionSet { match self { Set::Toplevel(set) => set, - Set::Named(_, set, _) => set, - } - } - - fn level(&self) -> u32 { - match self { - Set::Toplevel(_) => 0, - Set::Named(_, _, level) => *level, + Set::Named { set, .. } => set, } } } -fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set: &Set) { - let header_level = if parent_set.level() < 2 { - "####" +fn emit_field(output: &mut String, name: &str, field: &OptionField, parents: &[Set]) { + let header_level = if parents.is_empty() { "####" } else { "#####" }; + let parents_anchor = parents.iter().filter_map(|parent| parent.name()).join("_"); + + if parents_anchor.is_empty() { + output.push_str(&format!( + "{header_level} [`{name}`](#{name}) {{: #{name} }}\n" + )); } else { - "#####" - }; + output.push_str(&format!( + "{header_level} [`{name}`](#{parents_anchor}_{name}) {{: #{parents_anchor}_{name} }}\n" + )); - // if there's a set name, we need to add it to the anchor - if let Some(set_name) = parent_set.name() { // the anchor used to just be the name, but now it's the group name // for backwards compatibility, we need to keep the old anchor output.push_str(&format!("\n")); - - output.push_str(&format!( - "{header_level} [`{name}`](#{set_name}-{name}) {{: #{set_name}-{name} }}\n" - )); - } else { - output.push_str(&format!("{header_level} [`{name}`](#{name})\n")); } + output.push('\n'); if let Some(deprecated) = &field.deprecated { @@ -129,12 +139,12 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set: output.push_str("**Example usage**:\n\n"); output.push_str(&format_tab( "pyproject.toml", - &format_header(field.scope, parent_set, ConfigurationFile::PyprojectToml), + &format_header(field.scope, parents, ConfigurationFile::PyprojectToml), field.example, )); output.push_str(&format_tab( "ruff.toml", - &format_header(field.scope, parent_set, ConfigurationFile::RuffToml), + &format_header(field.scope, parents, ConfigurationFile::RuffToml), field.example, )); output.push('\n'); @@ -152,52 +162,22 @@ fn format_tab(tab_name: &str, header: &str, content: &str) -> String { /// Format the TOML header for the example usage for a given option. /// /// For example: `[tool.ruff.format]` or `[tool.ruff.lint.isort]`. -fn format_header( - scope: Option<&str>, - parent_set: &Set, - configuration: ConfigurationFile, -) -> String { - match configuration { - ConfigurationFile::PyprojectToml => { - let mut header = if let Some(set_name) = parent_set.name() { - if set_name == "format" { - String::from("tool.ruff.format") - } else { - format!("tool.ruff.lint.{set_name}") - } - } else { - "tool.ruff".to_string() - }; - if let Some(scope) = scope { - if !header.is_empty() { - header.push('.'); - } - header.push_str(scope); - } - format!("[{header}]") - } - ConfigurationFile::RuffToml => { - let mut header = if let Some(set_name) = parent_set.name() { - if set_name == "format" { - String::from("format") - } else { - format!("lint.{set_name}") - } - } else { - String::new() - }; - if let Some(scope) = scope { - if !header.is_empty() { - header.push('.'); - } - header.push_str(scope); - } - if header.is_empty() { - String::new() - } else { - format!("[{header}]") - } - } +fn format_header(scope: Option<&str>, parents: &[Set], configuration: ConfigurationFile) -> String { + let tool_parent = match configuration { + ConfigurationFile::PyprojectToml => Some("tool.ruff"), + ConfigurationFile::RuffToml => None, + }; + + let header = tool_parent + .into_iter() + .chain(parents.iter().filter_map(|parent| parent.name())) + .chain(scope) + .join("."); + + if header.is_empty() { + String::new() + } else { + format!("[{header}]") } } diff --git a/crates/ruff_dev/src/generate_rules_table.rs b/crates/ruff_dev/src/generate_rules_table.rs index 8499a6d900d81..c167c018d05c2 100644 --- a/crates/ruff_dev/src/generate_rules_table.rs +++ b/crates/ruff_dev/src/generate_rules_table.rs @@ -3,6 +3,7 @@ //! Used for . use itertools::Itertools; +use ruff_linter::codes::RuleGroup; use std::borrow::Cow; use strum::IntoEnumIterator; @@ -14,6 +15,10 @@ use ruff_workspace::options_base::OptionsMetadata; const FIX_SYMBOL: &str = "🛠️"; const PREVIEW_SYMBOL: &str = "🧪"; +const REMOVED_SYMBOL: &str = "❌"; +const WARNING_SYMBOL: &str = "⚠️"; +const STABLE_SYMBOL: &str = "✔️"; +const SPACER: &str = "    "; fn generate_table(table_out: &mut String, rules: impl IntoIterator, linter: &Linter) { table_out.push_str("| Code | Name | Message | |"); @@ -21,20 +26,33 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator, table_out.push_str("| ---- | ---- | ------- | ------: |"); table_out.push('\n'); for rule in rules { + let status_token = match rule.group() { + RuleGroup::Removed => { + format!("{REMOVED_SYMBOL}") + } + RuleGroup::Deprecated => { + format!("{WARNING_SYMBOL}") + } + #[allow(deprecated)] + RuleGroup::Preview | RuleGroup::Nursery => { + format!("{PREVIEW_SYMBOL}") + } + RuleGroup::Stable => { + // A full opacity checkmark is a bit aggressive for indicating stable + format!("{STABLE_SYMBOL}") + } + }; + let fix_token = match rule.fixable() { FixAvailability::Always | FixAvailability::Sometimes => { format!("{FIX_SYMBOL}") } FixAvailability::None => { - format!("") + format!("") } }; - let preview_token = if rule.is_preview() || rule.is_nursery() { - format!("{PREVIEW_SYMBOL}") - } else { - format!("") - }; - let status_token = format!("{fix_token} {preview_token}"); + + let tokens = format!("{status_token} {fix_token}"); let rule_name = rule.as_ref(); @@ -48,9 +66,20 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator, Cow::Borrowed(message) }; + // Start and end of style spans + let mut ss = ""; + let mut se = ""; + if rule.is_removed() { + ss = ""; + se = ""; + } else if rule.is_deprecated() { + ss = ""; + se = ""; + } + #[allow(clippy::or_fun_call)] table_out.push_str(&format!( - "| {0}{1} {{ #{0}{1} }} | {2} | {3} | {4} |", + "| {ss}{0}{1}{se} {{ #{0}{1} }} | {ss}{2}{se} | {ss}{3}{se} | {ss}{4}{se} |", linter.common_prefix(), linter.code_for_rule(rule).unwrap(), rule.explanation() @@ -58,7 +87,7 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator, .then_some(format_args!("[{rule_name}](rules/{rule_name}.md)")) .unwrap_or(format_args!("{rule_name}")), message, - status_token, + tokens, )); table_out.push('\n'); } @@ -69,15 +98,33 @@ pub(crate) fn generate() -> String { // Generate the table string. let mut table_out = String::new(); - table_out.push_str(&format!( - "The {FIX_SYMBOL} emoji indicates that a rule is automatically fixable by the `--fix` command-line option.")); - table_out.push('\n'); + table_out.push_str("### Legend"); table_out.push('\n'); table_out.push_str(&format!( - "The {PREVIEW_SYMBOL} emoji indicates that a rule is in [\"preview\"](faq.md#what-is-preview)." + "{SPACER}{STABLE_SYMBOL}{SPACER} The rule is stable." )); - table_out.push('\n'); + table_out.push_str("
"); + + table_out.push_str(&format!( + "{SPACER}{PREVIEW_SYMBOL}{SPACER} The rule is unstable and is in [\"preview\"](faq.md#what-is-preview)." + )); + table_out.push_str("
"); + + table_out.push_str(&format!( + "{SPACER}{WARNING_SYMBOL}{SPACER} The rule has been deprecated and will be removed in a future release." + )); + table_out.push_str("
"); + + table_out.push_str(&format!( + "{SPACER}{REMOVED_SYMBOL}{SPACER} The rule has been removed only the documentation is available." + )); + table_out.push_str("
"); + + table_out.push_str(&format!( + "{SPACER}{FIX_SYMBOL}{SPACER} The rule is automatically fixable by the `--fix` command-line option." + )); + table_out.push_str("
"); table_out.push('\n'); for linter in Linter::iter() { diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF011.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B035.py similarity index 100% rename from crates/ruff_linter/resources/test/fixtures/ruff/RUF011.py rename to crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B035.py diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH006_1.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH006_1.py deleted file mode 100644 index 7e8df4ce10c17..0000000000000 --- a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH006_1.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import annotations - -from typing import TypeVar - - -x: "int" | str # TCH006 -x: ("int" | str) | "bool" # TCH006 - - -def func(): - x: "int" | str # OK - - -z: list[str, str | "int"] = [] # TCH006 - -type A = Value["int" | str] # OK - -OldS = TypeVar('OldS', int | 'str', str) # TCH006 diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH006_2.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH006_2.py deleted file mode 100644 index 0c7d5915b7f16..0000000000000 --- a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH006_2.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import TypeVar - - -x: "int" | str # TCH006 -x: ("int" | str) | "bool" # TCH006 - - -def func(): - x: "int" | str # OK - - -z: list[str, str | "int"] = [] # TCH006 - -type A = Value["int" | str] # OK - -OldS = TypeVar('OldS', int | 'str', str) # TCH006 diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH010_1.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH010_1.py new file mode 100644 index 0000000000000..d640994930ad8 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH010_1.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from typing import TypeVar + + +x: "int" | str # TCH010 +x: ("int" | str) | "bool" # TCH010 + + +def func(): + x: "int" | str # OK + + +z: list[str, str | "int"] = [] # TCH010 + +type A = Value["int" | str] # OK + +OldS = TypeVar('OldS', int | 'str', str) # TCH010 diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH010_2.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH010_2.py new file mode 100644 index 0000000000000..09fae9a5e6c93 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH010_2.py @@ -0,0 +1,16 @@ +from typing import TypeVar + + +x: "int" | str # TCH010 +x: ("int" | str) | "bool" # TCH010 + + +def func(): + x: "int" | str # OK + + +z: list[str, str | "int"] = [] # TCH010 + +type A = Value["int" | str] # OK + +OldS = TypeVar('OldS', int | 'str', str) # TCH010 diff --git a/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH001_0.py b/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH001_0.py deleted file mode 100644 index eed83b81f987c..0000000000000 --- a/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH001_0.py +++ /dev/null @@ -1,9 +0,0 @@ -from ast import literal_eval - -eval("3 + 4") - -literal_eval({1: 2}) - - -def fn() -> None: - eval("3 + 4") diff --git a/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH001_1.py b/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH001_1.py deleted file mode 100644 index ecb3e91a3a5d5..0000000000000 --- a/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH001_1.py +++ /dev/null @@ -1,11 +0,0 @@ -def eval(content: str) -> None: - pass - - -eval("3 + 4") - -literal_eval({1: 2}) - - -def fn() -> None: - eval("3 + 4") diff --git a/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH002_0.py b/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH002_0.py deleted file mode 100644 index d1bc313fc821d..0000000000000 --- a/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH002_0.py +++ /dev/null @@ -1,10 +0,0 @@ -import logging -import warnings -from warnings import warn - -warnings.warn("this is ok") -warn("by itself is also ok") -logging.warning("this is fine") - -logger = logging.getLogger(__name__) -logger.warning("this is fine") diff --git a/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH002_1.py b/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH002_1.py deleted file mode 100644 index 8ff160407c6fa..0000000000000 --- a/crates/ruff_linter/resources/test/fixtures/pygrep_hooks/PGH002_1.py +++ /dev/null @@ -1,8 +0,0 @@ -import logging -from logging import warn - -logging.warn("this is not ok") -warn("not ok") - -logger = logging.getLogger(__name__) -logger.warn("this is not ok") diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/and_or_ternary.py b/crates/ruff_linter/resources/test/fixtures/pylint/and_or_ternary.py deleted file mode 100644 index a65f0b02a0dc1..0000000000000 --- a/crates/ruff_linter/resources/test/fixtures/pylint/and_or_ternary.py +++ /dev/null @@ -1,73 +0,0 @@ -# OK - -1<2 and 'b' and 'c' - -1<2 or 'a' and 'b' - -1<2 and 'a' - -1<2 or 'a' - -2>1 - -1<2 and 'a' or 'b' and 'c' - -1<2 and 'a' or 'b' or 'c' - -1<2 and 'a' or 'b' or 'c' or (lambda x: x+1) - -1<2 and 'a' or 'b' or (lambda x: x+1) or 'c' - -default = 'default' -if (not isinstance(default, bool) and isinstance(default, int)) \ - or (isinstance(default, str) and default): - pass - -docid, token = None, None -(docid is None and token is None) or (docid is not None and token is not None) - -vendor, os_version = 'darwin', '14' -vendor == "debian" and os_version in ["12"] or vendor == "ubuntu" and os_version in [] - -# Don't emit if the parent is an `if` statement. -if (task_id in task_dict and task_dict[task_id] is not task) \ - or task_id in used_group_ids: - pass - -no_target, is_x64, target = True, False, 'target' -if (no_target and not is_x64) or target == 'ARM_APPL_RUST_TARGET': - pass - -# Don't emit if the parent is a `bool_op` expression. -isinstance(val, str) and ((len(val) == 7 and val[0] == "#") or val in enums.NamedColor) - -# Errors - -1<2 and 'a' or 'b' - -(lambda x: x+1) and 'a' or 'b' - -'a' and (lambda x: x+1) or 'orange' - -val = '#0000FF' -(len(val) == 7 and val[0] == "#") or val in {'green'} - -marker = 'marker' -isinstance(marker, dict) and 'field' in marker or marker in {} - -def has_oranges(oranges, apples=None) -> bool: - return apples and False or oranges - -[x for x in l if a and b or c] - -{x: y for x in l if a and b or c} - -{x for x in l if a and b or c} - -new_list = [ - x - for sublist in all_lists - if a and b or c - for x in sublist - if (isinstance(operator, list) and x in operator) or x != operator -] diff --git a/crates/ruff_linter/resources/test/fixtures/tryceratops/TRY200.py b/crates/ruff_linter/resources/test/fixtures/tryceratops/TRY200.py deleted file mode 100644 index 4b00c3cef0686..0000000000000 --- a/crates/ruff_linter/resources/test/fixtures/tryceratops/TRY200.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Violation: -Reraise without using 'from' -""" - - -class MyException(Exception): - pass - - -def func(): - try: - a = 1 - except Exception: - raise MyException() - - -def func(): - try: - a = 1 - except Exception: - if True: - raise MyException() - - -def good(): - try: - a = 1 - except MyException as e: - raise e # This is verbose violation, shouldn't trigger no cause - except Exception: - raise # Just re-raising don't need 'from' - - -def good(): - try: - from mod import f - except ImportError: - def f(): - raise MyException() # Raising within a new scope is fine diff --git a/crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs b/crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs index 665257b139b8c..64938222d5520 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs @@ -256,25 +256,23 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) { diagnostic.set_parent(range.start()); } - if checker.settings.preview.is_enabled() { - if let Some(import) = binding.as_any_import() { - if let Some(source) = binding.source { - diagnostic.try_set_fix(|| { - let statement = checker.semantic().statement(source); - let parent = checker.semantic().parent_statement(source); - let edit = fix::edits::remove_unused_imports( - std::iter::once(import.member_name().as_ref()), - statement, - parent, - checker.locator(), - checker.stylist(), - checker.indexer(), - )?; - Ok(Fix::safe_edit(edit).isolate(Checker::isolation( - checker.semantic().parent_statement_id(source), - ))) - }); - } + if let Some(import) = binding.as_any_import() { + if let Some(source) = binding.source { + diagnostic.try_set_fix(|| { + let statement = checker.semantic().statement(source); + let parent = checker.semantic().parent_statement(source); + let edit = fix::edits::remove_unused_imports( + std::iter::once(import.member_name().as_ref()), + statement, + parent, + checker.locator(), + checker.stylist(), + checker.indexer(), + )?; + Ok(Fix::safe_edit(edit).isolate(Checker::isolation( + checker.semantic().parent_statement_id(source), + ))) + }); } } diff --git a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs index d278f4c8f016b..2e39f0f965f3f 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs @@ -5,7 +5,6 @@ use crate::checkers::ast::Checker; use crate::registry::Rule; use crate::rules::{ flake8_bandit, flake8_blind_except, flake8_bugbear, flake8_builtins, pycodestyle, pylint, - tryceratops, }; /// Run lint rules over an [`ExceptHandler`] syntax node. @@ -66,9 +65,6 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Check if checker.enabled(Rule::ExceptWithNonExceptionClasses) { flake8_bugbear::rules::except_with_non_exception_classes(checker, except_handler); } - if checker.enabled(Rule::ReraiseNoCause) { - tryceratops::rules::reraise_no_cause(checker, body); - } if checker.enabled(Rule::BinaryOpException) { pylint::rules::binary_op_exception(checker, except_handler); } diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index 5b4505a49b231..f02fe6670a40a 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -16,8 +16,7 @@ use crate::rules::{ flake8_future_annotations, flake8_gettext, flake8_implicit_str_concat, flake8_logging, flake8_logging_format, flake8_pie, flake8_print, flake8_pyi, flake8_pytest_style, flake8_self, flake8_simplify, flake8_tidy_imports, flake8_trio, flake8_type_checking, flake8_use_pathlib, - flynt, numpy, pandas_vet, pep8_naming, pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, - refurb, ruff, + flynt, numpy, pandas_vet, pep8_naming, pycodestyle, pyflakes, pylint, pyupgrade, refurb, ruff, }; use crate::settings::types::PythonVersion; @@ -773,12 +772,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::CallDateFromtimestamp) { flake8_datetimez::rules::call_date_fromtimestamp(checker, func, expr.range()); } - if checker.enabled(Rule::Eval) { - pygrep_hooks::rules::no_eval(checker, func); - } - if checker.enabled(Rule::DeprecatedLogWarn) { - pygrep_hooks::rules::deprecated_log_warn(checker, call); - } if checker.enabled(Rule::UnnecessaryDirectLambdaCall) { pylint::rules::unnecessary_direct_lambda_call(checker, expr, func); } @@ -1446,7 +1439,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { } } if checker.enabled(Rule::StaticKeyDictComprehension) { - ruff::rules::static_key_dict_comprehension(checker, dict_comp); + flake8_bugbear::rules::static_key_dict_comprehension(checker, dict_comp); } } Expr::GeneratorExp( @@ -1508,9 +1501,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::RepeatedEqualityComparison) { pylint::rules::repeated_equality_comparison(checker, bool_op); } - if checker.enabled(Rule::AndOrTernary) { - pylint::rules::and_or_ternary(checker, bool_op); - } if checker.enabled(Rule::UnnecessaryKeyCheck) { ruff::rules::unnecessary_key_check(checker, expr); } diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index ac648ac64e942..d13a7a9270c0b 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -342,8 +342,7 @@ where || helpers::is_assignment_to_a_dunder(stmt) || helpers::in_nested_block(self.semantic.current_statements()) || imports::is_matplotlib_activation(stmt, self.semantic()) - || self.settings.preview.is_enabled() - && imports::is_sys_path_modification(stmt, self.semantic())) + || imports::is_sys_path_modification(stmt, self.semantic())) { self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY; } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 5c369809c01dd..4aca236ccc63e 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -52,6 +52,11 @@ pub enum RuleGroup { Stable, /// The rule is unstable, and preview mode must be enabled for usage. Preview, + /// The rule has been deprecated, warnings will be displayed during selection in stable + /// and errors will be raised if used with preview mode enabled. + Deprecated, + /// The rule has been removed, errors will be displayed on use. + Removed, /// Legacy category for unstable rules, supports backwards compatible selection. #[deprecated(note = "Use `RuleGroup::Preview` for new rules instead")] Nursery, @@ -265,7 +270,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pylint, "R1704") => (RuleGroup::Preview, rules::pylint::rules::RedefinedArgumentFromLocal), (Pylint, "R1711") => (RuleGroup::Stable, rules::pylint::rules::UselessReturn), (Pylint, "R1714") => (RuleGroup::Stable, rules::pylint::rules::RepeatedEqualityComparison), - (Pylint, "R1706") => (RuleGroup::Preview, rules::pylint::rules::AndOrTernary), + (Pylint, "R1706") => (RuleGroup::Removed, rules::pylint::rules::AndOrTernary), (Pylint, "R1722") => (RuleGroup::Stable, rules::pylint::rules::SysExitAlias), (Pylint, "R1733") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryDictIndexLookup), (Pylint, "R1736") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryListIndexLookup), @@ -306,11 +311,11 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Async, "102") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingOsCallInAsyncFunction), // flake8-trio - (Flake8Trio, "100") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioTimeoutWithoutAwait), - (Flake8Trio, "105") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioSyncCall), - (Flake8Trio, "109") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioAsyncFunctionWithTimeout), - (Flake8Trio, "110") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioUnneededSleep), - (Flake8Trio, "115") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioZeroSleepCall), + (Flake8Trio, "100") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioTimeoutWithoutAwait), + (Flake8Trio, "105") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioSyncCall), + (Flake8Trio, "109") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioAsyncFunctionWithTimeout), + (Flake8Trio, "110") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioUnneededSleep), + (Flake8Trio, "115") => (RuleGroup::Stable, rules::flake8_trio::rules::TrioZeroSleepCall), // flake8-builtins (Flake8Builtins, "001") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinVariableShadowing), @@ -351,6 +356,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Bugbear, "032") => (RuleGroup::Stable, rules::flake8_bugbear::rules::UnintentionalTypeAnnotation), (Flake8Bugbear, "033") => (RuleGroup::Stable, rules::flake8_bugbear::rules::DuplicateValue), (Flake8Bugbear, "034") => (RuleGroup::Stable, rules::flake8_bugbear::rules::ReSubPositionalArgs), + (Flake8Bugbear, "035") => (RuleGroup::Stable, rules::flake8_bugbear::rules::StaticKeyDictComprehension), (Flake8Bugbear, "904") => (RuleGroup::Stable, rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept), (Flake8Bugbear, "905") => (RuleGroup::Stable, rules::flake8_bugbear::rules::ZipWithoutExplicitStrict), @@ -417,14 +423,14 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Quotes, "001") => (RuleGroup::Stable, rules::flake8_quotes::rules::BadQuotesMultilineString), (Flake8Quotes, "002") => (RuleGroup::Stable, rules::flake8_quotes::rules::BadQuotesDocstring), (Flake8Quotes, "003") => (RuleGroup::Stable, rules::flake8_quotes::rules::AvoidableEscapedQuote), - (Flake8Quotes, "004") => (RuleGroup::Preview, rules::flake8_quotes::rules::UnnecessaryEscapedQuote), + (Flake8Quotes, "004") => (RuleGroup::Stable, rules::flake8_quotes::rules::UnnecessaryEscapedQuote), // flake8-annotations (Flake8Annotations, "001") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeFunctionArgument), (Flake8Annotations, "002") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeArgs), (Flake8Annotations, "003") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeKwargs), - (Flake8Annotations, "101") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeSelf), - (Flake8Annotations, "102") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeCls), + (Flake8Annotations, "101") => (RuleGroup::Deprecated, rules::flake8_annotations::rules::MissingTypeSelf), + (Flake8Annotations, "102") => (RuleGroup::Deprecated, rules::flake8_annotations::rules::MissingTypeCls), (Flake8Annotations, "201") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingReturnTypeUndocumentedPublicFunction), (Flake8Annotations, "202") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingReturnTypePrivateFunction), (Flake8Annotations, "204") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingReturnTypeSpecialMethod), @@ -458,7 +464,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Simplify, "109") => (RuleGroup::Stable, rules::flake8_simplify::rules::CompareWithTuple), (Flake8Simplify, "110") => (RuleGroup::Stable, rules::flake8_simplify::rules::ReimplementedBuiltin), (Flake8Simplify, "112") => (RuleGroup::Stable, rules::flake8_simplify::rules::UncapitalizedEnvironmentVariables), - (Flake8Simplify, "113") => (RuleGroup::Preview, rules::flake8_simplify::rules::EnumerateForLoop), + (Flake8Simplify, "113") => (RuleGroup::Stable, rules::flake8_simplify::rules::EnumerateForLoop), (Flake8Simplify, "114") => (RuleGroup::Stable, rules::flake8_simplify::rules::IfWithSameArms), (Flake8Simplify, "115") => (RuleGroup::Stable, rules::flake8_simplify::rules::OpenFileWithContextHandler), (Flake8Simplify, "116") => (RuleGroup::Stable, rules::flake8_simplify::rules::IfElseBlockInsteadOfDictLookup), @@ -477,7 +483,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Simplify, "300") => (RuleGroup::Stable, rules::flake8_simplify::rules::YodaConditions), (Flake8Simplify, "401") => (RuleGroup::Stable, rules::flake8_simplify::rules::IfElseBlockInsteadOfDictGet), (Flake8Simplify, "910") => (RuleGroup::Stable, rules::flake8_simplify::rules::DictGetWithNoneDefault), - (Flake8Simplify, "911") => (RuleGroup::Preview, rules::flake8_simplify::rules::ZipDictKeysAndValues), + (Flake8Simplify, "911") => (RuleGroup::Stable, rules::flake8_simplify::rules::ZipDictKeysAndValues), // flake8-copyright #[allow(deprecated)] @@ -522,7 +528,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pyupgrade, "038") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP604Isinstance), (Pyupgrade, "039") => (RuleGroup::Stable, rules::pyupgrade::rules::UnnecessaryClassParentheses), (Pyupgrade, "040") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP695TypeAlias), - (Pyupgrade, "041") => (RuleGroup::Preview, rules::pyupgrade::rules::TimeoutErrorAlias), + (Pyupgrade, "041") => (RuleGroup::Stable, rules::pyupgrade::rules::TimeoutErrorAlias), // pydocstyle (Pydocstyle, "100") => (RuleGroup::Stable, rules::pydocstyle::rules::UndocumentedPublicModule), @@ -609,8 +615,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Bandit, "110") => (RuleGroup::Stable, rules::flake8_bandit::rules::TryExceptPass), (Flake8Bandit, "112") => (RuleGroup::Stable, rules::flake8_bandit::rules::TryExceptContinue), (Flake8Bandit, "113") => (RuleGroup::Stable, rules::flake8_bandit::rules::RequestWithoutTimeout), - (Flake8Bandit, "201") => (RuleGroup::Preview, rules::flake8_bandit::rules::FlaskDebugTrue), - (Flake8Bandit, "202") => (RuleGroup::Preview, rules::flake8_bandit::rules::TarfileUnsafeMembers), + (Flake8Bandit, "201") => (RuleGroup::Stable, rules::flake8_bandit::rules::FlaskDebugTrue), + (Flake8Bandit, "202") => (RuleGroup::Stable, rules::flake8_bandit::rules::TarfileUnsafeMembers), (Flake8Bandit, "301") => (RuleGroup::Stable, rules::flake8_bandit::rules::SuspiciousPickleUsage), (Flake8Bandit, "302") => (RuleGroup::Stable, rules::flake8_bandit::rules::SuspiciousMarshalUsage), (Flake8Bandit, "303") => (RuleGroup::Stable, rules::flake8_bandit::rules::SuspiciousInsecureHashUsage), @@ -648,12 +654,12 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Bandit, "413") => (RuleGroup::Preview, rules::flake8_bandit::rules::SuspiciousPycryptoImport), (Flake8Bandit, "415") => (RuleGroup::Preview, rules::flake8_bandit::rules::SuspiciousPyghmiImport), (Flake8Bandit, "501") => (RuleGroup::Stable, rules::flake8_bandit::rules::RequestWithNoCertValidation), - (Flake8Bandit, "502") => (RuleGroup::Preview, rules::flake8_bandit::rules::SslInsecureVersion), - (Flake8Bandit, "503") => (RuleGroup::Preview, rules::flake8_bandit::rules::SslWithBadDefaults), - (Flake8Bandit, "504") => (RuleGroup::Preview, rules::flake8_bandit::rules::SslWithNoVersion), - (Flake8Bandit, "505") => (RuleGroup::Preview, rules::flake8_bandit::rules::WeakCryptographicKey), + (Flake8Bandit, "502") => (RuleGroup::Stable, rules::flake8_bandit::rules::SslInsecureVersion), + (Flake8Bandit, "503") => (RuleGroup::Stable, rules::flake8_bandit::rules::SslWithBadDefaults), + (Flake8Bandit, "504") => (RuleGroup::Stable, rules::flake8_bandit::rules::SslWithNoVersion), + (Flake8Bandit, "505") => (RuleGroup::Stable, rules::flake8_bandit::rules::WeakCryptographicKey), (Flake8Bandit, "506") => (RuleGroup::Stable, rules::flake8_bandit::rules::UnsafeYAMLLoad), - (Flake8Bandit, "507") => (RuleGroup::Preview, rules::flake8_bandit::rules::SSHNoHostKeyVerification), + (Flake8Bandit, "507") => (RuleGroup::Stable, rules::flake8_bandit::rules::SSHNoHostKeyVerification), (Flake8Bandit, "508") => (RuleGroup::Stable, rules::flake8_bandit::rules::SnmpInsecureVersion), (Flake8Bandit, "509") => (RuleGroup::Stable, rules::flake8_bandit::rules::SnmpWeakCryptography), (Flake8Bandit, "601") => (RuleGroup::Stable, rules::flake8_bandit::rules::ParamikoCall), @@ -665,10 +671,10 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Bandit, "607") => (RuleGroup::Stable, rules::flake8_bandit::rules::StartProcessWithPartialPath), (Flake8Bandit, "608") => (RuleGroup::Stable, rules::flake8_bandit::rules::HardcodedSQLExpression), (Flake8Bandit, "609") => (RuleGroup::Stable, rules::flake8_bandit::rules::UnixCommandWildcardInjection), - (Flake8Bandit, "611") => (RuleGroup::Preview, rules::flake8_bandit::rules::DjangoRawSql), + (Flake8Bandit, "611") => (RuleGroup::Stable, rules::flake8_bandit::rules::DjangoRawSql), (Flake8Bandit, "612") => (RuleGroup::Stable, rules::flake8_bandit::rules::LoggingConfigInsecureListen), (Flake8Bandit, "701") => (RuleGroup::Stable, rules::flake8_bandit::rules::Jinja2AutoescapeFalse), - (Flake8Bandit, "702") => (RuleGroup::Preview, rules::flake8_bandit::rules::MakoTemplates), + (Flake8Bandit, "702") => (RuleGroup::Stable, rules::flake8_bandit::rules::MakoTemplates), // flake8-boolean-trap (Flake8BooleanTrap, "001") => (RuleGroup::Stable, rules::flake8_boolean_trap::rules::BooleanTypeHintPositionalArgument), @@ -699,8 +705,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Datetimez, "012") => (RuleGroup::Stable, rules::flake8_datetimez::rules::CallDateFromtimestamp), // pygrep-hooks - (PygrepHooks, "001") => (RuleGroup::Stable, rules::pygrep_hooks::rules::Eval), - (PygrepHooks, "002") => (RuleGroup::Stable, rules::pygrep_hooks::rules::DeprecatedLogWarn), + (PygrepHooks, "001") => (RuleGroup::Removed, rules::pygrep_hooks::rules::Eval), + (PygrepHooks, "002") => (RuleGroup::Removed, rules::pygrep_hooks::rules::DeprecatedLogWarn), (PygrepHooks, "003") => (RuleGroup::Stable, rules::pygrep_hooks::rules::BlanketTypeIgnore), (PygrepHooks, "004") => (RuleGroup::Stable, rules::pygrep_hooks::rules::BlanketNOQA), (PygrepHooks, "005") => (RuleGroup::Stable, rules::pygrep_hooks::rules::InvalidMockAccess), @@ -773,7 +779,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Pyi, "053") => (RuleGroup::Stable, rules::flake8_pyi::rules::StringOrBytesTooLong), (Flake8Pyi, "055") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnnecessaryTypeUnion), (Flake8Pyi, "056") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnsupportedMethodCallOnAll), - (Flake8Pyi, "058") => (RuleGroup::Preview, rules::flake8_pyi::rules::GeneratorReturnFromIterMethod), + (Flake8Pyi, "058") => (RuleGroup::Stable, rules::flake8_pyi::rules::GeneratorReturnFromIterMethod), // flake8-pytest-style (Flake8PytestStyle, "001") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestFixtureIncorrectParenthesesStyle), @@ -835,13 +841,13 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8TypeChecking, "003") => (RuleGroup::Stable, rules::flake8_type_checking::rules::TypingOnlyStandardLibraryImport), (Flake8TypeChecking, "004") => (RuleGroup::Stable, rules::flake8_type_checking::rules::RuntimeImportInTypeCheckingBlock), (Flake8TypeChecking, "005") => (RuleGroup::Stable, rules::flake8_type_checking::rules::EmptyTypeCheckingBlock), - (Flake8TypeChecking, "006") => (RuleGroup::Preview, rules::flake8_type_checking::rules::RuntimeStringUnion), + (Flake8TypeChecking, "010") => (RuleGroup::Stable, rules::flake8_type_checking::rules::RuntimeStringUnion), // tryceratops (Tryceratops, "002") => (RuleGroup::Stable, rules::tryceratops::rules::RaiseVanillaClass), (Tryceratops, "003") => (RuleGroup::Stable, rules::tryceratops::rules::RaiseVanillaArgs), (Tryceratops, "004") => (RuleGroup::Stable, rules::tryceratops::rules::TypeCheckWithoutTypeError), - (Tryceratops, "200") => (RuleGroup::Stable, rules::tryceratops::rules::ReraiseNoCause), + (Tryceratops, "200") => (RuleGroup::Removed, rules::tryceratops::rules::ReraiseNoCause), (Tryceratops, "201") => (RuleGroup::Stable, rules::tryceratops::rules::VerboseRaise), (Tryceratops, "300") => (RuleGroup::Stable, rules::tryceratops::rules::TryConsiderElse), (Tryceratops, "301") => (RuleGroup::Stable, rules::tryceratops::rules::RaiseWithinTry), @@ -904,7 +910,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Numpy, "001") => (RuleGroup::Stable, rules::numpy::rules::NumpyDeprecatedTypeAlias), (Numpy, "002") => (RuleGroup::Stable, rules::numpy::rules::NumpyLegacyRandom), (Numpy, "003") => (RuleGroup::Stable, rules::numpy::rules::NumpyDeprecatedFunction), - (Numpy, "201") => (RuleGroup::Preview, rules::numpy::rules::Numpy2Deprecation), + (Numpy, "201") => (RuleGroup::Stable, rules::numpy::rules::Numpy2Deprecation), // ruff (Ruff, "001") => (RuleGroup::Stable, rules::ruff::rules::AmbiguousUnicodeCharacterString), @@ -916,16 +922,15 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Ruff, "008") => (RuleGroup::Stable, rules::ruff::rules::MutableDataclassDefault), (Ruff, "009") => (RuleGroup::Stable, rules::ruff::rules::FunctionCallInDataclassDefaultArgument), (Ruff, "010") => (RuleGroup::Stable, rules::ruff::rules::ExplicitFStringTypeConversion), - (Ruff, "011") => (RuleGroup::Stable, rules::ruff::rules::StaticKeyDictComprehension), + (Ruff, "011") => (RuleGroup::Removed, rules::ruff::rules::RuffStaticKeyDictComprehension), (Ruff, "012") => (RuleGroup::Stable, rules::ruff::rules::MutableClassDefault), (Ruff, "013") => (RuleGroup::Stable, rules::ruff::rules::ImplicitOptional), (Ruff, "015") => (RuleGroup::Stable, rules::ruff::rules::UnnecessaryIterableAllocationForFirstElement), (Ruff, "016") => (RuleGroup::Stable, rules::ruff::rules::InvalidIndexType), - #[allow(deprecated)] - (Ruff, "017") => (RuleGroup::Nursery, rules::ruff::rules::QuadraticListSummation), - (Ruff, "018") => (RuleGroup::Preview, rules::ruff::rules::AssignmentInAssert), - (Ruff, "019") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryKeyCheck), - (Ruff, "020") => (RuleGroup::Preview, rules::ruff::rules::NeverUnion), + (Ruff, "017") => (RuleGroup::Stable, rules::ruff::rules::QuadraticListSummation), + (Ruff, "018") => (RuleGroup::Stable, rules::ruff::rules::AssignmentInAssert), + (Ruff, "019") => (RuleGroup::Stable, rules::ruff::rules::UnnecessaryKeyCheck), + (Ruff, "020") => (RuleGroup::Stable, rules::ruff::rules::NeverUnion), (Ruff, "021") => (RuleGroup::Preview, rules::ruff::rules::ParenthesizeChainedOperators), (Ruff, "022") => (RuleGroup::Preview, rules::ruff::rules::UnsortedDunderAll), (Ruff, "023") => (RuleGroup::Preview, rules::ruff::rules::UnsortedDunderSlots), @@ -947,6 +952,20 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { #[cfg(feature = "test-rules")] #[allow(deprecated)] (Ruff, "912") => (RuleGroup::Nursery, rules::ruff::rules::NurseryTestRule), + #[cfg(feature = "test-rules")] + (Ruff, "920") => (RuleGroup::Deprecated, rules::ruff::rules::DeprecatedTestRule), + #[cfg(feature = "test-rules")] + (Ruff, "921") => (RuleGroup::Deprecated, rules::ruff::rules::AnotherDeprecatedTestRule), + #[cfg(feature = "test-rules")] + (Ruff, "930") => (RuleGroup::Removed, rules::ruff::rules::RemovedTestRule), + #[cfg(feature = "test-rules")] + (Ruff, "931") => (RuleGroup::Removed, rules::ruff::rules::AnotherRemovedTestRule), + #[cfg(feature = "test-rules")] + (Ruff, "940") => (RuleGroup::Removed, rules::ruff::rules::RedirectedFromTestRule), + #[cfg(feature = "test-rules")] + (Ruff, "950") => (RuleGroup::Stable, rules::ruff::rules::RedirectedToTestRule), + #[cfg(feature = "test-rules")] + (Ruff, "960") => (RuleGroup::Removed, rules::ruff::rules::RedirectedFromPrefixTestRule), // flake8-django @@ -1019,10 +1038,10 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Refurb, "181") => (RuleGroup::Preview, rules::refurb::rules::HashlibDigestHex), // flake8-logging - (Flake8Logging, "001") => (RuleGroup::Preview, rules::flake8_logging::rules::DirectLoggerInstantiation), - (Flake8Logging, "002") => (RuleGroup::Preview, rules::flake8_logging::rules::InvalidGetLoggerArgument), - (Flake8Logging, "007") => (RuleGroup::Preview, rules::flake8_logging::rules::ExceptionWithoutExcInfo), - (Flake8Logging, "009") => (RuleGroup::Preview, rules::flake8_logging::rules::UndocumentedWarn), + (Flake8Logging, "001") => (RuleGroup::Stable, rules::flake8_logging::rules::DirectLoggerInstantiation), + (Flake8Logging, "002") => (RuleGroup::Stable, rules::flake8_logging::rules::InvalidGetLoggerArgument), + (Flake8Logging, "007") => (RuleGroup::Stable, rules::flake8_logging::rules::ExceptionWithoutExcInfo), + (Flake8Logging, "009") => (RuleGroup::Stable, rules::flake8_logging::rules::UndocumentedWarn), _ => return None, }) diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index 003edc4446198..0196aeb933628 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -236,6 +236,25 @@ pub fn check_path( } Rule::NurseryTestRule => test_rules::NurseryTestRule::diagnostic(locator, indexer), Rule::PreviewTestRule => test_rules::PreviewTestRule::diagnostic(locator, indexer), + Rule::DeprecatedTestRule => { + test_rules::DeprecatedTestRule::diagnostic(locator, indexer) + } + Rule::AnotherDeprecatedTestRule => { + test_rules::AnotherDeprecatedTestRule::diagnostic(locator, indexer) + } + Rule::RemovedTestRule => test_rules::RemovedTestRule::diagnostic(locator, indexer), + Rule::AnotherRemovedTestRule => { + test_rules::AnotherRemovedTestRule::diagnostic(locator, indexer) + } + Rule::RedirectedToTestRule => { + test_rules::RedirectedToTestRule::diagnostic(locator, indexer) + } + Rule::RedirectedFromTestRule => { + test_rules::RedirectedFromTestRule::diagnostic(locator, indexer) + } + Rule::RedirectedFromPrefixTestRule => { + test_rules::RedirectedFromPrefixTestRule::diagnostic(locator, indexer) + } _ => unreachable!("All test rules must have an implementation"), }; if let Some(diagnostic) = diagnostic { diff --git a/crates/ruff_linter/src/rule_redirects.rs b/crates/ruff_linter/src/rule_redirects.rs index a9fd305027db2..afd8ac39580cc 100644 --- a/crates/ruff_linter/src/rule_redirects.rs +++ b/crates/ruff_linter/src/rule_redirects.rs @@ -98,5 +98,16 @@ static REDIRECTS: Lazy> = Lazy::new(|| { ("T002", "FIX002"), ("T003", "FIX003"), ("T004", "FIX004"), + ("RUF011", "B035"), + ("TCH006", "TCH010"), + ("TRY200", "B904"), + ("PGH001", "S307"), + ("PHG002", "G010"), + // Test redirect by exact code + #[cfg(feature = "test-rules")] + ("RUF940", "RUF950"), + // Test redirect by prefix + #[cfg(feature = "test-rules")] + ("RUF96", "RUF95"), ]) }); diff --git a/crates/ruff_linter/src/rule_selector.rs b/crates/ruff_linter/src/rule_selector.rs index 2f6e7a5da12ac..001b6a5f22a41 100644 --- a/crates/ruff_linter/src/rule_selector.rs +++ b/crates/ruff_linter/src/rule_selector.rs @@ -44,10 +44,25 @@ impl From for RuleSelector { } } +impl Ord for RuleSelector { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + // TODO(zanieb): We ought to put "ALL" and "Linter" selectors + // above those that are rule specific but it's not critical for now + self.prefix_and_code().cmp(&other.prefix_and_code()) + } +} + +impl PartialOrd for RuleSelector { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl FromStr for RuleSelector { type Err = ParseError; fn from_str(s: &str) -> Result { + // **Changes should be reflected in `parse_no_redirect` as well** match s { "ALL" => Ok(Self::All), #[allow(deprecated)] @@ -67,7 +82,6 @@ impl FromStr for RuleSelector { return Ok(Self::Linter(linter)); } - // Does the selector select a single rule? let prefix = RuleCodePrefix::parse(&linter, code) .map_err(|_| ParseError::Unknown(s.to_string()))?; @@ -172,7 +186,7 @@ impl Visitor<'_> for SelectorVisitor { } impl RuleSelector { - /// Return all matching rules, regardless of whether they're in preview. + /// Return all matching rules, regardless of rule group filters like preview and deprecated. pub fn all_rules(&self) -> impl Iterator + '_ { match self { RuleSelector::All => RuleSelectorIter::All(Rule::iter()), @@ -198,21 +212,30 @@ impl RuleSelector { } } - /// Returns rules matching the selector, taking into account preview options enabled. + /// Returns rules matching the selector, taking into account rule groups like preview and deprecated. pub fn rules<'a>(&'a self, preview: &PreviewOptions) -> impl Iterator + 'a { let preview_enabled = preview.mode.is_enabled(); let preview_require_explicit = preview.require_explicit; #[allow(deprecated)] self.all_rules().filter(move |rule| { - // Always include rules that are not in preview or the nursery - !(rule.is_preview() || rule.is_nursery()) + // Always include stable rules + rule.is_stable() // Backwards compatibility allows selection of nursery rules by exact code or dedicated group - || ((matches!(self, RuleSelector::Rule { .. }) || matches!(self, RuleSelector::Nursery { .. })) && rule.is_nursery()) + || ((self.is_exact() || matches!(self, RuleSelector::Nursery { .. })) && rule.is_nursery()) // Enabling preview includes all preview or nursery rules unless explicit selection // is turned on - || (preview_enabled && (matches!(self, RuleSelector::Rule { .. }) || !preview_require_explicit)) + || ((rule.is_preview() || rule.is_nursery()) && preview_enabled && (self.is_exact() || !preview_require_explicit)) + // Deprecated rules are excluded in preview mode unless explicitly selected + || (rule.is_deprecated() && (!preview_enabled || self.is_exact())) + // Removed rules are included if explicitly selected but will error downstream + || (rule.is_removed() && self.is_exact()) }) } + + /// Returns true if this selector is exact i.e. selects a single rule by code + pub fn is_exact(&self) -> bool { + matches!(self, Self::Rule { .. }) + } } pub enum RuleSelectorIter { @@ -267,7 +290,6 @@ mod schema { [ // Include the non-standard "ALL" and "NURSERY" selectors. "ALL".to_string(), - "NURSERY".to_string(), // Include the legacy "C" and "T" selectors. "C".to_string(), "T".to_string(), @@ -289,6 +311,16 @@ mod schema { (!prefix.is_empty()).then(|| prefix.to_string()) })), ) + .filter(|p| { + // Exclude any prefixes where all of the rules are removed + if let Ok(Self::Rule { prefix, .. } | Self::Prefix { prefix, .. }) = + RuleSelector::parse_no_redirect(p) + { + !prefix.rules().all(|rule| rule.is_removed()) + } else { + true + } + }) .sorted() .map(Value::String) .collect(), @@ -321,6 +353,41 @@ impl RuleSelector { } } } + + /// Parse [`RuleSelector`] from a string; but do not follow redirects. + pub fn parse_no_redirect(s: &str) -> Result { + // **Changes should be reflected in `from_str` as well** + match s { + "ALL" => Ok(Self::All), + #[allow(deprecated)] + "NURSERY" => Ok(Self::Nursery), + "C" => Ok(Self::C), + "T" => Ok(Self::T), + _ => { + let (linter, code) = + Linter::parse_code(s).ok_or_else(|| ParseError::Unknown(s.to_string()))?; + + if code.is_empty() { + return Ok(Self::Linter(linter)); + } + + let prefix = RuleCodePrefix::parse(&linter, code) + .map_err(|_| ParseError::Unknown(s.to_string()))?; + + if is_single_rule_selector(&prefix) { + Ok(Self::Rule { + prefix, + redirected_from: None, + }) + } else { + Ok(Self::Prefix { + prefix, + redirected_from: None, + }) + } + } + } + } } #[derive(EnumIter, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)] diff --git a/crates/ruff_linter/src/rules/eradicate/rules/commented_out_code.rs b/crates/ruff_linter/src/rules/eradicate/rules/commented_out_code.rs index e0668eb392572..74124b3c16950 100644 --- a/crates/ruff_linter/src/rules/eradicate/rules/commented_out_code.rs +++ b/crates/ruff_linter/src/rules/eradicate/rules/commented_out_code.rs @@ -24,7 +24,7 @@ use super::super::detection::comment_contains_code; /// ``` /// /// ## Options -/// - `task-tags` +/// - `lint.task-tags` /// /// [#4845]: https://github.com/astral-sh/ruff/issues/4845 #[violation] diff --git a/crates/ruff_linter/src/rules/flake8_annotations/rules/definition.rs b/crates/ruff_linter/src/rules/flake8_annotations/rules/definition.rs index 607d07cd5f82f..d8a9ea42ee3bb 100644 --- a/crates/ruff_linter/src/rules/flake8_annotations/rules/definition.rs +++ b/crates/ruff_linter/src/rules/flake8_annotations/rules/definition.rs @@ -111,6 +111,10 @@ impl Violation for MissingTypeKwargs { } } +/// ## Deprecation +/// This rule is commonly disabled because type checkers can infer this type without annotation. +/// It will be removed in a future release. +/// /// ## What it does /// Checks that instance method `self` arguments have type annotations. /// @@ -148,6 +152,10 @@ impl Violation for MissingTypeSelf { } } +/// ## Deprecation +/// This rule is commonly disabled because type checkers can infer this type without annotation. +/// It will be removed in a future release. +/// /// ## What it does /// Checks that class method `cls` arguments have type annotations. /// diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs index 8cde263044c04..cbcbed801176d 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs @@ -12,7 +12,7 @@ mod tests { use crate::assert_messages; use crate::registry::Rule; - use crate::settings::types::PreviewMode; + use crate::settings::LinterSettings; use crate::test::test_path; @@ -34,7 +34,6 @@ mod tests { #[test_case(Rule::GetAttrWithConstant, Path::new("B009_B010.py"))] #[test_case(Rule::JumpStatementInFinally, Path::new("B012.py"))] #[test_case(Rule::LoopVariableOverridesIterator, Path::new("B020.py"))] - #[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_2.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_3.py"))] @@ -42,6 +41,7 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_5.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_6.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_7.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))] #[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))] #[test_case(Rule::RaiseLiteral, Path::new("B016.py"))] #[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"))] @@ -50,16 +50,17 @@ mod tests { #[test_case(Rule::ReuseOfGroupbyGenerator, Path::new("B031.py"))] #[test_case(Rule::SetAttrWithConstant, Path::new("B009_B010.py"))] #[test_case(Rule::StarArgUnpackingAfterKeywordArg, Path::new("B026.py"))] + #[test_case(Rule::StaticKeyDictComprehension, Path::new("B035.py"))] #[test_case(Rule::StripWithMultiCharacters, Path::new("B005.py"))] #[test_case(Rule::UnaryPrefixIncrementDecrement, Path::new("B002.py"))] #[test_case(Rule::UnintentionalTypeAnnotation, Path::new("B032.py"))] #[test_case(Rule::UnreliableCallableCheck, Path::new("B004.py"))] #[test_case(Rule::UnusedLoopControlVariable, Path::new("B007.py"))] - #[test_case(Rule::UselessComparison, Path::new("B015.py"))] #[test_case(Rule::UselessComparison, Path::new("B015.ipynb"))] + #[test_case(Rule::UselessComparison, Path::new("B015.py"))] #[test_case(Rule::UselessContextlibSuppress, Path::new("B022.py"))] - #[test_case(Rule::UselessExpression, Path::new("B018.py"))] #[test_case(Rule::UselessExpression, Path::new("B018.ipynb"))] + #[test_case(Rule::UselessExpression, Path::new("B018.py"))] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( @@ -70,24 +71,6 @@ mod tests { Ok(()) } - #[test_case(Rule::DuplicateValue, Path::new("B033.py"))] - fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { - let snapshot = format!( - "preview__{}_{}", - rule_code.noqa_code(), - path.to_string_lossy() - ); - let diagnostics = test_path( - Path::new("flake8_bugbear").join(path).as_path(), - &LinterSettings { - preview: PreviewMode::Enabled, - ..LinterSettings::for_rule(rule_code) - }, - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } - #[test] fn zip_without_explicit_strict() -> Result<()> { let snapshot = "B905.py"; diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_value.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_value.rs index 7910f48bbe850..3abc90c2e281c 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_value.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_value.rs @@ -61,11 +61,9 @@ pub(crate) fn duplicate_value(checker: &mut Checker, set: &ast::ExprSet) { elt.range(), ); - if checker.settings.preview.is_enabled() { - diagnostic.try_set_fix(|| { - remove_member(set, index, checker.locator().contents()).map(Fix::safe_edit) - }); - } + diagnostic.try_set_fix(|| { + remove_member(set, index, checker.locator().contents()).map(Fix::safe_edit) + }); checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs index 9da675d797b71..760d9f6b6c7c8 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs @@ -23,11 +23,11 @@ use crate::checkers::ast::Checker; /// calls to the function, which can lead to unexpected behaviour. /// /// Calls can be marked as an exception to this rule with the -/// [`flake8-bugbear.extend-immutable-calls`] configuration option. +/// [`lint.flake8-bugbear.extend-immutable-calls`] configuration option. /// /// Arguments with immutable type annotations will be ignored by this rule. /// Types outside of the standard library can be marked as immutable with the -/// [`flake8-bugbear.extend-immutable-calls`] configuration option as well. +/// [`lint.flake8-bugbear.extend-immutable-calls`] configuration option as well. /// /// ## Example /// ```python @@ -61,7 +61,7 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `flake8-bugbear.extend-immutable-calls` +/// - `lint.flake8-bugbear.extend-immutable-calls` #[violation] pub struct FunctionCallInDefaultArgument { name: Option, diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mod.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mod.rs index 3f8a1fdaf646f..281d6020ab6d9 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mod.rs @@ -22,6 +22,7 @@ pub(crate) use redundant_tuple_in_exception_handler::*; pub(crate) use reuse_of_groupby_generator::*; pub(crate) use setattr_with_constant::*; pub(crate) use star_arg_unpacking_after_keyword_arg::*; +pub(crate) use static_key_dict_comprehension::*; pub(crate) use strip_with_multi_characters::*; pub(crate) use unary_prefix_increment_decrement::*; pub(crate) use unintentional_type_annotation::*; @@ -56,6 +57,7 @@ mod redundant_tuple_in_exception_handler; mod reuse_of_groupby_generator; mod setattr_with_constant; mod star_arg_unpacking_after_keyword_arg; +mod static_key_dict_comprehension; mod strip_with_multi_characters; mod unary_prefix_increment_decrement; mod unintentional_type_annotation; diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index 71925d0f2da08..525038fae961f 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -28,7 +28,7 @@ use crate::checkers::ast::Checker; /// /// Arguments with immutable type annotations will be ignored by this rule. /// Types outside of the standard library can be marked as immutable with the -/// [`flake8-bugbear.extend-immutable-calls`] configuration option. +/// [`lint.flake8-bugbear.extend-immutable-calls`] configuration option. /// /// ## Known problems /// Mutable argument defaults can be used intentionally to cache computation @@ -61,7 +61,7 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `flake8-bugbear.extend-immutable-calls` +/// - `lint.flake8-bugbear.extend-immutable-calls` /// /// ## References /// - [Python documentation: Default Argument Values](https://docs.python.org/3/tutorial/controlflow.html#default-argument-values) diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/static_key_dict_comprehension.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/static_key_dict_comprehension.rs new file mode 100644 index 0000000000000..1da5864e0927d --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/static_key_dict_comprehension.rs @@ -0,0 +1,91 @@ +use rustc_hash::FxHashMap; + +use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::helpers::StoredNameFinder; +use ruff_python_ast::visitor::Visitor; +use ruff_python_ast::{self as ast, Expr}; +use ruff_text_size::Ranged; + +use crate::checkers::ast::Checker; +use crate::fix::snippet::SourceCodeSnippet; + +/// ## What it does +/// Checks for dictionary comprehensions that use a static key, like a string +/// literal or a variable defined outside the comprehension. +/// +/// ## Why is this bad? +/// Using a static key (like a string literal) in a dictionary comprehension +/// is usually a mistake, as it will result in a dictionary with only one key, +/// despite the comprehension iterating over multiple values. +/// +/// ## Example +/// ```python +/// data = ["some", "Data"] +/// {"key": value.upper() for value in data} +/// ``` +/// +/// Use instead: +/// ```python +/// data = ["some", "Data"] +/// {value: value.upper() for value in data} +/// ``` +#[violation] +pub struct StaticKeyDictComprehension { + key: SourceCodeSnippet, +} + +impl Violation for StaticKeyDictComprehension { + #[derive_message_formats] + fn message(&self) -> String { + let StaticKeyDictComprehension { key } = self; + if let Some(key) = key.full_display() { + format!("Dictionary comprehension uses static key: `{key}`") + } else { + format!("Dictionary comprehension uses static key") + } + } +} + +/// RUF011 +pub(crate) fn static_key_dict_comprehension(checker: &mut Checker, dict_comp: &ast::ExprDictComp) { + // Collect the bound names in the comprehension's generators. + let names = { + let mut visitor = StoredNameFinder::default(); + for generator in &dict_comp.generators { + visitor.visit_comprehension(generator); + } + visitor.names + }; + + if is_constant(&dict_comp.key, &names) { + checker.diagnostics.push(Diagnostic::new( + StaticKeyDictComprehension { + key: SourceCodeSnippet::from_str(checker.locator().slice(dict_comp.key.as_ref())), + }, + dict_comp.key.range(), + )); + } +} + +/// Returns `true` if the given expression is a constant in the context of the dictionary +/// comprehension. +fn is_constant(key: &Expr, names: &FxHashMap<&str, &ast::ExprName>) -> bool { + match key { + Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().all(|elt| is_constant(elt, names)), + Expr::Name(ast::ExprName { id, .. }) => !names.contains_key(id.as_str()), + Expr::Attribute(ast::ExprAttribute { value, .. }) => is_constant(value, names), + Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => { + is_constant(value, names) && is_constant(slice, names) + } + Expr::BinOp(ast::ExprBinOp { left, right, .. }) => { + is_constant(left, names) && is_constant(right, names) + } + Expr::BoolOp(ast::ExprBoolOp { values, .. }) => { + values.iter().all(|value| is_constant(value, names)) + } + Expr::UnaryOp(ast::ExprUnaryOp { operand, .. }) => is_constant(operand, names), + expr if expr.is_literal_expr() => true, + _ => false, + } +} diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B033_B033.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B033_B033.py.snap index 662a7158e542c..6d468e2683cd3 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B033_B033.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B033_B033.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs --- -B033.py:4:35: B033 Sets should not contain duplicate item `"value1"` +B033.py:4:35: B033 [*] Sets should not contain duplicate item `"value1"` | 2 | # Errors. 3 | ### @@ -12,7 +12,17 @@ B033.py:4:35: B033 Sets should not contain duplicate item `"value1"` | = help: Remove duplicate item -B033.py:5:21: B033 Sets should not contain duplicate item `1` +ℹ Safe fix +1 1 | ### +2 2 | # Errors. +3 3 | ### +4 |-incorrect_set = {"value1", 23, 5, "value1"} + 4 |+incorrect_set = {"value1", 23, 5} +5 5 | incorrect_set = {1, 1, 2} +6 6 | incorrect_set_multiline = { +7 7 | "value1", + +B033.py:5:21: B033 [*] Sets should not contain duplicate item `1` | 3 | ### 4 | incorrect_set = {"value1", 23, 5, "value1"} @@ -23,7 +33,17 @@ B033.py:5:21: B033 Sets should not contain duplicate item `1` | = help: Remove duplicate item -B033.py:10:5: B033 Sets should not contain duplicate item `"value1"` +ℹ Safe fix +2 2 | # Errors. +3 3 | ### +4 4 | incorrect_set = {"value1", 23, 5, "value1"} +5 |-incorrect_set = {1, 1, 2} + 5 |+incorrect_set = {1, 2} +6 6 | incorrect_set_multiline = { +7 7 | "value1", +8 8 | 23, + +B033.py:10:5: B033 [*] Sets should not contain duplicate item `"value1"` | 8 | 23, 9 | 5, @@ -34,7 +54,16 @@ B033.py:10:5: B033 Sets should not contain duplicate item `"value1"` | = help: Remove duplicate item -B033.py:13:21: B033 Sets should not contain duplicate item `1` +ℹ Safe fix +7 7 | "value1", +8 8 | 23, +9 9 | 5, +10 |- "value1", +11 10 | # B033 +12 11 | } +13 12 | incorrect_set = {1, 1} + +B033.py:13:21: B033 [*] Sets should not contain duplicate item `1` | 11 | # B033 12 | } @@ -45,7 +74,17 @@ B033.py:13:21: B033 Sets should not contain duplicate item `1` | = help: Remove duplicate item -B033.py:14:21: B033 Sets should not contain duplicate item `1` +ℹ Safe fix +10 10 | "value1", +11 11 | # B033 +12 12 | } +13 |-incorrect_set = {1, 1} + 13 |+incorrect_set = {1} +14 14 | incorrect_set = {1, 1,} +15 15 | incorrect_set = {0, 1, 1,} +16 16 | incorrect_set = {0, 1, 1} + +B033.py:14:21: B033 [*] Sets should not contain duplicate item `1` | 12 | } 13 | incorrect_set = {1, 1} @@ -56,7 +95,17 @@ B033.py:14:21: B033 Sets should not contain duplicate item `1` | = help: Remove duplicate item -B033.py:15:24: B033 Sets should not contain duplicate item `1` +ℹ Safe fix +11 11 | # B033 +12 12 | } +13 13 | incorrect_set = {1, 1} +14 |-incorrect_set = {1, 1,} + 14 |+incorrect_set = {1,} +15 15 | incorrect_set = {0, 1, 1,} +16 16 | incorrect_set = {0, 1, 1} +17 17 | incorrect_set = { + +B033.py:15:24: B033 [*] Sets should not contain duplicate item `1` | 13 | incorrect_set = {1, 1} 14 | incorrect_set = {1, 1,} @@ -67,7 +116,17 @@ B033.py:15:24: B033 Sets should not contain duplicate item `1` | = help: Remove duplicate item -B033.py:16:24: B033 Sets should not contain duplicate item `1` +ℹ Safe fix +12 12 | } +13 13 | incorrect_set = {1, 1} +14 14 | incorrect_set = {1, 1,} +15 |-incorrect_set = {0, 1, 1,} + 15 |+incorrect_set = {0, 1,} +16 16 | incorrect_set = {0, 1, 1} +17 17 | incorrect_set = { +18 18 | 0, + +B033.py:16:24: B033 [*] Sets should not contain duplicate item `1` | 14 | incorrect_set = {1, 1,} 15 | incorrect_set = {0, 1, 1,} @@ -78,7 +137,17 @@ B033.py:16:24: B033 Sets should not contain duplicate item `1` | = help: Remove duplicate item -B033.py:20:5: B033 Sets should not contain duplicate item `1` +ℹ Safe fix +13 13 | incorrect_set = {1, 1} +14 14 | incorrect_set = {1, 1,} +15 15 | incorrect_set = {0, 1, 1,} +16 |-incorrect_set = {0, 1, 1} + 16 |+incorrect_set = {0, 1} +17 17 | incorrect_set = { +18 18 | 0, +19 19 | 1, + +B033.py:20:5: B033 [*] Sets should not contain duplicate item `1` | 18 | 0, 19 | 1, @@ -88,4 +157,13 @@ B033.py:20:5: B033 Sets should not contain duplicate item `1` | = help: Remove duplicate item +ℹ Safe fix +17 17 | incorrect_set = { +18 18 | 0, +19 19 | 1, +20 |- 1, +21 20 | } +22 21 | +23 22 | ### + diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF011_RUF011.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B035_B035.py.snap similarity index 69% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF011_RUF011.py.snap rename to crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B035_B035.py.snap index 14b57eb0cd1f1..2991c2e0d5bbe 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF011_RUF011.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B035_B035.py.snap @@ -1,90 +1,90 @@ --- -source: crates/ruff_linter/src/rules/ruff/mod.rs +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs --- -RUF011.py:17:2: RUF011 Dictionary comprehension uses static key: `"key"` +B035.py:17:2: B035 Dictionary comprehension uses static key: `"key"` | 16 | # Errors 17 | {"key": value.upper() for value in data} - | ^^^^^ RUF011 + | ^^^^^ B035 18 | {True: value.upper() for value in data} 19 | {0: value.upper() for value in data} | -RUF011.py:18:2: RUF011 Dictionary comprehension uses static key: `True` +B035.py:18:2: B035 Dictionary comprehension uses static key: `True` | 16 | # Errors 17 | {"key": value.upper() for value in data} 18 | {True: value.upper() for value in data} - | ^^^^ RUF011 + | ^^^^ B035 19 | {0: value.upper() for value in data} 20 | {(1, "a"): value.upper() for value in data} # Constant tuple | -RUF011.py:19:2: RUF011 Dictionary comprehension uses static key: `0` +B035.py:19:2: B035 Dictionary comprehension uses static key: `0` | 17 | {"key": value.upper() for value in data} 18 | {True: value.upper() for value in data} 19 | {0: value.upper() for value in data} - | ^ RUF011 + | ^ B035 20 | {(1, "a"): value.upper() for value in data} # Constant tuple 21 | {constant: value.upper() for value in data} | -RUF011.py:20:2: RUF011 Dictionary comprehension uses static key: `(1, "a")` +B035.py:20:2: B035 Dictionary comprehension uses static key: `(1, "a")` | 18 | {True: value.upper() for value in data} 19 | {0: value.upper() for value in data} 20 | {(1, "a"): value.upper() for value in data} # Constant tuple - | ^^^^^^^^ RUF011 + | ^^^^^^^^ B035 21 | {constant: value.upper() for value in data} 22 | {constant + constant: value.upper() for value in data} | -RUF011.py:21:2: RUF011 Dictionary comprehension uses static key: `constant` +B035.py:21:2: B035 Dictionary comprehension uses static key: `constant` | 19 | {0: value.upper() for value in data} 20 | {(1, "a"): value.upper() for value in data} # Constant tuple 21 | {constant: value.upper() for value in data} - | ^^^^^^^^ RUF011 + | ^^^^^^^^ B035 22 | {constant + constant: value.upper() for value in data} 23 | {constant.attribute: value.upper() for value in data} | -RUF011.py:22:2: RUF011 Dictionary comprehension uses static key: `constant + constant` +B035.py:22:2: B035 Dictionary comprehension uses static key: `constant + constant` | 20 | {(1, "a"): value.upper() for value in data} # Constant tuple 21 | {constant: value.upper() for value in data} 22 | {constant + constant: value.upper() for value in data} - | ^^^^^^^^^^^^^^^^^^^ RUF011 + | ^^^^^^^^^^^^^^^^^^^ B035 23 | {constant.attribute: value.upper() for value in data} 24 | {constant[0]: value.upper() for value in data} | -RUF011.py:23:2: RUF011 Dictionary comprehension uses static key: `constant.attribute` +B035.py:23:2: B035 Dictionary comprehension uses static key: `constant.attribute` | 21 | {constant: value.upper() for value in data} 22 | {constant + constant: value.upper() for value in data} 23 | {constant.attribute: value.upper() for value in data} - | ^^^^^^^^^^^^^^^^^^ RUF011 + | ^^^^^^^^^^^^^^^^^^ B035 24 | {constant[0]: value.upper() for value in data} 25 | {tokens: token for token in tokens} | -RUF011.py:24:2: RUF011 Dictionary comprehension uses static key: `constant[0]` +B035.py:24:2: B035 Dictionary comprehension uses static key: `constant[0]` | 22 | {constant + constant: value.upper() for value in data} 23 | {constant.attribute: value.upper() for value in data} 24 | {constant[0]: value.upper() for value in data} - | ^^^^^^^^^^^ RUF011 + | ^^^^^^^^^^^ B035 25 | {tokens: token for token in tokens} | -RUF011.py:25:2: RUF011 Dictionary comprehension uses static key: `tokens` +B035.py:25:2: B035 Dictionary comprehension uses static key: `tokens` | 23 | {constant.attribute: value.upper() for value in data} 24 | {constant[0]: value.upper() for value in data} 25 | {tokens: token for token in tokens} - | ^^^^^^ RUF011 + | ^^^^^^ B035 | diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B033_B033.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B033_B033.py.snap deleted file mode 100644 index 6d468e2683cd3..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B033_B033.py.snap +++ /dev/null @@ -1,169 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs ---- -B033.py:4:35: B033 [*] Sets should not contain duplicate item `"value1"` - | -2 | # Errors. -3 | ### -4 | incorrect_set = {"value1", 23, 5, "value1"} - | ^^^^^^^^ B033 -5 | incorrect_set = {1, 1, 2} -6 | incorrect_set_multiline = { - | - = help: Remove duplicate item - -ℹ Safe fix -1 1 | ### -2 2 | # Errors. -3 3 | ### -4 |-incorrect_set = {"value1", 23, 5, "value1"} - 4 |+incorrect_set = {"value1", 23, 5} -5 5 | incorrect_set = {1, 1, 2} -6 6 | incorrect_set_multiline = { -7 7 | "value1", - -B033.py:5:21: B033 [*] Sets should not contain duplicate item `1` - | -3 | ### -4 | incorrect_set = {"value1", 23, 5, "value1"} -5 | incorrect_set = {1, 1, 2} - | ^ B033 -6 | incorrect_set_multiline = { -7 | "value1", - | - = help: Remove duplicate item - -ℹ Safe fix -2 2 | # Errors. -3 3 | ### -4 4 | incorrect_set = {"value1", 23, 5, "value1"} -5 |-incorrect_set = {1, 1, 2} - 5 |+incorrect_set = {1, 2} -6 6 | incorrect_set_multiline = { -7 7 | "value1", -8 8 | 23, - -B033.py:10:5: B033 [*] Sets should not contain duplicate item `"value1"` - | - 8 | 23, - 9 | 5, -10 | "value1", - | ^^^^^^^^ B033 -11 | # B033 -12 | } - | - = help: Remove duplicate item - -ℹ Safe fix -7 7 | "value1", -8 8 | 23, -9 9 | 5, -10 |- "value1", -11 10 | # B033 -12 11 | } -13 12 | incorrect_set = {1, 1} - -B033.py:13:21: B033 [*] Sets should not contain duplicate item `1` - | -11 | # B033 -12 | } -13 | incorrect_set = {1, 1} - | ^ B033 -14 | incorrect_set = {1, 1,} -15 | incorrect_set = {0, 1, 1,} - | - = help: Remove duplicate item - -ℹ Safe fix -10 10 | "value1", -11 11 | # B033 -12 12 | } -13 |-incorrect_set = {1, 1} - 13 |+incorrect_set = {1} -14 14 | incorrect_set = {1, 1,} -15 15 | incorrect_set = {0, 1, 1,} -16 16 | incorrect_set = {0, 1, 1} - -B033.py:14:21: B033 [*] Sets should not contain duplicate item `1` - | -12 | } -13 | incorrect_set = {1, 1} -14 | incorrect_set = {1, 1,} - | ^ B033 -15 | incorrect_set = {0, 1, 1,} -16 | incorrect_set = {0, 1, 1} - | - = help: Remove duplicate item - -ℹ Safe fix -11 11 | # B033 -12 12 | } -13 13 | incorrect_set = {1, 1} -14 |-incorrect_set = {1, 1,} - 14 |+incorrect_set = {1,} -15 15 | incorrect_set = {0, 1, 1,} -16 16 | incorrect_set = {0, 1, 1} -17 17 | incorrect_set = { - -B033.py:15:24: B033 [*] Sets should not contain duplicate item `1` - | -13 | incorrect_set = {1, 1} -14 | incorrect_set = {1, 1,} -15 | incorrect_set = {0, 1, 1,} - | ^ B033 -16 | incorrect_set = {0, 1, 1} -17 | incorrect_set = { - | - = help: Remove duplicate item - -ℹ Safe fix -12 12 | } -13 13 | incorrect_set = {1, 1} -14 14 | incorrect_set = {1, 1,} -15 |-incorrect_set = {0, 1, 1,} - 15 |+incorrect_set = {0, 1,} -16 16 | incorrect_set = {0, 1, 1} -17 17 | incorrect_set = { -18 18 | 0, - -B033.py:16:24: B033 [*] Sets should not contain duplicate item `1` - | -14 | incorrect_set = {1, 1,} -15 | incorrect_set = {0, 1, 1,} -16 | incorrect_set = {0, 1, 1} - | ^ B033 -17 | incorrect_set = { -18 | 0, - | - = help: Remove duplicate item - -ℹ Safe fix -13 13 | incorrect_set = {1, 1} -14 14 | incorrect_set = {1, 1,} -15 15 | incorrect_set = {0, 1, 1,} -16 |-incorrect_set = {0, 1, 1} - 16 |+incorrect_set = {0, 1} -17 17 | incorrect_set = { -18 18 | 0, -19 19 | 1, - -B033.py:20:5: B033 [*] Sets should not contain duplicate item `1` - | -18 | 0, -19 | 1, -20 | 1, - | ^ B033 -21 | } - | - = help: Remove duplicate item - -ℹ Safe fix -17 17 | incorrect_set = { -18 18 | 0, -19 19 | 1, -20 |- 1, -21 20 | } -22 21 | -23 22 | ### - - diff --git a/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_argument_shadowing.rs b/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_argument_shadowing.rs index 91790de0ea15b..8a07a727b3c62 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_argument_shadowing.rs +++ b/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_argument_shadowing.rs @@ -19,7 +19,7 @@ use super::super::helpers::shadows_builtin; /// builtin and vice versa. /// /// Builtins can be marked as exceptions to this rule via the -/// [`flake8-builtins.builtins-ignorelist`] configuration option. +/// [`lint.flake8-builtins.builtins-ignorelist`] configuration option. /// /// ## Example /// ```python @@ -44,7 +44,7 @@ use super::super::helpers::shadows_builtin; /// ``` /// /// ## Options -/// - `flake8-builtins.builtins-ignorelist` +/// - `lint.flake8-builtins.builtins-ignorelist` /// /// ## References /// - [_Is it bad practice to use a built-in function name as an attribute or method identifier?_](https://stackoverflow.com/questions/9109333/is-it-bad-practice-to-use-a-built-in-function-name-as-an-attribute-or-method-ide) diff --git a/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_attribute_shadowing.rs b/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_attribute_shadowing.rs index 793d287c65d2d..de4a625c3dac9 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_attribute_shadowing.rs +++ b/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_attribute_shadowing.rs @@ -37,7 +37,7 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin; /// ``` /// /// Builtins can be marked as exceptions to this rule via the -/// [`flake8-builtins.builtins-ignorelist`] configuration option, or +/// [`lint.flake8-builtins.builtins-ignorelist`] configuration option, or /// converted to the appropriate dunder method. Methods decorated with /// `@typing.override` or `@typing_extensions.override` are also /// ignored. @@ -55,7 +55,7 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin; /// ``` /// /// ## Options -/// - `flake8-builtins.builtins-ignorelist` +/// - `lint.flake8-builtins.builtins-ignorelist` #[violation] pub struct BuiltinAttributeShadowing { kind: Kind, diff --git a/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_variable_shadowing.rs b/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_variable_shadowing.rs index 9815d8dffba4a..0b5a0080d063b 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_variable_shadowing.rs +++ b/crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_variable_shadowing.rs @@ -18,7 +18,7 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin; /// builtin and vice versa. /// /// Builtins can be marked as exceptions to this rule via the -/// [`flake8-builtins.builtins-ignorelist`] configuration option. +/// [`lint.flake8-builtins.builtins-ignorelist`] configuration option. /// /// ## Example /// ```python @@ -41,7 +41,7 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin; /// ``` /// /// ## Options -/// - `flake8-builtins.builtins-ignorelist` +/// - `lint.flake8-builtins.builtins-ignorelist` /// /// ## References /// - [_Why is it a bad idea to name a variable `id` in Python?_](https://stackoverflow.com/questions/77552/id-is-a-bad-variable-name-in-python) diff --git a/crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs b/crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs index 5e91f0ccead47..ae6917b6a00fc 100644 --- a/crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs +++ b/crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs @@ -59,7 +59,7 @@ impl Violation for SingleLineImplicitStringConcatenation { /// /// By default, this rule will only trigger if the string literal is /// concatenated via a backslash. To disallow implicit string concatenation -/// altogether, set the [`flake8-implicit-str-concat.allow-multiline`] option +/// altogether, set the [`lint.flake8-implicit-str-concat.allow-multiline`] option /// to `false`. /// /// ## Example @@ -77,7 +77,7 @@ impl Violation for SingleLineImplicitStringConcatenation { /// ``` /// /// ## Options -/// - `flake8-implicit-str-concat.allow-multiline` +/// - `lint.flake8-implicit-str-concat.allow-multiline` /// /// [PEP 8]: https://peps.python.org/pep-0008/#maximum-line-length #[violation] diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_alias.rs b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_alias.rs index 955967020847f..bf41de81a9225 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_alias.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_alias.rs @@ -29,7 +29,7 @@ use ruff_text_size::Ranged; /// ``` /// /// ## Options -/// - `flake8-import-conventions.banned-aliases` +/// - `lint.flake8-import-conventions.banned-aliases` #[violation] pub struct BannedImportAlias { name: String, diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_from.rs b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_from.rs index 6cd5eccd2c337..7cbeae6f831f8 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_from.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_from.rs @@ -30,7 +30,7 @@ use ruff_text_size::Ranged; /// ``` /// /// ## Options -/// - `flake8-import-conventions.banned-from` +/// - `lint.flake8-import-conventions.banned-from` #[violation] pub struct BannedImportFrom { name: String, diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/unconventional_import_alias.rs b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/unconventional_import_alias.rs index 403bdcda9f023..0cd207187cd62 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/unconventional_import_alias.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/unconventional_import_alias.rs @@ -32,8 +32,8 @@ use crate::renamer::Renamer; /// ``` /// /// ## Options -/// - `flake8-import-conventions.aliases` -/// - `flake8-import-conventions.extend-aliases` +/// - `lint.flake8-import-conventions.aliases` +/// - `lint.flake8-import-conventions.extend-aliases` #[violation] pub struct UnconventionalImportAlias { name: String, diff --git a/crates/ruff_linter/src/rules/flake8_logging_format/violations.rs b/crates/ruff_linter/src/rules/flake8_logging_format/violations.rs index 114a1fa68a538..9cc63601c11e9 100644 --- a/crates/ruff_linter/src/rules/flake8_logging_format/violations.rs +++ b/crates/ruff_linter/src/rules/flake8_logging_format/violations.rs @@ -30,9 +30,9 @@ use ruff_macros::{derive_message_formats, violation}; /// - Uses of `flask.current_app.logger` (e.g., `from flask import current_app; current_app.logger.info(...)`). /// - Objects whose name starts with `log` or ends with `logger` or `logging`, /// when used in the same file in which they are defined (e.g., `logger = logging.getLogger(); logger.info(...)`). -/// - Imported objects marked as loggers via the [`logger-objects`] setting, which can be +/// - Imported objects marked as loggers via the [`lint.logger-objects`] setting, which can be /// used to enforce these rules against shared logger objects (e.g., `from module import logger; logger.info(...)`, -/// when [`logger-objects`] is set to `["module.logger"]`). +/// when [`lint.logger-objects`] is set to `["module.logger"]`). /// /// ## Example /// ```python @@ -68,7 +68,7 @@ use ruff_macros::{derive_message_formats, violation}; /// ``` /// /// ## Options -/// - `logger-objects` +/// - `lint.logger-objects` /// /// ## References /// - [Python documentation: `logging`](https://docs.python.org/3/library/logging.html) @@ -114,9 +114,9 @@ impl Violation for LoggingStringFormat { /// - Uses of `flask.current_app.logger` (e.g., `from flask import current_app; current_app.logger.info(...)`). /// - Objects whose name starts with `log` or ends with `logger` or `logging`, /// when used in the same file in which they are defined (e.g., `logger = logging.getLogger(); logger.info(...)`). -/// - Imported objects marked as loggers via the [`logger-objects`] setting, which can be +/// - Imported objects marked as loggers via the [`lint.logger-objects`] setting, which can be /// used to enforce these rules against shared logger objects (e.g., `from module import logger; logger.info(...)`, -/// when [`logger-objects`] is set to `["module.logger"]`). +/// when [`lint.logger-objects`] is set to `["module.logger"]`). /// /// ## Example /// ```python @@ -152,7 +152,7 @@ impl Violation for LoggingStringFormat { /// ``` /// /// ## Options -/// - `logger-objects` +/// - `lint.logger-objects` /// /// ## References /// - [Python documentation: `logging`](https://docs.python.org/3/library/logging.html) @@ -197,9 +197,9 @@ impl Violation for LoggingPercentFormat { /// - Uses of `flask.current_app.logger` (e.g., `from flask import current_app; current_app.logger.info(...)`). /// - Objects whose name starts with `log` or ends with `logger` or `logging`, /// when used in the same file in which they are defined (e.g., `logger = logging.getLogger(); logger.info(...)`). -/// - Imported objects marked as loggers via the [`logger-objects`] setting, which can be +/// - Imported objects marked as loggers via the [`lint.logger-objects`] setting, which can be /// used to enforce these rules against shared logger objects (e.g., `from module import logger; logger.info(...)`, -/// when [`logger-objects`] is set to `["module.logger"]`). +/// when [`lint.logger-objects`] is set to `["module.logger"]`). /// /// ## Example /// ```python @@ -235,7 +235,7 @@ impl Violation for LoggingPercentFormat { /// ``` /// /// ## Options -/// - `logger-objects` +/// - `lint.logger-objects` /// /// ## References /// - [Python documentation: `logging`](https://docs.python.org/3/library/logging.html) @@ -279,9 +279,9 @@ impl Violation for LoggingStringConcat { /// - Uses of `flask.current_app.logger` (e.g., `from flask import current_app; current_app.logger.info(...)`). /// - Objects whose name starts with `log` or ends with `logger` or `logging`, /// when used in the same file in which they are defined (e.g., `logger = logging.getLogger(); logger.info(...)`). -/// - Imported objects marked as loggers via the [`logger-objects`] setting, which can be +/// - Imported objects marked as loggers via the [`lint.logger-objects`] setting, which can be /// used to enforce these rules against shared logger objects (e.g., `from module import logger; logger.info(...)`, -/// when [`logger-objects`] is set to `["module.logger"]`). +/// when [`lint.logger-objects`] is set to `["module.logger"]`). /// /// ## Example /// ```python @@ -317,7 +317,7 @@ impl Violation for LoggingStringConcat { /// ``` /// /// ## Options -/// - `logger-objects` +/// - `lint.logger-objects` /// /// ## References /// - [Python documentation: `logging`](https://docs.python.org/3/library/logging.html) @@ -349,9 +349,9 @@ impl Violation for LoggingFString { /// - Uses of `flask.current_app.logger` (e.g., `from flask import current_app; current_app.logger.info(...)`). /// - Objects whose name starts with `log` or ends with `logger` or `logging`, /// when used in the same file in which they are defined (e.g., `logger = logging.getLogger(); logger.info(...)`). -/// - Imported objects marked as loggers via the [`logger-objects`] setting, which can be +/// - Imported objects marked as loggers via the [`lint.logger-objects`] setting, which can be /// used to enforce these rules against shared logger objects (e.g., `from module import logger; logger.info(...)`, -/// when [`logger-objects`] is set to `["module.logger"]`). +/// when [`lint.logger-objects`] is set to `["module.logger"]`). /// /// ## Example /// ```python @@ -368,7 +368,7 @@ impl Violation for LoggingFString { /// ``` /// /// ## Options -/// - `logger-objects` +/// - `lint.logger-objects` /// /// ## References /// - [Python documentation: `logging.warning`](https://docs.python.org/3/library/logging.html#logging.warning) @@ -409,9 +409,9 @@ impl AlwaysFixableViolation for LoggingWarn { /// - Uses of `flask.current_app.logger` (e.g., `from flask import current_app; current_app.logger.info(...)`). /// - Objects whose name starts with `log` or ends with `logger` or `logging`, /// when used in the same file in which they are defined (e.g., `logger = logging.getLogger(); logger.info(...)`). -/// - Imported objects marked as loggers via the [`logger-objects`] setting, which can be +/// - Imported objects marked as loggers via the [`lint.logger-objects`] setting, which can be /// used to enforce these rules against shared logger objects (e.g., `from module import logger; logger.info(...)`, -/// when [`logger-objects`] is set to `["module.logger"]`). +/// when [`lint.logger-objects`] is set to `["module.logger"]`). /// /// ## Example /// ```python @@ -436,7 +436,7 @@ impl AlwaysFixableViolation for LoggingWarn { /// ``` /// /// ## Options -/// - `logger-objects` +/// - `lint.logger-objects` /// /// ## References /// - [Python documentation: LogRecord attributes](https://docs.python.org/3/library/logging.html#logrecord-attributes) @@ -470,9 +470,9 @@ impl Violation for LoggingExtraAttrClash { /// - Uses of `flask.current_app.logger` (e.g., `from flask import current_app; current_app.logger.info(...)`). /// - Objects whose name starts with `log` or ends with `logger` or `logging`, /// when used in the same file in which they are defined (e.g., `logger = logging.getLogger(); logger.info(...)`). -/// - Imported objects marked as loggers via the [`logger-objects`] setting, which can be +/// - Imported objects marked as loggers via the [`lint.logger-objects`] setting, which can be /// used to enforce these rules against shared logger objects (e.g., `from module import logger; logger.info(...)`, -/// when [`logger-objects`] is set to `["module.logger"]`). +/// when [`lint.logger-objects`] is set to `["module.logger"]`). /// /// ## Example /// ```python @@ -495,7 +495,7 @@ impl Violation for LoggingExtraAttrClash { /// ``` /// /// ## Options -/// - `logger-objects` +/// - `lint.logger-objects` /// /// ## References /// - [Python documentation: `logging.exception`](https://docs.python.org/3/library/logging.html#logging.exception) @@ -531,9 +531,9 @@ impl Violation for LoggingExcInfo { /// - Uses of `flask.current_app.logger` (e.g., `from flask import current_app; current_app.logger.info(...)`). /// - Objects whose name starts with `log` or ends with `logger` or `logging`, /// when used in the same file in which they are defined (e.g., `logger = logging.getLogger(); logger.info(...)`). -/// - Imported objects marked as loggers via the [`logger-objects`] setting, which can be +/// - Imported objects marked as loggers via the [`lint.logger-objects`] setting, which can be /// used to enforce these rules against shared logger objects (e.g., `from module import logger; logger.info(...)`, -/// when [`logger-objects`] is set to `["module.logger"]`). +/// when [`lint.logger-objects`] is set to `["module.logger"]`). /// /// ## Example /// ```python @@ -556,7 +556,7 @@ impl Violation for LoggingExcInfo { /// ``` /// /// ## Options -/// - `logger-objects` +/// - `lint.logger-objects` /// /// ## References /// - [Python documentation: `logging.exception`](https://docs.python.org/3/library/logging.html#logging.exception) diff --git a/crates/ruff_linter/src/rules/flake8_pie/mod.rs b/crates/ruff_linter/src/rules/flake8_pie/mod.rs index 046b43b097aae..8b312f1fb136e 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/mod.rs @@ -9,7 +9,6 @@ mod tests { use test_case::test_case; use crate::registry::Rule; - use crate::settings::types::PreviewMode; use crate::test::test_path; use crate::{assert_messages, settings}; @@ -31,24 +30,4 @@ mod tests { assert_messages!(snapshot, diagnostics); Ok(()) } - - #[test_case(Rule::UnnecessaryPlaceholder, Path::new("PIE790.py"))] - #[test_case(Rule::UnnecessarySpread, Path::new("PIE800.py"))] - #[test_case(Rule::ReimplementedContainerBuiltin, Path::new("PIE807.py"))] - fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { - let snapshot = format!( - "preview__{}_{}", - rule_code.noqa_code(), - path.to_string_lossy() - ); - let diagnostics = test_path( - Path::new("flake8_pie").join(path).as_path(), - &settings::LinterSettings { - preview: PreviewMode::Enabled, - ..settings::LinterSettings::for_rule(rule_code) - }, - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } } diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/reimplemented_container_builtin.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/reimplemented_container_builtin.rs index fbc001d9b304a..7921ff0197c2f 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/reimplemented_container_builtin.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/reimplemented_container_builtin.rs @@ -8,10 +8,7 @@ use ruff_text_size::Ranged; use crate::checkers::ast::Checker; /// ## What it does -/// Checks for lambdas that can be replaced with the `list` builtin. -/// -/// In [preview], this rule will also flag lambdas that can be replaced with -/// the `dict` builtin. +/// Checks for lambdas that can be replaced with the `list` or `dict` builtins. /// /// ## Why is this bad? /// Using container builtins are more succinct and idiomatic than wrapping @@ -40,8 +37,6 @@ use crate::checkers::ast::Checker; /// /// ## References /// - [Python documentation: `list`](https://docs.python.org/3/library/functions.html#func-list) -/// -/// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] pub struct ReimplementedContainerBuiltin { container: Container, @@ -73,11 +68,7 @@ pub(crate) fn reimplemented_container_builtin(checker: &mut Checker, expr: &Expr if parameters.is_none() { let container = match body.as_ref() { Expr::List(ast::ExprList { elts, .. }) if elts.is_empty() => Some(Container::List), - Expr::Dict(ast::ExprDict { values, .. }) - if values.is_empty() & checker.settings.preview.is_enabled() => - { - Some(Container::Dict) - } + Expr::Dict(ast::ExprDict { values, .. }) if values.is_empty() => Some(Container::Dict), _ => None, }; if let Some(container) = container { diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_placeholder.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_placeholder.rs index 8d847744d1e19..c3997a534fdf1 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_placeholder.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_placeholder.rs @@ -10,11 +10,8 @@ use crate::checkers::ast::Checker; use crate::fix; /// ## What it does -/// Checks for unnecessary `pass` statements in functions, classes, and other -/// blocks. -/// -/// In [preview], this rule also checks for unnecessary ellipsis (`...`) -/// literals. +/// Checks for unnecessary `pass` statements and ellipsis (`...`) literals in +/// functions, classes, and other blocks. /// /// ## Why is this bad? /// In Python, the `pass` statement and ellipsis (`...`) literal serve as @@ -40,7 +37,7 @@ use crate::fix; /// """Placeholder docstring.""" /// ``` /// -/// In [preview]: +/// Or, given: /// ```python /// def func(): /// """Placeholder docstring.""" @@ -55,8 +52,6 @@ use crate::fix; /// /// ## References /// - [Python documentation: The `pass` statement](https://docs.python.org/3/reference/simple_stmts.html#the-pass-statement) -/// -/// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] pub struct UnnecessaryPlaceholder { kind: Placeholder, @@ -90,10 +85,7 @@ pub(crate) fn unnecessary_placeholder(checker: &mut Checker, body: &[Stmt]) { for stmt in body { let kind = match stmt { Stmt::Pass(_) => Placeholder::Pass, - Stmt::Expr(expr) - if expr.value.is_ellipsis_literal_expr() - && checker.settings.preview.is_enabled() => - { + Stmt::Expr(expr) if expr.value.is_ellipsis_literal_expr() => { // Ellipses are significant in protocol methods and abstract methods. Specifically, // Pyright uses the presence of an ellipsis to indicate that a method is a stub, // rather than a default implementation. diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_spread.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_spread.rs index 12b33b6075548..aa81145bd872f 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_spread.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_spread.rs @@ -55,10 +55,8 @@ pub(crate) fn unnecessary_spread(checker: &mut Checker, dict: &ast::ExprDict) { // inside a dict. if let Expr::Dict(inner) = value { let mut diagnostic = Diagnostic::new(UnnecessarySpread, value.range()); - if checker.settings.preview.is_enabled() { - if let Some(fix) = unnecessary_spread_fix(inner, prev_end, checker.locator()) { - diagnostic.set_fix(fix); - } + if let Some(fix) = unnecessary_spread_fix(inner, prev_end, checker.locator()) { + diagnostic.set_fix(fix); } checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE790_PIE790.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE790_PIE790.py.snap index aaaa7e4ce3978..e881a177deee5 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE790_PIE790.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE790_PIE790.py.snap @@ -467,6 +467,176 @@ PIE790.py:150:5: PIE790 [*] Unnecessary `pass` statement 152 151 | 153 152 | def foo(): +PIE790.py:155:5: PIE790 [*] Unnecessary `...` literal + | +153 | def foo(): +154 | print("foo") +155 | ... + | ^^^ PIE790 + | + = help: Remove unnecessary `...` + +ℹ Safe fix +152 152 | +153 153 | def foo(): +154 154 | print("foo") +155 |- ... +156 155 | +157 156 | +158 157 | def foo(): + +PIE790.py:161:5: PIE790 [*] Unnecessary `...` literal + | +159 | """A docstring.""" +160 | print("foo") +161 | ... + | ^^^ PIE790 + | + = help: Remove unnecessary `...` + +ℹ Safe fix +158 158 | def foo(): +159 159 | """A docstring.""" +160 160 | print("foo") +161 |- ... +162 161 | +163 162 | +164 163 | for i in range(10): + +PIE790.py:165:5: PIE790 [*] Unnecessary `...` literal + | +164 | for i in range(10): +165 | ... + | ^^^ PIE790 +166 | ... + | + = help: Remove unnecessary `...` + +ℹ Safe fix +163 163 | +164 164 | for i in range(10): +165 165 | ... +166 |- ... +167 166 | +168 167 | for i in range(10): +169 168 | ... + +PIE790.py:166:5: PIE790 [*] Unnecessary `...` literal + | +164 | for i in range(10): +165 | ... +166 | ... + | ^^^ PIE790 +167 | +168 | for i in range(10): + | + = help: Remove unnecessary `...` + +ℹ Safe fix +163 163 | +164 164 | for i in range(10): +165 165 | ... +166 |- ... +167 166 | +168 167 | for i in range(10): +169 168 | ... + +PIE790.py:169:5: PIE790 [*] Unnecessary `...` literal + | +168 | for i in range(10): +169 | ... + | ^^^ PIE790 +170 | +171 | ... + | + = help: Remove unnecessary `...` + +ℹ Safe fix +166 166 | ... +167 167 | +168 168 | for i in range(10): +169 |- ... +170 169 | +171 170 | ... +172 171 | + +PIE790.py:171:5: PIE790 [*] Unnecessary `...` literal + | +169 | ... +170 | +171 | ... + | ^^^ PIE790 +172 | +173 | for i in range(10): + | + = help: Remove unnecessary `...` + +ℹ Safe fix +168 168 | for i in range(10): +169 169 | ... +170 170 | +171 |- ... +172 171 | +173 172 | for i in range(10): +174 173 | ... # comment + +PIE790.py:174:5: PIE790 [*] Unnecessary `...` literal + | +173 | for i in range(10): +174 | ... # comment + | ^^^ PIE790 +175 | ... + | + = help: Remove unnecessary `...` + +ℹ Safe fix +171 171 | ... +172 172 | +173 173 | for i in range(10): +174 |- ... # comment + 174 |+ # comment +175 175 | ... +176 176 | +177 177 | for i in range(10): + +PIE790.py:175:5: PIE790 [*] Unnecessary `...` literal + | +173 | for i in range(10): +174 | ... # comment +175 | ... + | ^^^ PIE790 +176 | +177 | for i in range(10): + | + = help: Remove unnecessary `...` + +ℹ Safe fix +172 172 | +173 173 | for i in range(10): +174 174 | ... # comment +175 |- ... +176 175 | +177 176 | for i in range(10): +178 177 | ... + +PIE790.py:178:5: PIE790 [*] Unnecessary `...` literal + | +177 | for i in range(10): +178 | ... + | ^^^ PIE790 +179 | pass + | + = help: Remove unnecessary `...` + +ℹ Safe fix +175 175 | ... +176 176 | +177 177 | for i in range(10): +178 |- ... +179 178 | pass +180 179 | +181 180 | from typing import Protocol + PIE790.py:179:5: PIE790 [*] Unnecessary `pass` statement | 177 | for i in range(10): @@ -487,4 +657,19 @@ PIE790.py:179:5: PIE790 [*] Unnecessary `pass` statement 181 180 | from typing import Protocol 182 181 | +PIE790.py:209:9: PIE790 [*] Unnecessary `...` literal + | +207 | def stub(self) -> str: +208 | """Docstring""" +209 | ... + | ^^^ PIE790 + | + = help: Remove unnecessary `...` + +ℹ Safe fix +206 206 | +207 207 | def stub(self) -> str: +208 208 | """Docstring""" +209 |- ... + diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE800_PIE800.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE800_PIE800.py.snap index ad6c9e293eead..217e4c71f3a21 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE800_PIE800.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE800_PIE800.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_pie/mod.rs --- -PIE800.py:1:14: PIE800 Unnecessary spread `**` +PIE800.py:1:14: PIE800 [*] Unnecessary spread `**` | 1 | {"foo": 1, **{"bar": 1}} # PIE800 | ^^^^^^^^^^ PIE800 @@ -10,7 +10,14 @@ PIE800.py:1:14: PIE800 Unnecessary spread `**` | = help: Remove unnecessary dict -PIE800.py:3:4: PIE800 Unnecessary spread `**` +ℹ Safe fix +1 |-{"foo": 1, **{"bar": 1}} # PIE800 + 1 |+{"foo": 1, "bar": 1} # PIE800 +2 2 | +3 3 | {**{"bar": 10}, "a": "b"} # PIE800 +4 4 | + +PIE800.py:3:4: PIE800 [*] Unnecessary spread `**` | 1 | {"foo": 1, **{"bar": 1}} # PIE800 2 | @@ -21,7 +28,16 @@ PIE800.py:3:4: PIE800 Unnecessary spread `**` | = help: Remove unnecessary dict -PIE800.py:5:15: PIE800 Unnecessary spread `**` +ℹ Safe fix +1 1 | {"foo": 1, **{"bar": 1}} # PIE800 +2 2 | +3 |-{**{"bar": 10}, "a": "b"} # PIE800 + 3 |+{"bar": 10, "a": "b"} # PIE800 +4 4 | +5 5 | foo({**foo, **{"bar": True}}) # PIE800 +6 6 | + +PIE800.py:5:15: PIE800 [*] Unnecessary spread `**` | 3 | {**{"bar": 10}, "a": "b"} # PIE800 4 | @@ -32,7 +48,17 @@ PIE800.py:5:15: PIE800 Unnecessary spread `**` | = help: Remove unnecessary dict -PIE800.py:7:11: PIE800 Unnecessary spread `**` +ℹ Safe fix +2 2 | +3 3 | {**{"bar": 10}, "a": "b"} # PIE800 +4 4 | +5 |-foo({**foo, **{"bar": True}}) # PIE800 + 5 |+foo({**foo, "bar": True}) # PIE800 +6 6 | +7 7 | {**foo, **{"bar": 10}} # PIE800 +8 8 | + +PIE800.py:7:11: PIE800 [*] Unnecessary spread `**` | 5 | foo({**foo, **{"bar": True}}) # PIE800 6 | @@ -43,7 +69,17 @@ PIE800.py:7:11: PIE800 Unnecessary spread `**` | = help: Remove unnecessary dict -PIE800.py:12:7: PIE800 Unnecessary spread `**` +ℹ Safe fix +4 4 | +5 5 | foo({**foo, **{"bar": True}}) # PIE800 +6 6 | +7 |-{**foo, **{"bar": 10}} # PIE800 + 7 |+{**foo, "bar": 10} # PIE800 +8 8 | +9 9 | { # PIE800 +10 10 | "a": "b", + +PIE800.py:12:7: PIE800 [*] Unnecessary spread `**` | 10 | "a": "b", 11 | # Preserve @@ -58,7 +94,23 @@ PIE800.py:12:7: PIE800 Unnecessary spread `**` | = help: Remove unnecessary dict -PIE800.py:19:19: PIE800 Unnecessary spread `**` +ℹ Safe fix +9 9 | { # PIE800 +10 10 | "a": "b", +11 11 | # Preserve +12 |- **{ + 12 |+ +13 13 | # all +14 |- "bar": 10, # the + 14 |+ "bar": 10 # the +15 15 | # comments +16 |- }, + 16 |+ , +17 17 | } +18 18 | +19 19 | {**foo, **buzz, **{bar: 10}} # PIE800 + +PIE800.py:19:19: PIE800 [*] Unnecessary spread `**` | 17 | } 18 | @@ -69,4 +121,14 @@ PIE800.py:19:19: PIE800 Unnecessary spread `**` | = help: Remove unnecessary dict +ℹ Safe fix +16 16 | }, +17 17 | } +18 18 | +19 |-{**foo, **buzz, **{bar: 10}} # PIE800 + 19 |+{**foo, **buzz, bar: 10} # PIE800 +20 20 | +21 21 | {**foo, "bar": True } # OK +22 22 | + diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE807_PIE807.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE807_PIE807.py.snap index ad09dcb5de8ff..474c3456d53f9 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE807_PIE807.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE807_PIE807.py.snap @@ -20,6 +20,25 @@ PIE807.py:3:44: PIE807 [*] Prefer `list` over useless lambda 5 5 | 6 6 | +PIE807.py:4:49: PIE807 [*] Prefer `dict` over useless lambda + | +2 | class Foo: +3 | foo: List[str] = field(default_factory=lambda: []) # PIE807 +4 | bar: Dict[str, int] = field(default_factory=lambda: {}) # PIE807 + | ^^^^^^^^^^ PIE807 + | + = help: Replace with `lambda` with `dict` + +ℹ Safe fix +1 1 | @dataclass +2 2 | class Foo: +3 3 | foo: List[str] = field(default_factory=lambda: []) # PIE807 +4 |- bar: Dict[str, int] = field(default_factory=lambda: {}) # PIE807 + 4 |+ bar: Dict[str, int] = field(default_factory=dict) # PIE807 +5 5 | +6 6 | +7 7 | class FooTable(BaseTable): + PIE807.py:8:36: PIE807 [*] Prefer `list` over useless lambda | 7 | class FooTable(BaseTable): @@ -39,6 +58,25 @@ PIE807.py:8:36: PIE807 [*] Prefer `list` over useless lambda 10 10 | 11 11 | +PIE807.py:9:36: PIE807 [*] Prefer `dict` over useless lambda + | +7 | class FooTable(BaseTable): +8 | foo = fields.ListField(default=lambda: []) # PIE807 +9 | bar = fields.ListField(default=lambda: {}) # PIE807 + | ^^^^^^^^^^ PIE807 + | + = help: Replace with `lambda` with `dict` + +ℹ Safe fix +6 6 | +7 7 | class FooTable(BaseTable): +8 8 | foo = fields.ListField(default=lambda: []) # PIE807 +9 |- bar = fields.ListField(default=lambda: {}) # PIE807 + 9 |+ bar = fields.ListField(default=dict) # PIE807 +10 10 | +11 11 | +12 12 | class FooTable(BaseTable): + PIE807.py:13:28: PIE807 [*] Prefer `list` over useless lambda | 12 | class FooTable(BaseTable): @@ -58,4 +96,23 @@ PIE807.py:13:28: PIE807 [*] Prefer `list` over useless lambda 15 15 | 16 16 | +PIE807.py:14:36: PIE807 [*] Prefer `dict` over useless lambda + | +12 | class FooTable(BaseTable): +13 | foo = fields.ListField(lambda: []) # PIE807 +14 | bar = fields.ListField(default=lambda: {}) # PIE807 + | ^^^^^^^^^^ PIE807 + | + = help: Replace with `lambda` with `dict` + +ℹ Safe fix +11 11 | +12 12 | class FooTable(BaseTable): +13 13 | foo = fields.ListField(lambda: []) # PIE807 +14 |- bar = fields.ListField(default=lambda: {}) # PIE807 + 14 |+ bar = fields.ListField(default=dict) # PIE807 +15 15 | +16 16 | +17 17 | @dataclass + diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE790_PIE790.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE790_PIE790.py.snap deleted file mode 100644 index e881a177deee5..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE790_PIE790.py.snap +++ /dev/null @@ -1,675 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_pie/mod.rs ---- -PIE790.py:4:5: PIE790 [*] Unnecessary `pass` statement - | -2 | """buzz""" -3 | -4 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -1 1 | class Foo: -2 2 | """buzz""" -3 3 | -4 |- pass -5 4 | -6 5 | -7 6 | if foo: - -PIE790.py:9:5: PIE790 [*] Unnecessary `pass` statement - | -7 | if foo: -8 | """foo""" -9 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -6 6 | -7 7 | if foo: -8 8 | """foo""" -9 |- pass -10 9 | -11 10 | -12 11 | def multi_statement() -> None: - -PIE790.py:14:5: PIE790 [*] Unnecessary `pass` statement - | -12 | def multi_statement() -> None: -13 | """This is a function.""" -14 | pass; print("hello") - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -11 11 | -12 12 | def multi_statement() -> None: -13 13 | """This is a function.""" -14 |- pass; print("hello") - 14 |+ print("hello") -15 15 | -16 16 | -17 17 | if foo: - -PIE790.py:21:5: PIE790 [*] Unnecessary `pass` statement - | -19 | else: -20 | """bar""" -21 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -18 18 | pass -19 19 | else: -20 20 | """bar""" -21 |- pass -22 21 | -23 22 | -24 23 | while True: - -PIE790.py:28:5: PIE790 [*] Unnecessary `pass` statement - | -26 | else: -27 | """bar""" -28 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -25 25 | pass -26 26 | else: -27 27 | """bar""" -28 |- pass -29 28 | -30 29 | -31 30 | for _ in range(10): - -PIE790.py:35:5: PIE790 [*] Unnecessary `pass` statement - | -33 | else: -34 | """bar""" -35 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -32 32 | pass -33 33 | else: -34 34 | """bar""" -35 |- pass -36 35 | -37 36 | -38 37 | async for _ in range(10): - -PIE790.py:42:5: PIE790 [*] Unnecessary `pass` statement - | -40 | else: -41 | """bar""" -42 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -39 39 | pass -40 40 | else: -41 41 | """bar""" -42 |- pass -43 42 | -44 43 | -45 44 | def foo() -> None: - -PIE790.py:50:5: PIE790 [*] Unnecessary `pass` statement - | -48 | """ -49 | -50 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -47 47 | buzz -48 48 | """ -49 49 | -50 |- pass -51 50 | -52 51 | -53 52 | async def foo(): - -PIE790.py:58:5: PIE790 [*] Unnecessary `pass` statement - | -56 | """ -57 | -58 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -55 55 | buzz -56 56 | """ -57 57 | -58 |- pass -59 58 | -60 59 | -61 60 | try: - -PIE790.py:65:5: PIE790 [*] Unnecessary `pass` statement - | -63 | buzz -64 | """ -65 | pass - | ^^^^ PIE790 -66 | except ValueError: -67 | pass - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -62 62 | """ -63 63 | buzz -64 64 | """ -65 |- pass -66 65 | except ValueError: -67 66 | pass -68 67 | - -PIE790.py:74:5: PIE790 [*] Unnecessary `pass` statement - | -72 | except ValueError: -73 | """bar""" -74 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -71 71 | bar() -72 72 | except ValueError: -73 73 | """bar""" -74 |- pass -75 74 | -76 75 | -77 76 | for _ in range(10): - -PIE790.py:79:5: PIE790 [*] Unnecessary `pass` statement - | -77 | for _ in range(10): -78 | """buzz""" -79 | pass - | ^^^^ PIE790 -80 | -81 | async for _ in range(10): - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -76 76 | -77 77 | for _ in range(10): -78 78 | """buzz""" -79 |- pass -80 79 | -81 80 | async for _ in range(10): -82 81 | """buzz""" - -PIE790.py:83:5: PIE790 [*] Unnecessary `pass` statement - | -81 | async for _ in range(10): -82 | """buzz""" -83 | pass - | ^^^^ PIE790 -84 | -85 | while cond: - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -80 80 | -81 81 | async for _ in range(10): -82 82 | """buzz""" -83 |- pass -84 83 | -85 84 | while cond: -86 85 | """buzz""" - -PIE790.py:87:5: PIE790 [*] Unnecessary `pass` statement - | -85 | while cond: -86 | """buzz""" -87 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -84 84 | -85 85 | while cond: -86 86 | """buzz""" -87 |- pass -88 87 | -89 88 | -90 89 | with bar: - -PIE790.py:92:5: PIE790 [*] Unnecessary `pass` statement - | -90 | with bar: -91 | """buzz""" -92 | pass - | ^^^^ PIE790 -93 | -94 | async with bar: - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -89 89 | -90 90 | with bar: -91 91 | """buzz""" -92 |- pass -93 92 | -94 93 | async with bar: -95 94 | """buzz""" - -PIE790.py:96:5: PIE790 [*] Unnecessary `pass` statement - | -94 | async with bar: -95 | """buzz""" -96 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -93 93 | -94 94 | async with bar: -95 95 | """buzz""" -96 |- pass -97 96 | -98 97 | -99 98 | def foo() -> None: - -PIE790.py:101:5: PIE790 [*] Unnecessary `pass` statement - | - 99 | def foo() -> None: -100 | """buzz""" -101 | pass # bar - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -98 98 | -99 99 | def foo() -> None: -100 100 | """buzz""" -101 |- pass # bar - 101 |+ # bar -102 102 | -103 103 | -104 104 | class Foo: - -PIE790.py:130:5: PIE790 [*] Unnecessary `pass` statement - | -128 | def foo(): -129 | print("foo") -130 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -127 127 | -128 128 | def foo(): -129 129 | print("foo") -130 |- pass -131 130 | -132 131 | -133 132 | def foo(): - -PIE790.py:136:5: PIE790 [*] Unnecessary `pass` statement - | -134 | """A docstring.""" -135 | print("foo") -136 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -133 133 | def foo(): -134 134 | """A docstring.""" -135 135 | print("foo") -136 |- pass -137 136 | -138 137 | -139 138 | for i in range(10): - -PIE790.py:140:5: PIE790 [*] Unnecessary `pass` statement - | -139 | for i in range(10): -140 | pass - | ^^^^ PIE790 -141 | pass - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -138 138 | -139 139 | for i in range(10): -140 140 | pass -141 |- pass -142 141 | -143 142 | for i in range(10): -144 143 | pass - -PIE790.py:141:5: PIE790 [*] Unnecessary `pass` statement - | -139 | for i in range(10): -140 | pass -141 | pass - | ^^^^ PIE790 -142 | -143 | for i in range(10): - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -138 138 | -139 139 | for i in range(10): -140 140 | pass -141 |- pass -142 141 | -143 142 | for i in range(10): -144 143 | pass - -PIE790.py:144:5: PIE790 [*] Unnecessary `pass` statement - | -143 | for i in range(10): -144 | pass - | ^^^^ PIE790 -145 | -146 | pass - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -141 141 | pass -142 142 | -143 143 | for i in range(10): -144 |- pass -145 144 | -146 145 | pass -147 146 | - -PIE790.py:146:5: PIE790 [*] Unnecessary `pass` statement - | -144 | pass -145 | -146 | pass - | ^^^^ PIE790 -147 | -148 | for i in range(10): - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -143 143 | for i in range(10): -144 144 | pass -145 145 | -146 |- pass -147 146 | -148 147 | for i in range(10): -149 148 | pass # comment - -PIE790.py:149:5: PIE790 [*] Unnecessary `pass` statement - | -148 | for i in range(10): -149 | pass # comment - | ^^^^ PIE790 -150 | pass - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -146 146 | pass -147 147 | -148 148 | for i in range(10): -149 |- pass # comment - 149 |+ # comment -150 150 | pass -151 151 | -152 152 | - -PIE790.py:150:5: PIE790 [*] Unnecessary `pass` statement - | -148 | for i in range(10): -149 | pass # comment -150 | pass - | ^^^^ PIE790 - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -147 147 | -148 148 | for i in range(10): -149 149 | pass # comment -150 |- pass -151 150 | -152 151 | -153 152 | def foo(): - -PIE790.py:155:5: PIE790 [*] Unnecessary `...` literal - | -153 | def foo(): -154 | print("foo") -155 | ... - | ^^^ PIE790 - | - = help: Remove unnecessary `...` - -ℹ Safe fix -152 152 | -153 153 | def foo(): -154 154 | print("foo") -155 |- ... -156 155 | -157 156 | -158 157 | def foo(): - -PIE790.py:161:5: PIE790 [*] Unnecessary `...` literal - | -159 | """A docstring.""" -160 | print("foo") -161 | ... - | ^^^ PIE790 - | - = help: Remove unnecessary `...` - -ℹ Safe fix -158 158 | def foo(): -159 159 | """A docstring.""" -160 160 | print("foo") -161 |- ... -162 161 | -163 162 | -164 163 | for i in range(10): - -PIE790.py:165:5: PIE790 [*] Unnecessary `...` literal - | -164 | for i in range(10): -165 | ... - | ^^^ PIE790 -166 | ... - | - = help: Remove unnecessary `...` - -ℹ Safe fix -163 163 | -164 164 | for i in range(10): -165 165 | ... -166 |- ... -167 166 | -168 167 | for i in range(10): -169 168 | ... - -PIE790.py:166:5: PIE790 [*] Unnecessary `...` literal - | -164 | for i in range(10): -165 | ... -166 | ... - | ^^^ PIE790 -167 | -168 | for i in range(10): - | - = help: Remove unnecessary `...` - -ℹ Safe fix -163 163 | -164 164 | for i in range(10): -165 165 | ... -166 |- ... -167 166 | -168 167 | for i in range(10): -169 168 | ... - -PIE790.py:169:5: PIE790 [*] Unnecessary `...` literal - | -168 | for i in range(10): -169 | ... - | ^^^ PIE790 -170 | -171 | ... - | - = help: Remove unnecessary `...` - -ℹ Safe fix -166 166 | ... -167 167 | -168 168 | for i in range(10): -169 |- ... -170 169 | -171 170 | ... -172 171 | - -PIE790.py:171:5: PIE790 [*] Unnecessary `...` literal - | -169 | ... -170 | -171 | ... - | ^^^ PIE790 -172 | -173 | for i in range(10): - | - = help: Remove unnecessary `...` - -ℹ Safe fix -168 168 | for i in range(10): -169 169 | ... -170 170 | -171 |- ... -172 171 | -173 172 | for i in range(10): -174 173 | ... # comment - -PIE790.py:174:5: PIE790 [*] Unnecessary `...` literal - | -173 | for i in range(10): -174 | ... # comment - | ^^^ PIE790 -175 | ... - | - = help: Remove unnecessary `...` - -ℹ Safe fix -171 171 | ... -172 172 | -173 173 | for i in range(10): -174 |- ... # comment - 174 |+ # comment -175 175 | ... -176 176 | -177 177 | for i in range(10): - -PIE790.py:175:5: PIE790 [*] Unnecessary `...` literal - | -173 | for i in range(10): -174 | ... # comment -175 | ... - | ^^^ PIE790 -176 | -177 | for i in range(10): - | - = help: Remove unnecessary `...` - -ℹ Safe fix -172 172 | -173 173 | for i in range(10): -174 174 | ... # comment -175 |- ... -176 175 | -177 176 | for i in range(10): -178 177 | ... - -PIE790.py:178:5: PIE790 [*] Unnecessary `...` literal - | -177 | for i in range(10): -178 | ... - | ^^^ PIE790 -179 | pass - | - = help: Remove unnecessary `...` - -ℹ Safe fix -175 175 | ... -176 176 | -177 177 | for i in range(10): -178 |- ... -179 178 | pass -180 179 | -181 180 | from typing import Protocol - -PIE790.py:179:5: PIE790 [*] Unnecessary `pass` statement - | -177 | for i in range(10): -178 | ... -179 | pass - | ^^^^ PIE790 -180 | -181 | from typing import Protocol - | - = help: Remove unnecessary `pass` - -ℹ Safe fix -176 176 | -177 177 | for i in range(10): -178 178 | ... -179 |- pass -180 179 | -181 180 | from typing import Protocol -182 181 | - -PIE790.py:209:9: PIE790 [*] Unnecessary `...` literal - | -207 | def stub(self) -> str: -208 | """Docstring""" -209 | ... - | ^^^ PIE790 - | - = help: Remove unnecessary `...` - -ℹ Safe fix -206 206 | -207 207 | def stub(self) -> str: -208 208 | """Docstring""" -209 |- ... - - diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE800_PIE800.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE800_PIE800.py.snap deleted file mode 100644 index 217e4c71f3a21..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE800_PIE800.py.snap +++ /dev/null @@ -1,134 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_pie/mod.rs ---- -PIE800.py:1:14: PIE800 [*] Unnecessary spread `**` - | -1 | {"foo": 1, **{"bar": 1}} # PIE800 - | ^^^^^^^^^^ PIE800 -2 | -3 | {**{"bar": 10}, "a": "b"} # PIE800 - | - = help: Remove unnecessary dict - -ℹ Safe fix -1 |-{"foo": 1, **{"bar": 1}} # PIE800 - 1 |+{"foo": 1, "bar": 1} # PIE800 -2 2 | -3 3 | {**{"bar": 10}, "a": "b"} # PIE800 -4 4 | - -PIE800.py:3:4: PIE800 [*] Unnecessary spread `**` - | -1 | {"foo": 1, **{"bar": 1}} # PIE800 -2 | -3 | {**{"bar": 10}, "a": "b"} # PIE800 - | ^^^^^^^^^^^ PIE800 -4 | -5 | foo({**foo, **{"bar": True}}) # PIE800 - | - = help: Remove unnecessary dict - -ℹ Safe fix -1 1 | {"foo": 1, **{"bar": 1}} # PIE800 -2 2 | -3 |-{**{"bar": 10}, "a": "b"} # PIE800 - 3 |+{"bar": 10, "a": "b"} # PIE800 -4 4 | -5 5 | foo({**foo, **{"bar": True}}) # PIE800 -6 6 | - -PIE800.py:5:15: PIE800 [*] Unnecessary spread `**` - | -3 | {**{"bar": 10}, "a": "b"} # PIE800 -4 | -5 | foo({**foo, **{"bar": True}}) # PIE800 - | ^^^^^^^^^^^^^ PIE800 -6 | -7 | {**foo, **{"bar": 10}} # PIE800 - | - = help: Remove unnecessary dict - -ℹ Safe fix -2 2 | -3 3 | {**{"bar": 10}, "a": "b"} # PIE800 -4 4 | -5 |-foo({**foo, **{"bar": True}}) # PIE800 - 5 |+foo({**foo, "bar": True}) # PIE800 -6 6 | -7 7 | {**foo, **{"bar": 10}} # PIE800 -8 8 | - -PIE800.py:7:11: PIE800 [*] Unnecessary spread `**` - | -5 | foo({**foo, **{"bar": True}}) # PIE800 -6 | -7 | {**foo, **{"bar": 10}} # PIE800 - | ^^^^^^^^^^^ PIE800 -8 | -9 | { # PIE800 - | - = help: Remove unnecessary dict - -ℹ Safe fix -4 4 | -5 5 | foo({**foo, **{"bar": True}}) # PIE800 -6 6 | -7 |-{**foo, **{"bar": 10}} # PIE800 - 7 |+{**foo, "bar": 10} # PIE800 -8 8 | -9 9 | { # PIE800 -10 10 | "a": "b", - -PIE800.py:12:7: PIE800 [*] Unnecessary spread `**` - | -10 | "a": "b", -11 | # Preserve -12 | **{ - | _______^ -13 | | # all -14 | | "bar": 10, # the -15 | | # comments -16 | | }, - | |_____^ PIE800 -17 | } - | - = help: Remove unnecessary dict - -ℹ Safe fix -9 9 | { # PIE800 -10 10 | "a": "b", -11 11 | # Preserve -12 |- **{ - 12 |+ -13 13 | # all -14 |- "bar": 10, # the - 14 |+ "bar": 10 # the -15 15 | # comments -16 |- }, - 16 |+ , -17 17 | } -18 18 | -19 19 | {**foo, **buzz, **{bar: 10}} # PIE800 - -PIE800.py:19:19: PIE800 [*] Unnecessary spread `**` - | -17 | } -18 | -19 | {**foo, **buzz, **{bar: 10}} # PIE800 - | ^^^^^^^^^ PIE800 -20 | -21 | {**foo, "bar": True } # OK - | - = help: Remove unnecessary dict - -ℹ Safe fix -16 16 | }, -17 17 | } -18 18 | -19 |-{**foo, **buzz, **{bar: 10}} # PIE800 - 19 |+{**foo, **buzz, bar: 10} # PIE800 -20 20 | -21 21 | {**foo, "bar": True } # OK -22 22 | - - diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE807_PIE807.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE807_PIE807.py.snap deleted file mode 100644 index 474c3456d53f9..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__preview__PIE807_PIE807.py.snap +++ /dev/null @@ -1,118 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_pie/mod.rs ---- -PIE807.py:3:44: PIE807 [*] Prefer `list` over useless lambda - | -1 | @dataclass -2 | class Foo: -3 | foo: List[str] = field(default_factory=lambda: []) # PIE807 - | ^^^^^^^^^^ PIE807 -4 | bar: Dict[str, int] = field(default_factory=lambda: {}) # PIE807 - | - = help: Replace with `lambda` with `list` - -ℹ Safe fix -1 1 | @dataclass -2 2 | class Foo: -3 |- foo: List[str] = field(default_factory=lambda: []) # PIE807 - 3 |+ foo: List[str] = field(default_factory=list) # PIE807 -4 4 | bar: Dict[str, int] = field(default_factory=lambda: {}) # PIE807 -5 5 | -6 6 | - -PIE807.py:4:49: PIE807 [*] Prefer `dict` over useless lambda - | -2 | class Foo: -3 | foo: List[str] = field(default_factory=lambda: []) # PIE807 -4 | bar: Dict[str, int] = field(default_factory=lambda: {}) # PIE807 - | ^^^^^^^^^^ PIE807 - | - = help: Replace with `lambda` with `dict` - -ℹ Safe fix -1 1 | @dataclass -2 2 | class Foo: -3 3 | foo: List[str] = field(default_factory=lambda: []) # PIE807 -4 |- bar: Dict[str, int] = field(default_factory=lambda: {}) # PIE807 - 4 |+ bar: Dict[str, int] = field(default_factory=dict) # PIE807 -5 5 | -6 6 | -7 7 | class FooTable(BaseTable): - -PIE807.py:8:36: PIE807 [*] Prefer `list` over useless lambda - | -7 | class FooTable(BaseTable): -8 | foo = fields.ListField(default=lambda: []) # PIE807 - | ^^^^^^^^^^ PIE807 -9 | bar = fields.ListField(default=lambda: {}) # PIE807 - | - = help: Replace with `lambda` with `list` - -ℹ Safe fix -5 5 | -6 6 | -7 7 | class FooTable(BaseTable): -8 |- foo = fields.ListField(default=lambda: []) # PIE807 - 8 |+ foo = fields.ListField(default=list) # PIE807 -9 9 | bar = fields.ListField(default=lambda: {}) # PIE807 -10 10 | -11 11 | - -PIE807.py:9:36: PIE807 [*] Prefer `dict` over useless lambda - | -7 | class FooTable(BaseTable): -8 | foo = fields.ListField(default=lambda: []) # PIE807 -9 | bar = fields.ListField(default=lambda: {}) # PIE807 - | ^^^^^^^^^^ PIE807 - | - = help: Replace with `lambda` with `dict` - -ℹ Safe fix -6 6 | -7 7 | class FooTable(BaseTable): -8 8 | foo = fields.ListField(default=lambda: []) # PIE807 -9 |- bar = fields.ListField(default=lambda: {}) # PIE807 - 9 |+ bar = fields.ListField(default=dict) # PIE807 -10 10 | -11 11 | -12 12 | class FooTable(BaseTable): - -PIE807.py:13:28: PIE807 [*] Prefer `list` over useless lambda - | -12 | class FooTable(BaseTable): -13 | foo = fields.ListField(lambda: []) # PIE807 - | ^^^^^^^^^^ PIE807 -14 | bar = fields.ListField(default=lambda: {}) # PIE807 - | - = help: Replace with `lambda` with `list` - -ℹ Safe fix -10 10 | -11 11 | -12 12 | class FooTable(BaseTable): -13 |- foo = fields.ListField(lambda: []) # PIE807 - 13 |+ foo = fields.ListField(list) # PIE807 -14 14 | bar = fields.ListField(default=lambda: {}) # PIE807 -15 15 | -16 16 | - -PIE807.py:14:36: PIE807 [*] Prefer `dict` over useless lambda - | -12 | class FooTable(BaseTable): -13 | foo = fields.ListField(lambda: []) # PIE807 -14 | bar = fields.ListField(default=lambda: {}) # PIE807 - | ^^^^^^^^^^ PIE807 - | - = help: Replace with `lambda` with `dict` - -ℹ Safe fix -11 11 | -12 12 | class FooTable(BaseTable): -13 13 | foo = fields.ListField(lambda: []) # PIE807 -14 |- bar = fields.ListField(default=lambda: {}) # PIE807 - 14 |+ bar = fields.ListField(default=dict) # PIE807 -15 15 | -16 16 | -17 17 | @dataclass - - diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unaliased_collections_abc_set_import.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unaliased_collections_abc_set_import.rs index 79e6ba61a776e..5d3b0cd2f6844 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unaliased_collections_abc_set_import.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unaliased_collections_abc_set_import.rs @@ -1,4 +1,4 @@ -use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, Violation}; +use ruff_diagnostics::{Applicability, Diagnostic, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_semantic::Imported; use ruff_python_semantic::{Binding, BindingKind}; @@ -29,6 +29,12 @@ use crate::renamer::Renamer; /// ```python /// from collections.abc import Set as AbstractSet /// ``` +/// +/// ## Fix safety +/// This rule's fix is marked as unsafe for `Set` imports defined at the +/// top-level of a module. Top-level symbols are implicitly exported by the +/// module, and so renaming a top-level symbol may break downstream modules +/// that import it. #[violation] pub struct UnaliasedCollectionsAbcSetImport; @@ -69,7 +75,15 @@ pub(crate) fn unaliased_collections_abc_set_import( diagnostic.try_set_fix(|| { let scope = &checker.semantic().scopes[binding.scope]; let (edit, rest) = Renamer::rename(name, "AbstractSet", scope, checker.semantic())?; - Ok(Fix::unsafe_edits(edit, rest)) + Ok(Fix::applicable_edits( + edit, + rest, + if scope.kind.is_module() { + Applicability::Unsafe + } else { + Applicability::Safe + }, + )) }); } Some(diagnostic) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_literal_union.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_literal_union.rs index 8fd0169f4bd79..08040938a7eca 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_literal_union.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_literal_union.rs @@ -116,7 +116,7 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Exp expr.range(), ); - if checker.settings.preview.is_enabled() { + diagnostic.set_fix({ let literal = Expr::Subscript(ast::ExprSubscript { value: Box::new(literal_subscript.clone()), slice: Box::new(Expr::Tuple(ast::ExprTuple { @@ -130,10 +130,10 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Exp if other_exprs.is_empty() { // if the union is only literals, we just replace the whole thing with a single literal - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + Fix::safe_edit(Edit::range_replacement( checker.generator().expr(&literal), expr.range(), - ))); + )) } else { let elts: Vec = std::iter::once(literal) .chain(other_exprs.into_iter().cloned()) @@ -156,12 +156,9 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Exp checker.generator().expr(&pep_604_union(&elts)) }; - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - content, - expr.range(), - ))); + Fix::safe_edit(Edit::range_replacement(content, expr.range())) } - } + }); checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI025_PYI025.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI025_PYI025.py.snap index 4c065291fd825..ebbbebba5ce05 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI025_PYI025.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI025_PYI025.py.snap @@ -9,7 +9,7 @@ PYI025.py:10:33: PYI025 [*] Use `from collections.abc import Set as AbstractSet` | = help: Alias `Set` to `AbstractSet` -ℹ Unsafe fix +ℹ Safe fix 7 7 | 8 8 | 9 9 | def f(): @@ -29,7 +29,7 @@ PYI025.py:14:51: PYI025 [*] Use `from collections.abc import Set as AbstractSet` | = help: Alias `Set` to `AbstractSet` -ℹ Unsafe fix +ℹ Safe fix 11 11 | 12 12 | 13 13 | def f(): diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI025_PYI025.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI025_PYI025.pyi.snap index 96752937fb791..7ae80bb5f2b85 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI025_PYI025.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI025_PYI025.pyi.snap @@ -11,7 +11,7 @@ PYI025.pyi:8:33: PYI025 [*] Use `from collections.abc import Set as AbstractSet` | = help: Alias `Set` to `AbstractSet` -ℹ Unsafe fix +ℹ Safe fix 5 5 | from collections.abc import Container, Sized, Set as AbstractSet, ValuesView # Ok 6 6 | 7 7 | def f(): @@ -31,7 +31,7 @@ PYI025.pyi:11:51: PYI025 [*] Use `from collections.abc import Set as AbstractSet | = help: Alias `Set` to `AbstractSet` -ℹ Unsafe fix +ℹ Safe fix 8 8 | from collections.abc import Set # PYI025 9 9 | 10 10 | def f(): @@ -52,7 +52,7 @@ PYI025.pyi:16:37: PYI025 [*] Use `from collections.abc import Set as AbstractSet | = help: Alias `Set` to `AbstractSet` -ℹ Unsafe fix +ℹ Safe fix 13 13 | def f(): 14 14 | """Test: local symbol renaming.""" 15 15 | if True: @@ -130,7 +130,7 @@ PYI025.pyi:44:33: PYI025 [*] Use `from collections.abc import Set as AbstractSet | = help: Alias `Set` to `AbstractSet` -ℹ Unsafe fix +ℹ Safe fix 41 41 | 42 42 | def f(): 43 43 | """Test: nonlocal symbol renaming.""" diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI030_PYI030.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI030_PYI030.py.snap index b06ed30fe2396..47a803b8da44a 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI030_PYI030.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI030_PYI030.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI030.py:9:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +PYI030.py:9:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 8 | # Should emit for duplicate field types. 9 | field2: Literal[1] | Literal[2] # Error @@ -11,7 +11,17 @@ PYI030.py:9:9: PYI030 Multiple literal members in a union. Use a single literal, | = help: Replace with a single `Literal` -PYI030.py:12:17: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +6 6 | field1: Literal[1] # OK +7 7 | +8 8 | # Should emit for duplicate field types. +9 |-field2: Literal[1] | Literal[2] # Error + 9 |+field2: Literal[1, 2] # Error +10 10 | +11 11 | # Should emit for union types in arguments. +12 12 | def func1(arg1: Literal[1] | Literal[2]): # Error + +PYI030.py:12:17: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 11 | # Should emit for union types in arguments. 12 | def func1(arg1: Literal[1] | Literal[2]): # Error @@ -20,7 +30,17 @@ PYI030.py:12:17: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:17:16: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +9 9 | field2: Literal[1] | Literal[2] # Error +10 10 | +11 11 | # Should emit for union types in arguments. +12 |-def func1(arg1: Literal[1] | Literal[2]): # Error + 12 |+def func1(arg1: Literal[1, 2]): # Error +13 13 | print(arg1) +14 14 | +15 15 | + +PYI030.py:17:16: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 16 | # Should emit for unions in return types. 17 | def func2() -> Literal[1] | Literal[2]: # Error @@ -29,7 +49,17 @@ PYI030.py:17:16: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:22:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +14 14 | +15 15 | +16 16 | # Should emit for unions in return types. +17 |-def func2() -> Literal[1] | Literal[2]: # Error + 17 |+def func2() -> Literal[1, 2]: # Error +18 18 | return "my Literal[1]ing" +19 19 | +20 20 | + +PYI030.py:22:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 21 | # Should emit in longer unions, even if not directly adjacent. 22 | field3: Literal[1] | Literal[2] | str # Error @@ -39,7 +69,17 @@ PYI030.py:22:9: PYI030 Multiple literal members in a union. Use a single literal | = help: Replace with a single `Literal` -PYI030.py:23:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +19 19 | +20 20 | +21 21 | # Should emit in longer unions, even if not directly adjacent. +22 |-field3: Literal[1] | Literal[2] | str # Error + 22 |+field3: Literal[1, 2] | str # Error +23 23 | field4: str | Literal[1] | Literal[2] # Error +24 24 | field5: Literal[1] | str | Literal[2] # Error +25 25 | field6: Literal[1] | bool | Literal[2] | str # Error + +PYI030.py:23:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 21 | # Should emit in longer unions, even if not directly adjacent. 22 | field3: Literal[1] | Literal[2] | str # Error @@ -50,7 +90,17 @@ PYI030.py:23:9: PYI030 Multiple literal members in a union. Use a single literal | = help: Replace with a single `Literal` -PYI030.py:24:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +20 20 | +21 21 | # Should emit in longer unions, even if not directly adjacent. +22 22 | field3: Literal[1] | Literal[2] | str # Error +23 |-field4: str | Literal[1] | Literal[2] # Error + 23 |+field4: Literal[1, 2] | str # Error +24 24 | field5: Literal[1] | str | Literal[2] # Error +25 25 | field6: Literal[1] | bool | Literal[2] | str # Error +26 26 | + +PYI030.py:24:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 22 | field3: Literal[1] | Literal[2] | str # Error 23 | field4: str | Literal[1] | Literal[2] # Error @@ -60,7 +110,17 @@ PYI030.py:24:9: PYI030 Multiple literal members in a union. Use a single literal | = help: Replace with a single `Literal` -PYI030.py:25:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +21 21 | # Should emit in longer unions, even if not directly adjacent. +22 22 | field3: Literal[1] | Literal[2] | str # Error +23 23 | field4: str | Literal[1] | Literal[2] # Error +24 |-field5: Literal[1] | str | Literal[2] # Error + 24 |+field5: Literal[1, 2] | str # Error +25 25 | field6: Literal[1] | bool | Literal[2] | str # Error +26 26 | +27 27 | # Should emit for non-type unions. + +PYI030.py:25:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 23 | field4: str | Literal[1] | Literal[2] # Error 24 | field5: Literal[1] | str | Literal[2] # Error @@ -71,7 +131,17 @@ PYI030.py:25:9: PYI030 Multiple literal members in a union. Use a single literal | = help: Replace with a single `Literal` -PYI030.py:28:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +22 22 | field3: Literal[1] | Literal[2] | str # Error +23 23 | field4: str | Literal[1] | Literal[2] # Error +24 24 | field5: Literal[1] | str | Literal[2] # Error +25 |-field6: Literal[1] | bool | Literal[2] | str # Error + 25 |+field6: Literal[1, 2] | bool | str # Error +26 26 | +27 27 | # Should emit for non-type unions. +28 28 | field7 = Literal[1] | Literal[2] # Error + +PYI030.py:28:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 27 | # Should emit for non-type unions. 28 | field7 = Literal[1] | Literal[2] # Error @@ -81,7 +151,17 @@ PYI030.py:28:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:31:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +25 25 | field6: Literal[1] | bool | Literal[2] | str # Error +26 26 | +27 27 | # Should emit for non-type unions. +28 |-field7 = Literal[1] | Literal[2] # Error + 28 |+field7 = Literal[1, 2] # Error +29 29 | +30 30 | # Should emit for parenthesized unions. +31 31 | field8: Literal[1] | (Literal[2] | str) # Error + +PYI030.py:31:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 30 | # Should emit for parenthesized unions. 31 | field8: Literal[1] | (Literal[2] | str) # Error @@ -91,7 +171,17 @@ PYI030.py:31:9: PYI030 Multiple literal members in a union. Use a single literal | = help: Replace with a single `Literal` -PYI030.py:34:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +28 28 | field7 = Literal[1] | Literal[2] # Error +29 29 | +30 30 | # Should emit for parenthesized unions. +31 |-field8: Literal[1] | (Literal[2] | str) # Error + 31 |+field8: Literal[1, 2] | str # Error +32 32 | +33 33 | # Should handle user parentheses when fixing. +34 34 | field9: Literal[1] | (Literal[2] | str) # Error + +PYI030.py:34:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 33 | # Should handle user parentheses when fixing. 34 | field9: Literal[1] | (Literal[2] | str) # Error @@ -100,7 +190,17 @@ PYI030.py:34:9: PYI030 Multiple literal members in a union. Use a single literal | = help: Replace with a single `Literal` -PYI030.py:35:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +31 31 | field8: Literal[1] | (Literal[2] | str) # Error +32 32 | +33 33 | # Should handle user parentheses when fixing. +34 |-field9: Literal[1] | (Literal[2] | str) # Error + 34 |+field9: Literal[1, 2] | str # Error +35 35 | field10: (Literal[1] | str) | Literal[2] # Error +36 36 | +37 37 | # Should emit for union in generic parent type. + +PYI030.py:35:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 33 | # Should handle user parentheses when fixing. 34 | field9: Literal[1] | (Literal[2] | str) # Error @@ -111,7 +211,17 @@ PYI030.py:35:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:38:15: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +32 32 | +33 33 | # Should handle user parentheses when fixing. +34 34 | field9: Literal[1] | (Literal[2] | str) # Error +35 |-field10: (Literal[1] | str) | Literal[2] # Error + 35 |+field10: Literal[1, 2] | str # Error +36 36 | +37 37 | # Should emit for union in generic parent type. +38 38 | field11: dict[Literal[1] | Literal[2], str] # Error + +PYI030.py:38:15: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 37 | # Should emit for union in generic parent type. 38 | field11: dict[Literal[1] | Literal[2], str] # Error @@ -121,7 +231,17 @@ PYI030.py:38:15: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:41:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3]` +ℹ Safe fix +35 35 | field10: (Literal[1] | str) | Literal[2] # Error +36 36 | +37 37 | # Should emit for union in generic parent type. +38 |-field11: dict[Literal[1] | Literal[2], str] # Error + 38 |+field11: dict[Literal[1, 2], str] # Error +39 39 | +40 40 | # Should emit for unions with more than two cases +41 41 | field12: Literal[1] | Literal[2] | Literal[3] # Error + +PYI030.py:41:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3]` | 40 | # Should emit for unions with more than two cases 41 | field12: Literal[1] | Literal[2] | Literal[3] # Error @@ -130,7 +250,17 @@ PYI030.py:41:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:42:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` +ℹ Safe fix +38 38 | field11: dict[Literal[1] | Literal[2], str] # Error +39 39 | +40 40 | # Should emit for unions with more than two cases +41 |-field12: Literal[1] | Literal[2] | Literal[3] # Error + 41 |+field12: Literal[1, 2, 3] # Error +42 42 | field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error +43 43 | +44 44 | # Should emit for unions with more than two cases, even if not directly adjacent + +PYI030.py:42:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` | 40 | # Should emit for unions with more than two cases 41 | field12: Literal[1] | Literal[2] | Literal[3] # Error @@ -141,7 +271,17 @@ PYI030.py:42:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:45:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3]` +ℹ Safe fix +39 39 | +40 40 | # Should emit for unions with more than two cases +41 41 | field12: Literal[1] | Literal[2] | Literal[3] # Error +42 |-field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error + 42 |+field13: Literal[1, 2, 3, 4] # Error +43 43 | +44 44 | # Should emit for unions with more than two cases, even if not directly adjacent +45 45 | field14: Literal[1] | Literal[2] | str | Literal[3] # Error + +PYI030.py:45:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3]` | 44 | # Should emit for unions with more than two cases, even if not directly adjacent 45 | field14: Literal[1] | Literal[2] | str | Literal[3] # Error @@ -151,7 +291,17 @@ PYI030.py:45:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:48:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, "foo", True]` +ℹ Safe fix +42 42 | field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error +43 43 | +44 44 | # Should emit for unions with more than two cases, even if not directly adjacent +45 |-field14: Literal[1] | Literal[2] | str | Literal[3] # Error + 45 |+field14: Literal[1, 2, 3] | str # Error +46 46 | +47 47 | # Should emit for unions with mixed literal internal types +48 48 | field15: Literal[1] | Literal["foo"] | Literal[True] # Error + +PYI030.py:48:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, "foo", True]` | 47 | # Should emit for unions with mixed literal internal types 48 | field15: Literal[1] | Literal["foo"] | Literal[True] # Error @@ -161,7 +311,17 @@ PYI030.py:48:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:51:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 1]` +ℹ Safe fix +45 45 | field14: Literal[1] | Literal[2] | str | Literal[3] # Error +46 46 | +47 47 | # Should emit for unions with mixed literal internal types +48 |-field15: Literal[1] | Literal["foo"] | Literal[True] # Error + 48 |+field15: Literal[1, "foo", True] # Error +49 49 | +50 50 | # Shouldn't emit for duplicate field types with same value; covered by Y016 +51 51 | field16: Literal[1] | Literal[1] # OK + +PYI030.py:51:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 1]` | 50 | # Shouldn't emit for duplicate field types with same value; covered by Y016 51 | field16: Literal[1] | Literal[1] # OK @@ -171,7 +331,17 @@ PYI030.py:51:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:60:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +48 48 | field15: Literal[1] | Literal["foo"] | Literal[True] # Error +49 49 | +50 50 | # Shouldn't emit for duplicate field types with same value; covered by Y016 +51 |-field16: Literal[1] | Literal[1] # OK + 51 |+field16: Literal[1, 1] # OK +52 52 | +53 53 | # Shouldn't emit if in new parent type +54 54 | field17: Literal[1] | dict[Literal[2], str] # OK + +PYI030.py:60:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 59 | # Should respect name of literal type used 60 | field19: typing.Literal[1] | typing.Literal[2] # Error @@ -181,7 +351,17 @@ PYI030.py:60:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:63:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +57 57 | field18: dict[Literal[1], Literal[2]] # OK +58 58 | +59 59 | # Should respect name of literal type used +60 |-field19: typing.Literal[1] | typing.Literal[2] # Error + 60 |+field19: typing.Literal[1, 2] # Error +61 61 | +62 62 | # Should emit in cases with newlines +63 63 | field20: typing.Union[ + +PYI030.py:63:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 62 | # Should emit in cases with newlines 63 | field20: typing.Union[ @@ -197,7 +377,22 @@ PYI030.py:63:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:71:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` +ℹ Safe fix +60 60 | field19: typing.Literal[1] | typing.Literal[2] # Error +61 61 | +62 62 | # Should emit in cases with newlines +63 |-field20: typing.Union[ +64 |- Literal[ +65 |- 1 # test +66 |- ], +67 |- Literal[2], +68 |-] # Error, newline and comment will not be emitted in message + 63 |+field20: Literal[1, 2] # Error, newline and comment will not be emitted in message +69 64 | +70 65 | # Should handle multiple unions with multiple members +71 66 | field21: Literal[1, 2] | Literal[3, 4] # Error + +PYI030.py:71:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` | 70 | # Should handle multiple unions with multiple members 71 | field21: Literal[1, 2] | Literal[3, 4] # Error @@ -207,7 +402,17 @@ PYI030.py:71:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:74:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +68 68 | ] # Error, newline and comment will not be emitted in message +69 69 | +70 70 | # Should handle multiple unions with multiple members +71 |-field21: Literal[1, 2] | Literal[3, 4] # Error + 71 |+field21: Literal[1, 2, 3, 4] # Error +72 72 | +73 73 | # Should emit in cases with `typing.Union` instead of `|` +74 74 | field22: typing.Union[Literal[1], Literal[2]] # Error + +PYI030.py:74:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 73 | # Should emit in cases with `typing.Union` instead of `|` 74 | field22: typing.Union[Literal[1], Literal[2]] # Error @@ -217,7 +422,17 @@ PYI030.py:74:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:77:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +71 71 | field21: Literal[1, 2] | Literal[3, 4] # Error +72 72 | +73 73 | # Should emit in cases with `typing.Union` instead of `|` +74 |-field22: typing.Union[Literal[1], Literal[2]] # Error + 74 |+field22: Literal[1, 2] # Error +75 75 | +76 76 | # Should emit in cases with `typing_extensions.Literal` +77 77 | field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error + +PYI030.py:77:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 76 | # Should emit in cases with `typing_extensions.Literal` 77 | field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error @@ -227,7 +442,17 @@ PYI030.py:77:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:80:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +74 74 | field22: typing.Union[Literal[1], Literal[2]] # Error +75 75 | +76 76 | # Should emit in cases with `typing_extensions.Literal` +77 |-field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error + 77 |+field23: typing_extensions.Literal[1, 2] # Error +78 78 | +79 79 | # Should emit in cases with nested `typing.Union` +80 80 | field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error + +PYI030.py:80:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 79 | # Should emit in cases with nested `typing.Union` 80 | field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error @@ -237,7 +462,17 @@ PYI030.py:80:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:83:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +77 77 | field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error +78 78 | +79 79 | # Should emit in cases with nested `typing.Union` +80 |-field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error + 80 |+field24: typing.Union[Literal[1, 2], str] # Error +81 81 | +82 82 | # Should emit in cases with mixed `typing.Union` and `|` +83 83 | field25: typing.Union[Literal[1], Literal[2] | str] # Error + +PYI030.py:83:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 82 | # Should emit in cases with mixed `typing.Union` and `|` 83 | field25: typing.Union[Literal[1], Literal[2] | str] # Error @@ -247,7 +482,17 @@ PYI030.py:83:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:86:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` +ℹ Safe fix +80 80 | field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error +81 81 | +82 82 | # Should emit in cases with mixed `typing.Union` and `|` +83 |-field25: typing.Union[Literal[1], Literal[2] | str] # Error + 83 |+field25: typing.Union[Literal[1, 2], str] # Error +84 84 | +85 85 | # Should emit only once in cases with multiple nested `typing.Union` +86 86 | field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error + +PYI030.py:86:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` | 85 | # Should emit only once in cases with multiple nested `typing.Union` 86 | field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error @@ -257,7 +502,17 @@ PYI030.py:86:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.py:89:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` +ℹ Safe fix +83 83 | field25: typing.Union[Literal[1], Literal[2] | str] # Error +84 84 | +85 85 | # Should emit only once in cases with multiple nested `typing.Union` +86 |-field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error + 86 |+field24: Literal[1, 2, 3, 4] # Error +87 87 | +88 88 | # Should use the first literal subscript attribute when fixing +89 89 | field25: typing.Union[typing_extensions.Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]], str] # Error + +PYI030.py:89:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` | 88 | # Should use the first literal subscript attribute when fixing 89 | field25: typing.Union[typing_extensions.Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]], str] # Error @@ -265,4 +520,11 @@ PYI030.py:89:10: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` +ℹ Safe fix +86 86 | field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error +87 87 | +88 88 | # Should use the first literal subscript attribute when fixing +89 |-field25: typing.Union[typing_extensions.Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]], str] # Error + 89 |+field25: typing.Union[typing_extensions.Literal[1, 2, 3, 4], str] # Error + diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI030_PYI030.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI030_PYI030.pyi.snap index ec113b46dd315..7d49337d4cf41 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI030_PYI030.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI030_PYI030.pyi.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI030.pyi:9:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +PYI030.pyi:9:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 8 | # Should emit for duplicate field types. 9 | field2: Literal[1] | Literal[2] # Error @@ -11,7 +11,17 @@ PYI030.pyi:9:9: PYI030 Multiple literal members in a union. Use a single literal | = help: Replace with a single `Literal` -PYI030.pyi:12:17: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +6 6 | field1: Literal[1] # OK +7 7 | +8 8 | # Should emit for duplicate field types. +9 |-field2: Literal[1] | Literal[2] # Error + 9 |+field2: Literal[1, 2] # Error +10 10 | +11 11 | # Should emit for union types in arguments. +12 12 | def func1(arg1: Literal[1] | Literal[2]): # Error + +PYI030.pyi:12:17: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 11 | # Should emit for union types in arguments. 12 | def func1(arg1: Literal[1] | Literal[2]): # Error @@ -20,7 +30,17 @@ PYI030.pyi:12:17: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:17:16: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +9 9 | field2: Literal[1] | Literal[2] # Error +10 10 | +11 11 | # Should emit for union types in arguments. +12 |-def func1(arg1: Literal[1] | Literal[2]): # Error + 12 |+def func1(arg1: Literal[1, 2]): # Error +13 13 | print(arg1) +14 14 | +15 15 | + +PYI030.pyi:17:16: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 16 | # Should emit for unions in return types. 17 | def func2() -> Literal[1] | Literal[2]: # Error @@ -29,7 +49,17 @@ PYI030.pyi:17:16: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:22:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +14 14 | +15 15 | +16 16 | # Should emit for unions in return types. +17 |-def func2() -> Literal[1] | Literal[2]: # Error + 17 |+def func2() -> Literal[1, 2]: # Error +18 18 | return "my Literal[1]ing" +19 19 | +20 20 | + +PYI030.pyi:22:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 21 | # Should emit in longer unions, even if not directly adjacent. 22 | field3: Literal[1] | Literal[2] | str # Error @@ -39,7 +69,17 @@ PYI030.pyi:22:9: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.pyi:23:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +19 19 | +20 20 | +21 21 | # Should emit in longer unions, even if not directly adjacent. +22 |-field3: Literal[1] | Literal[2] | str # Error + 22 |+field3: Literal[1, 2] | str # Error +23 23 | field4: str | Literal[1] | Literal[2] # Error +24 24 | field5: Literal[1] | str | Literal[2] # Error +25 25 | field6: Literal[1] | bool | Literal[2] | str # Error + +PYI030.pyi:23:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 21 | # Should emit in longer unions, even if not directly adjacent. 22 | field3: Literal[1] | Literal[2] | str # Error @@ -50,7 +90,17 @@ PYI030.pyi:23:9: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.pyi:24:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +20 20 | +21 21 | # Should emit in longer unions, even if not directly adjacent. +22 22 | field3: Literal[1] | Literal[2] | str # Error +23 |-field4: str | Literal[1] | Literal[2] # Error + 23 |+field4: Literal[1, 2] | str # Error +24 24 | field5: Literal[1] | str | Literal[2] # Error +25 25 | field6: Literal[1] | bool | Literal[2] | str # Error +26 26 | + +PYI030.pyi:24:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 22 | field3: Literal[1] | Literal[2] | str # Error 23 | field4: str | Literal[1] | Literal[2] # Error @@ -60,7 +110,17 @@ PYI030.pyi:24:9: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.pyi:25:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +21 21 | # Should emit in longer unions, even if not directly adjacent. +22 22 | field3: Literal[1] | Literal[2] | str # Error +23 23 | field4: str | Literal[1] | Literal[2] # Error +24 |-field5: Literal[1] | str | Literal[2] # Error + 24 |+field5: Literal[1, 2] | str # Error +25 25 | field6: Literal[1] | bool | Literal[2] | str # Error +26 26 | +27 27 | # Should emit for non-type unions. + +PYI030.pyi:25:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 23 | field4: str | Literal[1] | Literal[2] # Error 24 | field5: Literal[1] | str | Literal[2] # Error @@ -71,7 +131,17 @@ PYI030.pyi:25:9: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.pyi:28:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +22 22 | field3: Literal[1] | Literal[2] | str # Error +23 23 | field4: str | Literal[1] | Literal[2] # Error +24 24 | field5: Literal[1] | str | Literal[2] # Error +25 |-field6: Literal[1] | bool | Literal[2] | str # Error + 25 |+field6: Literal[1, 2] | bool | str # Error +26 26 | +27 27 | # Should emit for non-type unions. +28 28 | field7 = Literal[1] | Literal[2] # Error + +PYI030.pyi:28:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 27 | # Should emit for non-type unions. 28 | field7 = Literal[1] | Literal[2] # Error @@ -81,7 +151,17 @@ PYI030.pyi:28:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:31:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +25 25 | field6: Literal[1] | bool | Literal[2] | str # Error +26 26 | +27 27 | # Should emit for non-type unions. +28 |-field7 = Literal[1] | Literal[2] # Error + 28 |+field7 = Literal[1, 2] # Error +29 29 | +30 30 | # Should emit for parenthesized unions. +31 31 | field8: Literal[1] | (Literal[2] | str) # Error + +PYI030.pyi:31:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 30 | # Should emit for parenthesized unions. 31 | field8: Literal[1] | (Literal[2] | str) # Error @@ -91,7 +171,17 @@ PYI030.pyi:31:9: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.pyi:34:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +28 28 | field7 = Literal[1] | Literal[2] # Error +29 29 | +30 30 | # Should emit for parenthesized unions. +31 |-field8: Literal[1] | (Literal[2] | str) # Error + 31 |+field8: Literal[1, 2] | str # Error +32 32 | +33 33 | # Should handle user parentheses when fixing. +34 34 | field9: Literal[1] | (Literal[2] | str) # Error + +PYI030.pyi:34:9: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 33 | # Should handle user parentheses when fixing. 34 | field9: Literal[1] | (Literal[2] | str) # Error @@ -100,7 +190,17 @@ PYI030.pyi:34:9: PYI030 Multiple literal members in a union. Use a single litera | = help: Replace with a single `Literal` -PYI030.pyi:35:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +31 31 | field8: Literal[1] | (Literal[2] | str) # Error +32 32 | +33 33 | # Should handle user parentheses when fixing. +34 |-field9: Literal[1] | (Literal[2] | str) # Error + 34 |+field9: Literal[1, 2] | str # Error +35 35 | field10: (Literal[1] | str) | Literal[2] # Error +36 36 | +37 37 | # Should emit for union in generic parent type. + +PYI030.pyi:35:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 33 | # Should handle user parentheses when fixing. 34 | field9: Literal[1] | (Literal[2] | str) # Error @@ -111,7 +211,17 @@ PYI030.pyi:35:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:38:15: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +32 32 | +33 33 | # Should handle user parentheses when fixing. +34 34 | field9: Literal[1] | (Literal[2] | str) # Error +35 |-field10: (Literal[1] | str) | Literal[2] # Error + 35 |+field10: Literal[1, 2] | str # Error +36 36 | +37 37 | # Should emit for union in generic parent type. +38 38 | field11: dict[Literal[1] | Literal[2], str] # Error + +PYI030.pyi:38:15: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 37 | # Should emit for union in generic parent type. 38 | field11: dict[Literal[1] | Literal[2], str] # Error @@ -121,7 +231,17 @@ PYI030.pyi:38:15: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:41:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3]` +ℹ Safe fix +35 35 | field10: (Literal[1] | str) | Literal[2] # Error +36 36 | +37 37 | # Should emit for union in generic parent type. +38 |-field11: dict[Literal[1] | Literal[2], str] # Error + 38 |+field11: dict[Literal[1, 2], str] # Error +39 39 | +40 40 | # Should emit for unions with more than two cases +41 41 | field12: Literal[1] | Literal[2] | Literal[3] # Error + +PYI030.pyi:41:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3]` | 40 | # Should emit for unions with more than two cases 41 | field12: Literal[1] | Literal[2] | Literal[3] # Error @@ -130,7 +250,17 @@ PYI030.pyi:41:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:42:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` +ℹ Safe fix +38 38 | field11: dict[Literal[1] | Literal[2], str] # Error +39 39 | +40 40 | # Should emit for unions with more than two cases +41 |-field12: Literal[1] | Literal[2] | Literal[3] # Error + 41 |+field12: Literal[1, 2, 3] # Error +42 42 | field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error +43 43 | +44 44 | # Should emit for unions with more than two cases, even if not directly adjacent + +PYI030.pyi:42:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` | 40 | # Should emit for unions with more than two cases 41 | field12: Literal[1] | Literal[2] | Literal[3] # Error @@ -141,7 +271,17 @@ PYI030.pyi:42:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:45:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3]` +ℹ Safe fix +39 39 | +40 40 | # Should emit for unions with more than two cases +41 41 | field12: Literal[1] | Literal[2] | Literal[3] # Error +42 |-field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error + 42 |+field13: Literal[1, 2, 3, 4] # Error +43 43 | +44 44 | # Should emit for unions with more than two cases, even if not directly adjacent +45 45 | field14: Literal[1] | Literal[2] | str | Literal[3] # Error + +PYI030.pyi:45:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3]` | 44 | # Should emit for unions with more than two cases, even if not directly adjacent 45 | field14: Literal[1] | Literal[2] | str | Literal[3] # Error @@ -151,7 +291,17 @@ PYI030.pyi:45:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:48:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, "foo", True]` +ℹ Safe fix +42 42 | field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error +43 43 | +44 44 | # Should emit for unions with more than two cases, even if not directly adjacent +45 |-field14: Literal[1] | Literal[2] | str | Literal[3] # Error + 45 |+field14: Literal[1, 2, 3] | str # Error +46 46 | +47 47 | # Should emit for unions with mixed literal internal types +48 48 | field15: Literal[1] | Literal["foo"] | Literal[True] # Error + +PYI030.pyi:48:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, "foo", True]` | 47 | # Should emit for unions with mixed literal internal types 48 | field15: Literal[1] | Literal["foo"] | Literal[True] # Error @@ -161,7 +311,17 @@ PYI030.pyi:48:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:51:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 1]` +ℹ Safe fix +45 45 | field14: Literal[1] | Literal[2] | str | Literal[3] # Error +46 46 | +47 47 | # Should emit for unions with mixed literal internal types +48 |-field15: Literal[1] | Literal["foo"] | Literal[True] # Error + 48 |+field15: Literal[1, "foo", True] # Error +49 49 | +50 50 | # Shouldn't emit for duplicate field types with same value; covered by Y016 +51 51 | field16: Literal[1] | Literal[1] # OK + +PYI030.pyi:51:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 1]` | 50 | # Shouldn't emit for duplicate field types with same value; covered by Y016 51 | field16: Literal[1] | Literal[1] # OK @@ -171,7 +331,17 @@ PYI030.pyi:51:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:60:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +48 48 | field15: Literal[1] | Literal["foo"] | Literal[True] # Error +49 49 | +50 50 | # Shouldn't emit for duplicate field types with same value; covered by Y016 +51 |-field16: Literal[1] | Literal[1] # OK + 51 |+field16: Literal[1, 1] # OK +52 52 | +53 53 | # Shouldn't emit if in new parent type +54 54 | field17: Literal[1] | dict[Literal[2], str] # OK + +PYI030.pyi:60:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 59 | # Should respect name of literal type used 60 | field19: typing.Literal[1] | typing.Literal[2] # Error @@ -181,7 +351,17 @@ PYI030.pyi:60:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:63:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +57 57 | field18: dict[Literal[1], Literal[2]] # OK +58 58 | +59 59 | # Should respect name of literal type used +60 |-field19: typing.Literal[1] | typing.Literal[2] # Error + 60 |+field19: typing.Literal[1, 2] # Error +61 61 | +62 62 | # Should emit in cases with newlines +63 63 | field20: typing.Union[ + +PYI030.pyi:63:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 62 | # Should emit in cases with newlines 63 | field20: typing.Union[ @@ -197,7 +377,22 @@ PYI030.pyi:63:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:71:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` +ℹ Safe fix +60 60 | field19: typing.Literal[1] | typing.Literal[2] # Error +61 61 | +62 62 | # Should emit in cases with newlines +63 |-field20: typing.Union[ +64 |- Literal[ +65 |- 1 # test +66 |- ], +67 |- Literal[2], +68 |-] # Error, newline and comment will not be emitted in message + 63 |+field20: Literal[1, 2] # Error, newline and comment will not be emitted in message +69 64 | +70 65 | # Should handle multiple unions with multiple members +71 66 | field21: Literal[1, 2] | Literal[3, 4] # Error + +PYI030.pyi:71:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` | 70 | # Should handle multiple unions with multiple members 71 | field21: Literal[1, 2] | Literal[3, 4] # Error @@ -207,7 +402,17 @@ PYI030.pyi:71:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:74:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +68 68 | ] # Error, newline and comment will not be emitted in message +69 69 | +70 70 | # Should handle multiple unions with multiple members +71 |-field21: Literal[1, 2] | Literal[3, 4] # Error + 71 |+field21: Literal[1, 2, 3, 4] # Error +72 72 | +73 73 | # Should emit in cases with `typing.Union` instead of `|` +74 74 | field22: typing.Union[Literal[1], Literal[2]] # Error + +PYI030.pyi:74:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 73 | # Should emit in cases with `typing.Union` instead of `|` 74 | field22: typing.Union[Literal[1], Literal[2]] # Error @@ -217,7 +422,17 @@ PYI030.pyi:74:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:77:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +71 71 | field21: Literal[1, 2] | Literal[3, 4] # Error +72 72 | +73 73 | # Should emit in cases with `typing.Union` instead of `|` +74 |-field22: typing.Union[Literal[1], Literal[2]] # Error + 74 |+field22: Literal[1, 2] # Error +75 75 | +76 76 | # Should emit in cases with `typing_extensions.Literal` +77 77 | field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error + +PYI030.pyi:77:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 76 | # Should emit in cases with `typing_extensions.Literal` 77 | field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error @@ -227,7 +442,17 @@ PYI030.pyi:77:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:80:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +74 74 | field22: typing.Union[Literal[1], Literal[2]] # Error +75 75 | +76 76 | # Should emit in cases with `typing_extensions.Literal` +77 |-field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error + 77 |+field23: typing_extensions.Literal[1, 2] # Error +78 78 | +79 79 | # Should emit in cases with nested `typing.Union` +80 80 | field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error + +PYI030.pyi:80:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 79 | # Should emit in cases with nested `typing.Union` 80 | field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error @@ -237,7 +462,17 @@ PYI030.pyi:80:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:83:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` +ℹ Safe fix +77 77 | field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error +78 78 | +79 79 | # Should emit in cases with nested `typing.Union` +80 |-field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error + 80 |+field24: typing.Union[Literal[1, 2], str] # Error +81 81 | +82 82 | # Should emit in cases with mixed `typing.Union` and `|` +83 83 | field25: typing.Union[Literal[1], Literal[2] | str] # Error + +PYI030.pyi:83:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]` | 82 | # Should emit in cases with mixed `typing.Union` and `|` 83 | field25: typing.Union[Literal[1], Literal[2] | str] # Error @@ -247,7 +482,17 @@ PYI030.pyi:83:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:86:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` +ℹ Safe fix +80 80 | field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error +81 81 | +82 82 | # Should emit in cases with mixed `typing.Union` and `|` +83 |-field25: typing.Union[Literal[1], Literal[2] | str] # Error + 83 |+field25: typing.Union[Literal[1, 2], str] # Error +84 84 | +85 85 | # Should emit only once in cases with multiple nested `typing.Union` +86 86 | field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error + +PYI030.pyi:86:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` | 85 | # Should emit only once in cases with multiple nested `typing.Union` 86 | field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error @@ -257,7 +502,17 @@ PYI030.pyi:86:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` -PYI030.pyi:89:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` +ℹ Safe fix +83 83 | field25: typing.Union[Literal[1], Literal[2] | str] # Error +84 84 | +85 85 | # Should emit only once in cases with multiple nested `typing.Union` +86 |-field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error + 86 |+field24: Literal[1, 2, 3, 4] # Error +87 87 | +88 88 | # Should use the first literal subscript attribute when fixing +89 89 | field25: typing.Union[typing_extensions.Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]], str] # Error + +PYI030.pyi:89:10: PYI030 [*] Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2, 3, 4]` | 88 | # Should use the first literal subscript attribute when fixing 89 | field25: typing.Union[typing_extensions.Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]], str] # Error @@ -265,4 +520,11 @@ PYI030.pyi:89:10: PYI030 Multiple literal members in a union. Use a single liter | = help: Replace with a single `Literal` +ℹ Safe fix +86 86 | field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error +87 87 | +88 88 | # Should use the first literal subscript attribute when fixing +89 |-field25: typing.Union[typing_extensions.Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]], str] # Error + 89 |+field25: typing.Union[typing_extensions.Literal[1, 2, 3, 4], str] # Error + diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs index 5918dfd02718f..f30773733f0f2 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs @@ -24,7 +24,7 @@ use super::helpers::{ /// ## What it does /// Checks for argument-free `@pytest.fixture()` decorators with or without -/// parentheses, depending on the `flake8-pytest-style.fixture-parentheses` +/// parentheses, depending on the [`lint.flake8-pytest-style.fixture-parentheses`] /// setting. /// /// ## Why is this bad? @@ -55,7 +55,7 @@ use super::helpers::{ /// ``` /// /// ## Options -/// - `flake8-pytest-style.fixture-parentheses` +/// - `lint.flake8-pytest-style.fixture-parentheses` /// /// ## References /// - [`pytest` documentation: API Reference: Fixtures](https://docs.pytest.org/en/latest/reference/reference.html#fixtures-api) diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/marks.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/marks.rs index 0be4eb4d508c8..cc00f2c6ddba4 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/marks.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/marks.rs @@ -11,7 +11,7 @@ use super::helpers::get_mark_decorators; /// ## What it does /// Checks for argument-free `@pytest.mark.()` decorators with or -/// without parentheses, depending on the `flake8-pytest-style.mark-parentheses` +/// without parentheses, depending on the [`lint.flake8-pytest-style.mark-parentheses`] /// setting. /// /// ## Why is this bad? @@ -42,7 +42,7 @@ use super::helpers::get_mark_decorators; /// ``` /// /// ## Options -/// - `flake8-pytest-style.mark-parentheses` +/// - `lint.flake8-pytest-style.mark-parentheses` /// /// ## References /// - [`pytest` documentation: Marks](https://docs.pytest.org/en/latest/reference/reference.html#marks) diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/parametrize.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/parametrize.rs index 4ff975393b38a..d71eb361fce40 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/parametrize.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/parametrize.rs @@ -26,7 +26,7 @@ use super::helpers::{is_pytest_parametrize, split_names}; /// The `argnames` argument of `pytest.mark.parametrize` takes a string or /// a sequence of strings. For a single parameter, it's preferable to use a /// string. For multiple parameters, it's preferable to use the style -/// configured via the [`flake8-pytest-style.parametrize-names-type`] setting. +/// configured via the [`lint.flake8-pytest-style.parametrize-names-type`] setting. /// /// ## Example /// ```python @@ -67,7 +67,7 @@ use super::helpers::{is_pytest_parametrize, split_names}; /// ``` /// /// ## Options -/// - `flake8-pytest-style.parametrize-names-type` +/// - `lint.flake8-pytest-style.parametrize-names-type` /// /// ## References /// - [`pytest` documentation: How to parametrize fixtures and test functions](https://docs.pytest.org/en/latest/how-to/parametrize.html#pytest-mark-parametrize) @@ -103,17 +103,17 @@ impl Violation for PytestParametrizeNamesWrongType { /// of values. /// /// The style for the list of values rows can be configured via the -/// the [`flake8-pytest-style.parametrize-values-type`] setting, while the +/// the [`lint.flake8-pytest-style.parametrize-values-type`] setting, while the /// style for each row of values can be configured via the -/// the [`flake8-pytest-style.parametrize-values-row-type`] setting. +/// the [`lint.flake8-pytest-style.parametrize-values-row-type`] setting. /// -/// For example, [`flake8-pytest-style.parametrize-values-type`] will lead to +/// For example, [`lint.flake8-pytest-style.parametrize-values-type`] will lead to /// the following expectations: /// /// - `tuple`: `@pytest.mark.parametrize("value", ("a", "b", "c"))` /// - `list`: `@pytest.mark.parametrize("value", ["a", "b", "c"])` /// -/// Similarly, [`flake8-pytest-style.parametrize-values-row-type`] will lead to +/// Similarly, [`lint.flake8-pytest-style.parametrize-values-row-type`] will lead to /// the following expectations: /// /// - `tuple`: `@pytest.mark.parametrize(("key", "value"), [("a", "b"), ("c", "d")])` @@ -170,8 +170,8 @@ impl Violation for PytestParametrizeNamesWrongType { /// ``` /// /// ## Options -/// - `flake8-pytest-style.parametrize-values-type` -/// - `flake8-pytest-style.parametrize-values-row-type` +/// - `lint.flake8-pytest-style.parametrize-values-type` +/// - `lint.flake8-pytest-style.parametrize-values-row-type` /// /// ## References /// - [`pytest` documentation: How to parametrize fixtures and test functions](https://docs.pytest.org/en/latest/how-to/parametrize.html#pytest-mark-parametrize) diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs index ec457155bd1e4..254db5b06cbc3 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs @@ -64,8 +64,8 @@ impl Violation for PytestRaisesWithMultipleStatements { /// unrelated to the code under test. To avoid this, `pytest.raises` should be /// called with a `match` parameter. The exception names that require a `match` /// parameter can be configured via the -/// `flake8-pytest-style.raises-require-match-for` and -/// `flake8-pytest-style.raises-extend-require-match-for` settings. +/// [`lint.flake8-pytest-style.raises-require-match-for`] and +/// [`lint.flake8-pytest-style.raises-extend-require-match-for`] settings. /// /// ## Example /// ```python @@ -92,8 +92,8 @@ impl Violation for PytestRaisesWithMultipleStatements { /// ``` /// /// ## Options -/// - `flake8-pytest-style.raises-require-match-for` -/// - `flake8-pytest-style.raises-extend-require-match-for` +/// - `lint.flake8-pytest-style.raises-require-match-for` +/// - `lint.flake8-pytest-style.raises-extend-require-match-for` /// /// ## References /// - [`pytest` documentation: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises) diff --git a/crates/ruff_linter/src/rules/flake8_quotes/rules/check_string_quotes.rs b/crates/ruff_linter/src/rules/flake8_quotes/rules/check_string_quotes.rs index 45a845fc6459a..b182b36e899e1 100644 --- a/crates/ruff_linter/src/rules/flake8_quotes/rules/check_string_quotes.rs +++ b/crates/ruff_linter/src/rules/flake8_quotes/rules/check_string_quotes.rs @@ -14,7 +14,7 @@ use super::super::settings::Quote; /// ## What it does /// Checks for inline strings that use single quotes or double quotes, -/// depending on the value of the [`flake8-quotes.inline-quotes`] option. +/// depending on the value of the [`lint.flake8-quotes.inline-quotes`] option. /// /// ## Why is this bad? /// Consistency is good. Use either single or double quotes for inline @@ -31,7 +31,7 @@ use super::super::settings::Quote; /// ``` /// /// ## Options -/// - `flake8-quotes.inline-quotes` +/// - `lint.flake8-quotes.inline-quotes` /// /// ## Formatter compatibility /// We recommend against using this rule alongside the [formatter]. The @@ -65,7 +65,7 @@ impl AlwaysFixableViolation for BadQuotesInlineString { /// ## What it does /// Checks for multiline strings that use single quotes or double quotes, -/// depending on the value of the [`flake8-quotes.multiline-quotes`] +/// depending on the value of the [`lint.flake8-quotes.multiline-quotes`] /// setting. /// /// ## Why is this bad? @@ -87,7 +87,7 @@ impl AlwaysFixableViolation for BadQuotesInlineString { /// ``` /// /// ## Options -/// - `flake8-quotes.multiline-quotes` +/// - `lint.flake8-quotes.multiline-quotes` /// /// ## Formatter compatibility /// We recommend against using this rule alongside the [formatter]. The @@ -121,7 +121,7 @@ impl AlwaysFixableViolation for BadQuotesMultilineString { /// ## What it does /// Checks for docstrings that use single quotes or double quotes, depending -/// on the value of the [`flake8-quotes.docstring-quotes`] setting. +/// on the value of the [`lint.flake8-quotes.docstring-quotes`] setting. /// /// ## Why is this bad? /// Consistency is good. Use either single or double quotes for docstring @@ -142,7 +142,7 @@ impl AlwaysFixableViolation for BadQuotesMultilineString { /// ``` /// /// ## Options -/// - `flake8-quotes.docstring-quotes` +/// - `lint.flake8-quotes.docstring-quotes` /// /// ## Formatter compatibility /// We recommend against using this rule alongside the [formatter]. The diff --git a/crates/ruff_linter/src/rules/flake8_self/rules/private_member_access.rs b/crates/ruff_linter/src/rules/flake8_self/rules/private_member_access.rs index fdbf3e8def12b..ccabe9ba32023 100644 --- a/crates/ruff_linter/src/rules/flake8_self/rules/private_member_access.rs +++ b/crates/ruff_linter/src/rules/flake8_self/rules/private_member_access.rs @@ -43,7 +43,7 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `flake8-self.ignore-names` +/// - `lint.flake8-self.ignore-names` /// /// ## References /// - [_What is the meaning of single or double underscores before an object name?_](https://stackoverflow.com/questions/1301346/what-is-the-meaning-of-single-and-double-underscore-before-an-object-name) diff --git a/crates/ruff_linter/src/rules/flake8_simplify/mod.rs b/crates/ruff_linter/src/rules/flake8_simplify/mod.rs index c6b7526ec44f7..e68c9d6b471ca 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/mod.rs @@ -56,11 +56,7 @@ mod tests { Ok(()) } - #[test_case(Rule::InDictKeys, Path::new("SIM118.py"))] #[test_case(Rule::YodaConditions, Path::new("SIM300.py"))] - #[test_case(Rule::IfElseBlockInsteadOfDictGet, Path::new("SIM401.py"))] - #[test_case(Rule::DictGetWithNoneDefault, Path::new("SIM910.py"))] - #[test_case(Rule::IfWithSameArms, Path::new("SIM114.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_expr.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_expr.rs index 32604d2ae9693..9f45a0adf80d2 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_expr.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_expr.rs @@ -69,10 +69,6 @@ impl Violation for UncapitalizedEnvironmentVariables { /// `None` is the default value for `dict.get()`, so it is redundant to pass it /// explicitly. /// -/// In [preview], this rule applies to variables that are inferred to be -/// dictionaries; in stable, it's limited to dictionary literals (e.g., -/// `{"foo": 1}.get("foo", None)`). -/// /// ## Example /// ```python /// ages = {"Tom": 23, "Maria": 23, "Dog": 11} @@ -87,8 +83,6 @@ impl Violation for UncapitalizedEnvironmentVariables { /// /// ## References /// - [Python documentation: `dict.get`](https://docs.python.org/3/library/stdtypes.html#dict.get) -/// -/// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] pub struct DictGetWithNoneDefault { expected: SourceCodeSnippet, @@ -266,10 +260,6 @@ pub(crate) fn dict_get_with_none_default(checker: &mut Checker, expr: &Expr) { match value.as_ref() { Expr::Dict(_) | Expr::DictComp(_) => {} Expr::Name(name) => { - if checker.settings.preview.is_disabled() { - return; - } - let Some(binding) = checker .semantic() .only_binding(name) diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_dict_get.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_dict_get.rs index 50d6e8b7bb129..1c710427fe077 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_dict_get.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_dict_get.rs @@ -224,10 +224,6 @@ pub(crate) fn if_exp_instead_of_dict_get( body: &Expr, orelse: &Expr, ) { - if checker.settings.preview.is_disabled() { - return; - } - let Expr::Compare(ast::ExprCompare { left: test_key, ops, diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_with_same_arms.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_with_same_arms.rs index ae396192b69d5..2e6ad2beb78c1 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_with_same_arms.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_with_same_arms.rs @@ -94,17 +94,15 @@ pub(crate) fn if_with_same_arms(checker: &mut Checker, stmt_if: &ast::StmtIf) { TextRange::new(current_branch.start(), following_branch.end()), ); - if checker.settings.preview.is_enabled() { - diagnostic.try_set_fix(|| { - merge_branches( - stmt_if, - ¤t_branch, - following_branch, - checker.locator(), - checker.indexer(), - ) - }); - } + diagnostic.try_set_fix(|| { + merge_branches( + stmt_if, + ¤t_branch, + following_branch, + checker.locator(), + checker.indexer(), + ) + }); checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs index 0811226c5c04b..2594722f34523 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs @@ -31,11 +31,9 @@ use crate::checkers::ast::Checker; /// ## Fix safety /// Given `key in obj.keys()`, `obj` _could_ be a dictionary, or it could be /// another type that defines a `.keys()` method. In the latter case, removing -/// the `.keys()` attribute could lead to a runtime error. -/// -/// As such, this rule's fixes are marked as unsafe. In [preview], though, -/// fixes are marked as safe when Ruff can determine that `obj` is a -/// dictionary. +/// the `.keys()` attribute could lead to a runtime error. The fix is marked +/// as safe when the type of `obj` is known to be a dictionary; otherwise, it +/// is marked as unsafe. /// /// ## References /// - [Python documentation: Mapping Types](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict) @@ -127,7 +125,7 @@ fn key_in_dict( { // The fix is only safe if we know the expression is a dictionary, since other types // can define a `.keys()` method. - let applicability = if checker.settings.preview.is_enabled() { + let applicability = { let is_dict = value.as_name_expr().is_some_and(|name| { let Some(binding) = checker .semantic() @@ -143,8 +141,6 @@ fn key_in_dict( } else { Applicability::Unsafe } - } else { - Applicability::Unsafe }; // If the `.keys()` is followed by (e.g.) a keyword, we need to insert a space, diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM114_SIM114.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM114_SIM114.py.snap index 8ce27dcdb7046..eaf2057160b39 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM114_SIM114.py.snap +++ b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM114_SIM114.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs --- -SIM114.py:2:1: SIM114 Combine `if` branches using logical `or` operator +SIM114.py:2:1: SIM114 [*] Combine `if` branches using logical `or` operator | 1 | # Errors 2 | / if a: @@ -14,7 +14,17 @@ SIM114.py:2:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:7:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +1 1 | # Errors +2 |-if a: +3 |- b +4 |-elif c: + 2 |+if a or c: +5 3 | b +6 4 | +7 5 | if a: # we preserve comments, too! + +SIM114.py:7:1: SIM114 [*] Combine `if` branches using logical `or` operator | 5 | b 6 | @@ -28,7 +38,19 @@ SIM114.py:7:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:12:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +4 4 | elif c: +5 5 | b +6 6 | +7 |-if a: # we preserve comments, too! +8 |- b +9 |-elif c: # but not on the second branch + 7 |+if a or c: # we preserve comments, too! +10 8 | b +11 9 | +12 10 | if x == 1: + +SIM114.py:12:1: SIM114 [*] Combine `if` branches using logical `or` operator | 10 | b 11 | @@ -44,7 +66,20 @@ SIM114.py:12:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:19:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +9 9 | elif c: # but not on the second branch +10 10 | b +11 11 | +12 |-if x == 1: +13 |- for _ in range(20): +14 |- print("hello") +15 |-elif x == 2: + 12 |+if x == 1 or x == 2: +16 13 | for _ in range(20): +17 14 | print("hello") +18 15 | + +SIM114.py:19:1: SIM114 [*] Combine `if` branches using logical `or` operator | 17 | print("hello") 18 | @@ -62,7 +97,21 @@ SIM114.py:19:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:28:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +16 16 | for _ in range(20): +17 17 | print("hello") +18 18 | +19 |-if x == 1: +20 |- if True: +21 |- for _ in range(20): +22 |- print("hello") +23 |-elif x == 2: + 19 |+if x == 1 or x == 2: +24 20 | if True: +25 21 | for _ in range(20): +26 22 | print("hello") + +SIM114.py:28:1: SIM114 [*] Combine `if` branches using logical `or` operator | 26 | print("hello") 27 | @@ -86,7 +135,24 @@ SIM114.py:28:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:29:5: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +25 25 | for _ in range(20): +26 26 | print("hello") +27 27 | +28 |-if x == 1: +29 |- if True: +30 |- for _ in range(20): +31 |- print("hello") +32 |- elif False: +33 |- for _ in range(20): +34 |- print("hello") +35 |-elif x == 2: + 28 |+if x == 1 or x == 2: +36 29 | if True: +37 30 | for _ in range(20): +38 31 | print("hello") + +SIM114.py:29:5: SIM114 [*] Combine `if` branches using logical `or` operator | 28 | if x == 1: 29 | if True: @@ -102,7 +168,20 @@ SIM114.py:29:5: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:36:5: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +26 26 | print("hello") +27 27 | +28 28 | if x == 1: +29 |- if True: +30 |- for _ in range(20): +31 |- print("hello") +32 |- elif False: + 29 |+ if True or False: +33 30 | for _ in range(20): +34 31 | print("hello") +35 32 | elif x == 2: + +SIM114.py:36:5: SIM114 [*] Combine `if` branches using logical `or` operator | 34 | print("hello") 35 | elif x == 2: @@ -119,7 +198,20 @@ SIM114.py:36:5: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:43:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +33 33 | for _ in range(20): +34 34 | print("hello") +35 35 | elif x == 2: +36 |- if True: +37 |- for _ in range(20): +38 |- print("hello") +39 |- elif False: + 36 |+ if True or False: +40 37 | for _ in range(20): +41 38 | print("hello") +42 39 | + +SIM114.py:43:1: SIM114 [*] Combine `if` branches using logical `or` operator | 41 | print("hello") 42 | @@ -148,7 +240,19 @@ SIM114.py:43:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:67:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +55 55 | and i == 12 +56 56 | and j == 13 +57 57 | and k == 14 +58 |-): +59 |- pass +60 |-elif 1 == 2: + 58 |+) or 1 == 2: +61 59 | pass +62 60 | +63 61 | if result.eofs == "O": + +SIM114.py:67:1: SIM114 [*] Combine `if` branches using logical `or` operator | 65 | elif result.eofs == "S": 66 | skipped = 1 @@ -162,7 +266,19 @@ SIM114.py:67:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:69:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +64 64 | pass +65 65 | elif result.eofs == "S": +66 66 | skipped = 1 +67 |-elif result.eofs == "F": +68 |- errors = 1 +69 |-elif result.eofs == "E": + 67 |+elif result.eofs == "F" or result.eofs == "E": +70 68 | errors = 1 +71 69 | elif result.eofs == "X": +72 70 | errors = 1 + +SIM114.py:69:1: SIM114 [*] Combine `if` branches using logical `or` operator | 67 | elif result.eofs == "F": 68 | errors = 1 @@ -176,7 +292,19 @@ SIM114.py:69:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:71:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +66 66 | skipped = 1 +67 67 | elif result.eofs == "F": +68 68 | errors = 1 +69 |-elif result.eofs == "E": +70 |- errors = 1 +71 |-elif result.eofs == "X": + 69 |+elif result.eofs == "E" or result.eofs == "X": +72 70 | errors = 1 +73 71 | elif result.eofs == "C": +74 72 | errors = 1 + +SIM114.py:71:1: SIM114 [*] Combine `if` branches using logical `or` operator | 69 | elif result.eofs == "E": 70 | errors = 1 @@ -188,7 +316,19 @@ SIM114.py:71:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:118:5: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +68 68 | errors = 1 +69 69 | elif result.eofs == "E": +70 70 | errors = 1 +71 |-elif result.eofs == "X": +72 |- errors = 1 +73 |-elif result.eofs == "C": + 71 |+elif result.eofs == "X" or result.eofs == "C": +74 72 | errors = 1 +75 73 | +76 74 | + +SIM114.py:118:5: SIM114 [*] Combine `if` branches using logical `or` operator | 116 | a = True 117 | b = False @@ -203,7 +343,19 @@ SIM114.py:118:5: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:122:5: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +115 115 | def func(): +116 116 | a = True +117 117 | b = False +118 |- if a > b: # end-of-line +119 |- return 3 +120 |- elif a == b: + 118 |+ if a > b or a == b: # end-of-line +121 119 | return 3 +122 120 | elif a < b: # end-of-line +123 121 | return 4 + +SIM114.py:122:5: SIM114 [*] Combine `if` branches using logical `or` operator | 120 | elif a == b: 121 | return 3 @@ -216,7 +368,19 @@ SIM114.py:122:5: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:132:5: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +119 119 | return 3 +120 120 | elif a == b: +121 121 | return 3 +122 |- elif a < b: # end-of-line +123 |- return 4 +124 |- elif b is None: + 122 |+ elif a < b or b is None: # end-of-line +125 123 | return 4 +126 124 | +127 125 | + +SIM114.py:132:5: SIM114 [*] Combine `if` branches using logical `or` operator | 130 | a = True 131 | b = False @@ -229,7 +393,19 @@ SIM114.py:132:5: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:138:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +129 129 | """Ensure that the named expression is parenthesized when merged.""" +130 130 | a = True +131 131 | b = False +132 |- if a > b: # end-of-line +133 |- return 3 +134 |- elif a := 1: + 132 |+ if a > b or (a := 1): # end-of-line +135 133 | return 3 +136 134 | +137 135 | + +SIM114.py:138:1: SIM114 [*] Combine `if` branches using logical `or` operator | 138 | / if a: # we preserve comments, too! 139 | | b @@ -239,7 +415,19 @@ SIM114.py:138:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:144:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +135 135 | return 3 +136 136 | +137 137 | +138 |-if a: # we preserve comments, too! +139 |- b +140 |-elif c: # but not on the second branch + 138 |+if a or c: # we preserve comments, too! +141 139 | b +142 140 | +143 141 | + +SIM114.py:144:1: SIM114 [*] Combine `if` branches using logical `or` operator | 144 | / if a: b # here's a comment 145 | | elif c: b @@ -247,7 +435,18 @@ SIM114.py:144:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches -SIM114.py:148:1: SIM114 Combine `if` branches using logical `or` operator +ℹ Safe fix +141 141 | b +142 142 | +143 143 | +144 |-if a: b # here's a comment +145 |-elif c: b + 144 |+if a or c: b # here's a comment +146 145 | +147 146 | +148 147 | if(x > 200): pass + +SIM114.py:148:1: SIM114 [*] Combine `if` branches using logical `or` operator | 148 | / if(x > 200): pass 149 | | elif(100 < x and x < 200 and 300 < y and y < 800): @@ -256,4 +455,13 @@ SIM114.py:148:1: SIM114 Combine `if` branches using logical `or` operator | = help: Combine `if` branches +ℹ Safe fix +145 145 | elif c: b +146 146 | +147 147 | +148 |-if(x > 200): pass +149 |-elif(100 < x and x < 200 and 300 < y and y < 800): +150 |- pass + 148 |+if(x > 200) or (100 < x and x < 200 and 300 < y and y < 800): pass + diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM118_SIM118.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM118_SIM118.py.snap index 278730a8ff055..f5ee64bb00f81 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM118_SIM118.py.snap +++ b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM118_SIM118.py.snap @@ -12,7 +12,7 @@ SIM118.py:3:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 1 1 | obj = {} 2 2 | 3 |-key in obj.keys() # SIM118 @@ -32,7 +32,7 @@ SIM118.py:5:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 2 2 | 3 3 | key in obj.keys() # SIM118 4 4 | @@ -53,7 +53,7 @@ SIM118.py:7:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 4 4 | 5 5 | key not in obj.keys() # SIM118 6 6 | @@ -74,7 +74,7 @@ SIM118.py:9:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 6 6 | 7 7 | foo["bar"] in obj.keys() # SIM118 8 8 | @@ -95,7 +95,7 @@ SIM118.py:11:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 8 8 | 9 9 | foo["bar"] not in obj.keys() # SIM118 10 10 | @@ -116,7 +116,7 @@ SIM118.py:13:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.key | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 10 10 | 11 11 | foo['bar'] in obj.keys() # SIM118 12 12 | @@ -137,7 +137,7 @@ SIM118.py:15:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 12 12 | 13 13 | foo['bar'] not in obj.keys() # SIM118 14 14 | @@ -158,7 +158,7 @@ SIM118.py:17:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.key | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 14 14 | 15 15 | foo() in obj.keys() # SIM118 16 16 | @@ -178,7 +178,7 @@ SIM118.py:19:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 16 16 | 17 17 | foo() not in obj.keys() # SIM118 18 18 | @@ -199,7 +199,7 @@ SIM118.py:26:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 23 23 | if some_property(key): 24 24 | del obj[key] 25 25 | @@ -220,7 +220,7 @@ SIM118.py:28:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 25 25 | 26 26 | [k for k in obj.keys()] # SIM118 27 27 | @@ -241,7 +241,7 @@ SIM118.py:30:11: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 27 27 | 28 28 | {k for k in obj.keys()} # SIM118 29 29 | @@ -262,7 +262,7 @@ SIM118.py:32:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 29 29 | 30 30 | {k: k for k in obj.keys()} # SIM118 31 31 | @@ -324,7 +324,7 @@ SIM118.py:50:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 47 47 | 48 48 | 49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 @@ -344,7 +344,7 @@ SIM118.py:51:2: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 48 48 | 49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 50 50 | key in obj.keys()and foo @@ -365,7 +365,7 @@ SIM118.py:52:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` | = help: Remove `.keys()` -ℹ Unsafe fix +ℹ Safe fix 49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 50 50 | key in obj.keys()and foo 51 51 | (key in obj.keys())and foo diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM401_SIM401.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM401_SIM401.py.snap index 452916c60e98b..15ceaf67f67b6 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM401_SIM401.py.snap +++ b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM401_SIM401.py.snap @@ -159,4 +159,44 @@ SIM401.py:45:5: SIM401 [*] Use `vars[idx] = a_dict.get(key, "default")` instead 50 47 | ### 51 48 | # Negative cases +SIM401.py:123:7: SIM401 [*] Use `a_dict.get(key, "default3")` instead of an `if` block + | +122 | # SIM401 +123 | var = a_dict[key] if key in a_dict else "default3" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM401 +124 | +125 | # SIM401 + | + = help: Replace with `a_dict.get(key, "default3")` + +ℹ Unsafe fix +120 120 | ### +121 121 | +122 122 | # SIM401 +123 |-var = a_dict[key] if key in a_dict else "default3" + 123 |+var = a_dict.get(key, "default3") +124 124 | +125 125 | # SIM401 +126 126 | var = "default-1" if key not in a_dict else a_dict[key] + +SIM401.py:126:7: SIM401 [*] Use `a_dict.get(key, "default-1")` instead of an `if` block + | +125 | # SIM401 +126 | var = "default-1" if key not in a_dict else a_dict[key] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM401 +127 | +128 | # OK (default contains effect) + | + = help: Replace with `a_dict.get(key, "default-1")` + +ℹ Unsafe fix +123 123 | var = a_dict[key] if key in a_dict else "default3" +124 124 | +125 125 | # SIM401 +126 |-var = "default-1" if key not in a_dict else a_dict[key] + 126 |+var = a_dict.get(key, "default-1") +127 127 | +128 128 | # OK (default contains effect) +129 129 | var = a_dict[key] if key in a_dict else val1 + val2 + diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM910_SIM910.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM910_SIM910.py.snap index 9be488576c846..a7951aa5e56ce 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM910_SIM910.py.snap +++ b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM910_SIM910.py.snap @@ -98,4 +98,25 @@ SIM910.py:27:1: SIM910 [*] Use `({}).get(key)` instead of `({}).get(key, None)` 29 29 | # SIM910 30 30 | ages = {"Tom": 23, "Maria": 23, "Dog": 11} +SIM910.py:31:7: SIM910 [*] Use `ages.get("Cat")` instead of `ages.get("Cat", None)` + | +29 | # SIM910 +30 | ages = {"Tom": 23, "Maria": 23, "Dog": 11} +31 | age = ages.get("Cat", None) + | ^^^^^^^^^^^^^^^^^^^^^ SIM910 +32 | +33 | # OK + | + = help: Replace `ages.get("Cat", None)` with `ages.get("Cat")` + +ℹ Safe fix +28 28 | +29 29 | # SIM910 +30 30 | ages = {"Tom": 23, "Maria": 23, "Dog": 11} +31 |-age = ages.get("Cat", None) + 31 |+age = ages.get("Cat") +32 32 | +33 33 | # OK +34 34 | ages = ["Tom", "Maria", "Dog"] + diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM114_SIM114.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM114_SIM114.py.snap deleted file mode 100644 index eaf2057160b39..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM114_SIM114.py.snap +++ /dev/null @@ -1,467 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs ---- -SIM114.py:2:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -1 | # Errors -2 | / if a: -3 | | b -4 | | elif c: -5 | | b - | |_____^ SIM114 -6 | -7 | if a: # we preserve comments, too! - | - = help: Combine `if` branches - -ℹ Safe fix -1 1 | # Errors -2 |-if a: -3 |- b -4 |-elif c: - 2 |+if a or c: -5 3 | b -6 4 | -7 5 | if a: # we preserve comments, too! - -SIM114.py:7:1: SIM114 [*] Combine `if` branches using logical `or` operator - | - 5 | b - 6 | - 7 | / if a: # we preserve comments, too! - 8 | | b - 9 | | elif c: # but not on the second branch -10 | | b - | |_____^ SIM114 -11 | -12 | if x == 1: - | - = help: Combine `if` branches - -ℹ Safe fix -4 4 | elif c: -5 5 | b -6 6 | -7 |-if a: # we preserve comments, too! -8 |- b -9 |-elif c: # but not on the second branch - 7 |+if a or c: # we preserve comments, too! -10 8 | b -11 9 | -12 10 | if x == 1: - -SIM114.py:12:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -10 | b -11 | -12 | / if x == 1: -13 | | for _ in range(20): -14 | | print("hello") -15 | | elif x == 2: -16 | | for _ in range(20): -17 | | print("hello") - | |______________________^ SIM114 -18 | -19 | if x == 1: - | - = help: Combine `if` branches - -ℹ Safe fix -9 9 | elif c: # but not on the second branch -10 10 | b -11 11 | -12 |-if x == 1: -13 |- for _ in range(20): -14 |- print("hello") -15 |-elif x == 2: - 12 |+if x == 1 or x == 2: -16 13 | for _ in range(20): -17 14 | print("hello") -18 15 | - -SIM114.py:19:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -17 | print("hello") -18 | -19 | / if x == 1: -20 | | if True: -21 | | for _ in range(20): -22 | | print("hello") -23 | | elif x == 2: -24 | | if True: -25 | | for _ in range(20): -26 | | print("hello") - | |__________________________^ SIM114 -27 | -28 | if x == 1: - | - = help: Combine `if` branches - -ℹ Safe fix -16 16 | for _ in range(20): -17 17 | print("hello") -18 18 | -19 |-if x == 1: -20 |- if True: -21 |- for _ in range(20): -22 |- print("hello") -23 |-elif x == 2: - 19 |+if x == 1 or x == 2: -24 20 | if True: -25 21 | for _ in range(20): -26 22 | print("hello") - -SIM114.py:28:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -26 | print("hello") -27 | -28 | / if x == 1: -29 | | if True: -30 | | for _ in range(20): -31 | | print("hello") -32 | | elif False: -33 | | for _ in range(20): -34 | | print("hello") -35 | | elif x == 2: -36 | | if True: -37 | | for _ in range(20): -38 | | print("hello") -39 | | elif False: -40 | | for _ in range(20): -41 | | print("hello") - | |__________________________^ SIM114 -42 | -43 | if ( - | - = help: Combine `if` branches - -ℹ Safe fix -25 25 | for _ in range(20): -26 26 | print("hello") -27 27 | -28 |-if x == 1: -29 |- if True: -30 |- for _ in range(20): -31 |- print("hello") -32 |- elif False: -33 |- for _ in range(20): -34 |- print("hello") -35 |-elif x == 2: - 28 |+if x == 1 or x == 2: -36 29 | if True: -37 30 | for _ in range(20): -38 31 | print("hello") - -SIM114.py:29:5: SIM114 [*] Combine `if` branches using logical `or` operator - | -28 | if x == 1: -29 | if True: - | _____^ -30 | | for _ in range(20): -31 | | print("hello") -32 | | elif False: -33 | | for _ in range(20): -34 | | print("hello") - | |__________________________^ SIM114 -35 | elif x == 2: -36 | if True: - | - = help: Combine `if` branches - -ℹ Safe fix -26 26 | print("hello") -27 27 | -28 28 | if x == 1: -29 |- if True: -30 |- for _ in range(20): -31 |- print("hello") -32 |- elif False: - 29 |+ if True or False: -33 30 | for _ in range(20): -34 31 | print("hello") -35 32 | elif x == 2: - -SIM114.py:36:5: SIM114 [*] Combine `if` branches using logical `or` operator - | -34 | print("hello") -35 | elif x == 2: -36 | if True: - | _____^ -37 | | for _ in range(20): -38 | | print("hello") -39 | | elif False: -40 | | for _ in range(20): -41 | | print("hello") - | |__________________________^ SIM114 -42 | -43 | if ( - | - = help: Combine `if` branches - -ℹ Safe fix -33 33 | for _ in range(20): -34 34 | print("hello") -35 35 | elif x == 2: -36 |- if True: -37 |- for _ in range(20): -38 |- print("hello") -39 |- elif False: - 36 |+ if True or False: -40 37 | for _ in range(20): -41 38 | print("hello") -42 39 | - -SIM114.py:43:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -41 | print("hello") -42 | -43 | / if ( -44 | | x == 1 -45 | | and y == 2 -46 | | and z == 3 -47 | | and a == 4 -48 | | and b == 5 -49 | | and c == 6 -50 | | and d == 7 -51 | | and e == 8 -52 | | and f == 9 -53 | | and g == 10 -54 | | and h == 11 -55 | | and i == 12 -56 | | and j == 13 -57 | | and k == 14 -58 | | ): -59 | | pass -60 | | elif 1 == 2: -61 | | pass - | |________^ SIM114 -62 | -63 | if result.eofs == "O": - | - = help: Combine `if` branches - -ℹ Safe fix -55 55 | and i == 12 -56 56 | and j == 13 -57 57 | and k == 14 -58 |-): -59 |- pass -60 |-elif 1 == 2: - 58 |+) or 1 == 2: -61 59 | pass -62 60 | -63 61 | if result.eofs == "O": - -SIM114.py:67:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -65 | elif result.eofs == "S": -66 | skipped = 1 -67 | / elif result.eofs == "F": -68 | | errors = 1 -69 | | elif result.eofs == "E": -70 | | errors = 1 - | |______________^ SIM114 -71 | elif result.eofs == "X": -72 | errors = 1 - | - = help: Combine `if` branches - -ℹ Safe fix -64 64 | pass -65 65 | elif result.eofs == "S": -66 66 | skipped = 1 -67 |-elif result.eofs == "F": -68 |- errors = 1 -69 |-elif result.eofs == "E": - 67 |+elif result.eofs == "F" or result.eofs == "E": -70 68 | errors = 1 -71 69 | elif result.eofs == "X": -72 70 | errors = 1 - -SIM114.py:69:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -67 | elif result.eofs == "F": -68 | errors = 1 -69 | / elif result.eofs == "E": -70 | | errors = 1 -71 | | elif result.eofs == "X": -72 | | errors = 1 - | |______________^ SIM114 -73 | elif result.eofs == "C": -74 | errors = 1 - | - = help: Combine `if` branches - -ℹ Safe fix -66 66 | skipped = 1 -67 67 | elif result.eofs == "F": -68 68 | errors = 1 -69 |-elif result.eofs == "E": -70 |- errors = 1 -71 |-elif result.eofs == "X": - 69 |+elif result.eofs == "E" or result.eofs == "X": -72 70 | errors = 1 -73 71 | elif result.eofs == "C": -74 72 | errors = 1 - -SIM114.py:71:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -69 | elif result.eofs == "E": -70 | errors = 1 -71 | / elif result.eofs == "X": -72 | | errors = 1 -73 | | elif result.eofs == "C": -74 | | errors = 1 - | |______________^ SIM114 - | - = help: Combine `if` branches - -ℹ Safe fix -68 68 | errors = 1 -69 69 | elif result.eofs == "E": -70 70 | errors = 1 -71 |-elif result.eofs == "X": -72 |- errors = 1 -73 |-elif result.eofs == "C": - 71 |+elif result.eofs == "X" or result.eofs == "C": -74 72 | errors = 1 -75 73 | -76 74 | - -SIM114.py:118:5: SIM114 [*] Combine `if` branches using logical `or` operator - | -116 | a = True -117 | b = False -118 | if a > b: # end-of-line - | _____^ -119 | | return 3 -120 | | elif a == b: -121 | | return 3 - | |________________^ SIM114 -122 | elif a < b: # end-of-line -123 | return 4 - | - = help: Combine `if` branches - -ℹ Safe fix -115 115 | def func(): -116 116 | a = True -117 117 | b = False -118 |- if a > b: # end-of-line -119 |- return 3 -120 |- elif a == b: - 118 |+ if a > b or a == b: # end-of-line -121 119 | return 3 -122 120 | elif a < b: # end-of-line -123 121 | return 4 - -SIM114.py:122:5: SIM114 [*] Combine `if` branches using logical `or` operator - | -120 | elif a == b: -121 | return 3 -122 | elif a < b: # end-of-line - | _____^ -123 | | return 4 -124 | | elif b is None: -125 | | return 4 - | |________________^ SIM114 - | - = help: Combine `if` branches - -ℹ Safe fix -119 119 | return 3 -120 120 | elif a == b: -121 121 | return 3 -122 |- elif a < b: # end-of-line -123 |- return 4 -124 |- elif b is None: - 122 |+ elif a < b or b is None: # end-of-line -125 123 | return 4 -126 124 | -127 125 | - -SIM114.py:132:5: SIM114 [*] Combine `if` branches using logical `or` operator - | -130 | a = True -131 | b = False -132 | if a > b: # end-of-line - | _____^ -133 | | return 3 -134 | | elif a := 1: -135 | | return 3 - | |________________^ SIM114 - | - = help: Combine `if` branches - -ℹ Safe fix -129 129 | """Ensure that the named expression is parenthesized when merged.""" -130 130 | a = True -131 131 | b = False -132 |- if a > b: # end-of-line -133 |- return 3 -134 |- elif a := 1: - 132 |+ if a > b or (a := 1): # end-of-line -135 133 | return 3 -136 134 | -137 135 | - -SIM114.py:138:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -138 | / if a: # we preserve comments, too! -139 | | b -140 | | elif c: # but not on the second branch -141 | | b - | |_____^ SIM114 - | - = help: Combine `if` branches - -ℹ Safe fix -135 135 | return 3 -136 136 | -137 137 | -138 |-if a: # we preserve comments, too! -139 |- b -140 |-elif c: # but not on the second branch - 138 |+if a or c: # we preserve comments, too! -141 139 | b -142 140 | -143 141 | - -SIM114.py:144:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -144 | / if a: b # here's a comment -145 | | elif c: b - | |_________^ SIM114 - | - = help: Combine `if` branches - -ℹ Safe fix -141 141 | b -142 142 | -143 143 | -144 |-if a: b # here's a comment -145 |-elif c: b - 144 |+if a or c: b # here's a comment -146 145 | -147 146 | -148 147 | if(x > 200): pass - -SIM114.py:148:1: SIM114 [*] Combine `if` branches using logical `or` operator - | -148 | / if(x > 200): pass -149 | | elif(100 < x and x < 200 and 300 < y and y < 800): -150 | | pass - | |________^ SIM114 - | - = help: Combine `if` branches - -ℹ Safe fix -145 145 | elif c: b -146 146 | -147 147 | -148 |-if(x > 200): pass -149 |-elif(100 < x and x < 200 and 300 < y and y < 800): -150 |- pass - 148 |+if(x > 200) or (100 < x and x < 200 and 300 < y and y < 800): pass - - diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM118_SIM118.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM118_SIM118.py.snap deleted file mode 100644 index f5ee64bb00f81..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM118_SIM118.py.snap +++ /dev/null @@ -1,401 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs ---- -SIM118.py:3:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -1 | obj = {} -2 | -3 | key in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^ SIM118 -4 | -5 | key not in obj.keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -1 1 | obj = {} -2 2 | -3 |-key in obj.keys() # SIM118 - 3 |+key in obj # SIM118 -4 4 | -5 5 | key not in obj.keys() # SIM118 -6 6 | - -SIM118.py:5:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` - | -3 | key in obj.keys() # SIM118 -4 | -5 | key not in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^ SIM118 -6 | -7 | foo["bar"] in obj.keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -2 2 | -3 3 | key in obj.keys() # SIM118 -4 4 | -5 |-key not in obj.keys() # SIM118 - 5 |+key not in obj # SIM118 -6 6 | -7 7 | foo["bar"] in obj.keys() # SIM118 -8 8 | - -SIM118.py:7:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -5 | key not in obj.keys() # SIM118 -6 | -7 | foo["bar"] in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 -8 | -9 | foo["bar"] not in obj.keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -4 4 | -5 5 | key not in obj.keys() # SIM118 -6 6 | -7 |-foo["bar"] in obj.keys() # SIM118 - 7 |+foo["bar"] in obj # SIM118 -8 8 | -9 9 | foo["bar"] not in obj.keys() # SIM118 -10 10 | - -SIM118.py:9:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` - | - 7 | foo["bar"] in obj.keys() # SIM118 - 8 | - 9 | foo["bar"] not in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 -10 | -11 | foo['bar'] in obj.keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -6 6 | -7 7 | foo["bar"] in obj.keys() # SIM118 -8 8 | -9 |-foo["bar"] not in obj.keys() # SIM118 - 9 |+foo["bar"] not in obj # SIM118 -10 10 | -11 11 | foo['bar'] in obj.keys() # SIM118 -12 12 | - -SIM118.py:11:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | - 9 | foo["bar"] not in obj.keys() # SIM118 -10 | -11 | foo['bar'] in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 -12 | -13 | foo['bar'] not in obj.keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -8 8 | -9 9 | foo["bar"] not in obj.keys() # SIM118 -10 10 | -11 |-foo['bar'] in obj.keys() # SIM118 - 11 |+foo['bar'] in obj # SIM118 -12 12 | -13 13 | foo['bar'] not in obj.keys() # SIM118 -14 14 | - -SIM118.py:13:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` - | -11 | foo['bar'] in obj.keys() # SIM118 -12 | -13 | foo['bar'] not in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 -14 | -15 | foo() in obj.keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -10 10 | -11 11 | foo['bar'] in obj.keys() # SIM118 -12 12 | -13 |-foo['bar'] not in obj.keys() # SIM118 - 13 |+foo['bar'] not in obj # SIM118 -14 14 | -15 15 | foo() in obj.keys() # SIM118 -16 16 | - -SIM118.py:15:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -13 | foo['bar'] not in obj.keys() # SIM118 -14 | -15 | foo() in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^ SIM118 -16 | -17 | foo() not in obj.keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -12 12 | -13 13 | foo['bar'] not in obj.keys() # SIM118 -14 14 | -15 |-foo() in obj.keys() # SIM118 - 15 |+foo() in obj # SIM118 -16 16 | -17 17 | foo() not in obj.keys() # SIM118 -18 18 | - -SIM118.py:17:1: SIM118 [*] Use `key not in dict` instead of `key not in dict.keys()` - | -15 | foo() in obj.keys() # SIM118 -16 | -17 | foo() not in obj.keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^ SIM118 -18 | -19 | for key in obj.keys(): # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -14 14 | -15 15 | foo() in obj.keys() # SIM118 -16 16 | -17 |-foo() not in obj.keys() # SIM118 - 17 |+foo() not in obj # SIM118 -18 18 | -19 19 | for key in obj.keys(): # SIM118 -20 20 | pass - -SIM118.py:19:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -17 | foo() not in obj.keys() # SIM118 -18 | -19 | for key in obj.keys(): # SIM118 - | ^^^^^^^^^^^^^^^^^ SIM118 -20 | pass - | - = help: Remove `.keys()` - -ℹ Safe fix -16 16 | -17 17 | foo() not in obj.keys() # SIM118 -18 18 | -19 |-for key in obj.keys(): # SIM118 - 19 |+for key in obj: # SIM118 -20 20 | pass -21 21 | -22 22 | for key in list(obj.keys()): - -SIM118.py:26:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -24 | del obj[key] -25 | -26 | [k for k in obj.keys()] # SIM118 - | ^^^^^^^^^^^^^^^ SIM118 -27 | -28 | {k for k in obj.keys()} # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -23 23 | if some_property(key): -24 24 | del obj[key] -25 25 | -26 |-[k for k in obj.keys()] # SIM118 - 26 |+[k for k in obj] # SIM118 -27 27 | -28 28 | {k for k in obj.keys()} # SIM118 -29 29 | - -SIM118.py:28:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -26 | [k for k in obj.keys()] # SIM118 -27 | -28 | {k for k in obj.keys()} # SIM118 - | ^^^^^^^^^^^^^^^ SIM118 -29 | -30 | {k: k for k in obj.keys()} # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -25 25 | -26 26 | [k for k in obj.keys()] # SIM118 -27 27 | -28 |-{k for k in obj.keys()} # SIM118 - 28 |+{k for k in obj} # SIM118 -29 29 | -30 30 | {k: k for k in obj.keys()} # SIM118 -31 31 | - -SIM118.py:30:11: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -28 | {k for k in obj.keys()} # SIM118 -29 | -30 | {k: k for k in obj.keys()} # SIM118 - | ^^^^^^^^^^^^^^^ SIM118 -31 | -32 | (k for k in obj.keys()) # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -27 27 | -28 28 | {k for k in obj.keys()} # SIM118 -29 29 | -30 |-{k: k for k in obj.keys()} # SIM118 - 30 |+{k: k for k in obj} # SIM118 -31 31 | -32 32 | (k for k in obj.keys()) # SIM118 -33 33 | - -SIM118.py:32:8: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -30 | {k: k for k in obj.keys()} # SIM118 -31 | -32 | (k for k in obj.keys()) # SIM118 - | ^^^^^^^^^^^^^^^ SIM118 -33 | -34 | key in (obj or {}).keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Safe fix -29 29 | -30 30 | {k: k for k in obj.keys()} # SIM118 -31 31 | -32 |-(k for k in obj.keys()) # SIM118 - 32 |+(k for k in obj) # SIM118 -33 33 | -34 34 | key in (obj or {}).keys() # SIM118 -35 35 | - -SIM118.py:34:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -32 | (k for k in obj.keys()) # SIM118 -33 | -34 | key in (obj or {}).keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 -35 | -36 | (key) in (obj or {}).keys() # SIM118 - | - = help: Remove `.keys()` - -ℹ Unsafe fix -31 31 | -32 32 | (k for k in obj.keys()) # SIM118 -33 33 | -34 |-key in (obj or {}).keys() # SIM118 - 34 |+key in (obj or {}) # SIM118 -35 35 | -36 36 | (key) in (obj or {}).keys() # SIM118 -37 37 | - -SIM118.py:36:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -34 | key in (obj or {}).keys() # SIM118 -35 | -36 | (key) in (obj or {}).keys() # SIM118 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM118 -37 | -38 | from typing import KeysView - | - = help: Remove `.keys()` - -ℹ Unsafe fix -33 33 | -34 34 | key in (obj or {}).keys() # SIM118 -35 35 | -36 |-(key) in (obj or {}).keys() # SIM118 - 36 |+(key) in (obj or {}) # SIM118 -37 37 | -38 38 | from typing import KeysView -39 39 | - -SIM118.py:50:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -50 | key in obj.keys()and foo - | ^^^^^^^^^^^^^^^^^ SIM118 -51 | (key in obj.keys())and foo -52 | key in (obj.keys())and foo - | - = help: Remove `.keys()` - -ℹ Safe fix -47 47 | -48 48 | -49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -50 |-key in obj.keys()and foo - 50 |+key in obj and foo -51 51 | (key in obj.keys())and foo -52 52 | key in (obj.keys())and foo -53 53 | - -SIM118.py:51:2: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -50 | key in obj.keys()and foo -51 | (key in obj.keys())and foo - | ^^^^^^^^^^^^^^^^^ SIM118 -52 | key in (obj.keys())and foo - | - = help: Remove `.keys()` - -ℹ Safe fix -48 48 | -49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -50 50 | key in obj.keys()and foo -51 |-(key in obj.keys())and foo - 51 |+(key in obj)and foo -52 52 | key in (obj.keys())and foo -53 53 | -54 54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 - -SIM118.py:52:1: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -50 | key in obj.keys()and foo -51 | (key in obj.keys())and foo -52 | key in (obj.keys())and foo - | ^^^^^^^^^^^^^^^^^^^ SIM118 -53 | -54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 - | - = help: Remove `.keys()` - -ℹ Safe fix -49 49 | # Regression test for: https://github.com/astral-sh/ruff/issues/7124 -50 50 | key in obj.keys()and foo -51 51 | (key in obj.keys())and foo -52 |-key in (obj.keys())and foo - 52 |+key in (obj)and foo -53 53 | -54 54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 -55 55 | for key in ( - -SIM118.py:55:5: SIM118 [*] Use `key in dict` instead of `key in dict.keys()` - | -54 | # Regression test for: https://github.com/astral-sh/ruff/issues/7200 -55 | for key in ( - | _____^ -56 | | self.experiment.surveys[0] -57 | | .stations[0] -58 | | .keys() -59 | | ): - | |_^ SIM118 -60 | continue - | - = help: Remove `.keys()` - -ℹ Unsafe fix -55 55 | for key in ( -56 56 | self.experiment.surveys[0] -57 57 | .stations[0] -58 |- .keys() - 58 |+ -59 59 | ): -60 60 | continue - - diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM401_SIM401.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM401_SIM401.py.snap deleted file mode 100644 index 15ceaf67f67b6..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM401_SIM401.py.snap +++ /dev/null @@ -1,202 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs ---- -SIM401.py:6:1: SIM401 [*] Use `var = a_dict.get(key, "default1")` instead of an `if` block - | - 5 | # SIM401 (pattern-1) - 6 | / if key in a_dict: - 7 | | var = a_dict[key] - 8 | | else: - 9 | | var = "default1" - | |____________________^ SIM401 -10 | -11 | # SIM401 (pattern-2) - | - = help: Replace with `var = a_dict.get(key, "default1")` - -ℹ Unsafe fix -3 3 | ### -4 4 | -5 5 | # SIM401 (pattern-1) -6 |-if key in a_dict: -7 |- var = a_dict[key] -8 |-else: -9 |- var = "default1" - 6 |+var = a_dict.get(key, "default1") -10 7 | -11 8 | # SIM401 (pattern-2) -12 9 | if key not in a_dict: - -SIM401.py:12:1: SIM401 [*] Use `var = a_dict.get(key, "default2")` instead of an `if` block - | -11 | # SIM401 (pattern-2) -12 | / if key not in a_dict: -13 | | var = "default2" -14 | | else: -15 | | var = a_dict[key] - | |_____________________^ SIM401 -16 | -17 | # OK (default contains effect) - | - = help: Replace with `var = a_dict.get(key, "default2")` - -ℹ Unsafe fix -9 9 | var = "default1" -10 10 | -11 11 | # SIM401 (pattern-2) -12 |-if key not in a_dict: -13 |- var = "default2" -14 |-else: -15 |- var = a_dict[key] - 12 |+var = a_dict.get(key, "default2") -16 13 | -17 14 | # OK (default contains effect) -18 15 | if key in a_dict: - -SIM401.py:24:1: SIM401 [*] Use `var = a_dict.get(keys[idx], "default")` instead of an `if` block - | -23 | # SIM401 (complex expression in key) -24 | / if keys[idx] in a_dict: -25 | | var = a_dict[keys[idx]] -26 | | else: -27 | | var = "default" - | |___________________^ SIM401 -28 | -29 | # SIM401 (complex expression in dict) - | - = help: Replace with `var = a_dict.get(keys[idx], "default")` - -ℹ Unsafe fix -21 21 | var = val1 + val2 -22 22 | -23 23 | # SIM401 (complex expression in key) -24 |-if keys[idx] in a_dict: -25 |- var = a_dict[keys[idx]] -26 |-else: -27 |- var = "default" - 24 |+var = a_dict.get(keys[idx], "default") -28 25 | -29 26 | # SIM401 (complex expression in dict) -30 27 | if key in dicts[idx]: - -SIM401.py:30:1: SIM401 [*] Use `var = dicts[idx].get(key, "default")` instead of an `if` block - | -29 | # SIM401 (complex expression in dict) -30 | / if key in dicts[idx]: -31 | | var = dicts[idx][key] -32 | | else: -33 | | var = "default" - | |___________________^ SIM401 -34 | -35 | # SIM401 (complex expression in var) - | - = help: Replace with `var = dicts[idx].get(key, "default")` - -ℹ Unsafe fix -27 27 | var = "default" -28 28 | -29 29 | # SIM401 (complex expression in dict) -30 |-if key in dicts[idx]: -31 |- var = dicts[idx][key] -32 |-else: -33 |- var = "default" - 30 |+var = dicts[idx].get(key, "default") -34 31 | -35 32 | # SIM401 (complex expression in var) -36 33 | if key in a_dict: - -SIM401.py:36:1: SIM401 [*] Use `vars[idx] = a_dict.get(key, "defaultß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789")` instead of an `if` block - | -35 | # SIM401 (complex expression in var) -36 | / if key in a_dict: -37 | | vars[idx] = a_dict[key] -38 | | else: -39 | | vars[idx] = "defaultß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789" - | |___________________________________________________________________________^ SIM401 -40 | -41 | # SIM401 - | - = help: Replace with `vars[idx] = a_dict.get(key, "defaultß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789")` - -ℹ Unsafe fix -33 33 | var = "default" -34 34 | -35 35 | # SIM401 (complex expression in var) -36 |-if key in a_dict: -37 |- vars[idx] = a_dict[key] -38 |-else: -39 |- vars[idx] = "defaultß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789" - 36 |+vars[idx] = a_dict.get(key, "defaultß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789ß9💣2ℝ6789") -40 37 | -41 38 | # SIM401 -42 39 | if foo(): - -SIM401.py:45:5: SIM401 [*] Use `vars[idx] = a_dict.get(key, "default")` instead of an `if` block - | -43 | pass -44 | else: -45 | if key in a_dict: - | _____^ -46 | | vars[idx] = a_dict[key] -47 | | else: -48 | | vars[idx] = "default" - | |_____________________________^ SIM401 -49 | -50 | ### - | - = help: Replace with `vars[idx] = a_dict.get(key, "default")` - -ℹ Unsafe fix -42 42 | if foo(): -43 43 | pass -44 44 | else: -45 |- if key in a_dict: -46 |- vars[idx] = a_dict[key] -47 |- else: -48 |- vars[idx] = "default" - 45 |+ vars[idx] = a_dict.get(key, "default") -49 46 | -50 47 | ### -51 48 | # Negative cases - -SIM401.py:123:7: SIM401 [*] Use `a_dict.get(key, "default3")` instead of an `if` block - | -122 | # SIM401 -123 | var = a_dict[key] if key in a_dict else "default3" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM401 -124 | -125 | # SIM401 - | - = help: Replace with `a_dict.get(key, "default3")` - -ℹ Unsafe fix -120 120 | ### -121 121 | -122 122 | # SIM401 -123 |-var = a_dict[key] if key in a_dict else "default3" - 123 |+var = a_dict.get(key, "default3") -124 124 | -125 125 | # SIM401 -126 126 | var = "default-1" if key not in a_dict else a_dict[key] - -SIM401.py:126:7: SIM401 [*] Use `a_dict.get(key, "default-1")` instead of an `if` block - | -125 | # SIM401 -126 | var = "default-1" if key not in a_dict else a_dict[key] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM401 -127 | -128 | # OK (default contains effect) - | - = help: Replace with `a_dict.get(key, "default-1")` - -ℹ Unsafe fix -123 123 | var = a_dict[key] if key in a_dict else "default3" -124 124 | -125 125 | # SIM401 -126 |-var = "default-1" if key not in a_dict else a_dict[key] - 126 |+var = a_dict.get(key, "default-1") -127 127 | -128 128 | # OK (default contains effect) -129 129 | var = a_dict[key] if key in a_dict else val1 + val2 - - diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM910_SIM910.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM910_SIM910.py.snap deleted file mode 100644 index a7951aa5e56ce..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM910_SIM910.py.snap +++ /dev/null @@ -1,122 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs ---- -SIM910.py:2:1: SIM910 [*] Use `{}.get(key)` instead of `{}.get(key, None)` - | -1 | # SIM910 -2 | {}.get(key, None) - | ^^^^^^^^^^^^^^^^^ SIM910 -3 | -4 | # SIM910 - | - = help: Replace `{}.get(key, None)` with `{}.get(key)` - -ℹ Safe fix -1 1 | # SIM910 -2 |-{}.get(key, None) - 2 |+{}.get(key) -3 3 | -4 4 | # SIM910 -5 5 | {}.get("key", None) - -SIM910.py:5:1: SIM910 [*] Use `{}.get("key")` instead of `{}.get("key", None)` - | -4 | # SIM910 -5 | {}.get("key", None) - | ^^^^^^^^^^^^^^^^^^^ SIM910 -6 | -7 | # OK - | - = help: Replace `{}.get("key", None)` with `{}.get("key")` - -ℹ Safe fix -2 2 | {}.get(key, None) -3 3 | -4 4 | # SIM910 -5 |-{}.get("key", None) - 5 |+{}.get("key") -6 6 | -7 7 | # OK -8 8 | {}.get(key) - -SIM910.py:20:9: SIM910 [*] Use `{}.get(key)` instead of `{}.get(key, None)` - | -19 | # SIM910 -20 | if a := {}.get(key, None): - | ^^^^^^^^^^^^^^^^^ SIM910 -21 | pass - | - = help: Replace `{}.get(key, None)` with `{}.get(key)` - -ℹ Safe fix -17 17 | {}.get("key", False) -18 18 | -19 19 | # SIM910 -20 |-if a := {}.get(key, None): - 20 |+if a := {}.get(key): -21 21 | pass -22 22 | -23 23 | # SIM910 - -SIM910.py:24:5: SIM910 [*] Use `{}.get(key)` instead of `{}.get(key, None)` - | -23 | # SIM910 -24 | a = {}.get(key, None) - | ^^^^^^^^^^^^^^^^^ SIM910 -25 | -26 | # SIM910 - | - = help: Replace `{}.get(key, None)` with `{}.get(key)` - -ℹ Safe fix -21 21 | pass -22 22 | -23 23 | # SIM910 -24 |-a = {}.get(key, None) - 24 |+a = {}.get(key) -25 25 | -26 26 | # SIM910 -27 27 | ({}).get(key, None) - -SIM910.py:27:1: SIM910 [*] Use `({}).get(key)` instead of `({}).get(key, None)` - | -26 | # SIM910 -27 | ({}).get(key, None) - | ^^^^^^^^^^^^^^^^^^^ SIM910 -28 | -29 | # SIM910 - | - = help: Replace `({}).get(key, None)` with `({}).get(key)` - -ℹ Safe fix -24 24 | a = {}.get(key, None) -25 25 | -26 26 | # SIM910 -27 |-({}).get(key, None) - 27 |+({}).get(key) -28 28 | -29 29 | # SIM910 -30 30 | ages = {"Tom": 23, "Maria": 23, "Dog": 11} - -SIM910.py:31:7: SIM910 [*] Use `ages.get("Cat")` instead of `ages.get("Cat", None)` - | -29 | # SIM910 -30 | ages = {"Tom": 23, "Maria": 23, "Dog": 11} -31 | age = ages.get("Cat", None) - | ^^^^^^^^^^^^^^^^^^^^^ SIM910 -32 | -33 | # OK - | - = help: Replace `ages.get("Cat", None)` with `ages.get("Cat")` - -ℹ Safe fix -28 28 | -29 29 | # SIM910 -30 30 | ages = {"Tom": 23, "Maria": 23, "Dog": 11} -31 |-age = ages.get("Cat", None) - 31 |+age = ages.get("Cat") -32 32 | -33 33 | # OK -34 34 | ages = ["Tom", "Maria", "Dog"] - - diff --git a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_api.rs b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_api.rs index 98ea3ab488887..fe3bdbada0a91 100644 --- a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_api.rs +++ b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_api.rs @@ -24,7 +24,7 @@ use crate::rules::flake8_tidy_imports::matchers::NameMatchPolicy; /// automatic way. /// /// ## Options -/// - `flake8-tidy-imports.banned-api` +/// - `lint.flake8-tidy-imports.banned-api` #[violation] pub struct BannedApi { name: String, diff --git a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_module_level_imports.rs b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_module_level_imports.rs index 61c0006544553..53b2601bea26e 100644 --- a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_module_level_imports.rs +++ b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_module_level_imports.rs @@ -38,7 +38,7 @@ use crate::rules::flake8_tidy_imports::matchers::NameMatchPolicy; /// ``` /// /// ## Options -/// - `flake8-tidy-imports.banned-module-level-imports` +/// - `lint.flake8-tidy-imports.banned-module-level-imports` #[violation] pub struct BannedModuleLevelImports { name: String, diff --git a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/relative_imports.rs b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/relative_imports.rs index 4f1c163774a8c..4a7420f9a67ad 100644 --- a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/relative_imports.rs +++ b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/relative_imports.rs @@ -42,7 +42,7 @@ use crate::rules::flake8_tidy_imports::settings::Strictness; /// ``` /// /// ## Options -/// - `flake8-tidy-imports.ban-relative-imports` +/// - `lint.flake8-tidy-imports.ban-relative-imports` /// /// [PEP 8]: https://peps.python.org/pep-0008/#imports #[violation] diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/mod.rs b/crates/ruff_linter/src/rules/flake8_type_checking/mod.rs index ef105e99ff29f..ed513d3236c6c 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/mod.rs @@ -35,8 +35,8 @@ mod tests { #[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_8.py"))] #[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("TCH004_9.py"))] #[test_case(Rule::RuntimeImportInTypeCheckingBlock, Path::new("quote.py"))] - #[test_case(Rule::RuntimeStringUnion, Path::new("TCH006_1.py"))] - #[test_case(Rule::RuntimeStringUnion, Path::new("TCH006_2.py"))] + #[test_case(Rule::RuntimeStringUnion, Path::new("TCH010_1.py"))] + #[test_case(Rule::RuntimeStringUnion, Path::new("TCH010_2.py"))] #[test_case(Rule::TypingOnlyFirstPartyImport, Path::new("TCH001.py"))] #[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("TCH003.py"))] #[test_case(Rule::TypingOnlyStandardLibraryImport, Path::new("init_var.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs b/crates/ruff_linter/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs index 2c15f1ff49911..b8558f89029b1 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs @@ -22,7 +22,7 @@ use crate::rules::flake8_type_checking::imports::ImportBinding; /// The type-checking block is not executed at runtime, so the import will not /// be available at runtime. /// -/// If [`flake8-type-checking.quote-annotations`] is set to `true`, +/// If [`lint.flake8-type-checking.quote-annotations`] is set to `true`, /// annotations will be wrapped in quotes if doing so would enable the /// corresponding import to remain in the type-checking block. /// @@ -48,7 +48,7 @@ use crate::rules::flake8_type_checking::imports::ImportBinding; /// ``` /// /// ## Options -/// - `flake8-type-checking.quote-annotations` +/// - `lint.flake8-type-checking.quote-annotations` /// /// ## References /// - [PEP 535](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking) diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs b/crates/ruff_linter/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs index 638799515c434..17812c89989cc 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs @@ -28,14 +28,14 @@ use crate::rules::isort::{categorize, ImportSection, ImportType}; /// instead be imported conditionally under an `if TYPE_CHECKING:` block to /// minimize runtime overhead. /// -/// If [`flake8-type-checking.quote-annotations`] is set to `true`, +/// If [`lint.flake8-type-checking.quote-annotations`] is set to `true`, /// annotations will be wrapped in quotes if doing so would enable the /// corresponding import to be moved into an `if TYPE_CHECKING:` block. /// /// If a class _requires_ that type annotations be available at runtime (as is /// the case for Pydantic, SQLAlchemy, and other libraries), consider using -/// the [`flake8-type-checking.runtime-evaluated-base-classes`] and -/// [`flake8-type-checking.runtime-evaluated-decorators`] settings to mark them +/// the [`lint.flake8-type-checking.runtime-evaluated-base-classes`] and +/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them /// as such. /// /// ## Example @@ -64,9 +64,9 @@ use crate::rules::isort::{categorize, ImportSection, ImportType}; /// ``` /// /// ## Options -/// - `flake8-type-checking.quote-annotations` -/// - `flake8-type-checking.runtime-evaluated-base-classes` -/// - `flake8-type-checking.runtime-evaluated-decorators` +/// - `lint.flake8-type-checking.quote-annotations` +/// - `lint.flake8-type-checking.runtime-evaluated-base-classes` +/// - `lint.flake8-type-checking.runtime-evaluated-decorators` /// /// ## References /// - [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking) @@ -101,14 +101,14 @@ impl Violation for TypingOnlyFirstPartyImport { /// instead be imported conditionally under an `if TYPE_CHECKING:` block to /// minimize runtime overhead. /// -/// If [`flake8-type-checking.quote-annotations`] is set to `true`, +/// If [`lint.flake8-type-checking.quote-annotations`] is set to `true`, /// annotations will be wrapped in quotes if doing so would enable the /// corresponding import to be moved into an `if TYPE_CHECKING:` block. /// /// If a class _requires_ that type annotations be available at runtime (as is /// the case for Pydantic, SQLAlchemy, and other libraries), consider using -/// the [`flake8-type-checking.runtime-evaluated-base-classes`] and -/// [`flake8-type-checking.runtime-evaluated-decorators`] settings to mark them +/// the [`lint.flake8-type-checking.runtime-evaluated-base-classes`] and +/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them /// as such. /// /// ## Example @@ -137,9 +137,9 @@ impl Violation for TypingOnlyFirstPartyImport { /// ``` /// /// ## Options -/// - `flake8-type-checking.quote-annotations` -/// - `flake8-type-checking.runtime-evaluated-base-classes` -/// - `flake8-type-checking.runtime-evaluated-decorators` +/// - `lint.flake8-type-checking.quote-annotations` +/// - `lint.flake8-type-checking.runtime-evaluated-base-classes` +/// - `lint.flake8-type-checking.runtime-evaluated-decorators` /// /// ## References /// - [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking) @@ -174,14 +174,14 @@ impl Violation for TypingOnlyThirdPartyImport { /// instead be imported conditionally under an `if TYPE_CHECKING:` block to /// minimize runtime overhead. /// -/// If [`flake8-type-checking.quote-annotations`] is set to `true`, +/// If [`lint.flake8-type-checking.quote-annotations`] is set to `true`, /// annotations will be wrapped in quotes if doing so would enable the /// corresponding import to be moved into an `if TYPE_CHECKING:` block. /// /// If a class _requires_ that type annotations be available at runtime (as is /// the case for Pydantic, SQLAlchemy, and other libraries), consider using -/// the [`flake8-type-checking.runtime-evaluated-base-classes`] and -/// [`flake8-type-checking.runtime-evaluated-decorators`] settings to mark them +/// the [`lint.flake8-type-checking.runtime-evaluated-base-classes`] and +/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them /// as such. /// /// ## Example @@ -210,9 +210,9 @@ impl Violation for TypingOnlyThirdPartyImport { /// ``` /// /// ## Options -/// - `flake8-type-checking.quote-annotations` -/// - `flake8-type-checking.runtime-evaluated-base-classes` -/// - `flake8-type-checking.runtime-evaluated-decorators` +/// - `lint.flake8-type-checking.quote-annotations` +/// - `lint.flake8-type-checking.runtime-evaluated-base-classes` +/// - `lint.flake8-type-checking.runtime-evaluated-decorators` /// /// ## References /// - [PEP 536](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking) diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH006_1.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH006_1.py.snap deleted file mode 100644 index 4ab8798d518c2..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH006_1.py.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs ---- -TCH006_1.py:18:30: TCH006 Invalid string member in `X | Y`-style union type - | -16 | type A = Value["int" | str] # OK -17 | -18 | OldS = TypeVar('OldS', int | 'str', str) # TCH006 - | ^^^^^ TCH006 - | - - diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH006_2.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH006_2.py.snap deleted file mode 100644 index 8a2023ed44189..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH006_2.py.snap +++ /dev/null @@ -1,41 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs ---- -TCH006_2.py:4:4: TCH006 Invalid string member in `X | Y`-style union type - | -4 | x: "int" | str # TCH006 - | ^^^^^ TCH006 -5 | x: ("int" | str) | "bool" # TCH006 - | - -TCH006_2.py:5:5: TCH006 Invalid string member in `X | Y`-style union type - | -4 | x: "int" | str # TCH006 -5 | x: ("int" | str) | "bool" # TCH006 - | ^^^^^ TCH006 - | - -TCH006_2.py:5:20: TCH006 Invalid string member in `X | Y`-style union type - | -4 | x: "int" | str # TCH006 -5 | x: ("int" | str) | "bool" # TCH006 - | ^^^^^^ TCH006 - | - -TCH006_2.py:12:20: TCH006 Invalid string member in `X | Y`-style union type - | -12 | z: list[str, str | "int"] = [] # TCH006 - | ^^^^^ TCH006 -13 | -14 | type A = Value["int" | str] # OK - | - -TCH006_2.py:16:30: TCH006 Invalid string member in `X | Y`-style union type - | -14 | type A = Value["int" | str] # OK -15 | -16 | OldS = TypeVar('OldS', int | 'str', str) # TCH006 - | ^^^^^ TCH006 - | - - diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH010_1.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH010_1.py.snap new file mode 100644 index 0000000000000..bf78da6c607ae --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH010_1.py.snap @@ -0,0 +1,12 @@ +--- +source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs +--- +TCH010_1.py:18:30: TCH010 Invalid string member in `X | Y`-style union type + | +16 | type A = Value["int" | str] # OK +17 | +18 | OldS = TypeVar('OldS', int | 'str', str) # TCH010 + | ^^^^^ TCH010 + | + + diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH010_2.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH010_2.py.snap new file mode 100644 index 0000000000000..f03037ad82933 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TCH010_2.py.snap @@ -0,0 +1,41 @@ +--- +source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs +--- +TCH010_2.py:4:4: TCH010 Invalid string member in `X | Y`-style union type + | +4 | x: "int" | str # TCH010 + | ^^^^^ TCH010 +5 | x: ("int" | str) | "bool" # TCH010 + | + +TCH010_2.py:5:5: TCH010 Invalid string member in `X | Y`-style union type + | +4 | x: "int" | str # TCH010 +5 | x: ("int" | str) | "bool" # TCH010 + | ^^^^^ TCH010 + | + +TCH010_2.py:5:20: TCH010 Invalid string member in `X | Y`-style union type + | +4 | x: "int" | str # TCH010 +5 | x: ("int" | str) | "bool" # TCH010 + | ^^^^^^ TCH010 + | + +TCH010_2.py:12:20: TCH010 Invalid string member in `X | Y`-style union type + | +12 | z: list[str, str | "int"] = [] # TCH010 + | ^^^^^ TCH010 +13 | +14 | type A = Value["int" | str] # OK + | + +TCH010_2.py:16:30: TCH010 Invalid string member in `X | Y`-style union type + | +14 | type A = Value["int" | str] # OK +15 | +16 | OldS = TypeVar('OldS', int | 'str', str) # TCH010 + | ^^^^^ TCH010 + | + + diff --git a/crates/ruff_linter/src/rules/mccabe/rules/function_is_too_complex.rs b/crates/ruff_linter/src/rules/mccabe/rules/function_is_too_complex.rs index 255a4eceade01..065ca04cf62c1 100644 --- a/crates/ruff_linter/src/rules/mccabe/rules/function_is_too_complex.rs +++ b/crates/ruff_linter/src/rules/mccabe/rules/function_is_too_complex.rs @@ -44,7 +44,7 @@ use ruff_python_ast::identifier::Identifier; /// ``` /// /// ## Options -/// - `mccabe.max-complexity` +/// - `lint.mccabe.max-complexity` #[violation] pub struct ComplexStructure { name: String, diff --git a/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs b/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs index cc7092c7f757e..3f362dc0fd9d6 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs @@ -22,10 +22,10 @@ use crate::checkers::ast::Checker; /// > append a single trailing underscore rather than use an abbreviation or spelling corruption. /// > Thus `class_` is better than `clss`. (Perhaps better is to avoid such clashes by using a synonym.) /// -/// Names can be excluded from this rule using the [`pep8-naming.ignore-names`] -/// or [`pep8-naming.extend-ignore-names`] configuration options. For example, +/// Names can be excluded from this rule using the [`lint.pep8-naming.ignore-names`] +/// or [`lint.pep8-naming.extend-ignore-names`] configuration options. For example, /// to allow the use of `klass` as the first argument to class methods, set -/// the [`pep8-naming.extend-ignore-names`] option to `["klass"]`. +/// the [`lint.pep8-naming.extend-ignore-names`] option to `["klass"]`. /// /// ## Example /// ```python @@ -44,10 +44,10 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `pep8-naming.classmethod-decorators` -/// - `pep8-naming.staticmethod-decorators` -/// - `pep8-naming.ignore-names` -/// - `pep8-naming.extend-ignore-names` +/// - `lint.pep8-naming.classmethod-decorators` +/// - `lint.pep8-naming.staticmethod-decorators` +/// - `lint.pep8-naming.ignore-names` +/// - `lint.pep8-naming.extend-ignore-names` /// /// [PEP 8]: https://peps.python.org/pep-0008/#function-and-method-arguments #[violation] diff --git a/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs b/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs index e2e83dcbf7381..c3c6322f1d4f3 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs @@ -22,10 +22,10 @@ use crate::checkers::ast::Checker; /// > append a single trailing underscore rather than use an abbreviation or spelling corruption. /// > Thus `class_` is better than `clss`. (Perhaps better is to avoid such clashes by using a synonym.) /// -/// Names can be excluded from this rule using the [`pep8-naming.ignore-names`] -/// or [`pep8-naming.extend-ignore-names`] configuration options. For example, +/// Names can be excluded from this rule using the [`lint.pep8-naming.ignore-names`] +/// or [`lint.pep8-naming.extend-ignore-names`] configuration options. For example, /// to allow the use of `this` as the first argument to instance methods, set -/// the [`pep8-naming.extend-ignore-names`] option to `["this"]`. +/// the [`lint.pep8-naming.extend-ignore-names`] option to `["this"]`. /// /// ## Example /// ```python @@ -42,10 +42,10 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `pep8-naming.classmethod-decorators` -/// - `pep8-naming.staticmethod-decorators` -/// - `pep8-naming.ignore-names` -/// - `pep8-naming.extend-ignore-names` +/// - `lint.pep8-naming.classmethod-decorators` +/// - `lint.pep8-naming.staticmethod-decorators` +/// - `lint.pep8-naming.ignore-names` +/// - `lint.pep8-naming.extend-ignore-names` /// /// [PEP 8]: https://peps.python.org/pep-0008/#function-and-method-arguments #[violation] diff --git a/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_function_name.rs b/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_function_name.rs index f38222a79061b..daac30bcd2141 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_function_name.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_function_name.rs @@ -20,10 +20,10 @@ use crate::settings::types::IdentifierPattern; /// > improve readability. mixedCase is allowed only in contexts where that’s already the /// > prevailing style (e.g. threading.py), to retain backwards compatibility. /// -/// Names can be excluded from this rule using the [`pep8-naming.ignore-names`] -/// or [`pep8-naming.extend-ignore-names`] configuration options. For example, +/// Names can be excluded from this rule using the [`lint.pep8-naming.ignore-names`] +/// or [`lint.pep8-naming.extend-ignore-names`] configuration options. For example, /// to ignore all functions starting with `test_` from this rule, set the -/// [`pep8-naming.extend-ignore-names`] option to `["test_*"]`. +/// [`lint.pep8-naming.extend-ignore-names`] option to `["test_*"]`. /// /// ## Example /// ```python @@ -38,8 +38,8 @@ use crate::settings::types::IdentifierPattern; /// ``` /// /// ## Options -/// - `pep8-naming.ignore-names` -/// - `pep8-naming.extend-ignore-names` +/// - `lint.pep8-naming.ignore-names` +/// - `lint.pep8-naming.extend-ignore-names` /// /// [PEP 8]: https://peps.python.org/pep-0008/#function-and-variable-names #[violation] diff --git a/crates/ruff_linter/src/rules/pep8_naming/rules/non_lowercase_variable_in_function.rs b/crates/ruff_linter/src/rules/pep8_naming/rules/non_lowercase_variable_in_function.rs index 3e073f343c9c9..b9bf944821b6f 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/rules/non_lowercase_variable_in_function.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/rules/non_lowercase_variable_in_function.rs @@ -34,8 +34,8 @@ use crate::rules::pep8_naming::helpers; /// ``` /// /// ## Options -/// - `pep8-naming.ignore-names` -/// - `pep8-naming.extend-ignore-names` +/// - `lint.pep8-naming.ignore-names` +/// - `lint.pep8-naming.extend-ignore-names` /// /// [PEP 8]: https://peps.python.org/pep-0008/#function-and-variable-names #[violation] diff --git a/crates/ruff_linter/src/rules/pycodestyle/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/mod.rs index 7cd5982fc9504..317993e97b65d 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/mod.rs @@ -68,8 +68,6 @@ mod tests { } #[test_case(Rule::IsLiteral, Path::new("constant_literals.py"))] - #[test_case(Rule::MultipleImportsOnOneLine, Path::new("E40.py"))] - #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_0.py"))] #[test_case(Rule::TypeComparison, Path::new("E721.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/doc_line_too_long.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/doc_line_too_long.rs index 99c7ba07030d4..f3c5fe4f252ff 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/doc_line_too_long.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/doc_line_too_long.rs @@ -13,7 +13,7 @@ use crate::settings::LinterSettings; /// For flowing long blocks of text (docstrings or comments), overlong lines /// can hurt readability. [PEP 8], for example, recommends that such lines be /// limited to 72 characters, while this rule enforces the limit specified by -/// the [`pycodestyle.max-doc-length`] setting. (If no value is provided, this +/// the [`lint.pycodestyle.max-doc-length`] setting. (If no value is provided, this /// rule will be ignored, even if it's added to your `--select` list.) /// /// In the context of this rule, a "doc line" is defined as a line consisting @@ -32,8 +32,8 @@ use crate::settings::LinterSettings; /// overlong if a pragma comment _causes_ it to exceed the line length. /// (This behavior aligns with that of the Ruff formatter.) /// -/// If [`pycodestyle.ignore-overlong-task-comments`] is `true`, this rule will -/// also ignore comments that start with any of the specified [`task-tags`] +/// If [`lint.pycodestyle.ignore-overlong-task-comments`] is `true`, this rule will +/// also ignore comments that start with any of the specified [`lint.task-tags`] /// (e.g., `# TODO:`). /// /// ## Example @@ -65,9 +65,9 @@ use crate::settings::LinterSettings; /// ``` /// /// ## Options -/// - `task-tags` -/// - `pycodestyle.max-doc-length` -/// - `pycodestyle.ignore-overlong-task-comments` +/// - `lint.task-tags` +/// - `lint.pycodestyle.max-doc-length` +/// - `lint.pycodestyle.ignore-overlong-task-comments` /// /// [PEP 8]: https://peps.python.org/pep-0008/#maximum-line-length #[violation] diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/line_too_long.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/line_too_long.rs index 8161ebb753a84..1d197516f074a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/line_too_long.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/line_too_long.rs @@ -28,8 +28,8 @@ use crate::settings::LinterSettings; /// overlong if a pragma comment _causes_ it to exceed the line length. /// (This behavior aligns with that of the Ruff formatter.) /// -/// If [`pycodestyle.ignore-overlong-task-comments`] is `true`, this rule will -/// also ignore comments that start with any of the specified [`task-tags`] +/// If [`lint.pycodestyle.ignore-overlong-task-comments`] is `true`, this rule will +/// also ignore comments that start with any of the specified [`lint.task-tags`] /// (e.g., `# TODO:`). /// /// ## Example @@ -60,9 +60,9 @@ use crate::settings::LinterSettings; /// /// ## Options /// - `line-length` -/// - `task-tags` -/// - `pycodestyle.ignore-overlong-task-comments` -/// - `pycodestyle.max-line-length` +/// - `lint.task-tags` +/// - `lint.pycodestyle.ignore-overlong-task-comments` +/// - `lint.pycodestyle.max-line-length` /// /// [PEP 8]: https://peps.python.org/pep-0008/#maximum-line-length #[violation] diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/module_import_not_at_top_of_file.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/module_import_not_at_top_of_file.rs index ff6c38bf0ce54..7e679809fc5f8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/module_import_not_at_top_of_file.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/module_import_not_at_top_of_file.rs @@ -13,9 +13,9 @@ use crate::checkers::ast::Checker; /// According to [PEP 8], "imports are always put at the top of the file, just after any /// module comments and docstrings, and before module globals and constants." /// -/// In [preview], this rule makes an exception for `sys.path` modifications, -/// allowing for `sys.path.insert`, `sys.path.append`, and similar -/// modifications between import statements. +/// This rule makes an exception for `sys.path` modifications, allowing for +/// `sys.path.insert`, `sys.path.append`, and similar modifications between import +/// statements. /// /// ## Example /// ```python @@ -37,7 +37,6 @@ use crate::checkers::ast::Checker; /// ``` /// /// [PEP 8]: https://peps.python.org/pep-0008/#imports -/// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] pub struct ModuleImportNotAtTopOfFile { source_type: PySourceType, diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/multiple_imports_on_one_line.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/multiple_imports_on_one_line.rs index 378f09277291f..e54715330cbe4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/multiple_imports_on_one_line.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/multiple_imports_on_one_line.rs @@ -49,15 +49,13 @@ impl Violation for MultipleImportsOnOneLine { pub(crate) fn multiple_imports_on_one_line(checker: &mut Checker, stmt: &Stmt, names: &[Alias]) { if names.len() > 1 { let mut diagnostic = Diagnostic::new(MultipleImportsOnOneLine, stmt.range()); - if checker.settings.preview.is_enabled() { - diagnostic.set_fix(split_imports( - stmt, - names, - checker.locator(), - checker.indexer(), - checker.stylist(), - )); - } + diagnostic.set_fix(split_imports( + stmt, + names, + checker.locator(), + checker.indexer(), + checker.stylist(), + )); checker.diagnostics.push(diagnostic); } } diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E401_E40.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E401_E40.py.snap index 6cf442f1bb096..165907675e5bc 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E401_E40.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E401_E40.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E40.py:2:1: E401 Multiple imports on one line +E40.py:2:1: E401 [*] Multiple imports on one line | 1 | #: E401 2 | import os, sys @@ -11,7 +11,16 @@ E40.py:2:1: E401 Multiple imports on one line | = help: Split imports -E40.py:65:1: E401 Multiple imports on one line +ℹ Safe fix +1 1 | #: E401 +2 |-import os, sys + 2 |+import os + 3 |+import sys +3 4 | +4 5 | #: Okay +5 6 | import os + +E40.py:65:1: E401 [*] Multiple imports on one line | 64 | #: E401 65 | import re as regex, string # also with a comment! @@ -20,7 +29,18 @@ E40.py:65:1: E401 Multiple imports on one line | = help: Split imports -E40.py:66:1: E401 Multiple imports on one line +ℹ Safe fix +62 62 | import bar +63 63 | +64 64 | #: E401 +65 |-import re as regex, string # also with a comment! + 65 |+import re as regex + 66 |+import string # also with a comment! +66 67 | import re as regex, string; x = 1 +67 68 | +68 69 | x = 1; import re as regex, string + +E40.py:66:1: E401 [*] Multiple imports on one line | 64 | #: E401 65 | import re as regex, string # also with a comment! @@ -31,7 +51,17 @@ E40.py:66:1: E401 Multiple imports on one line | = help: Split imports -E40.py:68:8: E401 Multiple imports on one line +ℹ Safe fix +63 63 | +64 64 | #: E401 +65 65 | import re as regex, string # also with a comment! +66 |-import re as regex, string; x = 1 + 66 |+import re as regex; import string; x = 1 +67 67 | +68 68 | x = 1; import re as regex, string +69 69 | + +E40.py:68:8: E401 [*] Multiple imports on one line | 66 | import re as regex, string; x = 1 67 | @@ -40,7 +70,17 @@ E40.py:68:8: E401 Multiple imports on one line | = help: Split imports -E40.py:72:5: E401 Multiple imports on one line +ℹ Safe fix +65 65 | import re as regex, string # also with a comment! +66 66 | import re as regex, string; x = 1 +67 67 | +68 |-x = 1; import re as regex, string + 68 |+x = 1; import re as regex; import string +69 69 | +70 70 | +71 71 | def blah(): + +E40.py:72:5: E401 [*] Multiple imports on one line | 71 | def blah(): 72 | import datetime as dt, copy @@ -50,7 +90,18 @@ E40.py:72:5: E401 Multiple imports on one line | = help: Split imports -E40.py:75:9: E401 Multiple imports on one line +ℹ Safe fix +69 69 | +70 70 | +71 71 | def blah(): +72 |- import datetime as dt, copy + 72 |+ import datetime as dt + 73 |+ import copy +73 74 | +74 75 | def nested_and_tested(): +75 76 | import builtins, textwrap as tw + +E40.py:75:9: E401 [*] Multiple imports on one line | 74 | def nested_and_tested(): 75 | import builtins, textwrap as tw @@ -60,7 +111,18 @@ E40.py:75:9: E401 Multiple imports on one line | = help: Split imports -E40.py:77:16: E401 Multiple imports on one line +ℹ Safe fix +72 72 | import datetime as dt, copy +73 73 | +74 74 | def nested_and_tested(): +75 |- import builtins, textwrap as tw + 75 |+ import builtins + 76 |+ import textwrap as tw +76 77 | +77 78 | x = 1; import re as regex, string +78 79 | import re as regex, string; x = 1 + +E40.py:77:16: E401 [*] Multiple imports on one line | 75 | import builtins, textwrap as tw 76 | @@ -70,7 +132,17 @@ E40.py:77:16: E401 Multiple imports on one line | = help: Split imports -E40.py:78:9: E401 Multiple imports on one line +ℹ Safe fix +74 74 | def nested_and_tested(): +75 75 | import builtins, textwrap as tw +76 76 | +77 |- x = 1; import re as regex, string + 77 |+ x = 1; import re as regex; import string +78 78 | import re as regex, string; x = 1 +79 79 | +80 80 | if True: import re as regex, string + +E40.py:78:9: E401 [*] Multiple imports on one line | 77 | x = 1; import re as regex, string 78 | import re as regex, string; x = 1 @@ -80,7 +152,16 @@ E40.py:78:9: E401 Multiple imports on one line | = help: Split imports -E40.py:80:14: E401 Multiple imports on one line +ℹ Safe fix +75 75 | import builtins, textwrap as tw +76 76 | +77 77 | x = 1; import re as regex, string +78 |- import re as regex, string; x = 1 + 78 |+ import re as regex; import string; x = 1 +79 79 | +80 80 | if True: import re as regex, string + +E40.py:80:14: E401 [*] Multiple imports on one line | 78 | import re as regex, string; x = 1 79 | @@ -89,4 +170,11 @@ E40.py:80:14: E401 Multiple imports on one line | = help: Split imports +ℹ Safe fix +77 77 | x = 1; import re as regex, string +78 78 | import re as regex, string; x = 1 +79 79 | +80 |- if True: import re as regex, string + 80 |+ if True: import re as regex; import string + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E402_E402_0.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E402_E402_0.py.snap index 29ab0d19c9a67..197644bcd690f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E402_E402_0.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E402_E402_0.py.snap @@ -1,36 +1,6 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E402_0.py:25:1: E402 Module level import not at top of file - | -23 | sys.path.insert(0, "some/path") -24 | -25 | import f - | ^^^^^^^^ E402 -26 | -27 | import matplotlib - | - -E402_0.py:27:1: E402 Module level import not at top of file - | -25 | import f -26 | -27 | import matplotlib - | ^^^^^^^^^^^^^^^^^ E402 -28 | -29 | matplotlib.use("Agg") - | - -E402_0.py:31:1: E402 Module level import not at top of file - | -29 | matplotlib.use("Agg") -30 | -31 | import g - | ^^^^^^^^ E402 -32 | -33 | __some__magic = 1 - | - E402_0.py:35:1: E402 Module level import not at top of file | 33 | __some__magic = 1 diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__E401_E40.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__E401_E40.py.snap deleted file mode 100644 index 165907675e5bc..0000000000000 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__E401_E40.py.snap +++ /dev/null @@ -1,180 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pycodestyle/mod.rs ---- -E40.py:2:1: E401 [*] Multiple imports on one line - | -1 | #: E401 -2 | import os, sys - | ^^^^^^^^^^^^^^ E401 -3 | -4 | #: Okay - | - = help: Split imports - -ℹ Safe fix -1 1 | #: E401 -2 |-import os, sys - 2 |+import os - 3 |+import sys -3 4 | -4 5 | #: Okay -5 6 | import os - -E40.py:65:1: E401 [*] Multiple imports on one line - | -64 | #: E401 -65 | import re as regex, string # also with a comment! - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ E401 -66 | import re as regex, string; x = 1 - | - = help: Split imports - -ℹ Safe fix -62 62 | import bar -63 63 | -64 64 | #: E401 -65 |-import re as regex, string # also with a comment! - 65 |+import re as regex - 66 |+import string # also with a comment! -66 67 | import re as regex, string; x = 1 -67 68 | -68 69 | x = 1; import re as regex, string - -E40.py:66:1: E401 [*] Multiple imports on one line - | -64 | #: E401 -65 | import re as regex, string # also with a comment! -66 | import re as regex, string; x = 1 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ E401 -67 | -68 | x = 1; import re as regex, string - | - = help: Split imports - -ℹ Safe fix -63 63 | -64 64 | #: E401 -65 65 | import re as regex, string # also with a comment! -66 |-import re as regex, string; x = 1 - 66 |+import re as regex; import string; x = 1 -67 67 | -68 68 | x = 1; import re as regex, string -69 69 | - -E40.py:68:8: E401 [*] Multiple imports on one line - | -66 | import re as regex, string; x = 1 -67 | -68 | x = 1; import re as regex, string - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ E401 - | - = help: Split imports - -ℹ Safe fix -65 65 | import re as regex, string # also with a comment! -66 66 | import re as regex, string; x = 1 -67 67 | -68 |-x = 1; import re as regex, string - 68 |+x = 1; import re as regex; import string -69 69 | -70 70 | -71 71 | def blah(): - -E40.py:72:5: E401 [*] Multiple imports on one line - | -71 | def blah(): -72 | import datetime as dt, copy - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ E401 -73 | -74 | def nested_and_tested(): - | - = help: Split imports - -ℹ Safe fix -69 69 | -70 70 | -71 71 | def blah(): -72 |- import datetime as dt, copy - 72 |+ import datetime as dt - 73 |+ import copy -73 74 | -74 75 | def nested_and_tested(): -75 76 | import builtins, textwrap as tw - -E40.py:75:9: E401 [*] Multiple imports on one line - | -74 | def nested_and_tested(): -75 | import builtins, textwrap as tw - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E401 -76 | -77 | x = 1; import re as regex, string - | - = help: Split imports - -ℹ Safe fix -72 72 | import datetime as dt, copy -73 73 | -74 74 | def nested_and_tested(): -75 |- import builtins, textwrap as tw - 75 |+ import builtins - 76 |+ import textwrap as tw -76 77 | -77 78 | x = 1; import re as regex, string -78 79 | import re as regex, string; x = 1 - -E40.py:77:16: E401 [*] Multiple imports on one line - | -75 | import builtins, textwrap as tw -76 | -77 | x = 1; import re as regex, string - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ E401 -78 | import re as regex, string; x = 1 - | - = help: Split imports - -ℹ Safe fix -74 74 | def nested_and_tested(): -75 75 | import builtins, textwrap as tw -76 76 | -77 |- x = 1; import re as regex, string - 77 |+ x = 1; import re as regex; import string -78 78 | import re as regex, string; x = 1 -79 79 | -80 80 | if True: import re as regex, string - -E40.py:78:9: E401 [*] Multiple imports on one line - | -77 | x = 1; import re as regex, string -78 | import re as regex, string; x = 1 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ E401 -79 | -80 | if True: import re as regex, string - | - = help: Split imports - -ℹ Safe fix -75 75 | import builtins, textwrap as tw -76 76 | -77 77 | x = 1; import re as regex, string -78 |- import re as regex, string; x = 1 - 78 |+ import re as regex; import string; x = 1 -79 79 | -80 80 | if True: import re as regex, string - -E40.py:80:14: E401 [*] Multiple imports on one line - | -78 | import re as regex, string; x = 1 -79 | -80 | if True: import re as regex, string - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ E401 - | - = help: Split imports - -ℹ Safe fix -77 77 | x = 1; import re as regex, string -78 78 | import re as regex, string; x = 1 -79 79 | -80 |- if True: import re as regex, string - 80 |+ if True: import re as regex; import string - - diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__E402_E402_0.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__E402_E402_0.py.snap deleted file mode 100644 index 197644bcd690f..0000000000000 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__E402_E402_0.py.snap +++ /dev/null @@ -1,28 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pycodestyle/mod.rs ---- -E402_0.py:35:1: E402 Module level import not at top of file - | -33 | __some__magic = 1 -34 | -35 | import h - | ^^^^^^^^ E402 - | - -E402_0.py:45:1: E402 Module level import not at top of file - | -43 | import j -44 | -45 | import k; import l - | ^^^^^^^^ E402 - | - -E402_0.py:45:11: E402 Module level import not at top of file - | -43 | import j -44 | -45 | import k; import l - | ^^^^^^^^ E402 - | - - diff --git a/crates/ruff_linter/src/rules/pydocstyle/mod.rs b/crates/ruff_linter/src/rules/pydocstyle/mod.rs index b4212ec2f8739..577df34dca02d 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/mod.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/mod.rs @@ -12,7 +12,7 @@ mod tests { use test_case::test_case; use crate::registry::Rule; - use crate::settings::types::PreviewMode; + use crate::test::test_path; use crate::{assert_messages, settings}; @@ -111,33 +111,6 @@ mod tests { Ok(()) } - #[test_case(Rule::TripleSingleQuotes, Path::new("D.py"))] - #[test_case(Rule::TripleSingleQuotes, Path::new("D300.py"))] - fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { - // Tests for rules with preview features - let snapshot = format!( - "preview__{}_{}", - rule_code.noqa_code(), - path.to_string_lossy() - ); - let diagnostics = test_path( - Path::new("pydocstyle").join(path).as_path(), - &settings::LinterSettings { - pydocstyle: Settings { - convention: None, - ignore_decorators: BTreeSet::from_iter(["functools.wraps".to_string()]), - property_decorators: BTreeSet::from_iter([ - "gi.repository.GObject.Property".to_string() - ]), - }, - preview: PreviewMode::Enabled, - ..settings::LinterSettings::for_rule(rule_code) - }, - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } - #[test] fn bom() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/blank_before_after_class.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/blank_before_after_class.rs index bd0a1a5c0753b..785de77578f8b 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/blank_before_after_class.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/blank_before_after_class.rs @@ -37,7 +37,7 @@ use crate::registry::Rule; /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// [D211]: https://docs.astral.sh/ruff/rules/blank-line-before-class #[violation] @@ -84,7 +84,7 @@ impl AlwaysFixableViolation for OneBlankLineBeforeClass { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -134,7 +134,7 @@ impl AlwaysFixableViolation for OneBlankLineAfterClass { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// [D203]: https://docs.astral.sh/ruff/rules/one-blank-line-before-class #[violation] diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/ends_with_period.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/ends_with_period.rs index 360a7fccaaffc..73c64b385057d 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/ends_with_period.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/ends_with_period.rs @@ -36,7 +36,7 @@ use crate::rules::pydocstyle::helpers::logical_line; /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/ends_with_punctuation.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/ends_with_punctuation.rs index df03f44cc0b1b..6f8cf9ef2f3b0 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/ends_with_punctuation.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/ends_with_punctuation.rs @@ -37,7 +37,7 @@ use crate::rules::pydocstyle::helpers::logical_line; /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/no_signature.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/no_signature.rs index b9e186c0929e9..bbd492cad10d2 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/no_signature.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/no_signature.rs @@ -32,7 +32,7 @@ use crate::docstrings::Docstring; /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/non_imperative_mood.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/non_imperative_mood.rs index ca6f210d0abd8..5d779ba1db6d9 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/non_imperative_mood.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/non_imperative_mood.rs @@ -43,7 +43,7 @@ static MOOD: Lazy = Lazy::new(Mood::new); /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs index 250838e0dd333..5724ab8e00af9 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs @@ -76,7 +76,7 @@ use crate::rules::pydocstyle::settings::Convention; /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -175,7 +175,7 @@ impl AlwaysFixableViolation for SectionNotOverIndented { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -253,7 +253,7 @@ impl AlwaysFixableViolation for SectionUnderlineNotOverIndented { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -350,7 +350,7 @@ impl AlwaysFixableViolation for CapitalizeSectionName { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -446,7 +446,7 @@ impl AlwaysFixableViolation for NewLineAfterSectionName { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -548,7 +548,7 @@ impl AlwaysFixableViolation for DashedUnderlineAfterSection { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -647,7 +647,7 @@ impl AlwaysFixableViolation for SectionUnderlineAfterName { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -741,7 +741,7 @@ impl AlwaysFixableViolation for SectionUnderlineMatchesSectionLength { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -835,7 +835,7 @@ impl AlwaysFixableViolation for NoBlankLineAfterSection { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -931,7 +931,7 @@ impl AlwaysFixableViolation for NoBlankLineBeforeSection { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -1021,7 +1021,7 @@ impl AlwaysFixableViolation for BlankLineAfterLastSection { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -1098,7 +1098,7 @@ impl Violation for EmptyDocstringSection { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -1180,7 +1180,7 @@ impl AlwaysFixableViolation for SectionNameEndsInColon { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) @@ -1264,7 +1264,7 @@ impl Violation for UndocumentedParam { /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/starts_with_this.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/starts_with_this.rs index 0cd38e1114c3a..2d9034a2b0991 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/starts_with_this.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/starts_with_this.rs @@ -33,7 +33,7 @@ use crate::rules::pydocstyle::helpers::normalize_word; /// ``` /// /// ## Options -/// - `pydocstyle.convention` +/// - `lint.pydocstyle.convention` /// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/triple_quotes.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/triple_quotes.rs index 773ee26fe76d4..1eed5a928c570 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/triple_quotes.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/triple_quotes.rs @@ -80,14 +80,12 @@ pub(crate) fn triple_quotes(checker: &mut Checker, docstring: &Docstring) { let mut diagnostic = Diagnostic::new(TripleSingleQuotes { expected_quote }, docstring.range()); - if checker.settings.preview.is_enabled() { - let body = docstring.body().as_str(); - if !body.ends_with('\'') { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - format!("{prefixes}'''{body}'''"), - docstring.range(), - ))); - } + let body = docstring.body().as_str(); + if !body.ends_with('\'') { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + format!("{prefixes}'''{body}'''"), + docstring.range(), + ))); } checker.diagnostics.push(diagnostic); @@ -98,14 +96,12 @@ pub(crate) fn triple_quotes(checker: &mut Checker, docstring: &Docstring) { let mut diagnostic = Diagnostic::new(TripleSingleQuotes { expected_quote }, docstring.range()); - if checker.settings.preview.is_enabled() { - let body = docstring.body().as_str(); - if !body.ends_with('"') { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - format!("{prefixes}\"\"\"{body}\"\"\""), - docstring.range(), - ))); - } + let body = docstring.body().as_str(); + if !body.ends_with('"') { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + format!("{prefixes}\"\"\"{body}\"\"\""), + docstring.range(), + ))); } checker.diagnostics.push(diagnostic); diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D300_D.py.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D300_D.py.snap index 6340613a91b53..7f14c7fe4c8ca 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D300_D.py.snap +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D300_D.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pydocstyle/mod.rs --- -D.py:307:5: D300 Use triple double quotes `"""` +D.py:307:5: D300 [*] Use triple double quotes `"""` | 305 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') 306 | def triple_single_quotes_raw(): @@ -10,7 +10,17 @@ D.py:307:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:312:5: D300 Use triple double quotes `"""` +ℹ Safe fix +304 304 | +305 305 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') +306 306 | def triple_single_quotes_raw(): +307 |- r'''Summary.''' + 307 |+ r"""Summary.""" +308 308 | +309 309 | +310 310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') + +D.py:312:5: D300 [*] Use triple double quotes `"""` | 310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') 311 | def triple_single_quotes_raw_uppercase(): @@ -19,7 +29,17 @@ D.py:312:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:317:5: D300 Use triple double quotes `"""` +ℹ Safe fix +309 309 | +310 310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') +311 311 | def triple_single_quotes_raw_uppercase(): +312 |- R'''Summary.''' + 312 |+ R"""Summary.""" +313 313 | +314 314 | +315 315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') + +D.py:317:5: D300 [*] Use triple double quotes `"""` | 315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') 316 | def single_quotes_raw(): @@ -28,7 +48,17 @@ D.py:317:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:322:5: D300 Use triple double quotes `"""` +ℹ Safe fix +314 314 | +315 315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') +316 316 | def single_quotes_raw(): +317 |- r'Summary.' + 317 |+ r"""Summary.""" +318 318 | +319 319 | +320 320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') + +D.py:322:5: D300 [*] Use triple double quotes `"""` | 320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') 321 | def single_quotes_raw_uppercase(): @@ -37,7 +67,17 @@ D.py:322:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:328:5: D300 Use triple double quotes `"""` +ℹ Safe fix +319 319 | +320 320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') +321 321 | def single_quotes_raw_uppercase(): +322 |- R'Summary.' + 322 |+ R"""Summary.""" +323 323 | +324 324 | +325 325 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') + +D.py:328:5: D300 [*] Use triple double quotes `"""` | 326 | @expect('D301: Use r""" if any backslashes in a docstring') 327 | def single_quotes_raw_uppercase_backslash(): @@ -46,7 +86,17 @@ D.py:328:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:645:5: D300 Use triple double quotes `"""` +ℹ Safe fix +325 325 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') +326 326 | @expect('D301: Use r""" if any backslashes in a docstring') +327 327 | def single_quotes_raw_uppercase_backslash(): +328 |- R'Sum\mary.' + 328 |+ R"""Sum\mary.""" +329 329 | +330 330 | +331 331 | @expect('D301: Use r""" if any backslashes in a docstring') + +D.py:645:5: D300 [*] Use triple double quotes `"""` | 644 | def single_line_docstring_with_an_escaped_backslash(): 645 | "\ @@ -58,7 +108,19 @@ D.py:645:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:649:5: D300 Use triple double quotes `"""` +ℹ Safe fix +642 642 | +643 643 | +644 644 | def single_line_docstring_with_an_escaped_backslash(): +645 |- "\ +646 |- " + 645 |+ """\ + 646 |+ """ +647 647 | +648 648 | class StatementOnSameLineAsDocstring: +649 649 | "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1 + +D.py:649:5: D300 [*] Use triple double quotes `"""` | 648 | class StatementOnSameLineAsDocstring: 649 | "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1 @@ -68,7 +130,17 @@ D.py:649:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:654:5: D300 Use triple double quotes `"""` +ℹ Safe fix +646 646 | " +647 647 | +648 648 | class StatementOnSameLineAsDocstring: +649 |- "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1 + 649 |+ """After this docstring there's another statement on the same line separated by a semicolon.""" ; priorities=1 +650 650 | def sort_services(self): +651 651 | pass +652 652 | + +D.py:654:5: D300 [*] Use triple double quotes `"""` | 653 | class StatementOnSameLineAsDocstring: 654 | "After this docstring there's another statement on the same line separated by a semicolon."; priorities=1 @@ -76,7 +148,17 @@ D.py:654:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:658:5: D300 Use triple double quotes `"""` +ℹ Safe fix +651 651 | pass +652 652 | +653 653 | class StatementOnSameLineAsDocstring: +654 |- "After this docstring there's another statement on the same line separated by a semicolon."; priorities=1 + 654 |+ """After this docstring there's another statement on the same line separated by a semicolon."""; priorities=1 +655 655 | +656 656 | +657 657 | class CommentAfterDocstring: + +D.py:658:5: D300 [*] Use triple double quotes `"""` | 657 | class CommentAfterDocstring: 658 | "After this docstring there's a comment." # priorities=1 @@ -86,7 +168,17 @@ D.py:658:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D.py:664:5: D300 Use triple double quotes `"""` +ℹ Safe fix +655 655 | +656 656 | +657 657 | class CommentAfterDocstring: +658 |- "After this docstring there's a comment." # priorities=1 + 658 |+ """After this docstring there's a comment.""" # priorities=1 +659 659 | def sort_services(self): +660 660 | pass +661 661 | + +D.py:664:5: D300 [*] Use triple double quotes `"""` | 663 | def newline_after_closing_quote(self): 664 | "We enforce a newline after the closing quote for a multi-line docstring \ @@ -96,4 +188,16 @@ D.py:664:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes +ℹ Safe fix +661 661 | +662 662 | +663 663 | def newline_after_closing_quote(self): +664 |- "We enforce a newline after the closing quote for a multi-line docstring \ +665 |- but continuations shouldn't be considered multi-line" + 664 |+ """We enforce a newline after the closing quote for a multi-line docstring \ + 665 |+ but continuations shouldn't be considered multi-line""" +666 666 | +667 667 | +668 668 | + diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D300_D300.py.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D300_D300.py.snap index 6f2b1e71b4683..2e3fedcf3d496 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D300_D300.py.snap +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D300_D300.py.snap @@ -9,7 +9,7 @@ D300.py:6:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes -D300.py:10:5: D300 Use triple double quotes `"""` +D300.py:10:5: D300 [*] Use triple double quotes `"""` | 9 | def contains_quote(): 10 | 'Sum"\\mary.' @@ -17,4 +17,14 @@ D300.py:10:5: D300 Use triple double quotes `"""` | = help: Convert to triple double quotes +ℹ Safe fix +7 7 | +8 8 | +9 9 | def contains_quote(): +10 |- 'Sum"\\mary.' + 10 |+ """Sum"\\mary.""" +11 11 | +12 12 | +13 13 | # OK + diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__bom.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__bom.snap index 10f9417ddf3e1..bf80e65243b0c 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__bom.snap +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__bom.snap @@ -1,11 +1,15 @@ --- source: crates/ruff_linter/src/rules/pydocstyle/mod.rs --- -bom.py:1:1: D300 Use triple double quotes `"""` +bom.py:1:1: D300 [*] Use triple double quotes `"""` | 1 | ''' SAM macro definitions ''' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300 | = help: Convert to triple double quotes +ℹ Safe fix +1 |-''' SAM macro definitions ''' + 1 |+""" SAM macro definitions """ + diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__preview__D300_D.py.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__preview__D300_D.py.snap deleted file mode 100644 index 7f14c7fe4c8ca..0000000000000 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__preview__D300_D.py.snap +++ /dev/null @@ -1,203 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pydocstyle/mod.rs ---- -D.py:307:5: D300 [*] Use triple double quotes `"""` - | -305 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') -306 | def triple_single_quotes_raw(): -307 | r'''Summary.''' - | ^^^^^^^^^^^^^^^ D300 - | - = help: Convert to triple double quotes - -ℹ Safe fix -304 304 | -305 305 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') -306 306 | def triple_single_quotes_raw(): -307 |- r'''Summary.''' - 307 |+ r"""Summary.""" -308 308 | -309 309 | -310 310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') - -D.py:312:5: D300 [*] Use triple double quotes `"""` - | -310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') -311 | def triple_single_quotes_raw_uppercase(): -312 | R'''Summary.''' - | ^^^^^^^^^^^^^^^ D300 - | - = help: Convert to triple double quotes - -ℹ Safe fix -309 309 | -310 310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)') -311 311 | def triple_single_quotes_raw_uppercase(): -312 |- R'''Summary.''' - 312 |+ R"""Summary.""" -313 313 | -314 314 | -315 315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') - -D.py:317:5: D300 [*] Use triple double quotes `"""` - | -315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') -316 | def single_quotes_raw(): -317 | r'Summary.' - | ^^^^^^^^^^^ D300 - | - = help: Convert to triple double quotes - -ℹ Safe fix -314 314 | -315 315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') -316 316 | def single_quotes_raw(): -317 |- r'Summary.' - 317 |+ r"""Summary.""" -318 318 | -319 319 | -320 320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') - -D.py:322:5: D300 [*] Use triple double quotes `"""` - | -320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') -321 | def single_quotes_raw_uppercase(): -322 | R'Summary.' - | ^^^^^^^^^^^ D300 - | - = help: Convert to triple double quotes - -ℹ Safe fix -319 319 | -320 320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') -321 321 | def single_quotes_raw_uppercase(): -322 |- R'Summary.' - 322 |+ R"""Summary.""" -323 323 | -324 324 | -325 325 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') - -D.py:328:5: D300 [*] Use triple double quotes `"""` - | -326 | @expect('D301: Use r""" if any backslashes in a docstring') -327 | def single_quotes_raw_uppercase_backslash(): -328 | R'Sum\mary.' - | ^^^^^^^^^^^^ D300 - | - = help: Convert to triple double quotes - -ℹ Safe fix -325 325 | @expect('D300: Use """triple double quotes""" (found \'-quotes)') -326 326 | @expect('D301: Use r""" if any backslashes in a docstring') -327 327 | def single_quotes_raw_uppercase_backslash(): -328 |- R'Sum\mary.' - 328 |+ R"""Sum\mary.""" -329 329 | -330 330 | -331 331 | @expect('D301: Use r""" if any backslashes in a docstring') - -D.py:645:5: D300 [*] Use triple double quotes `"""` - | -644 | def single_line_docstring_with_an_escaped_backslash(): -645 | "\ - | _____^ -646 | | " - | |_____^ D300 -647 | -648 | class StatementOnSameLineAsDocstring: - | - = help: Convert to triple double quotes - -ℹ Safe fix -642 642 | -643 643 | -644 644 | def single_line_docstring_with_an_escaped_backslash(): -645 |- "\ -646 |- " - 645 |+ """\ - 646 |+ """ -647 647 | -648 648 | class StatementOnSameLineAsDocstring: -649 649 | "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1 - -D.py:649:5: D300 [*] Use triple double quotes `"""` - | -648 | class StatementOnSameLineAsDocstring: -649 | "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300 -650 | def sort_services(self): -651 | pass - | - = help: Convert to triple double quotes - -ℹ Safe fix -646 646 | " -647 647 | -648 648 | class StatementOnSameLineAsDocstring: -649 |- "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1 - 649 |+ """After this docstring there's another statement on the same line separated by a semicolon.""" ; priorities=1 -650 650 | def sort_services(self): -651 651 | pass -652 652 | - -D.py:654:5: D300 [*] Use triple double quotes `"""` - | -653 | class StatementOnSameLineAsDocstring: -654 | "After this docstring there's another statement on the same line separated by a semicolon."; priorities=1 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300 - | - = help: Convert to triple double quotes - -ℹ Safe fix -651 651 | pass -652 652 | -653 653 | class StatementOnSameLineAsDocstring: -654 |- "After this docstring there's another statement on the same line separated by a semicolon."; priorities=1 - 654 |+ """After this docstring there's another statement on the same line separated by a semicolon."""; priorities=1 -655 655 | -656 656 | -657 657 | class CommentAfterDocstring: - -D.py:658:5: D300 [*] Use triple double quotes `"""` - | -657 | class CommentAfterDocstring: -658 | "After this docstring there's a comment." # priorities=1 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300 -659 | def sort_services(self): -660 | pass - | - = help: Convert to triple double quotes - -ℹ Safe fix -655 655 | -656 656 | -657 657 | class CommentAfterDocstring: -658 |- "After this docstring there's a comment." # priorities=1 - 658 |+ """After this docstring there's a comment.""" # priorities=1 -659 659 | def sort_services(self): -660 660 | pass -661 661 | - -D.py:664:5: D300 [*] Use triple double quotes `"""` - | -663 | def newline_after_closing_quote(self): -664 | "We enforce a newline after the closing quote for a multi-line docstring \ - | _____^ -665 | | but continuations shouldn't be considered multi-line" - | |_________________________________________________________^ D300 - | - = help: Convert to triple double quotes - -ℹ Safe fix -661 661 | -662 662 | -663 663 | def newline_after_closing_quote(self): -664 |- "We enforce a newline after the closing quote for a multi-line docstring \ -665 |- but continuations shouldn't be considered multi-line" - 664 |+ """We enforce a newline after the closing quote for a multi-line docstring \ - 665 |+ but continuations shouldn't be considered multi-line""" -666 666 | -667 667 | -668 668 | - - diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__preview__D300_D300.py.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__preview__D300_D300.py.snap deleted file mode 100644 index 2e3fedcf3d496..0000000000000 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__preview__D300_D300.py.snap +++ /dev/null @@ -1,30 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pydocstyle/mod.rs ---- -D300.py:6:5: D300 Use triple double quotes `"""` - | -5 | def ends_in_quote(): -6 | 'Sum\\mary."' - | ^^^^^^^^^^^^^ D300 - | - = help: Convert to triple double quotes - -D300.py:10:5: D300 [*] Use triple double quotes `"""` - | - 9 | def contains_quote(): -10 | 'Sum"\\mary.' - | ^^^^^^^^^^^^^ D300 - | - = help: Convert to triple double quotes - -ℹ Safe fix -7 7 | -8 8 | -9 9 | def contains_quote(): -10 |- 'Sum"\\mary.' - 10 |+ """Sum"\\mary.""" -11 11 | -12 12 | -13 13 | # OK - - diff --git a/crates/ruff_linter/src/rules/pyflakes/mod.rs b/crates/ruff_linter/src/rules/pyflakes/mod.rs index 13552252764b0..00cdfda23ee44 100644 --- a/crates/ruff_linter/src/rules/pyflakes/mod.rs +++ b/crates/ruff_linter/src/rules/pyflakes/mod.rs @@ -170,34 +170,6 @@ mod tests { Ok(()) } - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_0.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_1.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_10.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_11.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_12.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_13.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_14.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_15.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_16.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_17.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_18.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_19.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_2.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_20.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_21.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_22.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_23.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_24.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_25.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_26.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_27.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_3.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_4.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_5.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_6.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_7.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_8.py"))] - #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_9.py"))] #[test_case(Rule::UnusedVariable, Path::new("F841_4.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( diff --git a/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs b/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs index 158e45183ee5c..72b64491a63b4 100644 --- a/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs +++ b/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs @@ -54,7 +54,7 @@ enum UnusedImportContext { /// ``` /// /// ## Options -/// - `pyflakes.extend-generics` +/// - `lint.pyflakes.extend-generics` /// /// ## References /// - [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement) diff --git a/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs b/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs index e099c15f3110d..42d84b812e276 100644 --- a/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs +++ b/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs @@ -23,7 +23,7 @@ use crate::settings::types::PreviewMode; /// /// If a variable is intentionally defined-but-not-used, it should be /// prefixed with an underscore, or some other value that adheres to the -/// [`dummy-variable-rgx`] pattern. +/// [`lint.dummy-variable-rgx`] pattern. /// /// Under [preview mode](https://docs.astral.sh/ruff/preview), this rule also /// triggers on unused unpacked assignments (for example, `x, y = foo()`). @@ -44,7 +44,7 @@ use crate::settings::types::PreviewMode; /// ``` /// /// ## Options -/// - `dummy-variable-rgx` +/// - `lint.dummy-variable-rgx` #[violation] pub struct UnusedVariable { pub name: String, diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_1.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_1.py.snap index 3c5023ea35e37..b3d2ebd453667 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_1.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_1.py.snap @@ -1,11 +1,15 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F811_1.py:1:25: F811 Redefinition of unused `FU` from line 1 +F811_1.py:1:25: F811 [*] Redefinition of unused `FU` from line 1 | 1 | import fu as FU, bar as FU | ^^ F811 | = help: Remove definition: `FU` +ℹ Safe fix +1 |-import fu as FU, bar as FU + 1 |+import fu as FU + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_12.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_12.py.snap index ccc34ca245585..1411fddf047c7 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_12.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_12.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F811_12.py:6:20: F811 Redefinition of unused `mixer` from line 2 +F811_12.py:6:20: F811 [*] Redefinition of unused `mixer` from line 2 | 4 | pass 5 | else: @@ -11,4 +11,12 @@ F811_12.py:6:20: F811 Redefinition of unused `mixer` from line 2 | = help: Remove definition: `mixer` +ℹ Safe fix +3 3 | except ImportError: +4 4 | pass +5 5 | else: +6 |- from bb import mixer + 6 |+ pass +7 7 | mixer(123) + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_17.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_17.py.snap index d6a499c088e44..b2e62e8597fb6 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_17.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_17.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F811_17.py:6:12: F811 Redefinition of unused `fu` from line 2 +F811_17.py:6:12: F811 [*] Redefinition of unused `fu` from line 2 | 5 | def bar(): 6 | import fu @@ -11,6 +11,15 @@ F811_17.py:6:12: F811 Redefinition of unused `fu` from line 2 | = help: Remove definition: `fu` +ℹ Safe fix +3 3 | +4 4 | +5 5 | def bar(): +6 |- import fu +7 6 | +8 7 | def baz(): +9 8 | def fu(): + F811_17.py:9:13: F811 Redefinition of unused `fu` from line 6 | 8 | def baz(): diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_2.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_2.py.snap index 78c4a39f1cbed..9e87d29c8d81e 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_2.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_2.py.snap @@ -1,11 +1,15 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F811_2.py:1:34: F811 Redefinition of unused `FU` from line 1 +F811_2.py:1:34: F811 [*] Redefinition of unused `FU` from line 1 | 1 | from moo import fu as FU, bar as FU | ^^ F811 | = help: Remove definition: `FU` +ℹ Safe fix +1 |-from moo import fu as FU, bar as FU + 1 |+from moo import fu as FU + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_21.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_21.py.snap index 48525af0fd19a..6d0405c66f566 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_21.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_21.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F811_21.py:32:5: F811 Redefinition of unused `Sequence` from line 26 +F811_21.py:32:5: F811 [*] Redefinition of unused `Sequence` from line 26 | 30 | from typing import ( 31 | List, # noqa: F811 @@ -11,4 +11,15 @@ F811_21.py:32:5: F811 Redefinition of unused `Sequence` from line 26 | = help: Remove definition: `Sequence` +ℹ Safe fix +29 29 | # This should ignore the first error. +30 30 | from typing import ( +31 31 | List, # noqa: F811 +32 |- Sequence, +33 |-) + 32 |+ ) +34 33 | +35 34 | # This should ignore both errors. +36 35 | from typing import ( # noqa + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_23.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_23.py.snap index b4996ebf10b34..d03de5d32f98b 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_23.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_23.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F811_23.py:4:15: F811 Redefinition of unused `foo` from line 3 +F811_23.py:4:15: F811 [*] Redefinition of unused `foo` from line 3 | 3 | import foo as foo 4 | import bar as foo @@ -9,4 +9,10 @@ F811_23.py:4:15: F811 Redefinition of unused `foo` from line 3 | = help: Remove definition: `foo` +ℹ Safe fix +1 1 | """Test that shadowing an explicit re-export produces a warning.""" +2 2 | +3 3 | import foo as foo +4 |-import bar as foo + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_6.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_6.py.snap index a246b2275d0b5..92fc67263100e 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_6.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_6.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F811_6.py:6:12: F811 Redefinition of unused `os` from line 5 +F811_6.py:6:12: F811 [*] Redefinition of unused `os` from line 5 | 4 | if i == 1: 5 | import os @@ -11,4 +11,11 @@ F811_6.py:6:12: F811 Redefinition of unused `os` from line 5 | = help: Remove definition: `os` +ℹ Safe fix +3 3 | i = 2 +4 4 | if i == 1: +5 5 | import os +6 |- import os +7 6 | os.path + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_8.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_8.py.snap index 86799a2aaf7cc..07e8aeb1e6324 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_8.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_8.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F811_8.py:5:12: F811 Redefinition of unused `os` from line 4 +F811_8.py:5:12: F811 [*] Redefinition of unused `os` from line 4 | 3 | try: 4 | import os @@ -12,4 +12,13 @@ F811_8.py:5:12: F811 Redefinition of unused `os` from line 4 | = help: Remove definition: `os` +ℹ Safe fix +2 2 | +3 3 | try: +4 4 | import os +5 |- import os +6 5 | except: +7 6 | pass +8 7 | os.path + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__del_shadowed_global_import_in_local_scope.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__del_shadowed_global_import_in_local_scope.snap index 844c9a7cba6ff..a419b0ee52fdd 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__del_shadowed_global_import_in_local_scope.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__del_shadowed_global_import_in_local_scope.snap @@ -17,7 +17,7 @@ source: crates/ruff_linter/src/rules/pyflakes/mod.rs 4 3 | def f(): 5 4 | import os -:5:12: F811 Redefinition of unused `os` from line 2 +:5:12: F811 [*] Redefinition of unused `os` from line 2 | 4 | def f(): 5 | import os @@ -27,4 +27,13 @@ source: crates/ruff_linter/src/rules/pyflakes/mod.rs | = help: Remove definition: `os` +ℹ Safe fix +2 2 | import os +3 3 | +4 4 | def f(): +5 |- import os +6 5 | +7 6 | # Despite this `del`, `import os` in `f` should still be flagged as shadowing an unused +8 7 | # import. + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__del_shadowed_local_import_in_local_scope.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__del_shadowed_local_import_in_local_scope.snap index 97a806998dc7f..df130cc6e8937 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__del_shadowed_local_import_in_local_scope.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__del_shadowed_local_import_in_local_scope.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -:4:12: F811 Redefinition of unused `os` from line 3 +:4:12: F811 [*] Redefinition of unused `os` from line 3 | 2 | def f(): 3 | import os @@ -12,4 +12,13 @@ source: crates/ruff_linter/src/rules/pyflakes/mod.rs | = help: Remove definition: `os` +ℹ Safe fix +1 1 | +2 2 | def f(): +3 3 | import os +4 |- import os +5 4 | +6 5 | # Despite this `del`, `import os` should still be flagged as shadowing an unused +7 6 | # import. + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_0.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_0.py.snap deleted file mode 100644 index 009ba794b77b6..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_0.py.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_0.py:10:5: F811 Redefinition of unused `bar` from line 6 - | -10 | def bar(): - | ^^^ F811 -11 | pass - | - = help: Remove definition: `bar` - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_1.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_1.py.snap deleted file mode 100644 index b3d2ebd453667..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_1.py.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_1.py:1:25: F811 [*] Redefinition of unused `FU` from line 1 - | -1 | import fu as FU, bar as FU - | ^^ F811 - | - = help: Remove definition: `FU` - -ℹ Safe fix -1 |-import fu as FU, bar as FU - 1 |+import fu as FU - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_10.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_10.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_10.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_11.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_11.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_11.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_12.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_12.py.snap deleted file mode 100644 index 1411fddf047c7..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_12.py.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_12.py:6:20: F811 [*] Redefinition of unused `mixer` from line 2 - | -4 | pass -5 | else: -6 | from bb import mixer - | ^^^^^ F811 -7 | mixer(123) - | - = help: Remove definition: `mixer` - -ℹ Safe fix -3 3 | except ImportError: -4 4 | pass -5 5 | else: -6 |- from bb import mixer - 6 |+ pass -7 7 | mixer(123) - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_13.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_13.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_13.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_14.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_14.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_14.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_15.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_15.py.snap deleted file mode 100644 index 7f9b7cca18fee..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_15.py.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_15.py:4:5: F811 Redefinition of unused `fu` from line 1 - | -4 | def fu(): - | ^^ F811 -5 | pass - | - = help: Remove definition: `fu` - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_16.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_16.py.snap deleted file mode 100644 index ace7efc109b4e..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_16.py.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_16.py:8:13: F811 Redefinition of unused `fu` from line 3 - | -6 | def bar(): -7 | def baz(): -8 | def fu(): - | ^^ F811 -9 | pass - | - = help: Remove definition: `fu` - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_17.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_17.py.snap deleted file mode 100644 index b2e62e8597fb6..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_17.py.snap +++ /dev/null @@ -1,32 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_17.py:6:12: F811 [*] Redefinition of unused `fu` from line 2 - | -5 | def bar(): -6 | import fu - | ^^ F811 -7 | -8 | def baz(): - | - = help: Remove definition: `fu` - -ℹ Safe fix -3 3 | -4 4 | -5 5 | def bar(): -6 |- import fu -7 6 | -8 7 | def baz(): -9 8 | def fu(): - -F811_17.py:9:13: F811 Redefinition of unused `fu` from line 6 - | - 8 | def baz(): - 9 | def fu(): - | ^^ F811 -10 | pass - | - = help: Remove definition: `fu` - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_18.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_18.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_18.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_19.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_19.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_19.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_2.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_2.py.snap deleted file mode 100644 index 9e87d29c8d81e..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_2.py.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_2.py:1:34: F811 [*] Redefinition of unused `FU` from line 1 - | -1 | from moo import fu as FU, bar as FU - | ^^ F811 - | - = help: Remove definition: `FU` - -ℹ Safe fix -1 |-from moo import fu as FU, bar as FU - 1 |+from moo import fu as FU - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_20.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_20.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_20.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_21.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_21.py.snap deleted file mode 100644 index 6d0405c66f566..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_21.py.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_21.py:32:5: F811 [*] Redefinition of unused `Sequence` from line 26 - | -30 | from typing import ( -31 | List, # noqa: F811 -32 | Sequence, - | ^^^^^^^^ F811 -33 | ) - | - = help: Remove definition: `Sequence` - -ℹ Safe fix -29 29 | # This should ignore the first error. -30 30 | from typing import ( -31 31 | List, # noqa: F811 -32 |- Sequence, -33 |-) - 32 |+ ) -34 33 | -35 34 | # This should ignore both errors. -36 35 | from typing import ( # noqa - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_22.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_22.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_22.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_23.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_23.py.snap deleted file mode 100644 index d03de5d32f98b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_23.py.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_23.py:4:15: F811 [*] Redefinition of unused `foo` from line 3 - | -3 | import foo as foo -4 | import bar as foo - | ^^^ F811 - | - = help: Remove definition: `foo` - -ℹ Safe fix -1 1 | """Test that shadowing an explicit re-export produces a warning.""" -2 2 | -3 3 | import foo as foo -4 |-import bar as foo - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_24.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_24.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_24.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_25.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_25.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_25.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_26.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_26.py.snap deleted file mode 100644 index a51c892a97937..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_26.py.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_26.py:5:9: F811 Redefinition of unused `func` from line 2 - | -3 | pass -4 | -5 | def func(self): - | ^^^^ F811 -6 | pass - | - = help: Remove definition: `func` - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_27.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_27.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_27.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_3.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_3.py.snap deleted file mode 100644 index 7e749b088747a..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_3.py.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_3.py:1:12: F811 Redefinition of unused `fu` from line 1 - | -1 | import fu; fu = 3 - | ^^ F811 - | - = help: Remove definition: `fu` - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_4.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_4.py.snap deleted file mode 100644 index 0c7c2a4523d79..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_4.py.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_4.py:1:12: F811 Redefinition of unused `fu` from line 1 - | -1 | import fu; fu, bar = 3 - | ^^ F811 - | - = help: Remove definition: `fu` - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_5.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_5.py.snap deleted file mode 100644 index b21d2c3508948..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_5.py.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_5.py:1:13: F811 Redefinition of unused `fu` from line 1 - | -1 | import fu; [fu, bar] = 3 - | ^^ F811 - | - = help: Remove definition: `fu` - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_6.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_6.py.snap deleted file mode 100644 index 92fc67263100e..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_6.py.snap +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_6.py:6:12: F811 [*] Redefinition of unused `os` from line 5 - | -4 | if i == 1: -5 | import os -6 | import os - | ^^ F811 -7 | os.path - | - = help: Remove definition: `os` - -ℹ Safe fix -3 3 | i = 2 -4 4 | if i == 1: -5 5 | import os -6 |- import os -7 6 | os.path - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_7.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_7.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_7.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_8.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_8.py.snap deleted file mode 100644 index 07e8aeb1e6324..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_8.py.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- -F811_8.py:5:12: F811 [*] Redefinition of unused `os` from line 4 - | -3 | try: -4 | import os -5 | import os - | ^^ F811 -6 | except: -7 | pass - | - = help: Remove definition: `os` - -ℹ Safe fix -2 2 | -3 3 | try: -4 4 | import os -5 |- import os -6 5 | except: -7 6 | pass -8 7 | os.path - - diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_9.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_9.py.snap deleted file mode 100644 index d0b409f39ee0b..0000000000000 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F811_F811_9.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyflakes/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/mod.rs b/crates/ruff_linter/src/rules/pygrep_hooks/mod.rs index ef1e12b2b8f56..8cce061b151c3 100644 --- a/crates/ruff_linter/src/rules/pygrep_hooks/mod.rs +++ b/crates/ruff_linter/src/rules/pygrep_hooks/mod.rs @@ -9,15 +9,10 @@ mod tests { use test_case::test_case; use crate::registry::Rule; - use crate::settings::types::PreviewMode; - use crate::settings::LinterSettings; + use crate::test::test_path; use crate::{assert_messages, settings}; - #[test_case(Rule::Eval, Path::new("PGH001_0.py"))] - #[test_case(Rule::Eval, Path::new("PGH001_1.py"))] - #[test_case(Rule::DeprecatedLogWarn, Path::new("PGH002_0.py"))] - #[test_case(Rule::DeprecatedLogWarn, Path::new("PGH002_1.py"))] #[test_case(Rule::BlanketTypeIgnore, Path::new("PGH003_0.py"))] #[test_case(Rule::BlanketTypeIgnore, Path::new("PGH003_1.py"))] #[test_case(Rule::BlanketNOQA, Path::new("PGH004_0.py"))] @@ -31,22 +26,4 @@ mod tests { assert_messages!(snapshot, diagnostics); Ok(()) } - - #[test_case(Rule::DeprecatedLogWarn, Path::new("PGH002_1.py"))] - fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { - let snapshot = format!( - "preview__{}_{}", - rule_code.noqa_code(), - path.to_string_lossy() - ); - let diagnostics = test_path( - Path::new("pygrep_hooks").join(path).as_path(), - &LinterSettings { - preview: PreviewMode::Enabled, - ..LinterSettings::for_rule(rule_code) - }, - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } } diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs b/crates/ruff_linter/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs index 19f6df52f9986..ec9754414456b 100644 --- a/crates/ruff_linter/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs +++ b/crates/ruff_linter/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs @@ -1,13 +1,9 @@ -use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; +use ruff_diagnostics::{FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::{self as ast, Expr}; -use ruff_python_semantic::analyze::logging; -use ruff_python_stdlib::logging::LoggingLevel; -use ruff_text_size::Ranged; - -use crate::checkers::ast::Checker; -use crate::importer::ImportRequest; +/// ## Removed +/// This rule is identical to [G010] which should be used instead. +/// /// ## What it does /// Check for usages of the deprecated `warn` method from the `logging` module. /// @@ -34,9 +30,12 @@ use crate::importer::ImportRequest; /// /// ## References /// - [Python documentation: `logger.Logger.warning`](https://docs.python.org/3/library/logging.html#logging.Logger.warning) +/// +/// [G010]: https://docs.astral.sh/ruff/rules/logging-warn/ #[violation] pub struct DeprecatedLogWarn; +/// PGH002 impl Violation for DeprecatedLogWarn { const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; @@ -49,59 +48,3 @@ impl Violation for DeprecatedLogWarn { Some(format!("Replace with `warning`")) } } - -/// PGH002 -pub(crate) fn deprecated_log_warn(checker: &mut Checker, call: &ast::ExprCall) { - match call.func.as_ref() { - Expr::Attribute(ast::ExprAttribute { attr, .. }) => { - if !logging::is_logger_candidate( - &call.func, - checker.semantic(), - &checker.settings.logger_objects, - ) { - return; - } - if !matches!( - LoggingLevel::from_attribute(attr.as_str()), - Some(LoggingLevel::Warn) - ) { - return; - } - } - Expr::Name(_) => { - if !checker - .semantic() - .resolve_call_path(call.func.as_ref()) - .is_some_and(|call_path| matches!(call_path.as_slice(), ["logging", "warn"])) - { - return; - } - } - _ => return, - } - - let mut diagnostic = Diagnostic::new(DeprecatedLogWarn, call.func.range()); - if checker.settings.preview.is_enabled() { - match call.func.as_ref() { - Expr::Attribute(ast::ExprAttribute { attr, .. }) => { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - "warning".to_string(), - attr.range(), - ))); - } - Expr::Name(_) => { - diagnostic.try_set_fix(|| { - let (import_edit, binding) = checker.importer().get_or_import_symbol( - &ImportRequest::import("logging", "warning"), - call.start(), - checker.semantic(), - )?; - let name_edit = Edit::range_replacement(binding, call.func.range()); - Ok(Fix::safe_edits(import_edit, [name_edit])) - }); - } - _ => {} - } - } - checker.diagnostics.push(diagnostic); -} diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/rules/no_eval.rs b/crates/ruff_linter/src/rules/pygrep_hooks/rules/no_eval.rs index a9d49f7689fcc..4acfd491214b0 100644 --- a/crates/ruff_linter/src/rules/pygrep_hooks/rules/no_eval.rs +++ b/crates/ruff_linter/src/rules/pygrep_hooks/rules/no_eval.rs @@ -1,11 +1,9 @@ -use ruff_python_ast::{self as ast, Expr}; - -use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_diagnostics::Violation; use ruff_macros::{derive_message_formats, violation}; -use ruff_text_size::Ranged; - -use crate::checkers::ast::Checker; +/// ## Removed +/// This rule is identical to [S307] which should be used instead. +/// /// ## What it does /// Checks for uses of the builtin `eval()` function. /// @@ -29,28 +27,15 @@ use crate::checkers::ast::Checker; /// ## References /// - [Python documentation: `eval`](https://docs.python.org/3/library/functions.html#eval) /// - [_Eval really is dangerous_ by Ned Batchelder](https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html) +/// +/// [S307]: https://docs.astral.sh/ruff/rules/suspicious-eval-usage/ #[violation] pub struct Eval; +/// PGH001 impl Violation for Eval { #[derive_message_formats] fn message(&self) -> String { format!("No builtin `eval()` allowed") } } - -/// PGH001 -pub(crate) fn no_eval(checker: &mut Checker, func: &Expr) { - let Expr::Name(ast::ExprName { id, .. }) = func else { - return; - }; - if id != "eval" { - return; - } - if !checker.semantic().is_builtin("eval") { - return; - } - checker - .diagnostics - .push(Diagnostic::new(Eval, func.range())); -} diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH001_PGH001_0.py.snap b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH001_PGH001_0.py.snap deleted file mode 100644 index 8dad4d98ac34e..0000000000000 --- a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH001_PGH001_0.py.snap +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pygrep_hooks/mod.rs ---- -PGH001_0.py:3:1: PGH001 No builtin `eval()` allowed - | -1 | from ast import literal_eval -2 | -3 | eval("3 + 4") - | ^^^^ PGH001 -4 | -5 | literal_eval({1: 2}) - | - -PGH001_0.py:9:5: PGH001 No builtin `eval()` allowed - | -8 | def fn() -> None: -9 | eval("3 + 4") - | ^^^^ PGH001 - | - - diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH001_PGH001_1.py.snap b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH001_PGH001_1.py.snap deleted file mode 100644 index d5e81ab92031e..0000000000000 --- a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH001_PGH001_1.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pygrep_hooks/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_0.py.snap b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_0.py.snap deleted file mode 100644 index d5e81ab92031e..0000000000000 --- a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_0.py.snap +++ /dev/null @@ -1,4 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pygrep_hooks/mod.rs ---- - diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_1.py.snap b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_1.py.snap deleted file mode 100644 index 203a3fa5ee715..0000000000000 --- a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_1.py.snap +++ /dev/null @@ -1,32 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pygrep_hooks/mod.rs ---- -PGH002_1.py:4:1: PGH002 `warn` is deprecated in favor of `warning` - | -2 | from logging import warn -3 | -4 | logging.warn("this is not ok") - | ^^^^^^^^^^^^ PGH002 -5 | warn("not ok") - | - = help: Replace with `warning` - -PGH002_1.py:5:1: PGH002 `warn` is deprecated in favor of `warning` - | -4 | logging.warn("this is not ok") -5 | warn("not ok") - | ^^^^ PGH002 -6 | -7 | logger = logging.getLogger(__name__) - | - = help: Replace with `warning` - -PGH002_1.py:8:1: PGH002 `warn` is deprecated in favor of `warning` - | -7 | logger = logging.getLogger(__name__) -8 | logger.warn("this is not ok") - | ^^^^^^^^^^^ PGH002 - | - = help: Replace with `warning` - - diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__preview__PGH002_PGH002_1.py.snap b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__preview__PGH002_PGH002_1.py.snap deleted file mode 100644 index 6c1c5f1f712be..0000000000000 --- a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__preview__PGH002_PGH002_1.py.snap +++ /dev/null @@ -1,59 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pygrep_hooks/mod.rs ---- -PGH002_1.py:4:1: PGH002 [*] `warn` is deprecated in favor of `warning` - | -2 | from logging import warn -3 | -4 | logging.warn("this is not ok") - | ^^^^^^^^^^^^ PGH002 -5 | warn("not ok") - | - = help: Replace with `warning` - -ℹ Safe fix -1 1 | import logging -2 2 | from logging import warn -3 3 | -4 |-logging.warn("this is not ok") - 4 |+logging.warning("this is not ok") -5 5 | warn("not ok") -6 6 | -7 7 | logger = logging.getLogger(__name__) - -PGH002_1.py:5:1: PGH002 [*] `warn` is deprecated in favor of `warning` - | -4 | logging.warn("this is not ok") -5 | warn("not ok") - | ^^^^ PGH002 -6 | -7 | logger = logging.getLogger(__name__) - | - = help: Replace with `warning` - -ℹ Safe fix -2 2 | from logging import warn -3 3 | -4 4 | logging.warn("this is not ok") -5 |-warn("not ok") - 5 |+logging.warning("not ok") -6 6 | -7 7 | logger = logging.getLogger(__name__) -8 8 | logger.warn("this is not ok") - -PGH002_1.py:8:1: PGH002 [*] `warn` is deprecated in favor of `warning` - | -7 | logger = logging.getLogger(__name__) -8 | logger.warn("this is not ok") - | ^^^^^^^^^^^ PGH002 - | - = help: Replace with `warning` - -ℹ Safe fix -5 5 | warn("not ok") -6 6 | -7 7 | logger = logging.getLogger(__name__) -8 |-logger.warn("this is not ok") - 8 |+logger.warning("this is not ok") - - diff --git a/crates/ruff_linter/src/rules/pylint/mod.rs b/crates/ruff_linter/src/rules/pylint/mod.rs index 5948e9d15789c..518dd781459d5 100644 --- a/crates/ruff_linter/src/rules/pylint/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/mod.rs @@ -14,13 +14,12 @@ mod tests { use crate::registry::Rule; use crate::rules::pylint; - use crate::settings::types::PreviewMode; + + use crate::assert_messages; use crate::settings::types::PythonVersion; use crate::settings::LinterSettings; use crate::test::test_path; - use crate::{assert_messages, settings}; - #[test_case(Rule::AndOrTernary, Path::new("and_or_ternary.py"))] #[test_case(Rule::AssertOnStringLiteral, Path::new("assert_on_string_literal.py"))] #[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async.py"))] #[test_case(Rule::BadOpenMode, Path::new("bad_open_mode.py"))] @@ -194,25 +193,6 @@ mod tests { Ok(()) } - #[test_case(Rule::UselessElseOnLoop, Path::new("useless_else_on_loop.py"))] - #[test_case(Rule::CollapsibleElseIf, Path::new("collapsible_else_if.py"))] - fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { - let snapshot = format!( - "preview__{}_{}", - rule_code.noqa_code(), - path.to_string_lossy() - ); - let diagnostics = test_path( - Path::new("pylint").join(path).as_path(), - &settings::LinterSettings { - preview: PreviewMode::Enabled, - ..settings::LinterSettings::for_rule(rule_code) - }, - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } - #[test] fn repeated_isinstance_calls() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pylint/rules/and_or_ternary.rs b/crates/ruff_linter/src/rules/pylint/rules/and_or_ternary.rs index 00f2b5194d96b..c13ea44495764 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/and_or_ternary.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/and_or_ternary.rs @@ -1,13 +1,10 @@ -use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; -use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::{ - BoolOp, Expr, ExprBoolOp, ExprDictComp, ExprIfExp, ExprListComp, ExprSetComp, -}; -use ruff_text_size::{Ranged, TextRange}; - -use crate::checkers::ast::Checker; -use crate::fix::snippet::SourceCodeSnippet; +use ruff_diagnostics::Violation; +use ruff_macros::violation; +/// ## Removal +/// This rule was removed from Ruff because it was common for it to introduce behavioral changes. +/// See [#9007](https://github.com/astral-sh/ruff/issues/9007) for more information. +/// /// ## What it does /// Checks for uses of the known pre-Python 2.5 ternary syntax. /// @@ -31,100 +28,15 @@ use crate::fix::snippet::SourceCodeSnippet; /// maximum = x if x >= y else y /// ``` #[violation] -pub struct AndOrTernary { - ternary: SourceCodeSnippet, -} +pub struct AndOrTernary; +/// PLR1706 impl Violation for AndOrTernary { - const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; - - #[derive_message_formats] fn message(&self) -> String { - if let Some(ternary) = self.ternary.full_display() { - format!("Consider using if-else expression (`{ternary}`)") - } else { - format!("Consider using if-else expression") - } - } - - fn fix_title(&self) -> Option { - Some(format!("Convert to if-else expression")) - } -} - -/// Returns `Some((condition, true_value, false_value))`, if `bool_op` is of the form `condition and true_value or false_value`. -fn parse_and_or_ternary(bool_op: &ExprBoolOp) -> Option<(&Expr, &Expr, &Expr)> { - if bool_op.op != BoolOp::Or { - return None; + unreachable!("PLR1706 has been removed"); } - let [expr, false_value] = bool_op.values.as_slice() else { - return None; - }; - let Some(and_op) = expr.as_bool_op_expr() else { - return None; - }; - if and_op.op != BoolOp::And { - return None; - } - let [condition, true_value] = and_op.values.as_slice() else { - return None; - }; - if false_value.is_bool_op_expr() || true_value.is_bool_op_expr() { - return None; - } - Some((condition, true_value, false_value)) -} - -/// Returns `true` if the expression is used within a comprehension. -fn is_comprehension_if(parent: Option<&Expr>, expr: &ExprBoolOp) -> bool { - let comprehensions = match parent { - Some(Expr::ListComp(ExprListComp { generators, .. })) => generators, - Some(Expr::SetComp(ExprSetComp { generators, .. })) => generators, - Some(Expr::DictComp(ExprDictComp { generators, .. })) => generators, - _ => { - return false; - } - }; - comprehensions - .iter() - .any(|comp| comp.ifs.iter().any(|ifs| ifs.range() == expr.range())) -} -/// PLR1706 -pub(crate) fn and_or_ternary(checker: &mut Checker, bool_op: &ExprBoolOp) { - if checker.semantic().current_statement().is_if_stmt() { - return; - } - let parent_expr = checker.semantic().current_expression_parent(); - if parent_expr.is_some_and(Expr::is_bool_op_expr) { - return; + fn message_formats() -> &'static [&'static str] { + &["Consider using if-else expression"] } - let Some((condition, true_value, false_value)) = parse_and_or_ternary(bool_op) else { - return; - }; - - let if_expr = Expr::IfExp(ExprIfExp { - test: Box::new(condition.clone()), - body: Box::new(true_value.clone()), - orelse: Box::new(false_value.clone()), - range: TextRange::default(), - }); - - let ternary = if is_comprehension_if(parent_expr, bool_op) { - format!("({})", checker.generator().expr(&if_expr)) - } else { - checker.generator().expr(&if_expr) - }; - - let mut diagnostic = Diagnostic::new( - AndOrTernary { - ternary: SourceCodeSnippet::new(ternary.clone()), - }, - bool_op.range, - ); - diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( - ternary, - bool_op.range, - ))); - checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs b/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs index 5b42ba4e6ef16..828ba643546da 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs @@ -24,7 +24,7 @@ use crate::rules::pylint::helpers::is_known_dunder_method; /// `__init__`), as well as methods that are marked with `@override`. /// /// Additional dunder methods names can be allowed via the -/// [`pylint.allow-dunder-method-names`] setting. +/// [`lint.pylint.allow-dunder-method-names`] setting. /// /// ## Example /// ```python @@ -41,7 +41,7 @@ use crate::rules::pylint::helpers::is_known_dunder_method; /// ``` /// /// ## Options -/// - `pylint.allow-dunder-method-names` +/// - `lint.pylint.allow-dunder-method-names` #[violation] pub struct BadDunderMethodName { name: String, diff --git a/crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs b/crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs index 675e24351bfbb..fff3cf753eeed 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs @@ -84,13 +84,8 @@ pub(crate) fn collapsible_else_if(checker: &mut Checker, stmt: &Stmt) { CollapsibleElseIf, TextRange::new(else_clause.start(), first.start()), ); - - if checker.settings.preview.is_enabled() { - diagnostic.try_set_fix(|| { - convert_to_elif(first, else_clause, checker.locator(), checker.stylist()) - }); - } - + diagnostic + .try_set_fix(|| convert_to_elif(first, else_clause, checker.locator(), checker.stylist())); checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_arguments.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_arguments.rs index 84e5740f6d987..ea20fc78b449e 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_arguments.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_arguments.rs @@ -10,7 +10,7 @@ use crate::checkers::ast::Checker; /// Checks for function definitions that include too many arguments. /// /// By default, this rule allows up to five arguments, as configured by the -/// [`pylint.max-args`] option. +/// [`lint.pylint.max-args`] option. /// /// ## Why is this bad? /// Functions with many arguments are harder to understand, maintain, and call. @@ -42,7 +42,7 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `pylint.max-args` +/// - `lint.pylint.max-args` #[violation] pub struct TooManyArguments { c_args: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_boolean_expressions.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_boolean_expressions.rs index 33cead02fc3cf..152a335f99cf6 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_boolean_expressions.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_boolean_expressions.rs @@ -10,7 +10,7 @@ use crate::checkers::ast::Checker; /// Checks for too many Boolean expressions in an `if` statement. /// /// By default, this rule allows up to 5 expressions. This can be configured -/// using the [`pylint.max-bool-expr`] option. +/// using the [`lint.pylint.max-bool-expr`] option. /// /// ## Why is this bad? /// `if` statements with many Boolean expressions are harder to understand @@ -24,7 +24,7 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `pylint.max-bool-expr` +/// - `lint.pylint.max-bool-expr` #[violation] pub struct TooManyBooleanExpressions { expressions: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_branches.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_branches.rs index b966f8136347a..ca14e12dcc5e2 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_branches.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_branches.rs @@ -8,7 +8,7 @@ use ruff_python_ast::identifier::Identifier; /// Checks for functions or methods with too many branches. /// /// By default, this rule allows up to 12 branches. This can be configured -/// using the [`pylint.max-branches`] option. +/// using the [`lint.pylint.max-branches`] option. /// /// ## Why is this bad? /// Functions or methods with many branches are harder to understand @@ -67,7 +67,7 @@ use ruff_python_ast::identifier::Identifier; /// ``` /// /// ## Options -/// - `pylint.max-branches` +/// - `lint.pylint.max-branches` #[violation] pub struct TooManyBranches { branches: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_locals.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_locals.rs index ffdd7e34d283a..2f4a4c6d6ed0f 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_locals.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_locals.rs @@ -9,7 +9,7 @@ use crate::checkers::ast::Checker; /// Checks for functions that include too many local variables. /// /// By default, this rule allows up to fifteen locals, as configured by the -/// [`pylint.max-locals`] option. +/// [`lint.pylint.max-locals`] option. /// /// ## Why is this bad? /// Functions with many local variables are harder to understand and maintain. @@ -18,7 +18,7 @@ use crate::checkers::ast::Checker; /// functions with fewer assignments. /// /// ## Options -/// - `pylint.max-locals` +/// - `lint.pylint.max-locals` #[violation] pub struct TooManyLocals { current_amount: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_nested_blocks.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_nested_blocks.rs index bd641f0aa9205..3d406f047ffeb 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_nested_blocks.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_nested_blocks.rs @@ -10,14 +10,14 @@ use crate::checkers::ast::Checker; /// Checks for functions or methods with too many nested blocks. /// /// By default, this rule allows up to five nested blocks. -/// This can be configured using the [`pylint.max-nested-blocks`] option. +/// This can be configured using the [`lint.pylint.max-nested-blocks`] option. /// /// ## Why is this bad? /// Functions or methods with too many nested blocks are harder to understand /// and maintain. /// /// ## Options -/// - `pylint.max-nested-blocks` +/// - `lint.pylint.max-nested-blocks` #[violation] pub struct TooManyNestedBlocks { nested_blocks: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_positional.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_positional.rs index 2ca6f796151d8..0ec44c685512c 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_positional.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_positional.rs @@ -9,7 +9,7 @@ use crate::checkers::ast::Checker; /// Checks for function definitions that include too many positional arguments. /// /// By default, this rule allows up to five arguments, as configured by the -/// [`pylint.max-positional-args`] option. +/// [`lint.pylint.max-positional-args`] option. /// /// ## Why is this bad? /// Functions with many arguments are harder to understand, maintain, and call. @@ -40,7 +40,7 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `pylint.max-positional-args` +/// - `lint.pylint.max-positional-args` #[violation] pub struct TooManyPositional { c_pos: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_public_methods.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_public_methods.rs index 6679341a54f7a..c484c3ea0921a 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_public_methods.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_public_methods.rs @@ -10,7 +10,7 @@ use crate::checkers::ast::Checker; /// Checks for classes with too many public methods /// /// By default, this rule allows up to 20 public methods, as configured by -/// the [`pylint.max-public-methods`] option. +/// the [`lint.pylint.max-public-methods`] option. /// /// ## Why is this bad? /// Classes with many public methods are harder to understand @@ -81,7 +81,7 @@ use crate::checkers::ast::Checker; /// ``` /// /// ## Options -/// - `pylint.max-public-methods` +/// - `lint.pylint.max-public-methods` #[violation] pub struct TooManyPublicMethods { methods: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_return_statements.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_return_statements.rs index 4368ee5675e6a..39f573bb11fe7 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_return_statements.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_return_statements.rs @@ -10,7 +10,7 @@ use ruff_python_ast::visitor::Visitor; /// Checks for functions or methods with too many return statements. /// /// By default, this rule allows up to six return statements, as configured by -/// the [`pylint.max-returns`] option. +/// the [`lint.pylint.max-returns`] option. /// /// ## Why is this bad? /// Functions or methods with many return statements are harder to understand @@ -50,7 +50,7 @@ use ruff_python_ast::visitor::Visitor; /// ``` /// /// ## Options -/// - `pylint.max-returns` +/// - `lint.pylint.max-returns` #[violation] pub struct TooManyReturnStatements { returns: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_statements.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_statements.rs index 8529dd5ef7520..51167264c13cd 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_statements.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_statements.rs @@ -8,7 +8,7 @@ use ruff_python_ast::identifier::Identifier; /// Checks for functions or methods with too many statements. /// /// By default, this rule allows up to 50 statements, as configured by the -/// [`pylint.max-statements`] option. +/// [`lint.pylint.max-statements`] option. /// /// ## Why is this bad? /// Functions or methods with many statements are harder to understand @@ -44,7 +44,7 @@ use ruff_python_ast::identifier::Identifier; /// ``` /// /// ## Options -/// - `pylint.max-statements` +/// - `lint.pylint.max-statements` #[violation] pub struct TooManyStatements { statements: usize, diff --git a/crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs b/crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs index 24c9b9769b08b..e627ab49f35f4 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs @@ -75,19 +75,15 @@ pub(crate) fn useless_else_on_loop( let else_range = identifier::else_(stmt, checker.locator().contents()).expect("else clause"); let mut diagnostic = Diagnostic::new(UselessElseOnLoop, else_range); - - if checker.settings.preview.is_enabled() { - diagnostic.try_set_fix(|| { - remove_else( - stmt, - orelse, - else_range, - checker.locator(), - checker.stylist(), - ) - }); - } - + diagnostic.try_set_fix(|| { + remove_else( + stmt, + orelse, + else_range, + checker.locator(), + checker.stylist(), + ) + }); checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR1706_and_or_ternary.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR1706_and_or_ternary.py.snap deleted file mode 100644 index 1bade935083f2..0000000000000 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR1706_and_or_ternary.py.snap +++ /dev/null @@ -1,229 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pylint/mod.rs ---- -and_or_ternary.py:46:1: PLR1706 [*] Consider using if-else expression (`'a' if 1 < 2 else 'b'`) - | -44 | # Errors -45 | -46 | 1<2 and 'a' or 'b' - | ^^^^^^^^^^^^^^^^^^ PLR1706 -47 | -48 | (lambda x: x+1) and 'a' or 'b' - | - = help: Convert to if-else expression - -ℹ Unsafe fix -43 43 | -44 44 | # Errors -45 45 | -46 |-1<2 and 'a' or 'b' - 46 |+'a' if 1 < 2 else 'b' -47 47 | -48 48 | (lambda x: x+1) and 'a' or 'b' -49 49 | - -and_or_ternary.py:48:1: PLR1706 [*] Consider using if-else expression (`'a' if (lambda x: x + 1) else 'b'`) - | -46 | 1<2 and 'a' or 'b' -47 | -48 | (lambda x: x+1) and 'a' or 'b' - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706 -49 | -50 | 'a' and (lambda x: x+1) or 'orange' - | - = help: Convert to if-else expression - -ℹ Unsafe fix -45 45 | -46 46 | 1<2 and 'a' or 'b' -47 47 | -48 |-(lambda x: x+1) and 'a' or 'b' - 48 |+'a' if (lambda x: x + 1) else 'b' -49 49 | -50 50 | 'a' and (lambda x: x+1) or 'orange' -51 51 | - -and_or_ternary.py:50:1: PLR1706 [*] Consider using if-else expression (`(lambda x: x + 1) if 'a' else 'orange'`) - | -48 | (lambda x: x+1) and 'a' or 'b' -49 | -50 | 'a' and (lambda x: x+1) or 'orange' - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706 -51 | -52 | val = '#0000FF' - | - = help: Convert to if-else expression - -ℹ Unsafe fix -47 47 | -48 48 | (lambda x: x+1) and 'a' or 'b' -49 49 | -50 |-'a' and (lambda x: x+1) or 'orange' - 50 |+(lambda x: x + 1) if 'a' else 'orange' -51 51 | -52 52 | val = '#0000FF' -53 53 | (len(val) == 7 and val[0] == "#") or val in {'green'} - -and_or_ternary.py:53:1: PLR1706 [*] Consider using if-else expression - | -52 | val = '#0000FF' -53 | (len(val) == 7 and val[0] == "#") or val in {'green'} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706 -54 | -55 | marker = 'marker' - | - = help: Convert to if-else expression - -ℹ Unsafe fix -50 50 | 'a' and (lambda x: x+1) or 'orange' -51 51 | -52 52 | val = '#0000FF' -53 |-(len(val) == 7 and val[0] == "#") or val in {'green'} - 53 |+val[0] == '#' if len(val) == 7 else val in {'green'} -54 54 | -55 55 | marker = 'marker' -56 56 | isinstance(marker, dict) and 'field' in marker or marker in {} - -and_or_ternary.py:56:1: PLR1706 [*] Consider using if-else expression - | -55 | marker = 'marker' -56 | isinstance(marker, dict) and 'field' in marker or marker in {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706 -57 | -58 | def has_oranges(oranges, apples=None) -> bool: - | - = help: Convert to if-else expression - -ℹ Unsafe fix -53 53 | (len(val) == 7 and val[0] == "#") or val in {'green'} -54 54 | -55 55 | marker = 'marker' -56 |-isinstance(marker, dict) and 'field' in marker or marker in {} - 56 |+'field' in marker if isinstance(marker, dict) else marker in {} -57 57 | -58 58 | def has_oranges(oranges, apples=None) -> bool: -59 59 | return apples and False or oranges - -and_or_ternary.py:59:12: PLR1706 [*] Consider using if-else expression (`False if apples else oranges`) - | -58 | def has_oranges(oranges, apples=None) -> bool: -59 | return apples and False or oranges - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706 -60 | -61 | [x for x in l if a and b or c] - | - = help: Convert to if-else expression - -ℹ Unsafe fix -56 56 | isinstance(marker, dict) and 'field' in marker or marker in {} -57 57 | -58 58 | def has_oranges(oranges, apples=None) -> bool: -59 |- return apples and False or oranges - 59 |+ return False if apples else oranges -60 60 | -61 61 | [x for x in l if a and b or c] -62 62 | - -and_or_ternary.py:61:18: PLR1706 [*] Consider using if-else expression (`(b if a else c)`) - | -59 | return apples and False or oranges -60 | -61 | [x for x in l if a and b or c] - | ^^^^^^^^^^^^ PLR1706 -62 | -63 | {x: y for x in l if a and b or c} - | - = help: Convert to if-else expression - -ℹ Unsafe fix -58 58 | def has_oranges(oranges, apples=None) -> bool: -59 59 | return apples and False or oranges -60 60 | -61 |-[x for x in l if a and b or c] - 61 |+[x for x in l if (b if a else c)] -62 62 | -63 63 | {x: y for x in l if a and b or c} -64 64 | - -and_or_ternary.py:63:21: PLR1706 [*] Consider using if-else expression (`(b if a else c)`) - | -61 | [x for x in l if a and b or c] -62 | -63 | {x: y for x in l if a and b or c} - | ^^^^^^^^^^^^ PLR1706 -64 | -65 | {x for x in l if a and b or c} - | - = help: Convert to if-else expression - -ℹ Unsafe fix -60 60 | -61 61 | [x for x in l if a and b or c] -62 62 | -63 |-{x: y for x in l if a and b or c} - 63 |+{x: y for x in l if (b if a else c)} -64 64 | -65 65 | {x for x in l if a and b or c} -66 66 | - -and_or_ternary.py:65:18: PLR1706 [*] Consider using if-else expression (`(b if a else c)`) - | -63 | {x: y for x in l if a and b or c} -64 | -65 | {x for x in l if a and b or c} - | ^^^^^^^^^^^^ PLR1706 -66 | -67 | new_list = [ - | - = help: Convert to if-else expression - -ℹ Unsafe fix -62 62 | -63 63 | {x: y for x in l if a and b or c} -64 64 | -65 |-{x for x in l if a and b or c} - 65 |+{x for x in l if (b if a else c)} -66 66 | -67 67 | new_list = [ -68 68 | x - -and_or_ternary.py:70:8: PLR1706 [*] Consider using if-else expression (`(b if a else c)`) - | -68 | x -69 | for sublist in all_lists -70 | if a and b or c - | ^^^^^^^^^^^^ PLR1706 -71 | for x in sublist -72 | if (isinstance(operator, list) and x in operator) or x != operator - | - = help: Convert to if-else expression - -ℹ Unsafe fix -67 67 | new_list = [ -68 68 | x -69 69 | for sublist in all_lists -70 |- if a and b or c - 70 |+ if (b if a else c) -71 71 | for x in sublist -72 72 | if (isinstance(operator, list) and x in operator) or x != operator -73 73 | ] - -and_or_ternary.py:72:8: PLR1706 [*] Consider using if-else expression - | -70 | if a and b or c -71 | for x in sublist -72 | if (isinstance(operator, list) and x in operator) or x != operator - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1706 -73 | ] - | - = help: Convert to if-else expression - -ℹ Unsafe fix -69 69 | for sublist in all_lists -70 70 | if a and b or c -71 71 | for x in sublist -72 |- if (isinstance(operator, list) and x in operator) or x != operator - 72 |+ if (x in operator if isinstance(operator, list) else x != operator) -73 73 | ] - - diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR5501_collapsible_else_if.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR5501_collapsible_else_if.py.snap index b936ba56a2771..4d13e11126c9a 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR5501_collapsible_else_if.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR5501_collapsible_else_if.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -collapsible_else_if.py:37:5: PLR5501 Use `elif` instead of `else` then `if`, to reduce indentation +collapsible_else_if.py:37:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation | 35 | if 1: 36 | pass @@ -13,7 +13,20 @@ collapsible_else_if.py:37:5: PLR5501 Use `elif` instead of `else` then `if`, to | = help: Convert to `elif` -collapsible_else_if.py:45:5: PLR5501 Use `elif` instead of `else` then `if`, to reduce indentation +ℹ Safe fix +34 34 | def not_ok0(): +35 35 | if 1: +36 36 | pass +37 |- else: +38 |- if 2: +39 |- pass + 37 |+ elif 2: + 38 |+ pass +40 39 | +41 40 | +42 41 | def not_ok1(): + +collapsible_else_if.py:45:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation | 43 | if 1: 44 | pass @@ -26,7 +39,23 @@ collapsible_else_if.py:45:5: PLR5501 Use `elif` instead of `else` then `if`, to | = help: Convert to `elif` -collapsible_else_if.py:55:5: PLR5501 Use `elif` instead of `else` then `if`, to reduce indentation +ℹ Safe fix +42 42 | def not_ok1(): +43 43 | if 1: +44 44 | pass + 45 |+ elif 2: + 46 |+ pass +45 47 | else: +46 |- if 2: +47 |- pass +48 |- else: +49 |- pass + 48 |+ pass +50 49 | +51 50 | +52 51 | def not_ok1_with_comments(): + +collapsible_else_if.py:55:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation | 53 | if 1: 54 | pass @@ -40,7 +69,24 @@ collapsible_else_if.py:55:5: PLR5501 Use `elif` instead of `else` then `if`, to | = help: Convert to `elif` -collapsible_else_if.py:69:5: PLR5501 Use `elif` instead of `else` then `if`, to reduce indentation +ℹ Safe fix +52 52 | def not_ok1_with_comments(): +53 53 | if 1: +54 54 | pass + 55 |+ elif 2: + 56 |+ pass +55 57 | else: +56 |- # inner comment +57 |- if 2: +58 |- pass +59 |- else: +60 |- pass # final pass comment + 58 |+ pass # final pass comment +61 59 | +62 60 | +63 61 | # Regression test for https://github.com/apache/airflow/blob/f1e1cdcc3b2826e68ba133f350300b5065bbca33/airflow/models/dag.py#L1737 + +collapsible_else_if.py:69:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation | 67 | elif True: 68 | print(2) @@ -53,7 +99,23 @@ collapsible_else_if.py:69:5: PLR5501 Use `elif` instead of `else` then `if`, to | = help: Convert to `elif` -collapsible_else_if.py:79:5: PLR5501 Use `elif` instead of `else` then `if`, to reduce indentation +ℹ Safe fix +66 66 | print(1) +67 67 | elif True: +68 68 | print(2) + 69 |+ elif True: + 70 |+ print(3) +69 71 | else: +70 |- if True: +71 |- print(3) +72 |- else: +73 |- print(4) + 72 |+ print(4) +74 73 | +75 74 | +76 75 | def not_ok3(): + +collapsible_else_if.py:79:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation | 77 | if 1: 78 | pass @@ -65,7 +127,20 @@ collapsible_else_if.py:79:5: PLR5501 Use `elif` instead of `else` then `if`, to | = help: Convert to `elif` -collapsible_else_if.py:87:5: PLR5501 Use `elif` instead of `else` then `if`, to reduce indentation +ℹ Safe fix +76 76 | def not_ok3(): +77 77 | if 1: +78 78 | pass +79 |- else: +80 |- if 2: pass +81 |- else: pass + 79 |+ elif 2: pass + 80 |+ else: pass +82 81 | +83 82 | +84 83 | def not_ok4(): + +collapsible_else_if.py:87:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation | 85 | if 1: 86 | pass @@ -78,7 +153,21 @@ collapsible_else_if.py:87:5: PLR5501 Use `elif` instead of `else` then `if`, to | = help: Convert to `elif` -collapsible_else_if.py:96:5: PLR5501 Use `elif` instead of `else` then `if`, to reduce indentation +ℹ Safe fix +84 84 | def not_ok4(): +85 85 | if 1: +86 86 | pass + 87 |+ elif 2: pass +87 88 | else: +88 |- if 2: pass +89 |- else: +90 |- pass + 89 |+ pass +91 90 | +92 91 | +93 92 | def not_ok5(): + +collapsible_else_if.py:96:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation | 94 | if 1: 95 | pass @@ -91,4 +180,16 @@ collapsible_else_if.py:96:5: PLR5501 Use `elif` instead of `else` then `if`, to | = help: Convert to `elif` +ℹ Safe fix +93 93 | def not_ok5(): +94 94 | if 1: +95 95 | pass +96 |- else: +97 |- if 2: +98 |- pass +99 |- else: pass + 96 |+ elif 2: + 97 |+ pass + 98 |+ else: pass + diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0120_useless_else_on_loop.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0120_useless_else_on_loop.py.snap index 856658731629f..ea72439be9eb0 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0120_useless_else_on_loop.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0120_useless_else_on_loop.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -useless_else_on_loop.py:9:5: PLW0120 `else` clause on loop without a `break` statement; remove the `else` and dedent its contents +useless_else_on_loop.py:9:5: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents | 7 | if i % 2: 8 | return i @@ -12,7 +12,18 @@ useless_else_on_loop.py:9:5: PLW0120 `else` clause on loop without a `break` sta | = help: Remove `else` -useless_else_on_loop.py:18:5: PLW0120 `else` clause on loop without a `break` statement; remove the `else` and dedent its contents +ℹ Safe fix +6 6 | for i in range(10): +7 7 | if i % 2: +8 8 | return i +9 |- else: # [useless-else-on-loop] +10 |- print("math is broken") + 9 |+ print("math is broken") +11 10 | return None +12 11 | +13 12 | + +useless_else_on_loop.py:18:5: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents | 16 | while True: 17 | return 1 @@ -23,7 +34,18 @@ useless_else_on_loop.py:18:5: PLW0120 `else` clause on loop without a `break` st | = help: Remove `else` -useless_else_on_loop.py:30:1: PLW0120 `else` clause on loop without a `break` statement; remove the `else` and dedent its contents +ℹ Safe fix +15 15 | """else + return is not acceptable.""" +16 16 | while True: +17 17 | return 1 +18 |- else: # [useless-else-on-loop] +19 |- print("math is broken") + 18 |+ print("math is broken") +20 19 | return None +21 20 | +22 21 | + +useless_else_on_loop.py:30:1: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents | 28 | break 29 | @@ -33,7 +55,18 @@ useless_else_on_loop.py:30:1: PLW0120 `else` clause on loop without a `break` st | = help: Remove `else` -useless_else_on_loop.py:37:1: PLW0120 `else` clause on loop without a `break` statement; remove the `else` and dedent its contents +ℹ Safe fix +27 27 | for _ in range(10): +28 28 | break +29 29 | +30 |-else: # [useless-else-on-loop] +31 |- print("or else!") + 30 |+print("or else!") +32 31 | +33 32 | +34 33 | while True: + +useless_else_on_loop.py:37:1: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents | 35 | while False: 36 | break @@ -43,7 +76,18 @@ useless_else_on_loop.py:37:1: PLW0120 `else` clause on loop without a `break` st | = help: Remove `else` -useless_else_on_loop.py:42:1: PLW0120 `else` clause on loop without a `break` statement; remove the `else` and dedent its contents +ℹ Safe fix +34 34 | while True: +35 35 | while False: +36 36 | break +37 |-else: # [useless-else-on-loop] +38 |- print("or else!") + 37 |+print("or else!") +39 38 | +40 39 | for j in range(10): +41 40 | pass + +useless_else_on_loop.py:42:1: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents | 40 | for j in range(10): 41 | pass @@ -54,7 +98,22 @@ useless_else_on_loop.py:42:1: PLW0120 `else` clause on loop without a `break` st | = help: Remove `else` -useless_else_on_loop.py:88:5: PLW0120 `else` clause on loop without a `break` statement; remove the `else` and dedent its contents +ℹ Safe fix +39 39 | +40 40 | for j in range(10): +41 41 | pass +42 |-else: # [useless-else-on-loop] +43 |- print("fat chance") +44 |- for j in range(10): +45 |- break + 42 |+print("fat chance") + 43 |+for j in range(10): + 44 |+ break +46 45 | +47 46 | +48 47 | def test_return_for2(): + +useless_else_on_loop.py:88:5: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents | 86 | else: 87 | print("all right") @@ -65,7 +124,18 @@ useless_else_on_loop.py:88:5: PLW0120 `else` clause on loop without a `break` st | = help: Remove `else` -useless_else_on_loop.py:98:9: PLW0120 `else` clause on loop without a `break` statement; remove the `else` and dedent its contents +ℹ Safe fix +85 85 | break +86 86 | else: +87 87 | print("all right") +88 |- else: # [useless-else-on-loop] +89 |- return True + 88 |+ return True +90 89 | return False +91 90 | +92 91 | + +useless_else_on_loop.py:98:9: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents | 96 | for _ in range(3): 97 | pass @@ -76,7 +146,20 @@ useless_else_on_loop.py:98:9: PLW0120 `else` clause on loop without a `break` st | = help: Remove `else` -useless_else_on_loop.py:144:5: PLW0120 `else` clause on loop without a `break` statement; remove the `else` and dedent its contents +ℹ Safe fix +95 95 | for _ in range(10): +96 96 | for _ in range(3): +97 97 | pass +98 |- else: +99 |- if 1 < 2: # pylint: disable=comparison-of-constants +100 |- break + 98 |+ if 1 < 2: # pylint: disable=comparison-of-constants + 99 |+ break +101 100 | else: +102 101 | return True +103 102 | return False + +useless_else_on_loop.py:144:5: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents | 142 | for j in range(10): 143 | pass @@ -87,4 +170,18 @@ useless_else_on_loop.py:144:5: PLW0120 `else` clause on loop without a `break` s | = help: Remove `else` +ℹ Safe fix +141 141 | """Retain the comment within the `else` block""" +142 142 | for j in range(10): +143 143 | pass +144 |- else: +145 |- # [useless-else-on-loop] +146 |- print("fat chance") +147 |- for j in range(10): +148 |- break + 144 |+ # [useless-else-on-loop] + 145 |+ print("fat chance") + 146 |+ for j in range(10): + 147 |+ break + diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__preview__PLR5501_collapsible_else_if.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__preview__PLR5501_collapsible_else_if.py.snap deleted file mode 100644 index 4d13e11126c9a..0000000000000 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__preview__PLR5501_collapsible_else_if.py.snap +++ /dev/null @@ -1,195 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pylint/mod.rs ---- -collapsible_else_if.py:37:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation - | -35 | if 1: -36 | pass -37 | else: - | _____^ -38 | | if 2: - | |________^ PLR5501 -39 | pass - | - = help: Convert to `elif` - -ℹ Safe fix -34 34 | def not_ok0(): -35 35 | if 1: -36 36 | pass -37 |- else: -38 |- if 2: -39 |- pass - 37 |+ elif 2: - 38 |+ pass -40 39 | -41 40 | -42 41 | def not_ok1(): - -collapsible_else_if.py:45:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation - | -43 | if 1: -44 | pass -45 | else: - | _____^ -46 | | if 2: - | |________^ PLR5501 -47 | pass -48 | else: - | - = help: Convert to `elif` - -ℹ Safe fix -42 42 | def not_ok1(): -43 43 | if 1: -44 44 | pass - 45 |+ elif 2: - 46 |+ pass -45 47 | else: -46 |- if 2: -47 |- pass -48 |- else: -49 |- pass - 48 |+ pass -50 49 | -51 50 | -52 51 | def not_ok1_with_comments(): - -collapsible_else_if.py:55:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation - | -53 | if 1: -54 | pass -55 | else: - | _____^ -56 | | # inner comment -57 | | if 2: - | |________^ PLR5501 -58 | pass -59 | else: - | - = help: Convert to `elif` - -ℹ Safe fix -52 52 | def not_ok1_with_comments(): -53 53 | if 1: -54 54 | pass - 55 |+ elif 2: - 56 |+ pass -55 57 | else: -56 |- # inner comment -57 |- if 2: -58 |- pass -59 |- else: -60 |- pass # final pass comment - 58 |+ pass # final pass comment -61 59 | -62 60 | -63 61 | # Regression test for https://github.com/apache/airflow/blob/f1e1cdcc3b2826e68ba133f350300b5065bbca33/airflow/models/dag.py#L1737 - -collapsible_else_if.py:69:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation - | -67 | elif True: -68 | print(2) -69 | else: - | _____^ -70 | | if True: - | |________^ PLR5501 -71 | print(3) -72 | else: - | - = help: Convert to `elif` - -ℹ Safe fix -66 66 | print(1) -67 67 | elif True: -68 68 | print(2) - 69 |+ elif True: - 70 |+ print(3) -69 71 | else: -70 |- if True: -71 |- print(3) -72 |- else: -73 |- print(4) - 72 |+ print(4) -74 73 | -75 74 | -76 75 | def not_ok3(): - -collapsible_else_if.py:79:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation - | -77 | if 1: -78 | pass -79 | else: - | _____^ -80 | | if 2: pass - | |________^ PLR5501 -81 | else: pass - | - = help: Convert to `elif` - -ℹ Safe fix -76 76 | def not_ok3(): -77 77 | if 1: -78 78 | pass -79 |- else: -80 |- if 2: pass -81 |- else: pass - 79 |+ elif 2: pass - 80 |+ else: pass -82 81 | -83 82 | -84 83 | def not_ok4(): - -collapsible_else_if.py:87:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation - | -85 | if 1: -86 | pass -87 | else: - | _____^ -88 | | if 2: pass - | |________^ PLR5501 -89 | else: -90 | pass - | - = help: Convert to `elif` - -ℹ Safe fix -84 84 | def not_ok4(): -85 85 | if 1: -86 86 | pass - 87 |+ elif 2: pass -87 88 | else: -88 |- if 2: pass -89 |- else: -90 |- pass - 89 |+ pass -91 90 | -92 91 | -93 92 | def not_ok5(): - -collapsible_else_if.py:96:5: PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation - | -94 | if 1: -95 | pass -96 | else: - | _____^ -97 | | if 2: - | |________^ PLR5501 -98 | pass -99 | else: pass - | - = help: Convert to `elif` - -ℹ Safe fix -93 93 | def not_ok5(): -94 94 | if 1: -95 95 | pass -96 |- else: -97 |- if 2: -98 |- pass -99 |- else: pass - 96 |+ elif 2: - 97 |+ pass - 98 |+ else: pass - - diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__preview__PLW0120_useless_else_on_loop.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__preview__PLW0120_useless_else_on_loop.py.snap deleted file mode 100644 index ea72439be9eb0..0000000000000 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__preview__PLW0120_useless_else_on_loop.py.snap +++ /dev/null @@ -1,187 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pylint/mod.rs ---- -useless_else_on_loop.py:9:5: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents - | - 7 | if i % 2: - 8 | return i - 9 | else: # [useless-else-on-loop] - | ^^^^ PLW0120 -10 | print("math is broken") -11 | return None - | - = help: Remove `else` - -ℹ Safe fix -6 6 | for i in range(10): -7 7 | if i % 2: -8 8 | return i -9 |- else: # [useless-else-on-loop] -10 |- print("math is broken") - 9 |+ print("math is broken") -11 10 | return None -12 11 | -13 12 | - -useless_else_on_loop.py:18:5: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents - | -16 | while True: -17 | return 1 -18 | else: # [useless-else-on-loop] - | ^^^^ PLW0120 -19 | print("math is broken") -20 | return None - | - = help: Remove `else` - -ℹ Safe fix -15 15 | """else + return is not acceptable.""" -16 16 | while True: -17 17 | return 1 -18 |- else: # [useless-else-on-loop] -19 |- print("math is broken") - 18 |+ print("math is broken") -20 19 | return None -21 20 | -22 21 | - -useless_else_on_loop.py:30:1: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents - | -28 | break -29 | -30 | else: # [useless-else-on-loop] - | ^^^^ PLW0120 -31 | print("or else!") - | - = help: Remove `else` - -ℹ Safe fix -27 27 | for _ in range(10): -28 28 | break -29 29 | -30 |-else: # [useless-else-on-loop] -31 |- print("or else!") - 30 |+print("or else!") -32 31 | -33 32 | -34 33 | while True: - -useless_else_on_loop.py:37:1: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents - | -35 | while False: -36 | break -37 | else: # [useless-else-on-loop] - | ^^^^ PLW0120 -38 | print("or else!") - | - = help: Remove `else` - -ℹ Safe fix -34 34 | while True: -35 35 | while False: -36 36 | break -37 |-else: # [useless-else-on-loop] -38 |- print("or else!") - 37 |+print("or else!") -39 38 | -40 39 | for j in range(10): -41 40 | pass - -useless_else_on_loop.py:42:1: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents - | -40 | for j in range(10): -41 | pass -42 | else: # [useless-else-on-loop] - | ^^^^ PLW0120 -43 | print("fat chance") -44 | for j in range(10): - | - = help: Remove `else` - -ℹ Safe fix -39 39 | -40 40 | for j in range(10): -41 41 | pass -42 |-else: # [useless-else-on-loop] -43 |- print("fat chance") -44 |- for j in range(10): -45 |- break - 42 |+print("fat chance") - 43 |+for j in range(10): - 44 |+ break -46 45 | -47 46 | -48 47 | def test_return_for2(): - -useless_else_on_loop.py:88:5: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents - | -86 | else: -87 | print("all right") -88 | else: # [useless-else-on-loop] - | ^^^^ PLW0120 -89 | return True -90 | return False - | - = help: Remove `else` - -ℹ Safe fix -85 85 | break -86 86 | else: -87 87 | print("all right") -88 |- else: # [useless-else-on-loop] -89 |- return True - 88 |+ return True -90 89 | return False -91 90 | -92 91 | - -useless_else_on_loop.py:98:9: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents - | - 96 | for _ in range(3): - 97 | pass - 98 | else: - | ^^^^ PLW0120 - 99 | if 1 < 2: # pylint: disable=comparison-of-constants -100 | break - | - = help: Remove `else` - -ℹ Safe fix -95 95 | for _ in range(10): -96 96 | for _ in range(3): -97 97 | pass -98 |- else: -99 |- if 1 < 2: # pylint: disable=comparison-of-constants -100 |- break - 98 |+ if 1 < 2: # pylint: disable=comparison-of-constants - 99 |+ break -101 100 | else: -102 101 | return True -103 102 | return False - -useless_else_on_loop.py:144:5: PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents - | -142 | for j in range(10): -143 | pass -144 | else: - | ^^^^ PLW0120 -145 | # [useless-else-on-loop] -146 | print("fat chance") - | - = help: Remove `else` - -ℹ Safe fix -141 141 | """Retain the comment within the `else` block""" -142 142 | for j in range(10): -143 143 | pass -144 |- else: -145 |- # [useless-else-on-loop] -146 |- print("fat chance") -147 |- for j in range(10): -148 |- break - 144 |+ # [useless-else-on-loop] - 145 |+ print("fat chance") - 146 |+ for j in range(10): - 147 |+ break - - diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep585_annotation.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep585_annotation.rs index 36e10b0a1c1f6..c963d140cee27 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep585_annotation.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep585_annotation.rs @@ -30,7 +30,7 @@ use crate::settings::types::PythonVersion; /// `__future__` annotations are not evaluated at runtime. If your code relies /// on runtime type annotations (either directly or via a library like /// Pydantic), you can disable this behavior for Python versions prior to 3.9 -/// by setting [`pyupgrade.keep-runtime-typing`] to `true`. +/// by setting [`lint.pyupgrade.keep-runtime-typing`] to `true`. /// /// ## Example /// ```python @@ -51,7 +51,7 @@ use crate::settings::types::PythonVersion; /// /// ## Options /// - `target-version` -/// - `pyupgrade.keep-runtime-typing` +/// - `lint.pyupgrade.keep-runtime-typing` /// /// [PEP 585]: https://peps.python.org/pep-0585/ #[violation] @@ -98,14 +98,10 @@ pub(crate) fn use_pep585_annotation( if checker.semantic().is_builtin(name) { diagnostic.set_fix(Fix::applicable_edit( Edit::range_replacement((*name).to_string(), expr.range()), - if checker.settings.preview.is_enabled() { - if checker.settings.target_version >= PythonVersion::Py310 { - Applicability::Safe - } else { - Applicability::Unsafe - } - } else { + if checker.settings.target_version >= PythonVersion::Py310 { Applicability::Safe + } else { + Applicability::Unsafe }, )); } @@ -122,12 +118,8 @@ pub(crate) fn use_pep585_annotation( Ok(Fix::applicable_edits( import_edit, [reference_edit], - if checker.settings.preview.is_enabled() { - if checker.settings.target_version >= PythonVersion::Py310 { - Applicability::Safe - } else { - Applicability::Unsafe - } + if checker.settings.target_version >= PythonVersion::Py310 { + Applicability::Safe } else { Applicability::Unsafe }, diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs index d3ad5b7dea447..44057726b7ecb 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs @@ -23,7 +23,7 @@ use crate::settings::types::PythonVersion; /// `__future__` annotations are not evaluated at runtime. If your code relies /// on runtime type annotations (either directly or via a library like /// Pydantic), you can disable this behavior for Python versions prior to 3.10 -/// by setting [`pyupgrade.keep-runtime-typing`] to `true`. +/// by setting [`lint.pyupgrade.keep-runtime-typing`] to `true`. /// /// ## Example /// ```python @@ -46,7 +46,7 @@ use crate::settings::types::PythonVersion; /// /// ## Options /// - `target-version` -/// - `pyupgrade.keep-runtime-typing` +/// - `lint.pyupgrade.keep-runtime-typing` /// /// [PEP 604]: https://peps.python.org/pep-0604/ #[violation] @@ -78,12 +78,8 @@ pub(crate) fn use_pep604_annotation( && !checker.semantic().in_complex_string_type_definition() && is_allowed_value(slice); - let applicability = if checker.settings.preview.is_enabled() { - if checker.settings.target_version >= PythonVersion::Py310 { - Applicability::Safe - } else { - Applicability::Unsafe - } + let applicability = if checker.settings.target_version >= PythonVersion::Py310 { + Applicability::Safe } else { Applicability::Unsafe }; diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_0.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_0.py.snap index dc09fa42bc981..283b6f1b0c1a4 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_0.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_0.py.snap @@ -239,7 +239,7 @@ UP006_0.py:61:10: UP006 [*] Use `collections.deque` instead of `typing.Deque` fo | = help: Replace with `collections.deque` -ℹ Unsafe fix +ℹ Safe fix 20 20 | 21 21 | 22 22 | from typing import List as IList @@ -265,7 +265,7 @@ UP006_0.py:65:10: UP006 [*] Use `collections.defaultdict` instead of `typing.Def | = help: Replace with `collections.defaultdict` -ℹ Unsafe fix +ℹ Safe fix 20 20 | 21 21 | 22 22 | from typing import List as IList diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_1.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_1.py.snap index e0b8c7f4d5111..23d2e79f9a43f 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_1.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_1.py.snap @@ -9,7 +9,7 @@ UP006_1.py:9:10: UP006 [*] Use `collections.defaultdict` instead of `typing.Defa | = help: Replace with `collections.defaultdict` -ℹ Unsafe fix +ℹ Safe fix 6 6 | from collections import defaultdict 7 7 | 8 8 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_3.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_3.py.snap index e3d7598176b21..6e49fa82351ff 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_3.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_3.py.snap @@ -9,7 +9,7 @@ UP006_3.py:7:11: UP006 [*] Use `collections.defaultdict` instead of `typing.Defa | = help: Replace with `collections.defaultdict` -ℹ Unsafe fix +ℹ Safe fix 4 4 | from collections import defaultdict 5 5 | 6 6 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap index 77252939a755b..a607401c39adf 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap @@ -9,7 +9,7 @@ UP007.py:6:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 3 3 | from typing import Union 4 4 | 5 5 | @@ -27,7 +27,7 @@ UP007.py:10:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 7 7 | ... 8 8 | 9 9 | @@ -45,7 +45,7 @@ UP007.py:14:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 11 11 | ... 12 12 | 13 13 | @@ -63,7 +63,7 @@ UP007.py:14:26: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 11 11 | ... 12 12 | 13 13 | @@ -81,7 +81,7 @@ UP007.py:18:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 15 15 | ... 16 16 | 17 17 | @@ -99,7 +99,7 @@ UP007.py:22:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 19 19 | ... 20 20 | 21 21 | @@ -117,7 +117,7 @@ UP007.py:26:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 23 23 | ... 24 24 | 25 25 | @@ -135,7 +135,7 @@ UP007.py:30:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 27 27 | ... 28 28 | 29 29 | @@ -153,7 +153,7 @@ UP007.py:34:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 31 31 | ... 32 32 | 33 33 | @@ -171,7 +171,7 @@ UP007.py:38:11: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 35 35 | ... 36 36 | 37 37 | @@ -189,7 +189,7 @@ UP007.py:38:27: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 35 35 | ... 36 36 | 37 37 | @@ -207,7 +207,7 @@ UP007.py:42:11: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 39 39 | ... 40 40 | 41 41 | @@ -226,7 +226,7 @@ UP007.py:55:8: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 52 52 | 53 53 | 54 54 | def f() -> None: @@ -268,7 +268,7 @@ UP007.py:60:8: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 57 57 | 58 58 | x = Union[str, int] 59 59 | x = Union["str", "int"] @@ -287,7 +287,7 @@ UP007.py:61:8: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 58 58 | x = Union[str, int] 59 59 | x = Union["str", "int"] 60 60 | x: Union[str, int] @@ -382,7 +382,7 @@ UP007.py:102:28: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 99 99 | 100 100 | # Regression test for: https://github.com/astral-sh/ruff/issues/7131 101 101 | class ServiceRefOrValue: @@ -404,7 +404,7 @@ UP007.py:110:28: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 107 107 | 108 108 | # Regression test for: https://github.com/astral-sh/ruff/issues/7201 109 109 | class ServiceRefOrValue: @@ -423,7 +423,7 @@ UP007.py:120:10: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 117 117 | 118 118 | 119 119 | # Regression test for: https://github.com/astral-sh/ruff/issues/8609 diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_585_p37.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_585_p37.snap index 4ba308e14a5ef..71eba348b3fa9 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_585_p37.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_585_p37.snap @@ -10,7 +10,7 @@ future_annotations.py:34:18: UP006 [*] Use `list` instead of `List` for type ann | = help: Replace with `list` -ℹ Safe fix +ℹ Unsafe fix 31 31 | return cls(x=0, y=0) 32 32 | 33 33 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_py310.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_py310.snap index 39655b7cdf28d..386e009dfa9cb 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_py310.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_py310.snap @@ -10,7 +10,7 @@ future_annotations.py:40:4: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 37 37 | return y 38 38 | 39 39 | @@ -28,7 +28,7 @@ future_annotations.py:42:21: UP007 [*] Use `X | Y` for type annotations | = help: Convert to `X | Y` -ℹ Unsafe fix +ℹ Safe fix 39 39 | 40 40 | x: Optional[int] = None 41 41 | diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index 89bb231e986de..dbc805ff5dfd9 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -30,7 +30,6 @@ mod tests { #[test_case(Rule::MutableClassDefault, Path::new("RUF012.py"))] #[test_case(Rule::MutableDataclassDefault, Path::new("RUF008.py"))] #[test_case(Rule::PairwiseOverZipped, Path::new("RUF007.py"))] - #[test_case(Rule::StaticKeyDictComprehension, Path::new("RUF011.py"))] #[test_case( Rule::UnnecessaryIterableAllocationForFirstElement, Path::new("RUF015.py") diff --git a/crates/ruff_linter/src/rules/ruff/rules/ambiguous_unicode_character.rs b/crates/ruff_linter/src/rules/ruff/rules/ambiguous_unicode_character.rs index 085ebbdbb64c5..9f8e52fa0b829 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/ambiguous_unicode_character.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/ambiguous_unicode_character.rs @@ -28,7 +28,7 @@ use crate::settings::LinterSettings; /// spec recommends `GREEK CAPITAL LETTER OMEGA` over `OHM SIGN`. /// /// You can omit characters from being flagged as ambiguous via the -/// [`allowed-confusables`] setting. +/// [`lint.allowed-confusables`] setting. /// /// ## Example /// ```python @@ -41,7 +41,7 @@ use crate::settings::LinterSettings; /// ``` /// /// ## Options -/// - `allowed-confusables` +/// - `lint.allowed-confusables` /// /// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] @@ -94,7 +94,7 @@ impl Violation for AmbiguousUnicodeCharacterString { /// ``` /// /// ## Options -/// - `allowed-confusables` +/// - `lint.allowed-confusables` /// /// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] @@ -147,7 +147,7 @@ impl Violation for AmbiguousUnicodeCharacterDocstring { /// ``` /// /// ## Options -/// - `allowed-confusables` +/// - `lint.allowed-confusables` /// /// [preview]: https://docs.astral.sh/ruff/preview/ #[violation] diff --git a/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs b/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs index f8d5af0672698..5e23bcb9ab368 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs @@ -53,7 +53,7 @@ use crate::rules::ruff::rules::helpers::{ /// ``` /// /// ## Options -/// - `flake8-bugbear.extend-immutable-calls` +/// - `lint.flake8-bugbear.extend-immutable-calls` #[violation] pub struct FunctionCallInDataclassDefaultArgument { name: Option, diff --git a/crates/ruff_linter/src/rules/ruff/rules/mod.rs b/crates/ruff_linter/src/rules/ruff/rules/mod.rs index 4d245b5da844a..0122f2bd7b1e8 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mod.rs @@ -43,6 +43,7 @@ mod mutable_fromkeys_value; mod never_union; mod pairwise_over_zipped; mod parenthesize_logical_operators; +mod quadratic_list_summation; mod sequence_sorting; mod sort_dunder_all; mod sort_dunder_slots; @@ -60,5 +61,3 @@ pub(crate) enum Context { Docstring, Comment, } - -mod quadratic_list_summation; diff --git a/crates/ruff_linter/src/rules/ruff/rules/never_union.rs b/crates/ruff_linter/src/rules/ruff/rules/never_union.rs index 0258c454c2ce2..897a0e4855077 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/never_union.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/never_union.rs @@ -30,7 +30,7 @@ use crate::checkers::ast::Checker; /// ... /// ``` /// -/// ## Options +/// ## References /// - [Python documentation: `typing.Never`](https://docs.python.org/3/library/typing.html#typing.Never) /// - [Python documentation: `typing.NoReturn`](https://docs.python.org/3/library/typing.html#typing.NoReturn) #[violation] diff --git a/crates/ruff_linter/src/rules/ruff/rules/static_key_dict_comprehension.rs b/crates/ruff_linter/src/rules/ruff/rules/static_key_dict_comprehension.rs index 1da5864e0927d..1d2236c6737e7 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/static_key_dict_comprehension.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/static_key_dict_comprehension.rs @@ -1,15 +1,9 @@ -use rustc_hash::FxHashMap; - -use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_diagnostics::Violation; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::StoredNameFinder; -use ruff_python_ast::visitor::Visitor; -use ruff_python_ast::{self as ast, Expr}; -use ruff_text_size::Ranged; - -use crate::checkers::ast::Checker; -use crate::fix::snippet::SourceCodeSnippet; +/// ## Removed +/// This rule was implemented in `flake8-bugbear` and has been remapped to [B035] +/// /// ## What it does /// Checks for dictionary comprehensions that use a static key, like a string /// literal or a variable defined outside the comprehension. @@ -30,62 +24,14 @@ use crate::fix::snippet::SourceCodeSnippet; /// data = ["some", "Data"] /// {value: value.upper() for value in data} /// ``` +/// +/// [B035]: https://docs.astral.sh/ruff/rules/static-key-dict-comprehension/ #[violation] -pub struct StaticKeyDictComprehension { - key: SourceCodeSnippet, -} +pub struct RuffStaticKeyDictComprehension; -impl Violation for StaticKeyDictComprehension { +impl Violation for RuffStaticKeyDictComprehension { #[derive_message_formats] fn message(&self) -> String { - let StaticKeyDictComprehension { key } = self; - if let Some(key) = key.full_display() { - format!("Dictionary comprehension uses static key: `{key}`") - } else { - format!("Dictionary comprehension uses static key") - } - } -} - -/// RUF011 -pub(crate) fn static_key_dict_comprehension(checker: &mut Checker, dict_comp: &ast::ExprDictComp) { - // Collect the bound names in the comprehension's generators. - let names = { - let mut visitor = StoredNameFinder::default(); - for generator in &dict_comp.generators { - visitor.visit_comprehension(generator); - } - visitor.names - }; - - if is_constant(&dict_comp.key, &names) { - checker.diagnostics.push(Diagnostic::new( - StaticKeyDictComprehension { - key: SourceCodeSnippet::from_str(checker.locator().slice(dict_comp.key.as_ref())), - }, - dict_comp.key.range(), - )); - } -} - -/// Returns `true` if the given expression is a constant in the context of the dictionary -/// comprehension. -fn is_constant(key: &Expr, names: &FxHashMap<&str, &ast::ExprName>) -> bool { - match key { - Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().all(|elt| is_constant(elt, names)), - Expr::Name(ast::ExprName { id, .. }) => !names.contains_key(id.as_str()), - Expr::Attribute(ast::ExprAttribute { value, .. }) => is_constant(value, names), - Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => { - is_constant(value, names) && is_constant(slice, names) - } - Expr::BinOp(ast::ExprBinOp { left, right, .. }) => { - is_constant(left, names) && is_constant(right, names) - } - Expr::BoolOp(ast::ExprBoolOp { values, .. }) => { - values.iter().all(|value| is_constant(value, names)) - } - Expr::UnaryOp(ast::ExprUnaryOp { operand, .. }) => is_constant(operand, names), - expr if expr.is_literal_expr() => true, - _ => false, + format!("Dictionary comprehension uses static key") } } diff --git a/crates/ruff_linter/src/rules/ruff/rules/test_rules.rs b/crates/ruff_linter/src/rules/ruff/rules/test_rules.rs index 5a78526f663dc..d148dff835d95 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/test_rules.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/test_rules.rs @@ -39,6 +39,13 @@ pub(crate) const TEST_RULES: &[Rule] = &[ Rule::StableTestRuleDisplayOnlyFix, Rule::PreviewTestRule, Rule::NurseryTestRule, + Rule::DeprecatedTestRule, + Rule::AnotherDeprecatedTestRule, + Rule::RemovedTestRule, + Rule::AnotherRemovedTestRule, + Rule::RedirectedFromTestRule, + Rule::RedirectedToTestRule, + Rule::RedirectedFromPrefixTestRule, ]; pub(crate) trait TestRule { @@ -254,6 +261,7 @@ impl TestRule for PreviewTestRule { )) } } + /// ## What it does /// Fake rule for testing. /// @@ -289,3 +297,255 @@ impl TestRule for NurseryTestRule { )) } } + +/// ## What it does +/// Fake rule for testing. +/// +/// ## Why is this bad? +/// Tests must pass! +/// +/// ## Example +/// ```python +/// foo +/// ``` +/// +/// Use instead: +/// ```python +/// bar +/// ``` +#[violation] +pub struct DeprecatedTestRule; + +impl Violation for DeprecatedTestRule { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::None; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Hey this is a deprecated test rule.") + } +} + +impl TestRule for DeprecatedTestRule { + fn diagnostic(_locator: &Locator, _indexer: &Indexer) -> Option { + Some(Diagnostic::new( + DeprecatedTestRule, + ruff_text_size::TextRange::default(), + )) + } +} + +/// ## What it does +/// Fake rule for testing. +/// +/// ## Why is this bad? +/// Tests must pass! +/// +/// ## Example +/// ```python +/// foo +/// ``` +/// +/// Use instead: +/// ```python +/// bar +/// ``` +#[violation] +pub struct AnotherDeprecatedTestRule; + +impl Violation for AnotherDeprecatedTestRule { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::None; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Hey this is another deprecated test rule.") + } +} + +impl TestRule for AnotherDeprecatedTestRule { + fn diagnostic(_locator: &Locator, _indexer: &Indexer) -> Option { + Some(Diagnostic::new( + AnotherDeprecatedTestRule, + ruff_text_size::TextRange::default(), + )) + } +} + +/// ## What it does +/// Fake rule for testing. +/// +/// ## Why is this bad? +/// Tests must pass! +/// +/// ## Example +/// ```python +/// foo +/// ``` +/// +/// Use instead: +/// ```python +/// bar +/// ``` +#[violation] +pub struct RemovedTestRule; + +impl Violation for RemovedTestRule { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::None; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Hey this is a removed test rule.") + } +} + +impl TestRule for RemovedTestRule { + fn diagnostic(_locator: &Locator, _indexer: &Indexer) -> Option { + Some(Diagnostic::new( + RemovedTestRule, + ruff_text_size::TextRange::default(), + )) + } +} + +/// ## What it does +/// Fake rule for testing. +/// +/// ## Why is this bad? +/// Tests must pass! +/// +/// ## Example +/// ```python +/// foo +/// ``` +/// +/// Use instead: +/// ```python +/// bar +/// ``` +#[violation] +pub struct AnotherRemovedTestRule; + +impl Violation for AnotherRemovedTestRule { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::None; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Hey this is a another removed test rule.") + } +} + +impl TestRule for AnotherRemovedTestRule { + fn diagnostic(_locator: &Locator, _indexer: &Indexer) -> Option { + Some(Diagnostic::new( + AnotherRemovedTestRule, + ruff_text_size::TextRange::default(), + )) + } +} + +/// ## What it does +/// Fake rule for testing. +/// +/// ## Why is this bad? +/// Tests must pass! +/// +/// ## Example +/// ```python +/// foo +/// ``` +/// +/// Use instead: +/// ```python +/// bar +/// ``` +#[violation] +pub struct RedirectedFromTestRule; + +impl Violation for RedirectedFromTestRule { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::None; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Hey this is a test rule that was redirected to another.") + } +} + +impl TestRule for RedirectedFromTestRule { + fn diagnostic(_locator: &Locator, _indexer: &Indexer) -> Option { + Some(Diagnostic::new( + RedirectedFromTestRule, + ruff_text_size::TextRange::default(), + )) + } +} + +/// ## What it does +/// Fake rule for testing. +/// +/// ## Why is this bad? +/// Tests must pass! +/// +/// ## Example +/// ```python +/// foo +/// ``` +/// +/// Use instead: +/// ```python +/// bar +/// ``` +#[violation] +pub struct RedirectedToTestRule; + +impl Violation for RedirectedToTestRule { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::None; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Hey this is a test rule that was redirected from another.") + } +} + +impl TestRule for RedirectedToTestRule { + fn diagnostic(_locator: &Locator, _indexer: &Indexer) -> Option { + Some(Diagnostic::new( + RedirectedToTestRule, + ruff_text_size::TextRange::default(), + )) + } +} + +/// ## What it does +/// Fake rule for testing. +/// +/// ## Why is this bad? +/// Tests must pass! +/// +/// ## Example +/// ```python +/// foo +/// ``` +/// +/// Use instead: +/// ```python +/// bar +/// ``` +#[violation] +pub struct RedirectedFromPrefixTestRule; + +impl Violation for RedirectedFromPrefixTestRule { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::None; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Hey this is a test rule that was redirected to another by prefix.") + } +} + +impl TestRule for RedirectedFromPrefixTestRule { + fn diagnostic(_locator: &Locator, _indexer: &Indexer) -> Option { + Some(Diagnostic::new( + RedirectedFromPrefixTestRule, + ruff_text_size::TextRange::default(), + )) + } +} diff --git a/crates/ruff_linter/src/rules/ruff/rules/unused_noqa.rs b/crates/ruff_linter/src/rules/ruff/rules/unused_noqa.rs index 828c02a8c3b57..e80b227ed01ed 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/unused_noqa.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/unused_noqa.rs @@ -36,7 +36,7 @@ pub struct UnusedCodes { /// ``` /// /// ## Options -/// - `external` +/// - `lint.external` /// /// ## References /// - [Ruff error suppression](https://docs.astral.sh/ruff/linter/#error-suppression) diff --git a/crates/ruff_linter/src/rules/tryceratops/mod.rs b/crates/ruff_linter/src/rules/tryceratops/mod.rs index 7be5a106b495c..21eaa92962639 100644 --- a/crates/ruff_linter/src/rules/tryceratops/mod.rs +++ b/crates/ruff_linter/src/rules/tryceratops/mod.rs @@ -11,15 +11,13 @@ mod tests { use test_case::test_case; use crate::registry::Rule; - use crate::settings::types::PreviewMode; - use crate::settings::LinterSettings; + use crate::test::test_path; use crate::{assert_messages, settings}; #[test_case(Rule::RaiseVanillaClass, Path::new("TRY002.py"))] #[test_case(Rule::RaiseVanillaArgs, Path::new("TRY003.py"))] #[test_case(Rule::TypeCheckWithoutTypeError, Path::new("TRY004.py"))] - #[test_case(Rule::ReraiseNoCause, Path::new("TRY200.py"))] #[test_case(Rule::VerboseRaise, Path::new("TRY201.py"))] #[test_case(Rule::TryConsiderElse, Path::new("TRY300.py"))] #[test_case(Rule::RaiseWithinTry, Path::new("TRY301.py"))] @@ -35,22 +33,4 @@ mod tests { assert_messages!(snapshot, diagnostics); Ok(()) } - - #[test_case(Rule::ErrorInsteadOfException, Path::new("TRY400.py"))] - fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { - let snapshot = format!( - "preview__{}_{}", - rule_code.noqa_code(), - path.to_string_lossy() - ); - let diagnostics = test_path( - Path::new("tryceratops").join(path).as_path(), - &LinterSettings { - preview: PreviewMode::Enabled, - ..LinterSettings::for_rule(rule_code) - }, - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } } diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/error_instead_of_exception.rs b/crates/ruff_linter/src/rules/tryceratops/rules/error_instead_of_exception.rs index 71889aa915b19..de5dd2c47c237 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/error_instead_of_exception.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/error_instead_of_exception.rs @@ -81,11 +81,38 @@ pub(crate) fn error_instead_of_exception(checker: &mut Checker, handlers: &[Exce if matches!(logging_level, LoggingLevel::Error) { if exc_info(&expr.arguments, checker.semantic()).is_none() { let mut diagnostic = Diagnostic::new(ErrorInsteadOfException, expr.range()); - if checker.settings.preview.is_enabled() { - match expr.func.as_ref() { - Expr::Attribute(ast::ExprAttribute { attr, .. }) => { - diagnostic.set_fix(Fix::applicable_edit( - Edit::range_replacement("exception".to_string(), attr.range()), + + match expr.func.as_ref() { + Expr::Attribute(ast::ExprAttribute { attr, .. }) => { + diagnostic.set_fix(Fix::applicable_edit( + Edit::range_replacement("exception".to_string(), attr.range()), + // When run against `logging.error`, the fix is safe; otherwise, + // the object _may_ not be a logger. + if checker + .semantic() + .resolve_call_path(expr.func.as_ref()) + .is_some_and(|call_path| { + matches!(call_path.as_slice(), ["logging", "error"]) + }) + { + Applicability::Safe + } else { + Applicability::Unsafe + }, + )); + } + Expr::Name(_) => { + diagnostic.try_set_fix(|| { + let (import_edit, binding) = + checker.importer().get_or_import_symbol( + &ImportRequest::import("logging", "exception"), + expr.start(), + checker.semantic(), + )?; + let name_edit = Edit::range_replacement(binding, expr.func.range()); + Ok(Fix::applicable_edits( + import_edit, + [name_edit], // When run against `logging.error`, the fix is safe; otherwise, // the object _may_ not be a logger. if checker @@ -99,40 +126,12 @@ pub(crate) fn error_instead_of_exception(checker: &mut Checker, handlers: &[Exce } else { Applicability::Unsafe }, - )); - } - Expr::Name(_) => { - diagnostic.try_set_fix(|| { - let (import_edit, binding) = - checker.importer().get_or_import_symbol( - &ImportRequest::import("logging", "exception"), - expr.start(), - checker.semantic(), - )?; - let name_edit = - Edit::range_replacement(binding, expr.func.range()); - Ok(Fix::applicable_edits( - import_edit, - [name_edit], - // When run against `logging.error`, the fix is safe; otherwise, - // the object _may_ not be a logger. - if checker - .semantic() - .resolve_call_path(expr.func.as_ref()) - .is_some_and(|call_path| { - matches!(call_path.as_slice(), ["logging", "error"]) - }) - { - Applicability::Safe - } else { - Applicability::Unsafe - }, - )) - }); - } - _ => {} + )) + }); } + _ => {} } + checker.diagnostics.push(diagnostic); } } diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/reraise_no_cause.rs b/crates/ruff_linter/src/rules/tryceratops/rules/reraise_no_cause.rs index 523e97235e649..0de0ae4249d57 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/reraise_no_cause.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/reraise_no_cause.rs @@ -1,12 +1,9 @@ -use ruff_python_ast::{Expr, Stmt}; - -use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_diagnostics::Violation; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::RaiseStatementVisitor; -use ruff_python_ast::statement_visitor::StatementVisitor; - -use crate::checkers::ast::Checker; +/// ## Removed +/// This rule is identical to [B904] which should be used instead. +/// /// ## What it does /// Checks for exceptions that are re-raised without specifying the cause via /// the `from` keyword. @@ -22,7 +19,7 @@ use crate::checkers::ast::Checker; /// try: /// return 1 / n /// except ZeroDivisionError: -/// raise ValueError +/// raise ValueError() /// ``` /// /// Use instead: @@ -31,36 +28,20 @@ use crate::checkers::ast::Checker; /// try: /// return 1 / n /// except ZeroDivisionError as exc: -/// raise ValueError from exc +/// raise ValueError() from exc /// ``` /// /// ## References /// - [Python documentation: Exception context](https://docs.python.org/3/library/exceptions.html#exception-context) +/// +/// [B904]: https://docs.astral.sh/ruff/rules/raise-without-from-inside-except/ #[violation] pub struct ReraiseNoCause; +/// TRY200 impl Violation for ReraiseNoCause { #[derive_message_formats] fn message(&self) -> String { format!("Use `raise from` to specify exception cause") } } - -/// TRY200 -pub(crate) fn reraise_no_cause(checker: &mut Checker, body: &[Stmt]) { - let raises = { - let mut visitor = RaiseStatementVisitor::default(); - visitor.visit_body(body); - visitor.raises - }; - - for (range, exc, cause) in raises { - if cause.is_none() { - if exc.is_some_and(Expr::is_call_expr) { - checker - .diagnostics - .push(Diagnostic::new(ReraiseNoCause, range)); - } - } - } -} diff --git a/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__error-instead-of-exception_TRY400.py.snap b/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__error-instead-of-exception_TRY400.py.snap index d19d1d713403d..9171d46a3beb1 100644 --- a/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__error-instead-of-exception_TRY400.py.snap +++ b/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__error-instead-of-exception_TRY400.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/tryceratops/mod.rs --- -TRY400.py:16:9: TRY400 Use `logging.exception` instead of `logging.error` +TRY400.py:16:9: TRY400 [*] Use `logging.exception` instead of `logging.error` | 14 | a = 1 15 | except Exception: @@ -12,7 +12,17 @@ TRY400.py:16:9: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:19:13: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Safe fix +13 13 | try: +14 14 | a = 1 +15 15 | except Exception: +16 |- logging.error("Context message here") + 16 |+ logging.exception("Context message here") +17 17 | +18 18 | if True: +19 19 | logging.error("Context message here") + +TRY400.py:19:13: TRY400 [*] Use `logging.exception` instead of `logging.error` | 18 | if True: 19 | logging.error("Context message here") @@ -20,7 +30,17 @@ TRY400.py:19:13: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:26:9: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Safe fix +16 16 | logging.error("Context message here") +17 17 | +18 18 | if True: +19 |- logging.error("Context message here") + 19 |+ logging.exception("Context message here") +20 20 | +21 21 | +22 22 | def bad(): + +TRY400.py:26:9: TRY400 [*] Use `logging.exception` instead of `logging.error` | 24 | a = 1 25 | except Exception: @@ -31,7 +51,17 @@ TRY400.py:26:9: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:29:13: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Unsafe fix +23 23 | try: +24 24 | a = 1 +25 25 | except Exception: +26 |- logger.error("Context message here") + 26 |+ logger.exception("Context message here") +27 27 | +28 28 | if True: +29 29 | logger.error("Context message here") + +TRY400.py:29:13: TRY400 [*] Use `logging.exception` instead of `logging.error` | 28 | if True: 29 | logger.error("Context message here") @@ -39,7 +69,17 @@ TRY400.py:29:13: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:36:9: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Unsafe fix +26 26 | logger.error("Context message here") +27 27 | +28 28 | if True: +29 |- logger.error("Context message here") + 29 |+ logger.exception("Context message here") +30 30 | +31 31 | +32 32 | def bad(): + +TRY400.py:36:9: TRY400 [*] Use `logging.exception` instead of `logging.error` | 34 | a = 1 35 | except Exception: @@ -50,7 +90,17 @@ TRY400.py:36:9: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:39:13: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Unsafe fix +33 33 | try: +34 34 | a = 1 +35 35 | except Exception: +36 |- log.error("Context message here") + 36 |+ log.exception("Context message here") +37 37 | +38 38 | if True: +39 39 | log.error("Context message here") + +TRY400.py:39:13: TRY400 [*] Use `logging.exception` instead of `logging.error` | 38 | if True: 39 | log.error("Context message here") @@ -58,7 +108,17 @@ TRY400.py:39:13: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:46:9: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Unsafe fix +36 36 | log.error("Context message here") +37 37 | +38 38 | if True: +39 |- log.error("Context message here") + 39 |+ log.exception("Context message here") +40 40 | +41 41 | +42 42 | def bad(): + +TRY400.py:46:9: TRY400 [*] Use `logging.exception` instead of `logging.error` | 44 | a = 1 45 | except Exception: @@ -69,7 +129,17 @@ TRY400.py:46:9: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:49:13: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Unsafe fix +43 43 | try: +44 44 | a = 1 +45 45 | except Exception: +46 |- self.logger.error("Context message here") + 46 |+ self.logger.exception("Context message here") +47 47 | +48 48 | if True: +49 49 | self.logger.error("Context message here") + +TRY400.py:49:13: TRY400 [*] Use `logging.exception` instead of `logging.error` | 48 | if True: 49 | self.logger.error("Context message here") @@ -77,7 +147,17 @@ TRY400.py:49:13: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:87:9: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Unsafe fix +46 46 | self.logger.error("Context message here") +47 47 | +48 48 | if True: +49 |- self.logger.error("Context message here") + 49 |+ self.logger.exception("Context message here") +50 50 | +51 51 | +52 52 | def good(): + +TRY400.py:87:9: TRY400 [*] Use `logging.exception` instead of `logging.error` | 85 | a = 1 86 | except Exception: @@ -88,7 +168,17 @@ TRY400.py:87:9: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:90:13: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Safe fix +84 84 | try: +85 85 | a = 1 +86 86 | except Exception: +87 |- error("Context message here") + 87 |+ exception("Context message here") +88 88 | +89 89 | if True: +90 90 | error("Context message here") + +TRY400.py:90:13: TRY400 [*] Use `logging.exception` instead of `logging.error` | 89 | if True: 90 | error("Context message here") @@ -96,7 +186,17 @@ TRY400.py:90:13: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` -TRY400.py:121:13: TRY400 Use `logging.exception` instead of `logging.error` +ℹ Safe fix +87 87 | error("Context message here") +88 88 | +89 89 | if True: +90 |- error("Context message here") + 90 |+ exception("Context message here") +91 91 | +92 92 | +93 93 | def good(): + +TRY400.py:121:13: TRY400 [*] Use `logging.exception` instead of `logging.error` | 119 | b = 2 120 | except Exception: @@ -105,4 +205,11 @@ TRY400.py:121:13: TRY400 Use `logging.exception` instead of `logging.error` | = help: Replace with `exception` +ℹ Safe fix +118 118 | try: +119 119 | b = 2 +120 120 | except Exception: +121 |- error("Context message here") + 121 |+ exception("Context message here") + diff --git a/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__preview__TRY400_TRY400.py.snap b/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__preview__TRY400_TRY400.py.snap deleted file mode 100644 index 9171d46a3beb1..0000000000000 --- a/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__preview__TRY400_TRY400.py.snap +++ /dev/null @@ -1,215 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/tryceratops/mod.rs ---- -TRY400.py:16:9: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -14 | a = 1 -15 | except Exception: -16 | logging.error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 -17 | -18 | if True: - | - = help: Replace with `exception` - -ℹ Safe fix -13 13 | try: -14 14 | a = 1 -15 15 | except Exception: -16 |- logging.error("Context message here") - 16 |+ logging.exception("Context message here") -17 17 | -18 18 | if True: -19 19 | logging.error("Context message here") - -TRY400.py:19:13: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -18 | if True: -19 | logging.error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 - | - = help: Replace with `exception` - -ℹ Safe fix -16 16 | logging.error("Context message here") -17 17 | -18 18 | if True: -19 |- logging.error("Context message here") - 19 |+ logging.exception("Context message here") -20 20 | -21 21 | -22 22 | def bad(): - -TRY400.py:26:9: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -24 | a = 1 -25 | except Exception: -26 | logger.error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 -27 | -28 | if True: - | - = help: Replace with `exception` - -ℹ Unsafe fix -23 23 | try: -24 24 | a = 1 -25 25 | except Exception: -26 |- logger.error("Context message here") - 26 |+ logger.exception("Context message here") -27 27 | -28 28 | if True: -29 29 | logger.error("Context message here") - -TRY400.py:29:13: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -28 | if True: -29 | logger.error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 - | - = help: Replace with `exception` - -ℹ Unsafe fix -26 26 | logger.error("Context message here") -27 27 | -28 28 | if True: -29 |- logger.error("Context message here") - 29 |+ logger.exception("Context message here") -30 30 | -31 31 | -32 32 | def bad(): - -TRY400.py:36:9: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -34 | a = 1 -35 | except Exception: -36 | log.error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 -37 | -38 | if True: - | - = help: Replace with `exception` - -ℹ Unsafe fix -33 33 | try: -34 34 | a = 1 -35 35 | except Exception: -36 |- log.error("Context message here") - 36 |+ log.exception("Context message here") -37 37 | -38 38 | if True: -39 39 | log.error("Context message here") - -TRY400.py:39:13: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -38 | if True: -39 | log.error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 - | - = help: Replace with `exception` - -ℹ Unsafe fix -36 36 | log.error("Context message here") -37 37 | -38 38 | if True: -39 |- log.error("Context message here") - 39 |+ log.exception("Context message here") -40 40 | -41 41 | -42 42 | def bad(): - -TRY400.py:46:9: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -44 | a = 1 -45 | except Exception: -46 | self.logger.error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 -47 | -48 | if True: - | - = help: Replace with `exception` - -ℹ Unsafe fix -43 43 | try: -44 44 | a = 1 -45 45 | except Exception: -46 |- self.logger.error("Context message here") - 46 |+ self.logger.exception("Context message here") -47 47 | -48 48 | if True: -49 49 | self.logger.error("Context message here") - -TRY400.py:49:13: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -48 | if True: -49 | self.logger.error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 - | - = help: Replace with `exception` - -ℹ Unsafe fix -46 46 | self.logger.error("Context message here") -47 47 | -48 48 | if True: -49 |- self.logger.error("Context message here") - 49 |+ self.logger.exception("Context message here") -50 50 | -51 51 | -52 52 | def good(): - -TRY400.py:87:9: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -85 | a = 1 -86 | except Exception: -87 | error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 -88 | -89 | if True: - | - = help: Replace with `exception` - -ℹ Safe fix -84 84 | try: -85 85 | a = 1 -86 86 | except Exception: -87 |- error("Context message here") - 87 |+ exception("Context message here") -88 88 | -89 89 | if True: -90 90 | error("Context message here") - -TRY400.py:90:13: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -89 | if True: -90 | error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 - | - = help: Replace with `exception` - -ℹ Safe fix -87 87 | error("Context message here") -88 88 | -89 89 | if True: -90 |- error("Context message here") - 90 |+ exception("Context message here") -91 91 | -92 92 | -93 93 | def good(): - -TRY400.py:121:13: TRY400 [*] Use `logging.exception` instead of `logging.error` - | -119 | b = 2 -120 | except Exception: -121 | error("Context message here") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TRY400 - | - = help: Replace with `exception` - -ℹ Safe fix -118 118 | try: -119 119 | b = 2 -120 120 | except Exception: -121 |- error("Context message here") - 121 |+ exception("Context message here") - - diff --git a/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__reraise-no-cause_TRY200.py.snap b/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__reraise-no-cause_TRY200.py.snap deleted file mode 100644 index dd34fcc5528e3..0000000000000 --- a/crates/ruff_linter/src/rules/tryceratops/snapshots/ruff_linter__rules__tryceratops__tests__reraise-no-cause_TRY200.py.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/tryceratops/mod.rs ---- -TRY200.py:15:9: TRY200 Use `raise from` to specify exception cause - | -13 | a = 1 -14 | except Exception: -15 | raise MyException() - | ^^^^^^^^^^^^^^^^^^^ TRY200 - | - -TRY200.py:23:13: TRY200 Use `raise from` to specify exception cause - | -21 | except Exception: -22 | if True: -23 | raise MyException() - | ^^^^^^^^^^^^^^^^^^^ TRY200 - | - - diff --git a/crates/ruff_linter/src/settings/types.rs b/crates/ruff_linter/src/settings/types.rs index 18da863b692b6..d2106e351eec3 100644 --- a/crates/ruff_linter/src/settings/types.rs +++ b/crates/ruff_linter/src/settings/types.rs @@ -492,6 +492,8 @@ impl FromIterator for ExtensionMapping { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum SerializationFormat { Text, + Concise, + Full, Json, JsonLines, Junit, @@ -507,6 +509,8 @@ impl Display for SerializationFormat { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::Text => write!(f, "text"), + Self::Concise => write!(f, "concise"), + Self::Full => write!(f, "full"), Self::Json => write!(f, "json"), Self::JsonLines => write!(f, "json_lines"), Self::Junit => write!(f, "junit"), @@ -520,9 +524,13 @@ impl Display for SerializationFormat { } } -impl Default for SerializationFormat { - fn default() -> Self { - Self::Text +impl SerializationFormat { + pub fn default(preview: bool) -> Self { + if preview { + Self::Full + } else { + Self::Concise + } } } diff --git a/crates/ruff_macros/src/config.rs b/crates/ruff_macros/src/config.rs index 1a902498b16eb..6300f08c7c18e 100644 --- a/crates/ruff_macros/src/config.rs +++ b/crates/ruff_macros/src/config.rs @@ -48,10 +48,10 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result { 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)) + ty.span() => (<#ty>::record(visit)) )); + break; } } diff --git a/crates/ruff_macros/src/map_codes.rs b/crates/ruff_macros/src/map_codes.rs index e81fac4cdd33e..bcbba7276fe35 100644 --- a/crates/ruff_macros/src/map_codes.rs +++ b/crates/ruff_macros/src/map_codes.rs @@ -317,10 +317,22 @@ See also https://github.com/astral-sh/ruff/issues/2186. matches!(self.group(), RuleGroup::Preview) } + pub fn is_stable(&self) -> bool { + matches!(self.group(), RuleGroup::Stable) + } + #[allow(deprecated)] pub fn is_nursery(&self) -> bool { matches!(self.group(), RuleGroup::Nursery) } + + pub fn is_deprecated(&self) -> bool { + matches!(self.group(), RuleGroup::Deprecated) + } + + pub fn is_removed(&self) -> bool { + matches!(self.group(), RuleGroup::Removed) + } } impl Linter { diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index fbaeec25228ce..0c21393509e9c 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -9,6 +9,7 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, Result}; use glob::{glob, GlobError, Paths, PatternError}; +use itertools::Itertools; use regex::Regex; use ruff_linter::settings::fix_safety_table::FixSafetyTable; use rustc_hash::{FxHashMap, FxHashSet}; @@ -41,9 +42,9 @@ use crate::options::{ Flake8ComprehensionsOptions, Flake8CopyrightOptions, Flake8ErrMsgOptions, Flake8GetTextOptions, Flake8ImplicitStrConcatOptions, Flake8ImportConventionsOptions, Flake8PytestStyleOptions, Flake8QuotesOptions, Flake8SelfOptions, Flake8TidyImportsOptions, Flake8TypeCheckingOptions, - Flake8UnusedArgumentsOptions, FormatOptions, IsortOptions, LintOptions, McCabeOptions, Options, - Pep8NamingOptions, PyUpgradeOptions, PycodestyleOptions, PydocstyleOptions, PyflakesOptions, - PylintOptions, + Flake8UnusedArgumentsOptions, FormatOptions, IsortOptions, LintCommonOptions, LintOptions, + McCabeOptions, Options, Pep8NamingOptions, PyUpgradeOptions, PycodestyleOptions, + PydocstyleOptions, PyflakesOptions, PylintOptions, }; use crate::settings::{ FileResolverSettings, FormatterSettings, LineEnding, Settings, EXCLUDE, INCLUDE, @@ -117,7 +118,6 @@ pub struct Configuration { pub required_version: Option, pub extension: Option, pub show_fixes: Option, - pub show_source: Option, // File resolver options pub exclude: Option>, @@ -220,9 +220,10 @@ impl Configuration { fix: self.fix.unwrap_or(false), fix_only: self.fix_only.unwrap_or(false), unsafe_fixes: self.unsafe_fixes.unwrap_or_default(), - output_format: self.output_format.unwrap_or_default(), + output_format: self + .output_format + .unwrap_or_else(|| SerializationFormat::default(global_preview.is_enabled())), show_fixes: self.show_fixes.unwrap_or(false), - show_source: self.show_source.unwrap_or(false), file_resolver: FileResolverSettings { exclude: FilePatternSet::try_from_iter( @@ -395,12 +396,14 @@ impl Configuration { } pub fn from_options(options: Options, project_root: &Path) -> Result { + warn_about_deprecated_top_level_lint_options(&options.lint_top_level.0); + let lint = if let Some(mut lint) = options.lint { - lint.common = lint.common.combine(options.lint_top_level); + lint.common = lint.common.combine(options.lint_top_level.0); lint } else { LintOptions { - common: options.lint_top_level, + common: options.lint_top_level.0, ..LintOptions::default() } }; @@ -414,6 +417,32 @@ impl Configuration { options.indent_width.or(options.tab_size) }; + #[allow(deprecated)] + let output_format = { + if options.show_source.is_some() { + warn_user_once!( + r#"The `show-source` option has been deprecated in favor of `output-format`'s "full" and "concise" variants. Please update your configuration to use `output-format = ` instead."# + ); + } + + options + .output_format + .map(|format| match format { + SerializationFormat::Text => { + warn_user!(r#"Setting `output_format` to "text" is deprecated. Use "full" or "concise" instead. "text" will be treated as "{}"."#, SerializationFormat::default(options.preview.unwrap_or_default())); + SerializationFormat::default(options.preview.unwrap_or_default()) + }, + other => other + }) + .or(options.show_source.map(|show_source| { + if show_source { + SerializationFormat::Full + } else { + SerializationFormat::Concise + } + })) + }; + Ok(Self { builtins: options.builtins, cache_dir: options @@ -478,7 +507,7 @@ impl Configuration { fix: options.fix, fix_only: options.fix_only, unsafe_fixes: options.unsafe_fixes.map(UnsafeFixes::from), - output_format: options.output_format, + output_format, force_exclude: options.force_exclude, line_length: options.line_length, indent_width, @@ -489,7 +518,6 @@ impl Configuration { preview: options.preview.map(PreviewMode::from), required_version: options.required_version, respect_gitignore: options.respect_gitignore, - show_source: options.show_source, show_fixes: options.show_fixes, src: options .src @@ -536,7 +564,6 @@ impl Configuration { namespace_packages: self.namespace_packages.or(config.namespace_packages), required_version: self.required_version.or(config.required_version), respect_gitignore: self.respect_gitignore.or(config.respect_gitignore), - show_source: self.show_source.or(config.show_source), show_fixes: self.show_fixes.or(config.show_fixes), src: self.src.or(config.src), target_version: self.target_version.or(config.target_version), @@ -728,6 +755,8 @@ impl LintConfiguration { // Store selectors for displaying warnings let mut redirects = FxHashMap::default(); let mut deprecated_nursery_selectors = FxHashSet::default(); + let mut deprecated_selectors = FxHashSet::default(); + let mut removed_selectors = FxHashSet::default(); let mut ignored_preview_selectors = FxHashSet::default(); // Track which docstring rules are specifically enabled @@ -860,30 +889,61 @@ impl LintConfiguration { for (kind, selector) in selection.selectors_by_kind() { #[allow(deprecated)] if matches!(selector, RuleSelector::Nursery) { - if preview.mode.is_enabled() { - return Err(anyhow!("The `NURSERY` selector is deprecated and cannot be used with preview mode enabled.")); - } - warn_user_once!("The `NURSERY` selector has been deprecated. Use the `--preview` flag instead."); + let suggestion = if preview.mode.is_disabled() { + " Use the `--preview` flag instead." + } else { + " Unstable rules should be selected individually or by their respective groups." + }; + return Err(anyhow!("The `NURSERY` selector was removed.{suggestion}")); }; - // Only warn for the following selectors if used to enable rules - // e.g. use with `--ignore` or `--fixable` is okay + // Some of these checks are only for `Kind::Enable` which means only `--select` will warn + // and use with, e.g., `--ignore` or `--fixable` is okay + + // Unstable rules if preview.mode.is_disabled() && kind.is_enable() { - if let RuleSelector::Rule { prefix, .. } = selector { - if prefix.rules().any(|rule| rule.is_nursery()) { + if selector.is_exact() { + if selector.all_rules().all(|rule| rule.is_nursery()) { deprecated_nursery_selectors.insert(selector); } } // Check if the selector is empty because preview mode is disabled - if selector.rules(&PreviewOptions::default()).next().is_none() { + if selector.rules(&preview).next().is_none() + && selector + .rules(&PreviewOptions { + mode: PreviewMode::Enabled, + require_explicit: preview.require_explicit, + }) + .next() + .is_some() + { ignored_preview_selectors.insert(selector); } } + // Deprecated rules + if kind.is_enable() && selector.is_exact() { + if selector.all_rules().all(|rule| rule.is_deprecated()) { + deprecated_selectors.insert(selector.clone()); + } + } + + // Removed rules + if selector.is_exact() { + if selector.all_rules().all(|rule| rule.is_removed()) { + removed_selectors.insert(selector); + } + } + + // Redirected rules if let RuleSelector::Prefix { prefix, redirected_from: Some(redirect_from), + } + | RuleSelector::Rule { + prefix, + redirected_from: Some(redirect_from), } = selector { redirects.insert(redirect_from, prefix); @@ -891,7 +951,30 @@ impl LintConfiguration { } } - for (from, target) in redirects { + let removed_selectors = removed_selectors.iter().sorted().collect::>(); + match removed_selectors.as_slice() { + [] => (), + [selection] => { + let (prefix, code) = selection.prefix_and_code(); + return Err(anyhow!( + "Rule `{prefix}{code}` was removed and cannot be selected." + )); + } + [..] => { + let mut message = + "The following rules have been removed and cannot be selected:".to_string(); + for selection in removed_selectors { + let (prefix, code) = selection.prefix_and_code(); + message.push_str("\n - "); + message.push_str(prefix); + message.push_str(code); + } + message.push('\n'); + return Err(anyhow!(message)); + } + } + + for (from, target) in redirects.iter().sorted_by_key(|item| item.0) { // TODO(martin): This belongs into the ruff crate. warn_user_once_by_id!( from, @@ -901,16 +984,61 @@ impl LintConfiguration { ); } - for selection in deprecated_nursery_selectors { - let (prefix, code) = selection.prefix_and_code(); - warn_user!("Selection of nursery rule `{prefix}{code}` without the `--preview` flag is deprecated.",); + let deprecated_nursery_selectors = deprecated_nursery_selectors + .iter() + .sorted() + .collect::>(); + match deprecated_nursery_selectors.as_slice() { + [] => (), + [selection] => { + let (prefix, code) = selection.prefix_and_code(); + return Err(anyhow!("Selection of unstable rule `{prefix}{code}` without the `--preview` flag is not allowed.")); + } + [..] => { + let mut message = "Selection of unstable rules without the `--preview` flag is not allowed. Enable preview or remove selection of:".to_string(); + for selection in deprecated_nursery_selectors { + let (prefix, code) = selection.prefix_and_code(); + message.push_str("\n\t- "); + message.push_str(prefix); + message.push_str(code); + } + message.push('\n'); + return Err(anyhow!(message)); + } } - for selection in ignored_preview_selectors { + if preview.mode.is_disabled() { + for selection in deprecated_selectors.iter().sorted() { + let (prefix, code) = selection.prefix_and_code(); + warn_user!( + "Rule `{prefix}{code}` is deprecated and will be removed in a future release.", + ); + } + } else { + let deprecated_selectors = deprecated_selectors.iter().sorted().collect::>(); + match deprecated_selectors.as_slice() { + [] => (), + [selection] => { + let (prefix, code) = selection.prefix_and_code(); + return Err(anyhow!("Selection of deprecated rule `{prefix}{code}` is not allowed when preview is enabled.")); + } + [..] => { + let mut message = "Selection of deprecated rules is not allowed when preview is enabled. Remove selection of:".to_string(); + for selection in deprecated_selectors { + let (prefix, code) = selection.prefix_and_code(); + message.push_str("\n\t- "); + message.push_str(prefix); + message.push_str(code); + } + message.push('\n'); + return Err(anyhow!(message)); + } + } + } + + for selection in ignored_preview_selectors.iter().sorted() { let (prefix, code) = selection.prefix_and_code(); - warn_user!( - "Selection `{prefix}{code}` has no effect because the `--preview` flag was not included.", - ); + warn_user!("Selection `{prefix}{code}` has no effect because preview is not enabled.",); } let mut rules = RuleTable::empty(); @@ -1129,6 +1257,201 @@ pub fn resolve_src(src: &[String], project_root: &Path) -> Result> Ok(paths) } +fn warn_about_deprecated_top_level_lint_options(top_level_options: &LintCommonOptions) { + let mut used_options = Vec::new(); + + if top_level_options.allowed_confusables.is_some() { + used_options.push("allowed-confusables"); + } + + if top_level_options.dummy_variable_rgx.is_some() { + used_options.push("dummy-variable-rgx"); + } + + #[allow(deprecated)] + if top_level_options.extend_ignore.is_some() { + used_options.push("extend-ignore"); + } + + if top_level_options.extend_select.is_some() { + used_options.push("extend-select"); + } + + if top_level_options.extend_fixable.is_some() { + used_options.push("extend-fixable"); + } + + #[allow(deprecated)] + if top_level_options.extend_unfixable.is_some() { + used_options.push("extend-unfixable"); + } + + if top_level_options.external.is_some() { + used_options.push("external"); + } + + if top_level_options.fixable.is_some() { + used_options.push("fixable"); + } + + if top_level_options.ignore.is_some() { + used_options.push("ignore"); + } + + if top_level_options.extend_safe_fixes.is_some() { + used_options.push("extend-safe-fixes"); + } + + if top_level_options.extend_unsafe_fixes.is_some() { + used_options.push("extend-unsafe-fixes"); + } + + if top_level_options.ignore_init_module_imports.is_some() { + used_options.push("ignore-init-module-imports"); + } + + if top_level_options.logger_objects.is_some() { + used_options.push("logger-objects"); + } + + if top_level_options.select.is_some() { + used_options.push("select"); + } + + if top_level_options.explicit_preview_rules.is_some() { + used_options.push("explicit-preview-rules"); + } + + if top_level_options.task_tags.is_some() { + used_options.push("task-tags"); + } + + if top_level_options.typing_modules.is_some() { + used_options.push("typing-modules"); + } + + if top_level_options.unfixable.is_some() { + used_options.push("unfixable"); + } + + if top_level_options.flake8_annotations.is_some() { + used_options.push("flake8-annotations"); + } + + if top_level_options.flake8_bandit.is_some() { + used_options.push("flake8-bandit"); + } + + if top_level_options.flake8_bugbear.is_some() { + used_options.push("flake8-bugbear"); + } + + if top_level_options.flake8_builtins.is_some() { + used_options.push("flake8-builtins"); + } + + if top_level_options.flake8_comprehensions.is_some() { + used_options.push("flake8-comprehensions"); + } + + if top_level_options.flake8_copyright.is_some() { + used_options.push("flake8-copyright"); + } + + if top_level_options.flake8_errmsg.is_some() { + used_options.push("flake8-errmsg"); + } + + if top_level_options.flake8_quotes.is_some() { + used_options.push("flake8-quotes"); + } + + if top_level_options.flake8_self.is_some() { + used_options.push("flake8-self"); + } + + if top_level_options.flake8_tidy_imports.is_some() { + used_options.push("flake8-tidy-imports"); + } + + if top_level_options.flake8_type_checking.is_some() { + used_options.push("flake8-type-checking"); + } + + if top_level_options.flake8_gettext.is_some() { + used_options.push("flake8-gettext"); + } + + if top_level_options.flake8_implicit_str_concat.is_some() { + used_options.push("flake8-implicit-str-concat"); + } + + if top_level_options.flake8_import_conventions.is_some() { + used_options.push("flake8-import-conventions"); + } + + if top_level_options.flake8_pytest_style.is_some() { + used_options.push("flake8-pytest-style"); + } + + if top_level_options.flake8_unused_arguments.is_some() { + used_options.push("flake8-unused-arguments"); + } + + if top_level_options.isort.is_some() { + used_options.push("isort"); + } + + if top_level_options.mccabe.is_some() { + used_options.push("mccabe"); + } + + if top_level_options.pep8_naming.is_some() { + used_options.push("pep8-naming"); + } + + if top_level_options.pycodestyle.is_some() { + used_options.push("pycodestyle"); + } + + if top_level_options.pydocstyle.is_some() { + used_options.push("pydocstyle"); + } + + if top_level_options.pyflakes.is_some() { + used_options.push("pyflakes"); + } + + if top_level_options.pylint.is_some() { + used_options.push("pylint"); + } + + if top_level_options.pyupgrade.is_some() { + used_options.push("pyupgrade"); + } + + if top_level_options.per_file_ignores.is_some() { + used_options.push("per-file-ignores"); + } + + if top_level_options.extend_per_file_ignores.is_some() { + used_options.push("extend-per-file-ignores"); + } + + if used_options.is_empty() { + return; + } + + let options_mapping = used_options + .iter() + .map(|option| format!("- '{option}' -> 'lint.{option}'")) + .join("\n "); + + warn_user!( + "The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your configuration:\n {options_mapping}\n\n", + ); +} + #[cfg(test)] mod tests { use crate::configuration::{LintConfiguration, RuleSelection}; @@ -1141,57 +1464,7 @@ mod tests { use ruff_linter::RuleSelector; use std::str::FromStr; - const NURSERY_RULES: &[Rule] = &[ - Rule::MissingCopyrightNotice, - Rule::IndentationWithInvalidMultiple, - Rule::NoIndentedBlock, - Rule::UnexpectedIndentation, - Rule::IndentationWithInvalidMultipleComment, - Rule::NoIndentedBlockComment, - Rule::UnexpectedIndentationComment, - Rule::OverIndented, - Rule::WhitespaceAfterOpenBracket, - Rule::WhitespaceBeforeCloseBracket, - Rule::WhitespaceBeforePunctuation, - Rule::WhitespaceBeforeParameters, - Rule::MultipleSpacesBeforeOperator, - Rule::MultipleSpacesAfterOperator, - Rule::TabBeforeOperator, - Rule::TabAfterOperator, - Rule::MissingWhitespaceAroundOperator, - Rule::MissingWhitespaceAroundArithmeticOperator, - Rule::MissingWhitespaceAroundBitwiseOrShiftOperator, - Rule::MissingWhitespaceAroundModuloOperator, - Rule::MissingWhitespace, - Rule::MultipleSpacesAfterComma, - Rule::TabAfterComma, - Rule::UnexpectedSpacesAroundKeywordParameterEquals, - Rule::MissingWhitespaceAroundParameterEquals, - Rule::TooFewSpacesBeforeInlineComment, - Rule::NoSpaceAfterInlineComment, - Rule::NoSpaceAfterBlockComment, - Rule::MultipleLeadingHashesForBlockComment, - Rule::MultipleSpacesAfterKeyword, - Rule::MultipleSpacesBeforeKeyword, - Rule::TabAfterKeyword, - Rule::TabBeforeKeyword, - Rule::MissingWhitespaceAfterKeyword, - Rule::CompareToEmptyString, - Rule::NoSelfUse, - Rule::EqWithoutHash, - Rule::BadDunderMethodName, - Rule::RepeatedAppend, - Rule::DeleteFullSlice, - Rule::CheckAndRemoveFromSet, - Rule::QuadraticListSummation, - Rule::NurseryTestRule, - ]; - const PREVIEW_RULES: &[Rule] = &[ - Rule::AndOrTernary, - Rule::AssignmentInAssert, - Rule::DirectLoggerInstantiation, - Rule::InvalidGetLoggerArgument, Rule::IsinstanceTypeNone, Rule::IfExprMinMax, Rule::ManualDictComprehension, @@ -1199,7 +1472,6 @@ mod tests { Rule::SliceCopy, Rule::TooManyPublicMethods, Rule::TooManyPublicMethods, - Rule::UndocumentedWarn, Rule::UnnecessaryEnumerate, Rule::MathConstant, Rule::PreviewTestRule, @@ -1589,9 +1861,8 @@ mod tests { #[test] fn nursery_select_code() -> Result<()> { - // Backwards compatible behavior allows selection of nursery rules with their exact code - // when preview is disabled - let actual = resolve_rules( + // We do not allow selection of nursery rules when preview is disabled + assert!(resolve_rules( [RuleSelection { select: Some(vec![Flake8Copyright::_001.into()]), ..RuleSelection::default() @@ -1600,9 +1871,8 @@ mod tests { mode: PreviewMode::Disabled, ..PreviewOptions::default() }), - )?; - let expected = RuleSet::from_rule(Rule::MissingCopyrightNotice); - assert_eq!(actual, expected); + ) + .is_err()); let actual = resolve_rules( [RuleSelection { @@ -1621,10 +1891,9 @@ mod tests { #[test] #[allow(deprecated)] - fn select_nursery() -> Result<()> { - // Backwards compatible behavior allows selection of nursery rules with the nursery selector - // when preview is disabled - let actual = resolve_rules( + fn select_nursery() { + // We no longer allow use of the NURSERY selector and should error in both cases + assert!(resolve_rules( [RuleSelection { select: Some(vec![RuleSelector::Nursery]), ..RuleSelection::default() @@ -1633,11 +1902,8 @@ mod tests { mode: PreviewMode::Disabled, ..PreviewOptions::default() }), - )?; - let expected = RuleSet::from_rules(NURSERY_RULES); - assert_eq!(actual, expected); - - // When preview is enabled, use of NURSERY is banned + ) + .is_err()); assert!(resolve_rules( [RuleSelection { select: Some(vec![RuleSelector::Nursery]), @@ -1649,8 +1915,6 @@ mod tests { }), ) .is_err()); - - Ok(()) } #[test] diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index ff70baa6f4505..a6b0cdd9ba7af 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -6,6 +6,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; +use crate::options_base::{OptionsMetadata, Visit}; use ruff_formatter::IndentStyle; use ruff_linter::line_width::{IndentWidth, LineLength}; use ruff_linter::rules::flake8_pytest_style::settings::SettingsError; @@ -71,14 +72,14 @@ pub struct Options { )] pub extend: Option, - /// The style in which violation messages should be formatted: `"text"` - /// (default), `"grouped"` (group messages by file), `"json"` + /// The style in which violation messages should be formatted: `"full"` + /// (shows source),`"concise"` (default), `"grouped"` (group messages by file), `"json"` /// (machine-readable), `"junit"` (machine-readable XML), `"github"` (GitHub /// Actions annotations), `"gitlab"` (GitLab CI code quality report), /// `"pylint"` (Pylint text format) or `"azure"` (Azure Pipeline logging commands). #[option( - default = r#""text""#, - value_type = r#""text" | "json" | "junit" | "github" | "gitlab" | "pylint" | "azure""#, + default = r#""concise""#, + value_type = r#""full" | "concise" | "grouped" | "json" | "junit" | "github" | "gitlab" | "pylint" | "azure""#, example = r#" # Group violations by containing file. output-format = "grouped" @@ -116,6 +117,9 @@ pub struct Options { show-source = true "# )] + #[deprecated( + note = "`show_source` is deprecated and is now part of `output_format` in the form of `full` or `concise` options. Please update your configuration." + )] pub show_source: Option, /// Whether to show an enumeration of all fixed lint violations @@ -420,21 +424,21 @@ pub struct Options { )] pub tab_size: Option, + #[option_group] pub lint: Option, /// The lint sections specified at the top level. #[serde(flatten)] - pub lint_top_level: LintCommonOptions, + pub lint_top_level: DeprecatedTopLevelLintOptions, /// Options to configure code formatting. #[option_group] pub format: Option, } -/// Experimental section to configure Ruff's linting. This new section will eventually -/// replace the top-level linting options. +/// Configures how ruff checks your code. /// -/// Options specified in the `lint` section take precedence over the top-level settings. +/// Options specified in the `lint` section take precedence over the deprecated top-level settings. #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[derive(Debug, PartialEq, Eq, Default, OptionsMetadata, Serialize, Deserialize)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] @@ -477,15 +481,67 @@ pub struct LintOptions { pub preview: Option, } +/// Newtype wrapper for [`LintCommonOptions`] that allows customizing the JSON schema and omitting the fields from the [`OptionsMetadata`]. +#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[serde(transparent)] +pub struct DeprecatedTopLevelLintOptions(pub LintCommonOptions); + +impl OptionsMetadata for DeprecatedTopLevelLintOptions { + fn record(_visit: &mut dyn Visit) { + // Intentionally empty. Omit all fields from the documentation and instead promote the options under the `lint.` section. + // This doesn't create an empty 'common' option because the field in the `Options` struct is marked with `#[serde(flatten)]`. + // Meaning, the code here flattens no-properties into the parent, which is what we want. + } +} + +#[cfg(feature = "schemars")] +impl schemars::JsonSchema for DeprecatedTopLevelLintOptions { + fn schema_name() -> std::string::String { + "DeprecatedTopLevelLintOptions".to_owned() + } + fn schema_id() -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Borrowed(std::concat!( + std::module_path!(), + "::", + "DeprecatedTopLevelLintOptions" + )) + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + use schemars::schema::Schema; + + let common_schema = LintCommonOptions::json_schema(gen); + let mut schema_obj = common_schema.into_object(); + + if let Some(object) = schema_obj.object.as_mut() { + for property in object.properties.values_mut() { + if let Schema::Object(property_object) = property { + if let Some(metadata) = &mut property_object.metadata { + metadata.deprecated = true; + } else { + property_object.metadata = Some(Box::new(schemars::schema::Metadata { + deprecated: true, + ..schemars::schema::Metadata::default() + })); + } + } + } + } + + Schema::Object(schema_obj) + } +} + // Note: This struct should be inlined into [`LintOptions`] once support for the top-level lint settings // is removed. - +// Don't add any new options to this struct. Add them to [`LintOptions`] directly to avoid exposing them in the +// global settings. #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[derive( Debug, PartialEq, Eq, Default, OptionsMetadata, CombineOptions, Serialize, Deserialize, )] #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub struct LintCommonOptions { + // WARNING: Don't add new options to this type. Add them to `LintOptions` instead. /// A list of allowed "confusable" Unicode characters to ignore when /// enforcing `RUF001`, `RUF002`, and `RUF003`. #[option( @@ -734,6 +790,7 @@ pub struct LintCommonOptions { )] pub unfixable: Option>, + // WARNING: Don't add new options to this type. Add them to `LintOptions` instead. /// Options for the `flake8-annotations` plugin. #[option_group] pub flake8_annotations: Option, @@ -830,6 +887,8 @@ pub struct LintCommonOptions { #[option_group] pub pyupgrade: Option, + // WARNING: Don't add new options to this type. Add them to `LintOptions` instead. + // Tables are required to go last. /// A list of mappings from file pattern to rule codes or prefixes to /// exclude, when considering any matching files. @@ -857,6 +916,7 @@ pub struct LintCommonOptions { "# )] pub extend_per_file_ignores: Option>>, + // WARNING: Don't add new options to this type. Add them to `LintOptions` instead. } #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -2816,9 +2876,7 @@ impl PyUpgradeOptions { } } -/// Experimental: Configures how `ruff format` formats your code. -/// -/// Please provide feedback in [this discussion](https://github.com/astral-sh/ruff/discussions/7310). +/// Configures the way ruff formats your code. #[derive( Debug, PartialEq, Eq, Default, Deserialize, Serialize, OptionsMetadata, CombineOptions, )] diff --git a/crates/ruff_workspace/src/options_base.rs b/crates/ruff_workspace/src/options_base.rs index 8ac16d0a3b695..e766c66db1da7 100644 --- a/crates/ruff_workspace/src/options_base.rs +++ b/crates/ruff_workspace/src/options_base.rs @@ -29,6 +29,15 @@ pub trait OptionsMetadata { } } +impl OptionsMetadata for Option +where + T: OptionsMetadata, +{ + fn record(visit: &mut dyn Visit) { + T::record(visit); + } +} + /// Metadata of an option that can either be a [`OptionField`] or [`OptionSet`]. #[derive(Clone, PartialEq, Eq, Debug)] pub enum OptionEntry { diff --git a/crates/ruff_workspace/src/settings.rs b/crates/ruff_workspace/src/settings.rs index a4b5b785d0454..f37dd3723b692 100644 --- a/crates/ruff_workspace/src/settings.rs +++ b/crates/ruff_workspace/src/settings.rs @@ -31,8 +31,6 @@ pub struct Settings { pub output_format: SerializationFormat, #[cache_key(ignore)] pub show_fixes: bool, - #[cache_key(ignore)] - pub show_source: bool, pub file_resolver: FileResolverSettings, pub linter: LinterSettings, @@ -46,9 +44,8 @@ impl Default for Settings { cache_dir: cache_dir(project_root), fix: false, fix_only: false, - output_format: SerializationFormat::default(), + output_format: SerializationFormat::default(false), show_fixes: false, - show_source: false, unsafe_fixes: UnsafeFixes::default(), linter: LinterSettings::new(project_root), file_resolver: FileResolverSettings::new(project_root), @@ -70,7 +67,6 @@ impl fmt::Display for Settings { self.fix_only, self.output_format, self.show_fixes, - self.show_source, self.unsafe_fixes, self.file_resolver | nested, self.linter | nested, diff --git a/docs/configuration.md b/docs/configuration.md index 2114f2f6e774b..715ef73bccf83 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -510,7 +510,9 @@ Options: Include fixes that may not retain the original intent of the code. Use `--no-unsafe-fixes` to disable --show-source - Show violations with source code. Use `--no-show-source` to disable + Show violations with source code. Use `--no-show-source` to disable. + (Deprecated: use `--output-format=full` or `--output-format=concise` + instead of `--show-source` and `--no-show-source`, respectively) --show-fixes Show an enumeration of all fixed lint violations. Use `--no-show-fixes` to disable @@ -526,9 +528,11 @@ Options: --ignore-noqa Ignore any `# noqa` comments --output-format - Output serialization format for violations [env: RUFF_OUTPUT_FORMAT=] - [possible values: text, json, json-lines, junit, grouped, github, - gitlab, pylint, azure, sarif] + Output serialization format for violations. The default serialization + format is "concise". In preview mode, the default serialization + format is "full" [env: RUFF_OUTPUT_FORMAT=] [possible values: text, + concise, full, json, json-lines, junit, grouped, github, gitlab, + pylint, azure, sarif] -o, --output-file Specify file to write the linter output to (default: stdout) --target-version diff --git a/docs/formatter.md b/docs/formatter.md index 7404aad0b8e92..52e912361b528 100644 --- a/docs/formatter.md +++ b/docs/formatter.md @@ -321,16 +321,16 @@ leading to [`line-too-long`](rules/line-too-long.md) (`E501`) errors. None of the above are included in Ruff's default configuration. However, if you've enabled any of these rules or their parent categories (like `Q`), we recommend disabling them via the -linter's [`ignore`](settings.md#ignore) setting. +linter's [`lint.ignore`](settings.md#lint_ignore) setting. Similarly, we recommend avoiding the following isort settings, which are incompatible with the formatter's treatment of import statements when set to non-default values: -- [`force-single-line`](settings.md#isort-force-single-line) -- [`force-wrap-aliases`](settings.md#isort-force-wrap-aliases) -- [`lines-after-imports`](settings.md#isort-lines-after-imports) -- [`lines-between-types`](settings.md#isort-lines-between-types) -- [`split-on-trailing-comma`](settings.md#isort-split-on-trailing-comma) +- [`force-single-line`](settings.md#lint_isort_force-single-line) +- [`force-wrap-aliases`](settings.md#lint_isort_force-wrap-aliases) +- [`lines-after-imports`](settings.md#lint_isort_lines-after-imports) +- [`lines-between-types`](settings.md#lint_isort_lines-between-types) +- [`split-on-trailing-comma`](settings.md#lint_isort_split-on-trailing-comma) If you've configured any of these settings to take on non-default values, we recommend removing them from your Ruff configuration. diff --git a/docs/linter.md b/docs/linter.md index 7b8dbd086e288..13f791db1019b 100644 --- a/docs/linter.md +++ b/docs/linter.md @@ -24,14 +24,14 @@ For the full list of supported options, run `ruff check --help`. ## Rule selection -The set of enabled rules is controlled via the [`select`](settings.md#select), -[`extend-select`](settings.md#extend-select), and [`ignore`](settings.md#ignore) settings. +The set of enabled rules is controlled via the [`lint.select`](settings.md#lint_select), +[`lint.extend-select`](settings.md#lint_extend-select), and [`lint.ignore`](settings.md#lint_ignore) settings. Ruff's linter mirrors Flake8's rule code system, in which each rule code consists of a one-to-three letter prefix, followed by three digits (e.g., `F401`). The prefix indicates that "source" of the rule (e.g., `F` for Pyflakes, `E` for pycodestyle, `ANN` for flake8-annotations). -Rule selectors like [`select`](settings.md#select) and [`ignore`](settings.md#ignore) accept either +Rule selectors like [`lint.select`](settings.md#lint_select) and [`lint.ignore`](settings.md#lint_ignore) accept either a full rule code (e.g., `F401`) or any valid prefix (e.g., `F`). For example, given the following configuration file: @@ -60,7 +60,7 @@ formats. Ruff will automatically disable any conflicting rules when `ALL` is ena If you're wondering how to configure Ruff, here are some **recommended guidelines**: -- Prefer [`select`](settings.md#select) over [`extend-select`](settings.md#extend-select) to make your rule set explicit. +- Prefer [`lint.select`](settings.md#lint_select) over [`lint.extend-select`](settings.md#lint_extend-select) to make your rule set explicit. - Use `ALL` with discretion. Enabling `ALL` will implicitly enable new rules whenever you upgrade. - Start with a small set of rules (`select = ["E", "F"]`) and add a category at-a-time. For example, you might consider expanding to `select = ["E", "F", "B"]` to enable the popular flake8-bugbear @@ -109,13 +109,13 @@ pedantic) might look like the following: ] ``` -To resolve the enabled rule set, Ruff may need to reconcile [`select`](settings.md#select) and -[`ignore`](settings.md#ignore) from a variety of sources, including the current `pyproject.toml`, -any inherited `pyproject.toml` files, and the CLI (e.g., [`--select`](settings.md#select)). +To resolve the enabled rule set, Ruff may need to reconcile [`lint.select`](settings.md#lint_select) and +[`lint.ignore`](settings.md#lint_ignore) from a variety of sources, including the current `pyproject.toml`, +any inherited `pyproject.toml` files, and the CLI (e.g., [`--select`](settings.md#lint_select)). -In those scenarios, Ruff uses the "highest-priority" [`select`](settings.md#select) as the basis for -the rule set, and then applies [`extend-select`](settings.md#extend-select) and -[`ignore`](settings.md#ignore) adjustments. CLI options are given higher priority than +In those scenarios, Ruff uses the "highest-priority" [`select`](settings.md#lint_select) as the basis for +the rule set, and then applies [`extend-select`](settings.md#lint_extend-select) and +[`ignore`](settings.md#lint_ignore) adjustments. CLI options are given higher priority than `pyproject.toml` options, and the current `pyproject.toml` file is given higher priority than any inherited `pyproject.toml` files. @@ -233,9 +233,9 @@ You may use prefixes to select rules as well, e.g., `F` can be used to promote f ### Disabling fixes -To limit the set of rules that Ruff should fix, use the [`fixable`](settings.md#fixable) and -[`unfixable`](settings.md#unfixable) settings, along with their [`extend-fixable`](settings.md#extend-fixable) -and [`extend-unfixable`](settings.md#extend-unfixable) variants. +To limit the set of rules that Ruff should fix, use the [`lint.fixable`](settings.md#lint_fixable) and +[`lint.unfixable`](settings.md#lint_unfixable) settings, along with their [`lint.extend-fixable`](settings.md#lint_extend-fixable) +and [`lint.extend-unfixable`](settings.md#lint_extend-unfixable) variants. For example, the following configuration would enable fixes for all rules except [`unused-imports`](rules/unused-import.md) (`F401`): @@ -277,7 +277,7 @@ Conversely, the following configuration would only enable fixes for `F401`: Ruff supports several mechanisms for suppressing lint errors, be they false positives or permissible violations. -To omit a lint rule entirely, add it to the "ignore" list via the [`ignore`](settings.md#ignore) +To omit a lint rule entirely, add it to the "ignore" list via the [`lint.ignore`](settings.md#lint_ignore) setting, either on the command-line or in your `pyproject.toml` or `ruff.toml` file. To suppress a violation inline, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html). @@ -326,7 +326,7 @@ file, preferably towards the top, like so: # ruff: noqa: F841 ``` -Or see the [`per-file-ignores`](settings.md#per-file-ignores) setting, which enables the same +Or see the [`lint.per-file-ignores`](settings.md#lint_per-file-ignores) setting, which enables the same functionality from within your `pyproject.toml` or `ruff.toml` file. Global `noqa` comments must be on their own line to disambiguate from comments which ignore diff --git a/docs/preview.md b/docs/preview.md index 1bea9e451db6e..2321500f55a90 100644 --- a/docs/preview.md +++ b/docs/preview.md @@ -144,11 +144,7 @@ rule will need to be selected with its exact code, e.g. `--select ALL,HYP001`. If preview mode is not enabled, this setting has no effect. -## Legacy behavior +## Deprecated rules -Before the preview mode was introduced, new rules were added in a "nursery" category that required selection of -rules with their exact codes — similar to if `explicit-preview-rules` is enabled. - -The nursery category has been deprecated and all rules in the nursery are now considered to be in preview. -For backwards compatibility, nursery rules are selectable with their exact codes without enabling preview mode. -However, this behavior will display a warning and support will be removed in a future release. +When preview mode is enabled, deprecated rules will be disabled. If a deprecated rule is selected explicitly, an +error will be raised. Deprecated rules will not be included if selected via a rule category or prefix. diff --git a/python/ruff-ecosystem/ruff_ecosystem/projects.py b/python/ruff-ecosystem/ruff_ecosystem/projects.py index 4ed46565d82d7..c477588ef5970 100644 --- a/python/ruff-ecosystem/ruff_ecosystem/projects.py +++ b/python/ruff-ecosystem/ruff_ecosystem/projects.py @@ -209,6 +209,9 @@ def to_ruff_args(self) -> list[str]: # Ignore internal test rules "--ignore", "RUF9", + # Use the concise format for comparing violations + "--output-format", + "concise", f"--{'' if self.preview else 'no-'}preview", ] if self.select: diff --git a/ruff.schema.json b/ruff.schema.json index dbebae5df0613..7e1d8a62d017c 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -5,6 +5,7 @@ "properties": { "allowed-confusables": { "description": "A list of allowed \"confusable\" Unicode characters to ignore when enforcing `RUF001`, `RUF002`, and `RUF003`.", + "deprecated": true, "type": [ "array", "null" @@ -34,6 +35,7 @@ }, "dummy-variable-rgx": { "description": "A regular expression used to identify \"dummy\" variables, or those which should be ignored when enforcing (e.g.) unused-variable rules. The default expression matches `_`, `__`, and `_var`, but not `_var_`.", + "deprecated": true, "type": [ "string", "null" @@ -51,6 +53,7 @@ }, "explicit-preview-rules": { "description": "Whether to require exact codes to select preview rules. When enabled, preview rules will not be selected by prefixes — the full code of each preview rule will be required to enable the rule.", + "deprecated": true, "type": [ "boolean", "null" @@ -75,6 +78,7 @@ }, "extend-fixable": { "description": "A list of rule codes or prefixes to consider fixable, in addition to those specified by `fixable`.", + "deprecated": true, "type": [ "array", "null" @@ -106,6 +110,7 @@ }, "extend-per-file-ignores": { "description": "A list of mappings from file pattern to rule codes or prefixes to exclude, in addition to any rules excluded by `per-file-ignores`.", + "deprecated": true, "type": [ "object", "null" @@ -119,6 +124,7 @@ }, "extend-safe-fixes": { "description": "A list of rule codes or prefixes for which unsafe fixes should be considered safe.", + "deprecated": true, "type": [ "array", "null" @@ -129,6 +135,7 @@ }, "extend-select": { "description": "A list of rule codes or prefixes to enable, in addition to those specified by `select`.", + "deprecated": true, "type": [ "array", "null" @@ -150,6 +157,7 @@ }, "extend-unsafe-fixes": { "description": "A list of rule codes or prefixes for which safe fixes should be considered unsafe.", + "deprecated": true, "type": [ "array", "null" @@ -160,6 +168,7 @@ }, "external": { "description": "A list of rule codes or prefixes that are unsupported by Ruff, but should be preserved when (e.g.) validating `# noqa` directives. Useful for retaining `# noqa` directives that cover plugins not yet implemented by Ruff.", + "deprecated": true, "type": [ "array", "null" @@ -184,6 +193,7 @@ }, "fixable": { "description": "A list of rule codes or prefixes to consider fixable. By default, all rules are considered fixable.", + "deprecated": true, "type": [ "array", "null" @@ -194,6 +204,7 @@ }, "flake8-annotations": { "description": "Options for the `flake8-annotations` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8AnnotationsOptions" @@ -205,6 +216,7 @@ }, "flake8-bandit": { "description": "Options for the `flake8-bandit` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8BanditOptions" @@ -216,6 +228,7 @@ }, "flake8-bugbear": { "description": "Options for the `flake8-bugbear` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8BugbearOptions" @@ -227,6 +240,7 @@ }, "flake8-builtins": { "description": "Options for the `flake8-builtins` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8BuiltinsOptions" @@ -238,6 +252,7 @@ }, "flake8-comprehensions": { "description": "Options for the `flake8-comprehensions` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8ComprehensionsOptions" @@ -249,6 +264,7 @@ }, "flake8-copyright": { "description": "Options for the `flake8-copyright` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8CopyrightOptions" @@ -260,6 +276,7 @@ }, "flake8-errmsg": { "description": "Options for the `flake8-errmsg` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8ErrMsgOptions" @@ -271,6 +288,7 @@ }, "flake8-gettext": { "description": "Options for the `flake8-gettext` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8GetTextOptions" @@ -282,6 +300,7 @@ }, "flake8-implicit-str-concat": { "description": "Options for the `flake8-implicit-str-concat` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8ImplicitStrConcatOptions" @@ -293,6 +312,7 @@ }, "flake8-import-conventions": { "description": "Options for the `flake8-import-conventions` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8ImportConventionsOptions" @@ -304,6 +324,7 @@ }, "flake8-pytest-style": { "description": "Options for the `flake8-pytest-style` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8PytestStyleOptions" @@ -315,6 +336,7 @@ }, "flake8-quotes": { "description": "Options for the `flake8-quotes` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8QuotesOptions" @@ -326,6 +348,7 @@ }, "flake8-self": { "description": "Options for the `flake8_self` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8SelfOptions" @@ -337,6 +360,7 @@ }, "flake8-tidy-imports": { "description": "Options for the `flake8-tidy-imports` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8TidyImportsOptions" @@ -348,6 +372,7 @@ }, "flake8-type-checking": { "description": "Options for the `flake8-type-checking` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8TypeCheckingOptions" @@ -359,6 +384,7 @@ }, "flake8-unused-arguments": { "description": "Options for the `flake8-unused-arguments` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Flake8UnusedArgumentsOptions" @@ -388,6 +414,7 @@ }, "ignore": { "description": "A list of rule codes or prefixes to ignore. Prefixes can specify exact rules (like `F841`), entire categories (like `F`), or anything in between.\n\nWhen breaking ties between enabled and disabled rules (via `select` and `ignore`, respectively), more specific prefixes override less specific prefixes.", + "deprecated": true, "type": [ "array", "null" @@ -398,6 +425,7 @@ }, "ignore-init-module-imports": { "description": "Avoid automatically removing unused imports in `__init__.py` files. Such imports will still be flagged, but with a dedicated message suggesting that the import is either added to the module's `__all__` symbol, or re-exported with a redundant alias (e.g., `import os as os`).", + "deprecated": true, "type": [ "boolean", "null" @@ -426,6 +454,7 @@ }, "isort": { "description": "Options for the `isort` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/IsortOptions" @@ -458,6 +487,7 @@ }, "logger-objects": { "description": "A list of objects that should be treated equivalently to a `logging.Logger` object.\n\nThis is useful for ensuring proper diagnostics (e.g., to identify `logging` deprecations and other best-practices) for projects that re-export a `logging.Logger` object from a common module.\n\nFor example, if you have a module `logging_setup.py` with the following contents: ```python import logging\n\nlogger = logging.getLogger(__name__) ```\n\nAdding `\"logging_setup.logger\"` to `logger-objects` will ensure that `logging_setup.logger` is treated as a `logging.Logger` object when imported from other modules (e.g., `from logging_setup import logger`).", + "deprecated": true, "type": [ "array", "null" @@ -468,6 +498,7 @@ }, "mccabe": { "description": "Options for the `mccabe` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/McCabeOptions" @@ -488,7 +519,7 @@ } }, "output-format": { - "description": "The style in which violation messages should be formatted: `\"text\"` (default), `\"grouped\"` (group messages by file), `\"json\"` (machine-readable), `\"junit\"` (machine-readable XML), `\"github\"` (GitHub Actions annotations), `\"gitlab\"` (GitLab CI code quality report), `\"pylint\"` (Pylint text format) or `\"azure\"` (Azure Pipeline logging commands).", + "description": "The style in which violation messages should be formatted: `\"full\"` (shows source),`\"concise\"` (default), `\"grouped\"` (group messages by file), `\"json\"` (machine-readable), `\"junit\"` (machine-readable XML), `\"github\"` (GitHub Actions annotations), `\"gitlab\"` (GitLab CI code quality report), `\"pylint\"` (Pylint text format) or `\"azure\"` (Azure Pipeline logging commands).", "anyOf": [ { "$ref": "#/definitions/SerializationFormat" @@ -500,6 +531,7 @@ }, "pep8-naming": { "description": "Options for the `pep8-naming` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/Pep8NamingOptions" @@ -511,6 +543,7 @@ }, "per-file-ignores": { "description": "A list of mappings from file pattern to rule codes or prefixes to exclude, when considering any matching files.", + "deprecated": true, "type": [ "object", "null" @@ -531,6 +564,7 @@ }, "pycodestyle": { "description": "Options for the `pycodestyle` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/PycodestyleOptions" @@ -542,6 +576,7 @@ }, "pydocstyle": { "description": "Options for the `pydocstyle` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/PydocstyleOptions" @@ -553,6 +588,7 @@ }, "pyflakes": { "description": "Options for the `pyflakes` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/PyflakesOptions" @@ -564,6 +600,7 @@ }, "pylint": { "description": "Options for the `pylint` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/PylintOptions" @@ -575,6 +612,7 @@ }, "pyupgrade": { "description": "Options for the `pyupgrade` plugin.", + "deprecated": true, "anyOf": [ { "$ref": "#/definitions/PyUpgradeOptions" @@ -604,6 +642,7 @@ }, "select": { "description": "A list of rule codes or prefixes to enable. Prefixes can specify exact rules (like `F841`), entire categories (like `F`), or anything in between.\n\nWhen breaking ties between enabled and disabled rules (via `select` and `ignore`, respectively), more specific prefixes override less specific prefixes.", + "deprecated": true, "type": [ "array", "null" @@ -621,6 +660,7 @@ }, "show-source": { "description": "Whether to show source code snippets when reporting lint violations (overridden by the `--show-source` command-line flag).", + "deprecated": true, "type": [ "boolean", "null" @@ -661,6 +701,7 @@ }, "task-tags": { "description": "A list of task tags to recognize (e.g., \"TODO\", \"FIXME\", \"XXX\").\n\nComments starting with these tags will be ignored by commented-out code detection (`ERA`), and skipped by line-length rules (`E501`) if `ignore-overlong-task-comments` is set to `true`.", + "deprecated": true, "type": [ "array", "null" @@ -671,6 +712,7 @@ }, "typing-modules": { "description": "A list of modules whose exports should be treated equivalently to members of the `typing` module.\n\nThis is useful for ensuring proper type annotation inference for projects that re-export `typing` and `typing_extensions` members from a compatibility module. If omitted, any members imported from modules apart from `typing` and `typing_extensions` will be treated as ordinary Python objects.", + "deprecated": true, "type": [ "array", "null" @@ -681,6 +723,7 @@ }, "unfixable": { "description": "A list of rule codes or prefixes to consider non-fixable.", + "deprecated": true, "type": [ "array", "null" @@ -1269,7 +1312,7 @@ "additionalProperties": false }, "FormatOptions": { - "description": "Experimental: Configures how `ruff format` formats your code.\n\nPlease provide feedback in [this discussion](https://github.com/astral-sh/ruff/discussions/7310).", + "description": "Configures the way ruff formats your code.", "type": "object", "properties": { "docstring-code-format": { @@ -1701,7 +1744,7 @@ "minimum": 1.0 }, "LintOptions": { - "description": "Experimental section to configure Ruff's linting. This new section will eventually replace the top-level linting options.\n\nOptions specified in the `lint` section take precedence over the top-level settings.", + "description": "Configures how ruff checks your code.\n\nOptions specified in the `lint` section take precedence over the deprecated top-level settings.", "type": "object", "properties": { "allowed-confusables": { @@ -2623,6 +2666,7 @@ "B032", "B033", "B034", + "B035", "B9", "B90", "B904", @@ -3039,7 +3083,6 @@ "NPY2", "NPY20", "NPY201", - "NURSERY", "PD", "PD0", "PD00", @@ -3077,8 +3120,6 @@ "PGH", "PGH0", "PGH00", - "PGH001", - "PGH002", "PGH003", "PGH004", "PGH005", @@ -3224,7 +3265,6 @@ "PLR1701", "PLR1702", "PLR1704", - "PLR1706", "PLR171", "PLR1711", "PLR1714", @@ -3464,7 +3504,6 @@ "RUF009", "RUF01", "RUF010", - "RUF011", "RUF012", "RUF013", "RUF015", @@ -3646,7 +3685,8 @@ "TCH003", "TCH004", "TCH005", - "TCH006", + "TCH01", + "TCH010", "TD", "TD0", "TD00", @@ -3680,7 +3720,6 @@ "TRY004", "TRY2", "TRY20", - "TRY200", "TRY201", "TRY3", "TRY30", @@ -3775,6 +3814,8 @@ "type": "string", "enum": [ "text", + "concise", + "full", "json", "json-lines", "junit",