WIP: Nightmare scenario warning on redirected deprecated rules

This commit is contained in:
Zanie 2024-01-31 10:59:11 -06:00
parent 4a5d711a6e
commit b75864c5f9
6 changed files with 110 additions and 18 deletions

View File

@ -1228,8 +1228,50 @@ x = eval(input("Enter a number: "))
fn deprecated_indirect_redirect() { fn deprecated_indirect_redirect() {
// e.g. `PGH001` is deprecated and redirected, we see a violation in stable // e.g. `PGH001` is deprecated and redirected, we see a violation in stable
// without any warnings // without any warnings
let mut cmd = RuffCheck::default().args(["--select", "PGH"]).build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
x = eval(input("Enter a number: "))
"###), @r###"
success: false
exit_code: 1
----- stdout -----
-:2:5: S307 Use of possibly insecure function; consider using `ast.literal_eval`
Found 1 error.
----- stderr -----
warning: Selection `PGH` includes deprecated rule `PGH001` and will be removed in a future release; use `S307` instead.
warning: Selection `PGH` includes deprecated rule `PGH002` and will be removed in a future release; use `G010` instead.
"###);
}
#[test]
fn deprecated_indirect_redirect_select_new_code() {
// e.g. `PGH001` is deprecated and redirected
// but the user has moved to the new code so we should not see a warning
let mut cmd = RuffCheck::default() let mut cmd = RuffCheck::default()
.args(["--select", "PGH", "--preview"]) .args(["--select", "PGH", "--select", "S307"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
x = eval(input("Enter a number: "))
"###), @r###"
success: false
exit_code: 1
----- stdout -----
-:2:5: S307 Use of possibly insecure function; consider using `ast.literal_eval`
Found 1 error.
----- stderr -----
warning: Selection `PGH` includes deprecated rule `PGH001` and will be removed in a future release; use `S307` instead.
warning: Selection `PGH` includes deprecated rule `PGH002` and will be removed in a future release; use `G010` instead.
"###);
}
#[test]
fn deprecated_indirect_redirect_ignore_new_code() {
let mut cmd = RuffCheck::default()
.args(["--select", "PGH", "--ignore", "S307"])
.build(); .build();
assert_cmd_snapshot!(cmd assert_cmd_snapshot!(cmd
.pass_stdin(r###" .pass_stdin(r###"
@ -1240,8 +1282,30 @@ x = eval(input("Enter a number: "))
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
warning: Selection `PGH` includes deprecated rule `PGH001` and will be removed in a future release; use `S307` instead.
warning: Selection `PGH` includes deprecated rule `PGH002` and will be removed in a future release; use `G010` instead.
"###); "###);
} }
#[test]
fn deprecated_indirect_redirect_ignore_old_code() {
let mut cmd = RuffCheck::default()
.args(["--select", "PGH", "--ignore", "PGH001"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin(r###"
x = eval(input("Enter a number: "))
"###), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: Selection `PGH` includes deprecated rule `PGH001` and will be removed in a future release; use `S307` instead.
warning: Selection `PGH` includes deprecated rule `PGH002` and will be removed in a future release; use `G010` instead.
"###);
}
#[test] #[test]
fn deprecated_direct_preview_enabled() { fn deprecated_direct_preview_enabled() {
// Direct selection of a deprecated rule in preview should fail // Direct selection of a deprecated rule in preview should fail

View File

@ -34,7 +34,7 @@ pub mod packaging;
pub mod pyproject_toml; pub mod pyproject_toml;
pub mod registry; pub mod registry;
mod renamer; mod renamer;
mod rule_redirects; pub mod rule_redirects;
pub mod rule_selector; pub mod rule_selector;
pub mod rules; pub mod rules;
pub mod settings; pub mod settings;

View File

@ -31,7 +31,7 @@ pub enum FromCodeError {
Unknown, Unknown,
} }
#[derive(EnumIter, Debug, PartialEq, Eq, Clone, Hash, RuleNamespace)] #[derive(EnumIter, Debug, PartialEq, Eq, Clone, Hash, Copy, RuleNamespace)]
pub enum Linter { pub enum Linter {
/// [Pyflakes](https://pypi.org/project/pyflakes/) /// [Pyflakes](https://pypi.org/project/pyflakes/)
#[prefix = "F"] #[prefix = "F"]

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
/// Returns the redirect target for the given code. /// Returns the redirect target for the given code.
pub(crate) fn get_redirect_target(code: &str) -> Option<&'static str> { pub fn get_redirect_target(code: &str) -> Option<&'static str> {
REDIRECTS.get(code).copied() REDIRECTS.get(code).copied()
} }
@ -21,9 +21,6 @@ static REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
("C9", "C90"), ("C9", "C90"),
("T1", "T10"), ("T1", "T10"),
("T2", "T20"), ("T2", "T20"),
// The PGH category is deprecated
("PGH001", "S307"),
("PGH002", "G010"),
// TODO(charlie): Remove by 2023-02-01. // TODO(charlie): Remove by 2023-02-01.
("R", "RET"), ("R", "RET"),
("R5", "RET5"), ("R5", "RET5"),

View File

@ -8,7 +8,7 @@ use strum_macros::EnumIter;
use crate::codes::RuleCodePrefix; use crate::codes::RuleCodePrefix;
use crate::codes::RuleIter; use crate::codes::RuleIter;
use crate::registry::{Linter, Rule, RuleNamespace}; use crate::registry::{Linter, Rule, RuleNamespace};
use crate::rule_redirects::get_redirect; use crate::rule_redirects::{get_redirect, get_redirect_target};
use crate::settings::types::PreviewMode; use crate::settings::types::PreviewMode;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]

View File

@ -22,6 +22,7 @@ use ruff_formatter::IndentStyle;
use ruff_linter::line_width::{IndentWidth, LineLength}; use ruff_linter::line_width::{IndentWidth, LineLength};
use ruff_linter::registry::RuleNamespace; use ruff_linter::registry::RuleNamespace;
use ruff_linter::registry::{Rule, RuleSet, INCOMPATIBLE_CODES}; use ruff_linter::registry::{Rule, RuleSet, INCOMPATIBLE_CODES};
use ruff_linter::rule_redirects::get_redirect_target;
use ruff_linter::rule_selector::{PreviewOptions, Specificity}; use ruff_linter::rule_selector::{PreviewOptions, Specificity};
use ruff_linter::rules::pycodestyle; use ruff_linter::rules::pycodestyle;
use ruff_linter::settings::rule_table::RuleTable; use ruff_linter::settings::rule_table::RuleTable;
@ -756,7 +757,7 @@ impl LintConfiguration {
let mut redirects = FxHashMap::default(); let mut redirects = FxHashMap::default();
let mut deprecated_nursery_selectors = FxHashSet::default(); let mut deprecated_nursery_selectors = FxHashSet::default();
let mut deprecated_selectors = FxHashSet::default(); let mut deprecated_selectors = FxHashSet::default();
let mut deprecated_rules = FxHashSet::default(); let mut deprecated_redirected_rules = FxHashSet::default();
let mut deprecated_redirected_selectors = FxHashSet::default(); let mut deprecated_redirected_selectors = FxHashSet::default();
let mut removed_selectors = FxHashSet::default(); let mut removed_selectors = FxHashSet::default();
let mut ignored_preview_selectors = FxHashSet::default(); let mut ignored_preview_selectors = FxHashSet::default();
@ -790,7 +791,15 @@ impl LintConfiguration {
.chain(selection.extend_select.iter()) .chain(selection.extend_select.iter())
.filter(|s| s.specificity() == spec) .filter(|s| s.specificity() == spec)
{ {
for rule in selector.rules(&preview) { for rule in selector.rules(&preview).map(|rule| {
let code = &rule.noqa_code().to_string();
if let Some(target) = get_redirect_target(code) {
Rule::from_code(target)
.expect("Redirect targets must be valid rule codes")
} else {
rule
}
}) {
select_map_updates.insert(rule, true); select_map_updates.insert(rule, true);
if spec == Specificity::Rule { if spec == Specificity::Rule {
@ -931,11 +940,16 @@ impl LintConfiguration {
.insert((redirected_selector, selector)); .insert((redirected_selector, selector));
} }
} }
} else { } else if !matches!(selector, RuleSelector::All) {
// For non-exact selections note all of the deprecated rules that have been renamed // For non-exact selections note all of the deprecated rules that have been renamed
// Unless it is ALL, in which case this is a bit overzealous
for rule in selector.rules(&preview) { for rule in selector.rules(&preview) {
if rule.is_deprecated() { if rule.is_deprecated() {
deprecated_rules.insert((rule, selector)); if let Some(target) =
get_redirect_target(&rule.noqa_code().to_string())
{
deprecated_redirected_rules.insert((rule, target, selector));
}
} }
} }
} }
@ -1070,13 +1084,30 @@ impl LintConfiguration {
} }
} }
for (rule, selection) in deprecated_rules { for (rule, redirected_to, from_selection) in deprecated_redirected_rules
let (prefix, code) = selection.prefix_and_code(); .iter()
.sorted_by_key(|(rule, ..)| rule)
{
let (prefix, code) = from_selection.prefix_and_code();
let rule_code = rule.noqa_code(); let rule_code = rule.noqa_code();
// TODO(zanieb): Determine if we should warn here let redirected_to_rule = Rule::from_code(redirected_to)
// warn_user!( .expect("Redirected rules should always be valid codes");
// "Selection `{prefix}{code}` includes deprecated rule `{rule_code}` which will be removed in a future release.",
// ); // if !(select_set.contains(*rule) || select_set.contains(redirected_to_rule)) {
// continue;
// }
// If the user has selected the new rule as well, do not warn
// Due to limitations in our redirection infrastructure, we also must remove the deprecated rule from
// the selection or the user will see a violation for both the new and old rule.
// if select_set.contains(redirected_to_rule) {
// select_set.remove(*rule);
// continue;
// }
warn_user!(
"Selection `{prefix}{code}` includes deprecated rule `{rule_code}` and will be removed in a future release; use `{redirected_to}` instead.",
);
} }
for selection in ignored_preview_selectors { for selection in ignored_preview_selectors {