diff --git a/crates/ruff/tests/integration_test.rs b/crates/ruff/tests/integration_test.rs index 68bf81774c..93693dddcb 100644 --- a/crates/ruff/tests/integration_test.rs +++ b/crates/ruff/tests/integration_test.rs @@ -1299,9 +1299,16 @@ fn deprecated_indirect_redirect_preview_enabled() { .pass_stdin(r###" x = eval(input("Enter a number: ")) "###), @r###" - success: true - exit_code: 0 + success: false + exit_code: 1 ----- stdout ----- + -:2:5: PGH001 No builtin `eval()` allowed + | + 2 | x = eval(input("Enter a number: ")) + | ^^^^ PGH001 + | + + Found 1 error. ----- stderr ----- "###); diff --git a/crates/ruff_linter/src/rule_selector.rs b/crates/ruff_linter/src/rule_selector.rs index 37afac41c3..f5d49be9a4 100644 --- a/crates/ruff_linter/src/rule_selector.rs +++ b/crates/ruff_linter/src/rule_selector.rs @@ -242,16 +242,43 @@ impl RuleSelector { // 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)) + || (preview_enabled && (self.is_exact() || !preview_require_explicit)) // Deprecated rules are excluded in preview mode unless explicitly selected - || (rule.is_deprecated() && (!preview_enabled || matches!(self, RuleSelector::Rule { .. }))) + || (rule.is_deprecated() && (!preview_enabled || self.is_exact())) // Removed rules are included if explicitly selected but will error downstream - || (rule.is_removed() && matches!(self, RuleSelector::Rule { .. })) + || (rule.is_removed() && self.is_exact()) }) } + + /// Returns the selector string that this selector is a redirect of (if any) + /// + /// # Panics + /// + /// If a redirect is not a valid rule selector + pub fn redirected_from(&self) -> Option { + match self { + Self::Rule { + prefix: _, + redirected_from, + } + | Self::Prefix { + prefix: _, + redirected_from, + } => redirected_from.map(|literal| { + RuleSelector::from_str_no_redirect(literal) + .expect("Redirects should always be valid selectors") + }), + _ => None, + } + } + + /// 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 { diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index cfa8abd5b4..3a1f488712 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -916,32 +916,16 @@ impl LintConfiguration { } // Deprecated rules - if kind.is_enable() { - if let RuleSelector::Rule { - prefix, - redirected_from: None, - } = selector - { - if prefix.rules().any(|rule| rule.is_deprecated()) { - deprecated_selectors.insert(selector.clone()); - } + if kind.is_enable() && selector.is_exact() { + if selector.all_rules().all(|rule| rule.is_deprecated()) { + deprecated_selectors.insert(selector.clone()); } - if let RuleSelector::Rule { - prefix: redirected_to, - redirected_from: Some(redirected_from), - } = selector - { - if let Ok(redirected_selector) = - RuleSelector::from_str_no_redirect(redirected_from) + if let Some(redirected_selector) = selector.redirected_from() { + if redirected_selector + .all_rules() + .all(|rule| rule.is_deprecated()) { - if let RuleSelector::Rule { ref prefix, .. } - | RuleSelector::Prefix { ref prefix, .. } = redirected_selector - { - if prefix.rules().all(|rule| rule.is_deprecated()) { - deprecated_redirected_selectors - .insert((redirected_selector, redirected_to)); - } - } + deprecated_redirected_selectors.insert((redirected_selector, selector)); } } } @@ -1026,10 +1010,7 @@ impl LintConfiguration { } for (selection, redirected_to) in deprecated_redirected_selectors { let (prefix, code) = selection.prefix_and_code(); - let (redirect_prefix, redirect_code) = ( - redirected_to.linter().common_prefix(), - redirected_to.short_code(), - ); + let (redirect_prefix, redirect_code) = redirected_to.prefix_and_code(); warn_user!( "Rule `{prefix}{code}` is deprecated and will be removed in a future release. Use `{redirect_prefix}{redirect_code}` instead.", ); @@ -1051,8 +1032,7 @@ impl LintConfiguration { [(selection, redirect)] => { let (prefix, code) = selection.prefix_and_code(); let err = if let Some(redirect) = redirect { - let (redirect_prefix, redirect_code) = - (redirect.linter().common_prefix(), redirect.short_code()); + let (redirect_prefix, redirect_code) = redirect.prefix_and_code(); anyhow!("Selection of deprecated rule `{prefix}{code}` is not allowed when preview mode is enabled. Use `{redirect_prefix}{redirect_code}` instead.") } else { anyhow!("Selection of deprecated rule `{prefix}{code}` is not allowed when preview mode is enabled.") @@ -1067,8 +1047,7 @@ impl LintConfiguration { message.push_str(prefix); message.push_str(code); if let Some(redirect) = redirect { - let (redirect_prefix, redirect_code) = - (redirect.linter().common_prefix(), redirect.short_code()); + let (redirect_prefix, redirect_code) = redirect.prefix_and_code(); message.push_str(&format!( " (use `{redirect_prefix}{redirect_code}` instead)" ));