diff --git a/crates/ruff/tests/lint.rs b/crates/ruff/tests/lint.rs index 49191f5099b46..fa60551625003 100644 --- a/crates/ruff/tests/lint.rs +++ b/crates/ruff/tests/lint.rs @@ -5436,14 +5436,15 @@ match 2: print("it's one") "# ), - @r" - success: true - exit_code: 0 + @r###" + success: false + exit_code: 1 ----- stdout ----- - All checks passed! + test.py:2:1: SyntaxError: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10) + Found 1 error. ----- stderr ----- - " + "### ); // syntax error on 3.9 with preview diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 3ca0b6ed5bf09..0912c24ae014c 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -65,7 +65,7 @@ use crate::docstrings::extraction::ExtractionTarget; use crate::importer::{ImportRequest, Importer, ResolutionError}; use crate::noqa::NoqaMapping; use crate::package::PackageRoot; -use crate::preview::{is_semantic_errors_enabled, is_undefined_export_in_dunder_init_enabled}; +use crate::preview::is_undefined_export_in_dunder_init_enabled; use crate::registry::{AsRule, Rule}; use crate::rules::pyflakes::rules::{ LateFutureImport, ReturnOutsideFunction, YieldOutsideFunction, @@ -663,9 +663,7 @@ impl SemanticSyntaxContext for Checker<'_> { | SemanticSyntaxErrorKind::AsyncComprehensionInSyncComprehension(_) | SemanticSyntaxErrorKind::DuplicateParameter(_) | SemanticSyntaxErrorKind::NonlocalDeclarationAtModuleLevel => { - if is_semantic_errors_enabled(self.settings) { - self.semantic_errors.borrow_mut().push(error); - } + self.semantic_errors.borrow_mut().push(error); } } } diff --git a/crates/ruff_linter/src/checkers/noqa.rs b/crates/ruff_linter/src/checkers/noqa.rs index d87e4343a96e8..2f830479325ed 100644 --- a/crates/ruff_linter/src/checkers/noqa.rs +++ b/crates/ruff_linter/src/checkers/noqa.rs @@ -12,7 +12,6 @@ use crate::fix::edits::delete_comment; use crate::noqa::{ Code, Directive, FileExemption, FileNoqaDirectives, NoqaDirectives, NoqaMapping, }; -use crate::preview::is_check_file_level_directives_enabled; use crate::registry::{AsRule, Rule, RuleSet}; use crate::rule_redirects::get_redirect_target; use crate::rules::pygrep_hooks; @@ -112,25 +111,16 @@ pub(crate) fn check_noqa( && !exemption.includes(Rule::UnusedNOQA) && !per_file_ignores.contains(Rule::UnusedNOQA) { - let directives: Vec<_> = if is_check_file_level_directives_enabled(settings) { - noqa_directives - .lines() - .iter() - .map(|line| (&line.directive, &line.matches, false)) - .chain( - file_noqa_directives - .lines() - .iter() - .map(|line| (&line.parsed_file_exemption, &line.matches, true)), - ) - .collect() - } else { - noqa_directives - .lines() - .iter() - .map(|line| (&line.directive, &line.matches, false)) - .collect() - }; + let directives = noqa_directives + .lines() + .iter() + .map(|line| (&line.directive, &line.matches, false)) + .chain( + file_noqa_directives + .lines() + .iter() + .map(|line| (&line.parsed_file_exemption, &line.matches, true)), + ); for (directive, matches, is_file_level) in directives { match directive { Directive::All(directive) => { diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index c31e989a46c1b..d900be230958f 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -552,7 +552,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pyupgrade, "045") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP604AnnotationOptional), (Pyupgrade, "046") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP695GenericClass), (Pyupgrade, "047") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP695GenericFunction), - (Pyupgrade, "049") => (RuleGroup::Preview, rules::pyupgrade::rules::PrivateTypeParameter), + (Pyupgrade, "049") => (RuleGroup::Stable, rules::pyupgrade::rules::PrivateTypeParameter), (Pyupgrade, "050") => (RuleGroup::Preview, rules::pyupgrade::rules::UselessClassMetaclassType), // pydocstyle @@ -1019,7 +1019,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Ruff, "049") => (RuleGroup::Preview, rules::ruff::rules::DataclassEnum), (Ruff, "051") => (RuleGroup::Stable, rules::ruff::rules::IfKeyInDictDel), (Ruff, "052") => (RuleGroup::Preview, rules::ruff::rules::UsedDummyVariable), - (Ruff, "053") => (RuleGroup::Preview, rules::ruff::rules::ClassWithMixedTypeVars), + (Ruff, "053") => (RuleGroup::Stable, rules::ruff::rules::ClassWithMixedTypeVars), (Ruff, "054") => (RuleGroup::Preview, rules::ruff::rules::IndentedFormFeed), (Ruff, "055") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRegularExpression), (Ruff, "056") => (RuleGroup::Preview, rules::ruff::rules::FalsyDictGetFallback), @@ -1129,7 +1129,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Refurb, "156") => (RuleGroup::Preview, rules::refurb::rules::HardcodedStringCharset), (Refurb, "157") => (RuleGroup::Preview, rules::refurb::rules::VerboseDecimalConstructor), (Refurb, "161") => (RuleGroup::Stable, rules::refurb::rules::BitCount), - (Refurb, "162") => (RuleGroup::Preview, rules::refurb::rules::FromisoformatReplaceZ), + (Refurb, "162") => (RuleGroup::Stable, rules::refurb::rules::FromisoformatReplaceZ), (Refurb, "163") => (RuleGroup::Stable, rules::refurb::rules::RedundantLogBase), (Refurb, "164") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryFromFloat), (Refurb, "166") => (RuleGroup::Preview, rules::refurb::rules::IntOnSlicedStr), @@ -1148,7 +1148,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { // flake8-logging (Flake8Logging, "001") => (RuleGroup::Stable, rules::flake8_logging::rules::DirectLoggerInstantiation), (Flake8Logging, "002") => (RuleGroup::Stable, rules::flake8_logging::rules::InvalidGetLoggerArgument), - (Flake8Logging, "004") => (RuleGroup::Preview, rules::flake8_logging::rules::LogExceptionOutsideExceptHandler), + (Flake8Logging, "004") => (RuleGroup::Stable, rules::flake8_logging::rules::LogExceptionOutsideExceptHandler), (Flake8Logging, "007") => (RuleGroup::Stable, rules::flake8_logging::rules::ExceptionWithoutExcInfo), (Flake8Logging, "009") => (RuleGroup::Stable, rules::flake8_logging::rules::UndocumentedWarn), (Flake8Logging, "014") => (RuleGroup::Preview, rules::flake8_logging::rules::ExcInfoOutsideExceptHandler), diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index 2b31495b2a9ca..422c1348c58df 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -30,7 +30,7 @@ use crate::fix::{FixResult, fix_file}; use crate::message::Message; use crate::noqa::add_noqa; use crate::package::PackageRoot; -use crate::preview::{is_py314_support_enabled, is_unsupported_syntax_enabled}; +use crate::preview::is_py314_support_enabled; use crate::registry::{AsRule, Rule, RuleSet}; #[cfg(any(feature = "test-rules", test))] use crate::rules::ruff::rules::test_rules::{self, TEST_RULES, TestRule}; @@ -447,11 +447,7 @@ pub fn check_path( } } - let syntax_errors = if is_unsupported_syntax_enabled(settings) { - parsed.unsupported_syntax_errors() - } else { - &[] - }; + let syntax_errors = parsed.unsupported_syntax_errors(); diagnostics_to_messages( diagnostics, diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index 50b4fac603c04..32f4b06a5bc70 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -7,17 +7,6 @@ use crate::settings::LinterSettings; -// https://github.com/astral-sh/ruff/issues/17412 -// https://github.com/astral-sh/ruff/issues/11934 -pub(crate) const fn is_semantic_errors_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - -// https://github.com/astral-sh/ruff/pull/16429 -pub(crate) const fn is_unsupported_syntax_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - pub(crate) const fn is_py314_support_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() } @@ -29,23 +18,11 @@ pub(crate) const fn is_full_path_match_source_strategy_enabled(settings: &Linter // Rule-specific behavior -// https://github.com/astral-sh/ruff/pull/17136 -pub(crate) const fn is_shell_injection_only_trusted_input_enabled( - settings: &LinterSettings, -) -> bool { - settings.preview.is_enabled() -} - // https://github.com/astral-sh/ruff/pull/15541 pub(crate) const fn is_suspicious_function_reference_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() } -// https://github.com/astral-sh/ruff/pull/7501 -pub(crate) const fn is_bool_subtype_of_annotation_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - // https://github.com/astral-sh/ruff/pull/10759 pub(crate) const fn is_comprehension_with_min_max_sum_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() @@ -63,21 +40,11 @@ pub(crate) const fn is_bad_version_info_in_non_stub_enabled(settings: &LinterSet settings.preview.is_enabled() } -// https://github.com/astral-sh/ruff/pull/12676 -pub(crate) const fn is_fix_future_annotations_in_stub_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - // https://github.com/astral-sh/ruff/pull/11074 pub(crate) const fn is_only_add_return_none_at_end_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() } -// https://github.com/astral-sh/ruff/pull/12796 -pub(crate) const fn is_simplify_ternary_to_binary_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - // https://github.com/astral-sh/ruff/pull/16719 pub(crate) const fn is_fix_manual_dict_comprehension_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() @@ -104,13 +71,6 @@ pub(crate) const fn is_unicode_to_unicode_confusables_enabled(settings: &LinterS settings.preview.is_enabled() } -// https://github.com/astral-sh/ruff/pull/17078 -pub(crate) const fn is_support_slices_in_literal_concatenation_enabled( - settings: &LinterSettings, -) -> bool { - settings.preview.is_enabled() -} - // https://github.com/astral-sh/ruff/pull/11370 pub(crate) const fn is_undefined_export_in_dunder_init_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() @@ -121,16 +81,9 @@ pub(crate) const fn is_allow_nested_roots_enabled(settings: &LinterSettings) -> settings.preview.is_enabled() } -// https://github.com/astral-sh/ruff/pull/17061 -pub(crate) const fn is_check_file_level_directives_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - -// https://github.com/astral-sh/ruff/pull/17644 -pub(crate) const fn is_readlines_in_for_fix_safe_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - -pub(crate) const fn multiple_with_statements_fix_safe_enabled(settings: &LinterSettings) -> bool { +// https://github.com/astral-sh/ruff/pull/18208 +pub(crate) const fn is_multiple_with_statements_fix_safe_enabled( + settings: &LinterSettings, +) -> bool { settings.preview.is_enabled() } diff --git a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs index 868367d6f7635..48f3fe251f6ce 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs @@ -104,7 +104,6 @@ mod tests { #[test_case(Rule::SuspiciousURLOpenUsage, Path::new("S310.py"))] #[test_case(Rule::SuspiciousNonCryptographicRandomUsage, Path::new("S311.py"))] #[test_case(Rule::SuspiciousTelnetUsage, Path::new("S312.py"))] - #[test_case(Rule::SubprocessWithoutShellEqualsTrue, Path::new("S603.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs index e8bc2204f1e7d..6a423ea5b8f17 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs @@ -7,7 +7,6 @@ use ruff_python_semantic::SemanticModel; use ruff_text_size::Ranged; use crate::Violation; -use crate::preview::is_shell_injection_only_trusted_input_enabled; use crate::{ checkers::ast::Checker, registry::Rule, rules::flake8_bandit::helpers::string_literal, }; @@ -325,9 +324,7 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) { } // S603 _ => { - if !is_trusted_input(arg) - || !is_shell_injection_only_trusted_input_enabled(checker.settings) - { + if !is_trusted_input(arg) { if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) { checker.report_diagnostic( SubprocessWithoutShellEqualsTrue, diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap index 13003f8def1c3..2b8d7c974f855 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap @@ -106,74 +106,6 @@ S603.py:21:1: S603 `subprocess` call: check for execution of untrusted input 23 | # Literals are fine, they're trusted. | -S603.py:24:1: S603 `subprocess` call: check for execution of untrusted input - | -23 | # Literals are fine, they're trusted. -24 | run("true") - | ^^^ S603 -25 | Popen(["true"]) -26 | Popen("true", shell=False) - | - -S603.py:25:1: S603 `subprocess` call: check for execution of untrusted input - | -23 | # Literals are fine, they're trusted. -24 | run("true") -25 | Popen(["true"]) - | ^^^^^ S603 -26 | Popen("true", shell=False) -27 | call("true", shell=False) - | - -S603.py:26:1: S603 `subprocess` call: check for execution of untrusted input - | -24 | run("true") -25 | Popen(["true"]) -26 | Popen("true", shell=False) - | ^^^^^ S603 -27 | call("true", shell=False) -28 | check_call("true", shell=False) - | - -S603.py:27:1: S603 `subprocess` call: check for execution of untrusted input - | -25 | Popen(["true"]) -26 | Popen("true", shell=False) -27 | call("true", shell=False) - | ^^^^ S603 -28 | check_call("true", shell=False) -29 | check_output("true", shell=False) - | - -S603.py:28:1: S603 `subprocess` call: check for execution of untrusted input - | -26 | Popen("true", shell=False) -27 | call("true", shell=False) -28 | check_call("true", shell=False) - | ^^^^^^^^^^ S603 -29 | check_output("true", shell=False) -30 | run("true", shell=False) - | - -S603.py:29:1: S603 `subprocess` call: check for execution of untrusted input - | -27 | call("true", shell=False) -28 | check_call("true", shell=False) -29 | check_output("true", shell=False) - | ^^^^^^^^^^^^ S603 -30 | run("true", shell=False) - | - -S603.py:30:1: S603 `subprocess` call: check for execution of untrusted input - | -28 | check_call("true", shell=False) -29 | check_output("true", shell=False) -30 | run("true", shell=False) - | ^^^ S603 -31 | -32 | # Not through assignments though. - | - S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input | 32 | # Not through assignments though. @@ -184,15 +116,6 @@ S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input 36 | # Instant named expressions are fine. | -S603.py:37:1: S603 `subprocess` call: check for execution of untrusted input - | -36 | # Instant named expressions are fine. -37 | run(c := "true") - | ^^^ S603 -38 | -39 | # But non-instant are not. - | - S603.py:41:1: S603 `subprocess` call: check for execution of untrusted input | 39 | # But non-instant are not. @@ -200,20 +123,3 @@ S603.py:41:1: S603 `subprocess` call: check for execution of untrusted input 41 | run(e) | ^^^ S603 | - -S603.py:46:1: S603 `subprocess` call: check for execution of untrusted input - | -44 | # https://github.com/astral-sh/ruff/issues/17798 -45 | # Tuple literals are trusted -46 | check_output(("literal", "cmd", "using", "tuple"), text=True) - | ^^^^^^^^^^^^ S603 -47 | Popen(("literal", "cmd", "using", "tuple")) - | - -S603.py:47:1: S603 `subprocess` call: check for execution of untrusted input - | -45 | # Tuple literals are trusted -46 | check_output(("literal", "cmd", "using", "tuple"), text=True) -47 | Popen(("literal", "cmd", "using", "tuple")) - | ^^^^^ S603 - | diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S603_S603.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S603_S603.py.snap deleted file mode 100644 index 2b8d7c974f855..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S603_S603.py.snap +++ /dev/null @@ -1,125 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs ---- -S603.py:5:1: S603 `subprocess` call: check for execution of untrusted input - | -3 | # Different Popen wrappers are checked. -4 | a = input() -5 | Popen(a, shell=False) - | ^^^^^ S603 -6 | call(a, shell=False) -7 | check_call(a, shell=False) - | - -S603.py:6:1: S603 `subprocess` call: check for execution of untrusted input - | -4 | a = input() -5 | Popen(a, shell=False) -6 | call(a, shell=False) - | ^^^^ S603 -7 | check_call(a, shell=False) -8 | check_output(a, shell=False) - | - -S603.py:7:1: S603 `subprocess` call: check for execution of untrusted input - | -5 | Popen(a, shell=False) -6 | call(a, shell=False) -7 | check_call(a, shell=False) - | ^^^^^^^^^^ S603 -8 | check_output(a, shell=False) -9 | run(a, shell=False) - | - -S603.py:8:1: S603 `subprocess` call: check for execution of untrusted input - | -6 | call(a, shell=False) -7 | check_call(a, shell=False) -8 | check_output(a, shell=False) - | ^^^^^^^^^^^^ S603 -9 | run(a, shell=False) - | - -S603.py:9:1: S603 `subprocess` call: check for execution of untrusted input - | - 7 | check_call(a, shell=False) - 8 | check_output(a, shell=False) - 9 | run(a, shell=False) - | ^^^ S603 -10 | -11 | # Falsey values are treated as false. - | - -S603.py:12:1: S603 `subprocess` call: check for execution of untrusted input - | -11 | # Falsey values are treated as false. -12 | Popen(a, shell=0) - | ^^^^^ S603 -13 | Popen(a, shell=[]) -14 | Popen(a, shell={}) - | - -S603.py:13:1: S603 `subprocess` call: check for execution of untrusted input - | -11 | # Falsey values are treated as false. -12 | Popen(a, shell=0) -13 | Popen(a, shell=[]) - | ^^^^^ S603 -14 | Popen(a, shell={}) -15 | Popen(a, shell=None) - | - -S603.py:14:1: S603 `subprocess` call: check for execution of untrusted input - | -12 | Popen(a, shell=0) -13 | Popen(a, shell=[]) -14 | Popen(a, shell={}) - | ^^^^^ S603 -15 | Popen(a, shell=None) - | - -S603.py:15:1: S603 `subprocess` call: check for execution of untrusted input - | -13 | Popen(a, shell=[]) -14 | Popen(a, shell={}) -15 | Popen(a, shell=None) - | ^^^^^ S603 -16 | -17 | # Unknown values are treated as falsey. - | - -S603.py:18:1: S603 `subprocess` call: check for execution of untrusted input - | -17 | # Unknown values are treated as falsey. -18 | Popen(a, shell=True if True else False) - | ^^^^^ S603 -19 | -20 | # No value is also caught. - | - -S603.py:21:1: S603 `subprocess` call: check for execution of untrusted input - | -20 | # No value is also caught. -21 | Popen(a) - | ^^^^^ S603 -22 | -23 | # Literals are fine, they're trusted. - | - -S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input - | -32 | # Not through assignments though. -33 | cmd = ["true"] -34 | run(cmd) - | ^^^ S603 -35 | -36 | # Instant named expressions are fine. - | - -S603.py:41:1: S603 `subprocess` call: check for execution of untrusted input - | -39 | # But non-instant are not. -40 | (e := "echo") -41 | run(e) - | ^^^ S603 - | diff --git a/crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs b/crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs index 8268ca726b27d..3307cb949d869 100644 --- a/crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs @@ -12,7 +12,6 @@ mod tests { use crate::registry::Rule; use crate::settings::LinterSettings; - use crate::settings::types::PreviewMode; use crate::test::test_path; use crate::{assert_messages, settings}; @@ -29,24 +28,6 @@ mod tests { Ok(()) } - #[test_case(Rule::BooleanTypeHintPositionalArgument, Path::new("FBT.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_boolean_trap").join(path).as_path(), - &settings::LinterSettings { - preview: PreviewMode::Enabled, - ..settings::LinterSettings::for_rule(rule_code) - }, - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } - #[test] fn extend_allowed_callable() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_type_hint_positional_argument.rs b/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_type_hint_positional_argument.rs index 51bd830a66fd9..24dff35099b1f 100644 --- a/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_type_hint_positional_argument.rs +++ b/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_type_hint_positional_argument.rs @@ -7,12 +7,12 @@ use ruff_python_semantic::analyze::visibility; use crate::Violation; use crate::checkers::ast::Checker; -use crate::preview::is_bool_subtype_of_annotation_enabled; use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def; /// ## What it does /// Checks for the use of boolean positional arguments in function definitions, -/// as determined by the presence of a `bool` type hint. +/// as determined by the presence of a type hint containing `bool` as an +/// evident subtype - e.g. `bool`, `bool | int`, `typing.Optional[bool]`, etc. /// /// ## Why is this bad? /// Calling a function with boolean positional arguments is confusing as the @@ -30,9 +30,6 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def; /// Dunder methods that define operators are exempt from this rule, as are /// setters and `@override` definitions. /// -/// In [preview], this rule will also flag annotations that include boolean -/// variants, like `bool | int`. -/// /// ## Example /// /// ```python @@ -96,8 +93,6 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def; /// ## References /// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls) /// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/) -/// -/// [preview]: https://docs.astral.sh/ruff/preview/ #[derive(ViolationMetadata)] pub(crate) struct BooleanTypeHintPositionalArgument; @@ -128,14 +123,8 @@ pub(crate) fn boolean_type_hint_positional_argument( let Some(annotation) = parameter.annotation() else { continue; }; - if is_bool_subtype_of_annotation_enabled(checker.settings) { - if !match_annotation_to_complex_bool(annotation, checker.semantic()) { - continue; - } - } else { - if !match_annotation_to_literal_bool(annotation) { - continue; - } + if !match_annotation_to_complex_bool(annotation, checker.semantic()) { + continue; } // Allow Boolean type hints in setters. @@ -161,17 +150,6 @@ pub(crate) fn boolean_type_hint_positional_argument( } } -/// Returns `true` if the annotation is a boolean type hint (e.g., `bool`). -fn match_annotation_to_literal_bool(annotation: &Expr) -> bool { - match annotation { - // Ex) `True` - Expr::Name(name) => &name.id == "bool", - // Ex) `"True"` - Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value == "bool", - _ => false, - } -} - /// Returns `true` if the annotation is a boolean type hint (e.g., `bool`), or a type hint that /// includes boolean as a variant (e.g., `bool | int`). fn match_annotation_to_complex_bool(annotation: &Expr, semantic: &SemanticModel) -> bool { diff --git a/crates/ruff_linter/src/rules/flake8_boolean_trap/snapshots/ruff_linter__rules__flake8_boolean_trap__tests__FBT001_FBT.py.snap b/crates/ruff_linter/src/rules/flake8_boolean_trap/snapshots/ruff_linter__rules__flake8_boolean_trap__tests__FBT001_FBT.py.snap index ea925637b517e..ee91fa49f8658 100644 --- a/crates/ruff_linter/src/rules/flake8_boolean_trap/snapshots/ruff_linter__rules__flake8_boolean_trap__tests__FBT001_FBT.py.snap +++ b/crates/ruff_linter/src/rules/flake8_boolean_trap/snapshots/ruff_linter__rules__flake8_boolean_trap__tests__FBT001_FBT.py.snap @@ -1,6 +1,5 @@ --- source: crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs -snapshot_kind: text --- FBT.py:4:5: FBT001 Boolean-typed positional argument in function definition | @@ -89,3 +88,17 @@ FBT.py:90:19: FBT001 Boolean-typed positional argument in function definition | ^^^^^ FBT001 91 | pass | + +FBT.py:100:10: FBT001 Boolean-typed positional argument in function definition + | +100 | def func(x: Union[list, Optional[int | str | float | bool]]): + | ^ FBT001 +101 | pass + | + +FBT.py:104:10: FBT001 Boolean-typed positional argument in function definition + | +104 | def func(x: bool | str): + | ^ FBT001 +105 | pass + | diff --git a/crates/ruff_linter/src/rules/flake8_boolean_trap/snapshots/ruff_linter__rules__flake8_boolean_trap__tests__preview__FBT001_FBT.py.snap b/crates/ruff_linter/src/rules/flake8_boolean_trap/snapshots/ruff_linter__rules__flake8_boolean_trap__tests__preview__FBT001_FBT.py.snap deleted file mode 100644 index d3ab33ec5c3df..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_boolean_trap/snapshots/ruff_linter__rules__flake8_boolean_trap__tests__preview__FBT001_FBT.py.snap +++ /dev/null @@ -1,105 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs -snapshot_kind: text ---- -FBT.py:4:5: FBT001 Boolean-typed positional argument in function definition - | -2 | posonly_nohint, -3 | posonly_nonboolhint: int, -4 | posonly_boolhint: bool, - | ^^^^^^^^^^^^^^^^ FBT001 -5 | posonly_boolstrhint: "bool", -6 | /, - | - -FBT.py:5:5: FBT001 Boolean-typed positional argument in function definition - | -3 | posonly_nonboolhint: int, -4 | posonly_boolhint: bool, -5 | posonly_boolstrhint: "bool", - | ^^^^^^^^^^^^^^^^^^^ FBT001 -6 | /, -7 | offset, - | - -FBT.py:10:5: FBT001 Boolean-typed positional argument in function definition - | - 8 | posorkw_nonvalued_nohint, - 9 | posorkw_nonvalued_nonboolhint: int, -10 | posorkw_nonvalued_boolhint: bool, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001 -11 | posorkw_nonvalued_boolstrhint: "bool", -12 | posorkw_boolvalued_nohint=True, - | - -FBT.py:11:5: FBT001 Boolean-typed positional argument in function definition - | - 9 | posorkw_nonvalued_nonboolhint: int, -10 | posorkw_nonvalued_boolhint: bool, -11 | posorkw_nonvalued_boolstrhint: "bool", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001 -12 | posorkw_boolvalued_nohint=True, -13 | posorkw_boolvalued_nonboolhint: int = True, - | - -FBT.py:14:5: FBT001 Boolean-typed positional argument in function definition - | -12 | posorkw_boolvalued_nohint=True, -13 | posorkw_boolvalued_nonboolhint: int = True, -14 | posorkw_boolvalued_boolhint: bool = True, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001 -15 | posorkw_boolvalued_boolstrhint: "bool" = True, -16 | posorkw_nonboolvalued_nohint=1, - | - -FBT.py:15:5: FBT001 Boolean-typed positional argument in function definition - | -13 | posorkw_boolvalued_nonboolhint: int = True, -14 | posorkw_boolvalued_boolhint: bool = True, -15 | posorkw_boolvalued_boolstrhint: "bool" = True, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001 -16 | posorkw_nonboolvalued_nohint=1, -17 | posorkw_nonboolvalued_nonboolhint: int = 2, - | - -FBT.py:18:5: FBT001 Boolean-typed positional argument in function definition - | -16 | posorkw_nonboolvalued_nohint=1, -17 | posorkw_nonboolvalued_nonboolhint: int = 2, -18 | posorkw_nonboolvalued_boolhint: bool = 3, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001 -19 | posorkw_nonboolvalued_boolstrhint: "bool" = 4, -20 | *, - | - -FBT.py:19:5: FBT001 Boolean-typed positional argument in function definition - | -17 | posorkw_nonboolvalued_nonboolhint: int = 2, -18 | posorkw_nonboolvalued_boolhint: bool = 3, -19 | posorkw_nonboolvalued_boolstrhint: "bool" = 4, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001 -20 | *, -21 | kwonly_nonvalued_nohint, - | - -FBT.py:90:19: FBT001 Boolean-typed positional argument in function definition - | -89 | # FBT001: Boolean positional arg in function definition -90 | def foo(self, value: bool) -> None: - | ^^^^^ FBT001 -91 | pass - | - -FBT.py:100:10: FBT001 Boolean-typed positional argument in function definition - | -100 | def func(x: Union[list, Optional[int | str | float | bool]]): - | ^ FBT001 -101 | pass - | - -FBT.py:104:10: FBT001 Boolean-typed positional argument in function definition - | -104 | def func(x: bool | str): - | ^ FBT001 -105 | pass - | diff --git a/crates/ruff_linter/src/rules/flake8_logging/rules/log_exception_outside_except_handler.rs b/crates/ruff_linter/src/rules/flake8_logging/rules/log_exception_outside_except_handler.rs index 1f2eb173bd583..5a7062cb1b7fb 100644 --- a/crates/ruff_linter/src/rules/flake8_logging/rules/log_exception_outside_except_handler.rs +++ b/crates/ruff_linter/src/rules/flake8_logging/rules/log_exception_outside_except_handler.rs @@ -11,7 +11,7 @@ use crate::{Edit, Fix, FixAvailability, Violation}; /// Checks for `.exception()` logging calls outside of exception handlers. /// /// ## Why is this bad? -/// [The documentation] states: +/// The Python `logging` [documentation] states: /// > This function should only be called from an exception handler. /// /// Calling `.exception()` outside of an exception handler @@ -23,6 +23,9 @@ use crate::{Edit, Fix, FixAvailability, Violation}; /// NoneType: None /// ``` /// +/// Although this confusion can be avoided by passing an explicit `exc_info` keyword argument, this +/// rule will still emit a diagnostic, in line with the `logging` documentation. +/// /// ## Example /// /// ```python @@ -42,7 +45,7 @@ use crate::{Edit, Fix, FixAvailability, Violation}; /// ## Fix safety /// The fix, if available, will always be marked as unsafe, as it changes runtime behavior. /// -/// [The documentation]: https://docs.python.org/3/library/logging.html#logging.exception +/// [documentation]: https://docs.python.org/3/library/logging.html#logging.exception #[derive(ViolationMetadata)] pub(crate) struct LogExceptionOutsideExceptHandler; diff --git a/crates/ruff_linter/src/rules/flake8_pyi/mod.rs b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs index ea59b73d1c96f..605bf896b2302 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs @@ -11,7 +11,6 @@ mod tests { use crate::registry::Rule; use crate::rules::pep8_naming; - use crate::settings::types::PreviewMode; use crate::test::test_path; use crate::{assert_messages, settings}; @@ -172,22 +171,4 @@ mod tests { assert_messages!(snapshot, diagnostics); Ok(()) } - - #[test_case(Rule::FutureAnnotationsInStub, Path::new("PYI044.pyi"))] - 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_pyi").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_pyi/rules/future_annotations_in_stub.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/future_annotations_in_stub.rs index 398f4ad22e33d..20c70336eb642 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/future_annotations_in_stub.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/future_annotations_in_stub.rs @@ -3,7 +3,7 @@ use ruff_python_ast::StmtImportFrom; use ruff_macros::{ViolationMetadata, derive_message_formats}; use crate::{Fix, FixAvailability, Violation}; -use crate::{checkers::ast::Checker, fix, preview::is_fix_future_annotations_in_stub_enabled}; +use crate::{checkers::ast::Checker, fix}; /// ## What it does /// Checks for the presence of the `from __future__ import annotations` import @@ -55,20 +55,18 @@ pub(crate) fn from_future_import(checker: &Checker, target: &StmtImportFrom) { let mut diagnostic = checker.report_diagnostic(FutureAnnotationsInStub, *range); - if is_fix_future_annotations_in_stub_enabled(checker.settings) { - let stmt = checker.semantic().current_statement(); + let stmt = checker.semantic().current_statement(); - diagnostic.try_set_fix(|| { - let edit = fix::edits::remove_unused_imports( - std::iter::once("annotations"), - stmt, - None, - checker.locator(), - checker.stylist(), - checker.indexer(), - )?; + diagnostic.try_set_fix(|| { + let edit = fix::edits::remove_unused_imports( + std::iter::once("annotations"), + stmt, + None, + checker.locator(), + checker.stylist(), + checker.indexer(), + )?; - Ok(Fix::safe_edit(edit)) - }); - } + Ok(Fix::safe_edit(edit)) + }); } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI044_PYI044.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI044_PYI044.pyi.snap index bb018c27edb74..4ae21763be417 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI044_PYI044.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI044_PYI044.pyi.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI044.pyi:2:1: PYI044 `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics +PYI044.pyi:2:1: PYI044 [*] `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics | 1 | # Bad import. 2 | from __future__ import annotations # PYI044. @@ -10,7 +10,14 @@ PYI044.pyi:2:1: PYI044 `from __future__ import annotations` has no effect in stu | = help: Remove `from __future__ import annotations` -PYI044.pyi:3:1: PYI044 `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics +ℹ Safe fix +1 1 | # Bad import. +2 |-from __future__ import annotations # PYI044. +3 2 | from __future__ import annotations, with_statement # PYI044. +4 3 | +5 4 | # Good imports. + +PYI044.pyi:3:1: PYI044 [*] `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics | 1 | # Bad import. 2 | from __future__ import annotations # PYI044. @@ -20,3 +27,12 @@ PYI044.pyi:3:1: PYI044 `from __future__ import annotations` has no effect in stu 5 | # Good imports. | = help: Remove `from __future__ import annotations` + +ℹ Safe fix +1 1 | # Bad import. +2 2 | from __future__ import annotations # PYI044. +3 |-from __future__ import annotations, with_statement # PYI044. + 3 |+from __future__ import with_statement # PYI044. +4 4 | +5 5 | # Good imports. +6 6 | from __future__ import with_statement diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__preview__PYI044_PYI044.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__preview__PYI044_PYI044.pyi.snap deleted file mode 100644 index 4ae21763be417..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__preview__PYI044_PYI044.pyi.snap +++ /dev/null @@ -1,38 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs ---- -PYI044.pyi:2:1: PYI044 [*] `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics - | -1 | # Bad import. -2 | from __future__ import annotations # PYI044. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI044 -3 | from __future__ import annotations, with_statement # PYI044. - | - = help: Remove `from __future__ import annotations` - -ℹ Safe fix -1 1 | # Bad import. -2 |-from __future__ import annotations # PYI044. -3 2 | from __future__ import annotations, with_statement # PYI044. -4 3 | -5 4 | # Good imports. - -PYI044.pyi:3:1: PYI044 [*] `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics - | -1 | # Bad import. -2 | from __future__ import annotations # PYI044. -3 | from __future__ import annotations, with_statement # PYI044. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI044 -4 | -5 | # Good imports. - | - = help: Remove `from __future__ import annotations` - -ℹ Safe fix -1 1 | # Bad import. -2 2 | from __future__ import annotations # PYI044. -3 |-from __future__ import annotations, with_statement # PYI044. - 3 |+from __future__ import with_statement # PYI044. -4 4 | -5 5 | # Good imports. -6 6 | from __future__ import with_statement diff --git a/crates/ruff_linter/src/rules/flake8_simplify/mod.rs b/crates/ruff_linter/src/rules/flake8_simplify/mod.rs index 1041df4615c68..2e0bd735dcc17 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/mod.rs @@ -58,7 +58,6 @@ mod tests { Ok(()) } - #[test_case(Rule::IfElseBlockInsteadOfIfExp, Path::new("SIM108.py"))] #[test_case(Rule::MultipleWithStatements, Path::new("SIM117.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs index 537fb1f9d905d..e98ef097234c1 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs @@ -10,7 +10,7 @@ use super::fix_with; use crate::Fix; use crate::checkers::ast::Checker; use crate::fix::edits::fits; -use crate::preview::multiple_with_statements_fix_safe_enabled; +use crate::preview::is_multiple_with_statements_fix_safe_enabled; use crate::{FixAvailability, Violation}; /// ## What it does @@ -195,7 +195,7 @@ pub(crate) fn multiple_with_statements( checker.settings.tab_size, ) }) { - if multiple_with_statements_fix_safe_enabled(checker.settings) { + if is_multiple_with_statements_fix_safe_enabled(checker.settings) { Ok(Some(Fix::safe_edit(edit))) } else { Ok(Some(Fix::unsafe_edit(edit))) diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_if_exp.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_if_exp.rs index 7de2bb14d33cc..1b4c66bf24872 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_if_exp.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_if_exp.rs @@ -7,13 +7,14 @@ use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; use crate::fix::edits::fits; -use crate::preview::is_simplify_ternary_to_binary_enabled; use crate::{Edit, Fix, FixAvailability, Violation}; /// ## What it does -/// Check for `if`-`else`-blocks that can be replaced with a ternary operator. -/// Moreover, in [preview], check if these ternary expressions can be -/// further simplified to binary expressions. +/// Check for `if`-`else`-blocks that can be replaced with a ternary +/// or binary operator. +/// +/// The lint is suppressed if the suggested replacement would exceed +/// the maximum line length configured in [pycodestyle.max-line-length]. /// /// ## Why is this bad? /// `if`-`else`-blocks that assign a value to a variable in both branches can @@ -33,7 +34,7 @@ use crate::{Edit, Fix, FixAvailability, Violation}; /// bar = x if foo else y /// ``` /// -/// Or, in [preview]: +/// Or: /// /// ```python /// if cond: @@ -57,8 +58,8 @@ use crate::{Edit, Fix, FixAvailability, Violation}; /// ## References /// - [Python documentation: Conditional expressions](https://docs.python.org/3/reference/expressions.html#conditional-expressions) /// -/// [preview]: https://docs.astral.sh/ruff/preview/ /// [code coverage]: https://github.com/nedbat/coveragepy/issues/509 +/// [pycodestyle.max-line-length]: https://docs.astral.sh/ruff/settings/#lint_pycodestyle_max-line-length #[derive(ViolationMetadata)] pub(crate) struct IfElseBlockInsteadOfIfExp { /// The ternary or binary expression to replace the `if`-`else`-block. @@ -183,16 +184,12 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast:: // // The match statement below implements the following // logic: - // - If `test == body_value` and preview enabled, replace with `target_var = test or else_value` - // - If `test == not body_value` and preview enabled, replace with `target_var = body_value and else_value` - // - If `not test == body_value` and preview enabled, replace with `target_var = body_value and else_value` + // - If `test == body_value`, replace with `target_var = test or else_value` + // - If `test == not body_value`, replace with `target_var = body_value and else_value` + // - If `not test == body_value`, replace with `target_var = body_value and else_value` // - Otherwise, replace with `target_var = body_value if test else else_value` - let (contents, assignment_kind) = match ( - is_simplify_ternary_to_binary_enabled(checker.settings), - test, - body_value, - ) { - (true, test_node, body_node) + let (contents, assignment_kind) = match (test, body_value) { + (test_node, body_node) if ComparableExpr::from(test_node) == ComparableExpr::from(body_node) && !contains_effect(test_node, |id| checker.semantic().has_builtin_binding(id)) => { @@ -200,7 +197,7 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast:: let binary = assignment_binary_or(target_var, body_value, else_value); (checker.generator().stmt(&binary), AssignmentKind::Binary) } - (true, test_node, body_node) + (test_node, body_node) if (test_node.as_unary_op_expr().is_some_and(|op_expr| { op_expr.op.is_not() && ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(body_node) diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM108_SIM108.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM108_SIM108.py.snap index a18c5fdac5b1b..b4d70317ad555 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM108_SIM108.py.snap +++ b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM108_SIM108.py.snap @@ -118,7 +118,7 @@ SIM108.py:117:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of ` | = help: Replace `if`-`else`-block with `x = 3 if True else 5` -SIM108.py:141:1: SIM108 [*] Use ternary operator `z = cond if cond else other_cond` instead of `if`-`else`-block +SIM108.py:141:1: SIM108 [*] Use binary operator `z = cond or other_cond` instead of `if`-`else`-block | 139 | # SIM108 - should suggest 140 | # z = cond or other_cond @@ -130,7 +130,7 @@ SIM108.py:141:1: SIM108 [*] Use ternary operator `z = cond if cond else other_co 145 | 146 | # SIM108 - should suggest | - = help: Replace `if`-`else`-block with `z = cond if cond else other_cond` + = help: Replace `if`-`else`-block with `z = cond or other_cond` ℹ Unsafe fix 138 138 | @@ -140,12 +140,12 @@ SIM108.py:141:1: SIM108 [*] Use ternary operator `z = cond if cond else other_co 142 |- z = cond 143 |-else: 144 |- z = other_cond - 141 |+z = cond if cond else other_cond + 141 |+z = cond or other_cond 145 142 | 146 143 | # SIM108 - should suggest 147 144 | # z = cond and other_cond -SIM108.py:148:1: SIM108 [*] Use ternary operator `z = cond if not cond else other_cond` instead of `if`-`else`-block +SIM108.py:148:1: SIM108 [*] Use binary operator `z = cond and other_cond` instead of `if`-`else`-block | 146 | # SIM108 - should suggest 147 | # z = cond and other_cond @@ -157,7 +157,7 @@ SIM108.py:148:1: SIM108 [*] Use ternary operator `z = cond if not cond else othe 152 | 153 | # SIM108 - should suggest | - = help: Replace `if`-`else`-block with `z = cond if not cond else other_cond` + = help: Replace `if`-`else`-block with `z = cond and other_cond` ℹ Unsafe fix 145 145 | @@ -167,12 +167,12 @@ SIM108.py:148:1: SIM108 [*] Use ternary operator `z = cond if not cond else othe 149 |- z = cond 150 |-else: 151 |- z = other_cond - 148 |+z = cond if not cond else other_cond + 148 |+z = cond and other_cond 152 149 | 153 150 | # SIM108 - should suggest 154 151 | # z = not cond and other_cond -SIM108.py:155:1: SIM108 [*] Use ternary operator `z = not cond if cond else other_cond` instead of `if`-`else`-block +SIM108.py:155:1: SIM108 [*] Use binary operator `z = not cond and other_cond` instead of `if`-`else`-block | 153 | # SIM108 - should suggest 154 | # z = not cond and other_cond @@ -184,7 +184,7 @@ SIM108.py:155:1: SIM108 [*] Use ternary operator `z = not cond if cond else othe 159 | 160 | # SIM108 does not suggest | - = help: Replace `if`-`else`-block with `z = not cond if cond else other_cond` + = help: Replace `if`-`else`-block with `z = not cond and other_cond` ℹ Unsafe fix 152 152 | @@ -194,7 +194,7 @@ SIM108.py:155:1: SIM108 [*] Use ternary operator `z = not cond if cond else othe 156 |- z = not cond 157 |-else: 158 |- z = other_cond - 155 |+z = not cond if cond else other_cond + 155 |+z = not cond and other_cond 159 156 | 160 157 | # SIM108 does not suggest 161 158 | # a binary option in these cases, diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM108_SIM108.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM108_SIM108.py.snap deleted file mode 100644 index b4d70317ad555..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__preview__SIM108_SIM108.py.snap +++ /dev/null @@ -1,382 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs ---- -SIM108.py:2:1: SIM108 [*] Use ternary operator `b = c if a else d` instead of `if`-`else`-block - | -1 | # SIM108 -2 | / if a: -3 | | b = c -4 | | else: -5 | | b = d - | |_________^ SIM108 -6 | -7 | # OK - | - = help: Replace `if`-`else`-block with `b = c if a else d` - -ℹ Unsafe fix -1 1 | # SIM108 -2 |-if a: -3 |- b = c -4 |-else: -5 |- b = d - 2 |+b = c if a else d -6 3 | -7 4 | # OK -8 5 | b = c if a else d - -SIM108.py:30:5: SIM108 [*] Use ternary operator `b = 1 if a else 2` instead of `if`-`else`-block - | -28 | pass -29 | else: -30 | / if a: -31 | | b = 1 -32 | | else: -33 | | b = 2 - | |_____________^ SIM108 - | - = help: Replace `if`-`else`-block with `b = 1 if a else 2` - -ℹ Unsafe fix -27 27 | if True: -28 28 | pass -29 29 | else: -30 |- if a: -31 |- b = 1 -32 |- else: -33 |- b = 2 - 30 |+ b = 1 if a else 2 -34 31 | -35 32 | -36 33 | import sys - -SIM108.py:58:1: SIM108 Use ternary operator `abc = x if x > 0 else -x` instead of `if`-`else`-block - | -57 | # SIM108 (without fix due to comments) -58 | / if x > 0: -59 | | # test test -60 | | abc = x -61 | | else: -62 | | # test test test -63 | | abc = -x - | |____________^ SIM108 - | - = help: Replace `if`-`else`-block with `abc = x if x > 0 else -x` - -SIM108.py:82:1: SIM108 [*] Use ternary operator `b = "cccccccccccccccccccccccccccccccccß" if a else "ddddddddddddddddddddddddddddddddd💣"` instead of `if`-`else`-block - | -81 | # SIM108 -82 | / if a: -83 | | b = "cccccccccccccccccccccccccccccccccß" -84 | | else: -85 | | b = "ddddddddddddddddddddddddddddddddd💣" - | |_____________________________________________^ SIM108 - | - = help: Replace `if`-`else`-block with `b = "cccccccccccccccccccccccccccccccccß" if a else "ddddddddddddddddddddddddddddddddd💣"` - -ℹ Unsafe fix -79 79 | -80 80 | -81 81 | # SIM108 -82 |-if a: -83 |- b = "cccccccccccccccccccccccccccccccccß" -84 |-else: -85 |- b = "ddddddddddddddddddddddddddddddddd💣" - 82 |+b = "cccccccccccccccccccccccccccccccccß" if a else "ddddddddddddddddddddddddddddddddd💣" -86 83 | -87 84 | -88 85 | # OK (too long) - -SIM108.py:105:1: SIM108 Use ternary operator `exitcode = 0 if True else 1` instead of `if`-`else`-block - | -104 | # SIM108 (without fix due to trailing comment) -105 | / if True: -106 | | exitcode = 0 -107 | | else: -108 | | exitcode = 1 # Trailing comment - | |________________^ SIM108 - | - = help: Replace `if`-`else`-block with `exitcode = 0 if True else 1` - -SIM108.py:112:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `if`-`else`-block - | -111 | # SIM108 -112 | / if True: x = 3 # Foo -113 | | else: x = 5 - | |___________^ SIM108 - | - = help: Replace `if`-`else`-block with `x = 3 if True else 5` - -SIM108.py:117:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `if`-`else`-block - | -116 | # SIM108 -117 | / if True: # Foo -118 | | x = 3 -119 | | else: -120 | | x = 5 - | |_________^ SIM108 - | - = help: Replace `if`-`else`-block with `x = 3 if True else 5` - -SIM108.py:141:1: SIM108 [*] Use binary operator `z = cond or other_cond` instead of `if`-`else`-block - | -139 | # SIM108 - should suggest -140 | # z = cond or other_cond -141 | / if cond: -142 | | z = cond -143 | | else: -144 | | z = other_cond - | |__________________^ SIM108 -145 | -146 | # SIM108 - should suggest - | - = help: Replace `if`-`else`-block with `z = cond or other_cond` - -ℹ Unsafe fix -138 138 | -139 139 | # SIM108 - should suggest -140 140 | # z = cond or other_cond -141 |-if cond: -142 |- z = cond -143 |-else: -144 |- z = other_cond - 141 |+z = cond or other_cond -145 142 | -146 143 | # SIM108 - should suggest -147 144 | # z = cond and other_cond - -SIM108.py:148:1: SIM108 [*] Use binary operator `z = cond and other_cond` instead of `if`-`else`-block - | -146 | # SIM108 - should suggest -147 | # z = cond and other_cond -148 | / if not cond: -149 | | z = cond -150 | | else: -151 | | z = other_cond - | |__________________^ SIM108 -152 | -153 | # SIM108 - should suggest - | - = help: Replace `if`-`else`-block with `z = cond and other_cond` - -ℹ Unsafe fix -145 145 | -146 146 | # SIM108 - should suggest -147 147 | # z = cond and other_cond -148 |-if not cond: -149 |- z = cond -150 |-else: -151 |- z = other_cond - 148 |+z = cond and other_cond -152 149 | -153 150 | # SIM108 - should suggest -154 151 | # z = not cond and other_cond - -SIM108.py:155:1: SIM108 [*] Use binary operator `z = not cond and other_cond` instead of `if`-`else`-block - | -153 | # SIM108 - should suggest -154 | # z = not cond and other_cond -155 | / if cond: -156 | | z = not cond -157 | | else: -158 | | z = other_cond - | |__________________^ SIM108 -159 | -160 | # SIM108 does not suggest - | - = help: Replace `if`-`else`-block with `z = not cond and other_cond` - -ℹ Unsafe fix -152 152 | -153 153 | # SIM108 - should suggest -154 154 | # z = not cond and other_cond -155 |-if cond: -156 |- z = not cond -157 |-else: -158 |- z = other_cond - 155 |+z = not cond and other_cond -159 156 | -160 157 | # SIM108 does not suggest -161 158 | # a binary option in these cases, - -SIM108.py:167:1: SIM108 [*] Use ternary operator `z = 1 if True else other` instead of `if`-`else`-block - | -165 | # (Of course, these specific expressions -166 | # should be simplified for other reasons...) -167 | / if True: -168 | | z = 1 -169 | | else: -170 | | z = other - | |_____________^ SIM108 -171 | -172 | if False: - | - = help: Replace `if`-`else`-block with `z = 1 if True else other` - -ℹ Unsafe fix -164 164 | # so, e.g. `True == 1`. -165 165 | # (Of course, these specific expressions -166 166 | # should be simplified for other reasons...) -167 |-if True: -168 |- z = 1 -169 |-else: -170 |- z = other - 167 |+z = 1 if True else other -171 168 | -172 169 | if False: -173 170 | z = 1 - -SIM108.py:172:1: SIM108 [*] Use ternary operator `z = 1 if False else other` instead of `if`-`else`-block - | -170 | z = other -171 | -172 | / if False: -173 | | z = 1 -174 | | else: -175 | | z = other - | |_____________^ SIM108 -176 | -177 | if 1: - | - = help: Replace `if`-`else`-block with `z = 1 if False else other` - -ℹ Unsafe fix -169 169 | else: -170 170 | z = other -171 171 | -172 |-if False: -173 |- z = 1 -174 |-else: -175 |- z = other - 172 |+z = 1 if False else other -176 173 | -177 174 | if 1: -178 175 | z = True - -SIM108.py:177:1: SIM108 [*] Use ternary operator `z = True if 1 else other` instead of `if`-`else`-block - | -175 | z = other -176 | -177 | / if 1: -178 | | z = True -179 | | else: -180 | | z = other - | |_____________^ SIM108 -181 | -182 | # SIM108 does not suggest a binary option in this - | - = help: Replace `if`-`else`-block with `z = True if 1 else other` - -ℹ Unsafe fix -174 174 | else: -175 175 | z = other -176 176 | -177 |-if 1: -178 |- z = True -179 |-else: -180 |- z = other - 177 |+z = True if 1 else other -181 178 | -182 179 | # SIM108 does not suggest a binary option in this -183 180 | # case, since we'd be reducing the number of calls - -SIM108.py:185:1: SIM108 [*] Use ternary operator `z = foo() if foo() else other` instead of `if`-`else`-block - | -183 | # case, since we'd be reducing the number of calls -184 | # from Two to one. -185 | / if foo(): -186 | | z = foo() -187 | | else: -188 | | z = other - | |_____________^ SIM108 -189 | -190 | # SIM108 does not suggest a binary option in this - | - = help: Replace `if`-`else`-block with `z = foo() if foo() else other` - -ℹ Unsafe fix -182 182 | # SIM108 does not suggest a binary option in this -183 183 | # case, since we'd be reducing the number of calls -184 184 | # from Two to one. -185 |-if foo(): -186 |- z = foo() -187 |-else: -188 |- z = other - 185 |+z = foo() if foo() else other -189 186 | -190 187 | # SIM108 does not suggest a binary option in this -191 188 | # case, since we'd be reducing the number of calls - -SIM108.py:193:1: SIM108 [*] Use ternary operator `z = not foo() if foo() else other` instead of `if`-`else`-block - | -191 | # case, since we'd be reducing the number of calls -192 | # from Two to one. -193 | / if foo(): -194 | | z = not foo() -195 | | else: -196 | | z = other - | |_____________^ SIM108 - | - = help: Replace `if`-`else`-block with `z = not foo() if foo() else other` - -ℹ Unsafe fix -190 190 | # SIM108 does not suggest a binary option in this -191 191 | # case, since we'd be reducing the number of calls -192 192 | # from Two to one. -193 |-if foo(): -194 |- z = not foo() -195 |-else: -196 |- z = other - 193 |+z = not foo() if foo() else other -197 194 | -198 195 | -199 196 | # These two cases double as tests for f-string quote preservation. The first - -SIM108.py:202:1: SIM108 [*] Use ternary operator `var = "str" if cond else f"{first}-{second}"` instead of `if`-`else`-block - | -200 | # f-string should preserve its double quotes, and the second should preserve -201 | # single quotes -202 | / if cond: -203 | | var = "str" -204 | | else: -205 | | var = f"{first}-{second}" - | |_____________________________^ SIM108 -206 | -207 | if cond: - | - = help: Replace `if`-`else`-block with `var = "str" if cond else f"{first}-{second}"` - -ℹ Unsafe fix -199 199 | # These two cases double as tests for f-string quote preservation. The first -200 200 | # f-string should preserve its double quotes, and the second should preserve -201 201 | # single quotes -202 |-if cond: -203 |- var = "str" -204 |-else: -205 |- var = f"{first}-{second}" - 202 |+var = "str" if cond else f"{first}-{second}" -206 203 | -207 204 | if cond: -208 205 | var = "str" - -SIM108.py:207:1: SIM108 [*] Use ternary operator `var = "str" if cond else f'{first}-{second}'` instead of `if`-`else`-block - | -205 | var = f"{first}-{second}" -206 | -207 | / if cond: -208 | | var = "str" -209 | | else: -210 | | var = f'{first}-{second}' - | |_____________________________^ SIM108 - | - = help: Replace `if`-`else`-block with `var = "str" if cond else f'{first}-{second}'` - -ℹ Unsafe fix -204 204 | else: -205 205 | var = f"{first}-{second}" -206 206 | -207 |-if cond: -208 |- var = "str" -209 |-else: -210 |- var = f'{first}-{second}' - 207 |+var = "str" if cond else f'{first}-{second}' diff --git a/crates/ruff_linter/src/rules/refurb/mod.rs b/crates/ruff_linter/src/rules/refurb/mod.rs index a14531e43aa4a..9f74b865b6ab7 100644 --- a/crates/ruff_linter/src/rules/refurb/mod.rs +++ b/crates/ruff_linter/src/rules/refurb/mod.rs @@ -62,24 +62,6 @@ mod tests { Ok(()) } - #[test_case(Rule::ReadlinesInFor, Path::new("FURB129.py"))] - fn preview(rule_code: Rule, path: &Path) -> Result<()> { - let snapshot = format!( - "preview__{}_{}", - rule_code.noqa_code(), - path.to_string_lossy() - ); - let diagnostics = test_path( - Path::new("refurb").join(path).as_path(), - &settings::LinterSettings { - preview: settings::types::PreviewMode::Enabled, - ..settings::LinterSettings::for_rule(rule_code) - }, - )?; - assert_messages!(snapshot, diagnostics); - Ok(()) - } - #[test] fn write_whole_file_python_39() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/refurb/rules/readlines_in_for.rs b/crates/ruff_linter/src/rules/refurb/rules/readlines_in_for.rs index f945b8fdb2c92..aad840bcf172e 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/readlines_in_for.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/readlines_in_for.rs @@ -7,7 +7,6 @@ use ruff_text_size::Ranged; use crate::checkers::ast::Checker; use crate::fix::edits::pad_end; -use crate::preview::is_readlines_in_for_fix_safe_enabled; use crate::{AlwaysFixableViolation, Edit, Fix}; /// ## What it does @@ -106,9 +105,5 @@ fn readlines_in_iter(checker: &Checker, iter_expr: &Expr) { }; let mut diagnostic = checker.report_diagnostic(ReadlinesInFor, expr_call.range()); - diagnostic.set_fix(if is_readlines_in_for_fix_safe_enabled(checker.settings) { - Fix::safe_edit(edit) - } else { - Fix::unsafe_edit(edit) - }); + diagnostic.set_fix(Fix::safe_edit(edit)); } diff --git a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB129_FURB129.py.snap b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB129_FURB129.py.snap index ce9c375ba0650..f466c31940af8 100644 --- a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB129_FURB129.py.snap +++ b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB129_FURB129.py.snap @@ -12,7 +12,7 @@ FURB129.py:7:18: FURB129 [*] Instead of calling `readlines()`, iterate over file | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 4 4 | 5 5 | # Errors 6 6 | with open("FURB129.py") as f: @@ -33,7 +33,7 @@ FURB129.py:9:35: FURB129 [*] Instead of calling `readlines()`, iterate over file | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 6 6 | with open("FURB129.py") as f: 7 7 | for _line in f.readlines(): 8 8 | pass @@ -53,7 +53,7 @@ FURB129.py:10:35: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 7 7 | for _line in f.readlines(): 8 8 | pass 9 9 | a = [line.lower() for line in f.readlines()] @@ -74,7 +74,7 @@ FURB129.py:11:49: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 8 8 | pass 9 9 | a = [line.lower() for line in f.readlines()] 10 10 | b = {line.upper() for line in f.readlines()} @@ -93,7 +93,7 @@ FURB129.py:14:18: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 11 11 | c = {line.lower(): line.upper() for line in f.readlines()} 12 12 | 13 13 | with Path("FURB129.py").open() as f: @@ -113,7 +113,7 @@ FURB129.py:17:14: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 14 14 | for _line in f.readlines(): 15 15 | pass 16 16 | @@ -133,7 +133,7 @@ FURB129.py:20:14: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 17 17 | for _line in open("FURB129.py").readlines(): 18 18 | pass 19 19 | @@ -154,7 +154,7 @@ FURB129.py:26:18: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 23 23 | 24 24 | def func(): 25 25 | f = Path("FURB129.py").open() @@ -173,7 +173,7 @@ FURB129.py:32:18: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 29 29 | 30 30 | 31 31 | def func(f: io.BytesIO): @@ -194,7 +194,7 @@ FURB129.py:38:22: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 35 35 | 36 36 | def func(): 37 37 | with (open("FURB129.py") as f, foo as bar): @@ -213,7 +213,7 @@ FURB129.py:47:17: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 44 44 | import builtins 45 45 | 46 46 | with builtins.open("FURB129.py") as f: @@ -232,7 +232,7 @@ FURB129.py:54:17: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 51 51 | from builtins import open as o 52 52 | 53 53 | with o("FURB129.py") as f: @@ -252,7 +252,7 @@ FURB129.py:93:17: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 90 90 | 91 91 | # https://github.com/astral-sh/ruff/issues/18231 92 92 | with open("furb129.py") as f: @@ -270,7 +270,7 @@ FURB129.py:97:23: FURB129 [*] Instead of calling `readlines()`, iterate over fil | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 94 94 | pass 95 95 | 96 96 | with open("furb129.py") as f: @@ -290,7 +290,7 @@ FURB129.py:101:17: FURB129 [*] Instead of calling `readlines()`, iterate over fi | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 98 98 | 99 99 | 100 100 | with open("furb129.py") as f: @@ -310,7 +310,7 @@ FURB129.py:103:16: FURB129 [*] Instead of calling `readlines()`, iterate over fi | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 100 100 | with open("furb129.py") as f: 101 101 | for line in (((f))).readlines(): 102 102 | pass @@ -328,7 +328,7 @@ FURB129.py:107:29: FURB129 [*] Instead of calling `readlines()`, iterate over fi | = help: Remove `readlines()` -ℹ Unsafe fix +ℹ Safe fix 104 104 | pass 105 105 | 106 106 | # Test case for issue #17683 (missing space before keyword) diff --git a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__preview__FURB129_FURB129.py.snap b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__preview__FURB129_FURB129.py.snap deleted file mode 100644 index f466c31940af8..0000000000000 --- a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__preview__FURB129_FURB129.py.snap +++ /dev/null @@ -1,336 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/refurb/mod.rs ---- -FURB129.py:7:18: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -5 | # Errors -6 | with open("FURB129.py") as f: -7 | for _line in f.readlines(): - | ^^^^^^^^^^^^^ FURB129 -8 | pass -9 | a = [line.lower() for line in f.readlines()] - | - = help: Remove `readlines()` - -ℹ Safe fix -4 4 | -5 5 | # Errors -6 6 | with open("FURB129.py") as f: -7 |- for _line in f.readlines(): - 7 |+ for _line in f: -8 8 | pass -9 9 | a = [line.lower() for line in f.readlines()] -10 10 | b = {line.upper() for line in f.readlines()} - -FURB129.py:9:35: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | - 7 | for _line in f.readlines(): - 8 | pass - 9 | a = [line.lower() for line in f.readlines()] - | ^^^^^^^^^^^^^ FURB129 -10 | b = {line.upper() for line in f.readlines()} -11 | c = {line.lower(): line.upper() for line in f.readlines()} - | - = help: Remove `readlines()` - -ℹ Safe fix -6 6 | with open("FURB129.py") as f: -7 7 | for _line in f.readlines(): -8 8 | pass -9 |- a = [line.lower() for line in f.readlines()] - 9 |+ a = [line.lower() for line in f] -10 10 | b = {line.upper() for line in f.readlines()} -11 11 | c = {line.lower(): line.upper() for line in f.readlines()} -12 12 | - -FURB129.py:10:35: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | - 8 | pass - 9 | a = [line.lower() for line in f.readlines()] -10 | b = {line.upper() for line in f.readlines()} - | ^^^^^^^^^^^^^ FURB129 -11 | c = {line.lower(): line.upper() for line in f.readlines()} - | - = help: Remove `readlines()` - -ℹ Safe fix -7 7 | for _line in f.readlines(): -8 8 | pass -9 9 | a = [line.lower() for line in f.readlines()] -10 |- b = {line.upper() for line in f.readlines()} - 10 |+ b = {line.upper() for line in f} -11 11 | c = {line.lower(): line.upper() for line in f.readlines()} -12 12 | -13 13 | with Path("FURB129.py").open() as f: - -FURB129.py:11:49: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | - 9 | a = [line.lower() for line in f.readlines()] -10 | b = {line.upper() for line in f.readlines()} -11 | c = {line.lower(): line.upper() for line in f.readlines()} - | ^^^^^^^^^^^^^ FURB129 -12 | -13 | with Path("FURB129.py").open() as f: - | - = help: Remove `readlines()` - -ℹ Safe fix -8 8 | pass -9 9 | a = [line.lower() for line in f.readlines()] -10 10 | b = {line.upper() for line in f.readlines()} -11 |- c = {line.lower(): line.upper() for line in f.readlines()} - 11 |+ c = {line.lower(): line.upper() for line in f} -12 12 | -13 13 | with Path("FURB129.py").open() as f: -14 14 | for _line in f.readlines(): - -FURB129.py:14:18: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -13 | with Path("FURB129.py").open() as f: -14 | for _line in f.readlines(): - | ^^^^^^^^^^^^^ FURB129 -15 | pass - | - = help: Remove `readlines()` - -ℹ Safe fix -11 11 | c = {line.lower(): line.upper() for line in f.readlines()} -12 12 | -13 13 | with Path("FURB129.py").open() as f: -14 |- for _line in f.readlines(): - 14 |+ for _line in f: -15 15 | pass -16 16 | -17 17 | for _line in open("FURB129.py").readlines(): - -FURB129.py:17:14: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -15 | pass -16 | -17 | for _line in open("FURB129.py").readlines(): - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB129 -18 | pass - | - = help: Remove `readlines()` - -ℹ Safe fix -14 14 | for _line in f.readlines(): -15 15 | pass -16 16 | -17 |-for _line in open("FURB129.py").readlines(): - 17 |+for _line in open("FURB129.py"): -18 18 | pass -19 19 | -20 20 | for _line in Path("FURB129.py").open().readlines(): - -FURB129.py:20:14: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -18 | pass -19 | -20 | for _line in Path("FURB129.py").open().readlines(): - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB129 -21 | pass - | - = help: Remove `readlines()` - -ℹ Safe fix -17 17 | for _line in open("FURB129.py").readlines(): -18 18 | pass -19 19 | -20 |-for _line in Path("FURB129.py").open().readlines(): - 20 |+for _line in Path("FURB129.py").open(): -21 21 | pass -22 22 | -23 23 | - -FURB129.py:26:18: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -24 | def func(): -25 | f = Path("FURB129.py").open() -26 | for _line in f.readlines(): - | ^^^^^^^^^^^^^ FURB129 -27 | pass -28 | f.close() - | - = help: Remove `readlines()` - -ℹ Safe fix -23 23 | -24 24 | def func(): -25 25 | f = Path("FURB129.py").open() -26 |- for _line in f.readlines(): - 26 |+ for _line in f: -27 27 | pass -28 28 | f.close() -29 29 | - -FURB129.py:32:18: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -31 | def func(f: io.BytesIO): -32 | for _line in f.readlines(): - | ^^^^^^^^^^^^^ FURB129 -33 | pass - | - = help: Remove `readlines()` - -ℹ Safe fix -29 29 | -30 30 | -31 31 | def func(f: io.BytesIO): -32 |- for _line in f.readlines(): - 32 |+ for _line in f: -33 33 | pass -34 34 | -35 35 | - -FURB129.py:38:22: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -36 | def func(): -37 | with (open("FURB129.py") as f, foo as bar): -38 | for _line in f.readlines(): - | ^^^^^^^^^^^^^ FURB129 -39 | pass -40 | for _line in bar.readlines(): - | - = help: Remove `readlines()` - -ℹ Safe fix -35 35 | -36 36 | def func(): -37 37 | with (open("FURB129.py") as f, foo as bar): -38 |- for _line in f.readlines(): - 38 |+ for _line in f: -39 39 | pass -40 40 | for _line in bar.readlines(): -41 41 | pass - -FURB129.py:47:17: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -46 | with builtins.open("FURB129.py") as f: -47 | for line in f.readlines(): - | ^^^^^^^^^^^^^ FURB129 -48 | pass - | - = help: Remove `readlines()` - -ℹ Safe fix -44 44 | import builtins -45 45 | -46 46 | with builtins.open("FURB129.py") as f: -47 |- for line in f.readlines(): - 47 |+ for line in f: -48 48 | pass -49 49 | -50 50 | - -FURB129.py:54:17: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -53 | with o("FURB129.py") as f: -54 | for line in f.readlines(): - | ^^^^^^^^^^^^^ FURB129 -55 | pass - | - = help: Remove `readlines()` - -ℹ Safe fix -51 51 | from builtins import open as o -52 52 | -53 53 | with o("FURB129.py") as f: -54 |- for line in f.readlines(): - 54 |+ for line in f: -55 55 | pass -56 56 | -57 57 | - -FURB129.py:93:17: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -91 | # https://github.com/astral-sh/ruff/issues/18231 -92 | with open("furb129.py") as f: -93 | for line in (f).readlines(): - | ^^^^^^^^^^^^^^^ FURB129 -94 | pass - | - = help: Remove `readlines()` - -ℹ Safe fix -90 90 | -91 91 | # https://github.com/astral-sh/ruff/issues/18231 -92 92 | with open("furb129.py") as f: -93 |- for line in (f).readlines(): - 93 |+ for line in (f): -94 94 | pass -95 95 | -96 96 | with open("furb129.py") as f: - -FURB129.py:97:23: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -96 | with open("furb129.py") as f: -97 | [line for line in (f).readlines()] - | ^^^^^^^^^^^^^^^ FURB129 - | - = help: Remove `readlines()` - -ℹ Safe fix -94 94 | pass -95 95 | -96 96 | with open("furb129.py") as f: -97 |- [line for line in (f).readlines()] - 97 |+ [line for line in (f)] -98 98 | -99 99 | -100 100 | with open("furb129.py") as f: - -FURB129.py:101:17: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -100 | with open("furb129.py") as f: -101 | for line in (((f))).readlines(): - | ^^^^^^^^^^^^^^^^^^^ FURB129 -102 | pass -103 | for line in(f).readlines(): - | - = help: Remove `readlines()` - -ℹ Safe fix -98 98 | -99 99 | -100 100 | with open("furb129.py") as f: -101 |- for line in (((f))).readlines(): - 101 |+ for line in (((f))): -102 102 | pass -103 103 | for line in(f).readlines(): -104 104 | pass - -FURB129.py:103:16: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -101 | for line in (((f))).readlines(): -102 | pass -103 | for line in(f).readlines(): - | ^^^^^^^^^^^^^^^ FURB129 -104 | pass - | - = help: Remove `readlines()` - -ℹ Safe fix -100 100 | with open("furb129.py") as f: -101 101 | for line in (((f))).readlines(): -102 102 | pass -103 |- for line in(f).readlines(): - 103 |+ for line in(f): -104 104 | pass -105 105 | -106 106 | # Test case for issue #17683 (missing space before keyword) - -FURB129.py:107:29: FURB129 [*] Instead of calling `readlines()`, iterate over file object directly - | -106 | # Test case for issue #17683 (missing space before keyword) -107 | print([line for line in f.readlines()if True]) - | ^^^^^^^^^^^^^ FURB129 - | - = help: Remove `readlines()` - -ℹ Safe fix -104 104 | pass -105 105 | -106 106 | # Test case for issue #17683 (missing space before keyword) -107 |- print([line for line in f.readlines()if True]) - 107 |+ print([line for line in f if True]) diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index 39cab6081954a..39511fd3531e7 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -24,6 +24,7 @@ mod tests { use crate::{assert_messages, settings}; #[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005.py"))] + #[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005_slices.py"))] #[test_case(Rule::AsyncioDanglingTask, Path::new("RUF006.py"))] #[test_case(Rule::ZipInsteadOfPairwise, Path::new("RUF007.py"))] #[test_case(Rule::MutableDataclassDefault, Path::new("RUF008.py"))] @@ -94,6 +95,7 @@ mod tests { #[test_case(Rule::MapIntVersionParsing, Path::new("RUF048_1.py"))] #[test_case(Rule::IfKeyInDictDel, Path::new("RUF051.py"))] #[test_case(Rule::UsedDummyVariable, Path::new("RUF052.py"))] + #[test_case(Rule::ClassWithMixedTypeVars, Path::new("RUF053.py"))] #[test_case(Rule::FalsyDictGetFallback, Path::new("RUF056.py"))] #[test_case(Rule::UnusedUnpackedVariable, Path::new("RUF059_0.py"))] #[test_case(Rule::UnusedUnpackedVariable, Path::new("RUF059_1.py"))] @@ -322,10 +324,7 @@ mod tests { fn ruff_noqa_filedirective_unused() -> Result<()> { let diagnostics = test_path( Path::new("ruff/RUF100_6.py"), - &settings::LinterSettings { - preview: PreviewMode::Enabled, - ..settings::LinterSettings::for_rules(vec![Rule::UnusedNOQA]) - }, + &settings::LinterSettings::for_rules(vec![Rule::UnusedNOQA]), )?; assert_messages!(diagnostics); Ok(()) @@ -335,15 +334,12 @@ mod tests { fn ruff_noqa_filedirective_unused_last_of_many() -> Result<()> { let diagnostics = test_path( Path::new("ruff/RUF100_7.py"), - &settings::LinterSettings { - preview: PreviewMode::Enabled, - ..settings::LinterSettings::for_rules(vec![ - Rule::UnusedNOQA, - Rule::FStringMissingPlaceholders, - Rule::LineTooLong, - Rule::UnusedVariable, - ]) - }, + &settings::LinterSettings::for_rules(vec![ + Rule::UnusedNOQA, + Rule::FStringMissingPlaceholders, + Rule::LineTooLong, + Rule::UnusedVariable, + ]), )?; assert_messages!(diagnostics); Ok(()) @@ -480,10 +476,8 @@ mod tests { #[test_case(Rule::DataclassEnum, Path::new("RUF049.py"))] #[test_case(Rule::StarmapZip, Path::new("RUF058_0.py"))] #[test_case(Rule::StarmapZip, Path::new("RUF058_1.py"))] - #[test_case(Rule::ClassWithMixedTypeVars, Path::new("RUF053.py"))] #[test_case(Rule::IndentedFormFeed, Path::new("RUF054.py"))] #[test_case(Rule::ImplicitClassVarInDataclass, Path::new("RUF045.py"))] - #[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005_slices.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/ruff/rules/collection_literal_concatenation.rs b/crates/ruff_linter/src/rules/ruff/rules/collection_literal_concatenation.rs index 566b9e822e889..2efb9df96f871 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/collection_literal_concatenation.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/collection_literal_concatenation.rs @@ -4,7 +4,6 @@ use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; use crate::fix::snippet::SourceCodeSnippet; -use crate::preview::is_support_slices_in_literal_concatenation_enabled; use crate::{Edit, Fix, FixAvailability, Violation}; /// ## What it does @@ -96,7 +95,7 @@ enum Type { } /// Recursively merge all the tuples and lists in the expression. -fn concatenate_expressions(expr: &Expr, should_support_slices: bool) -> Option<(Expr, Type)> { +fn concatenate_expressions(expr: &Expr) -> Option<(Expr, Type)> { let Expr::BinOp(ast::ExprBinOp { left, op: Operator::Add, @@ -108,22 +107,18 @@ fn concatenate_expressions(expr: &Expr, should_support_slices: bool) -> Option<( }; let new_left = match left.as_ref() { - Expr::BinOp(ast::ExprBinOp { .. }) => { - match concatenate_expressions(left, should_support_slices) { - Some((new_left, _)) => new_left, - None => *left.clone(), - } - } + Expr::BinOp(ast::ExprBinOp { .. }) => match concatenate_expressions(left) { + Some((new_left, _)) => new_left, + None => *left.clone(), + }, _ => *left.clone(), }; let new_right = match right.as_ref() { - Expr::BinOp(ast::ExprBinOp { .. }) => { - match concatenate_expressions(right, should_support_slices) { - Some((new_right, _)) => new_right, - None => *right.clone(), - } - } + Expr::BinOp(ast::ExprBinOp { .. }) => match concatenate_expressions(right) { + Some((new_right, _)) => new_right, + None => *right.clone(), + }, _ => *right.clone(), }; @@ -151,9 +146,7 @@ fn concatenate_expressions(expr: &Expr, should_support_slices: bool) -> Option<( make_splat_elts(splat_element, other_elements, splat_at_left) } // Subscripts are also considered safe-ish to splat if the indexer is a slice. - Expr::Subscript(ast::ExprSubscript { slice, .. }) - if should_support_slices && matches!(&**slice, Expr::Slice(_)) => - { + Expr::Subscript(ast::ExprSubscript { slice, .. }) if matches!(&**slice, Expr::Slice(_)) => { make_splat_elts(splat_element, other_elements, splat_at_left) } // If the splat element is itself a list/tuple, insert them in the other list/tuple. @@ -198,10 +191,7 @@ pub(crate) fn collection_literal_concatenation(checker: &Checker, expr: &Expr) { return; } - let should_support_slices = - is_support_slices_in_literal_concatenation_enabled(checker.settings); - - let Some((new_expr, type_)) = concatenate_expressions(expr, should_support_slices) else { + let Some((new_expr, type_)) = concatenate_expressions(expr) else { return; }; diff --git a/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs b/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs index a3de93719e3a6..b7c55dc0b0e43 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs @@ -7,6 +7,7 @@ use ruff_python_semantic::analyze::function_type::is_stub; use crate::Violation; use crate::checkers::ast::Checker; + use crate::rules::fastapi::rules::is_fastapi_route; /// ## What it does diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF005_RUF005_slices.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF005_RUF005_slices.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF005_RUF005_slices.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF005_RUF005_slices.py.snap diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF053_RUF053.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF053_RUF053.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF053_RUF053.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF053_RUF053.py.snap diff --git a/crates/ruff_wasm/tests/api.rs b/crates/ruff_wasm/tests/api.rs index 32177e6bb6361..036a435de0ec4 100644 --- a/crates/ruff_wasm/tests/api.rs +++ b/crates/ruff_wasm/tests/api.rs @@ -65,7 +65,7 @@ fn syntax_error() { fn unsupported_syntax_error() { check!( "match 2:\n case 1: ...", - r#"{"preview": true, "target-version": "py39"}"#, + r#"{"target-version": "py39"}"#, [ExpandedMessage { code: None, message: "SyntaxError: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)".to_string(),