mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 07:04:53 +00:00
Add backwards compatible redirect map for U
-to-UP
rename (#1019)
This commit is contained in:
parent
58e7baedd0
commit
8a3f29497b
10 changed files with 342 additions and 16 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1896,6 +1896,7 @@ dependencies = [
|
|||
"codegen",
|
||||
"itertools",
|
||||
"libcst",
|
||||
"once_cell",
|
||||
"ruff",
|
||||
"rustpython-ast",
|
||||
"rustpython-common",
|
||||
|
|
9
resources/test/fixtures/ruff/redirects.py
vendored
Normal file
9
resources/test/fixtures/ruff/redirects.py
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
from typing import Optional
|
||||
|
||||
|
||||
def f(x: Optional[str]) -> None: # noqa: U007
|
||||
...
|
||||
|
||||
|
||||
def f(x: Optional[str]) -> None: # noqa: UP007
|
||||
...
|
|
@ -9,6 +9,7 @@ clap = { version = "4.0.1", features = ["derive"] }
|
|||
codegen = { version = "0.2.0" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
ruff = { path = ".." }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
|
||||
|
|
|
@ -8,7 +8,7 @@ use anyhow::Result;
|
|||
use clap::Parser;
|
||||
use codegen::{Scope, Type, Variant};
|
||||
use itertools::Itertools;
|
||||
use ruff::checks::CheckCode;
|
||||
use ruff::checks::{CheckCode, REDIRECTS};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
const FILE: &str = "src/checks_gen.rs";
|
||||
|
@ -26,18 +26,47 @@ pub fn main(cli: &Cli) -> Result<()> {
|
|||
// Build up a map from prefix to matching CheckCodes.
|
||||
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = BTreeMap::default();
|
||||
for check_code in CheckCode::iter() {
|
||||
let as_ref: String = check_code.as_ref().to_string();
|
||||
let prefix_len = as_ref
|
||||
let code_str: String = check_code.as_ref().to_string();
|
||||
let code_prefix_len = code_str
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
.count();
|
||||
for i in prefix_len..=as_ref.len() {
|
||||
let prefix = as_ref[..i].to_string();
|
||||
let code_suffix_len = code_str.len() - code_prefix_len;
|
||||
for i in 0..=code_suffix_len {
|
||||
let prefix = code_str[..code_prefix_len + i].to_string();
|
||||
let entry = prefix_to_codes.entry(prefix).or_default();
|
||||
entry.insert(check_code.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Add any aliases (e.g., "U001" to "UP001").
|
||||
for (alias, check_code) in REDIRECTS.iter() {
|
||||
// Compute the length of the prefix and suffix for both codes.
|
||||
let code_str: String = check_code.as_ref().to_string();
|
||||
let code_prefix_len = code_str
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
.count();
|
||||
let code_suffix_len = code_str.len() - code_prefix_len;
|
||||
let alias_prefix_len = alias
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
.count();
|
||||
let alias_suffix_len = alias.len() - alias_prefix_len;
|
||||
assert_eq!(code_suffix_len, alias_suffix_len);
|
||||
for i in 0..=code_suffix_len {
|
||||
let source = code_str[..code_prefix_len + i].to_string();
|
||||
let destination = alias[..alias_prefix_len + i].to_string();
|
||||
prefix_to_codes.insert(
|
||||
destination,
|
||||
prefix_to_codes
|
||||
.get(&source)
|
||||
.unwrap_or_else(|| panic!("Unknown CheckCode: {source:?}"))
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Create the `CheckCodePrefix` definition.
|
||||
|
@ -81,13 +110,27 @@ pub fn main(cli: &Cli) -> Result<()> {
|
|||
.line("#[allow(clippy::match_same_arms)]")
|
||||
.line("match self {");
|
||||
for (prefix, codes) in &prefix_to_codes {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => vec![{}],",
|
||||
codes
|
||||
.iter()
|
||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
||||
.join(", ")
|
||||
));
|
||||
if let Some(target) = REDIRECTS.get(&prefix.as_str()) {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => {{ eprintln!(\"{{}}{{}} {{}}\", \
|
||||
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been renamed to \
|
||||
`{}`\".bold()); \n vec![{}] }}",
|
||||
prefix,
|
||||
target.as_ref(),
|
||||
codes
|
||||
.iter()
|
||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
||||
.join(", ")
|
||||
));
|
||||
} else {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => vec![{}],",
|
||||
codes
|
||||
.iter()
|
||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
||||
.join(", ")
|
||||
));
|
||||
}
|
||||
}
|
||||
gen.line("}");
|
||||
|
||||
|
@ -123,6 +166,8 @@ pub fn main(cli: &Cli) -> Result<()> {
|
|||
.push_str("//! File automatically generated by `examples/generate_check_code_prefix.rs`.");
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
output.push_str("use colored::Colorize;");
|
||||
output.push('\n');
|
||||
output.push_str("use serde::{{Serialize, Deserialize}};");
|
||||
output.push('\n');
|
||||
output.push_str("use strum_macros::EnumString;");
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustpython_parser::ast::Location;
|
|||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::checks::{Check, CheckCode, CheckKind, REDIRECTS};
|
||||
use crate::noqa;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
use crate::settings::Settings;
|
||||
|
@ -70,7 +70,7 @@ pub fn check_lines(
|
|||
}
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&$check.kind.code().as_ref()) {
|
||||
if noqa::includes($check.kind.code(), codes) {
|
||||
matches.push($check.kind.code().as_ref());
|
||||
if ignore_noqa {
|
||||
line_checks.push($check);
|
||||
|
@ -140,7 +140,7 @@ pub fn check_lines(
|
|||
ignored.push(index);
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
if noqa::includes(check.kind.code(), codes) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
ignored.push(index);
|
||||
}
|
||||
|
@ -210,6 +210,7 @@ pub fn check_lines(
|
|||
let mut invalid_codes = vec![];
|
||||
let mut valid_codes = vec![];
|
||||
for code in codes {
|
||||
let code = REDIRECTS.get(code).map_or(code, AsRef::as_ref);
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
valid_codes.push(code.to_string());
|
||||
} else {
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::fmt;
|
|||
use std::str::FromStr;
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
|
||||
|
@ -2562,6 +2564,27 @@ impl Check {
|
|||
}
|
||||
}
|
||||
|
||||
/// A hash map from deprecated to latest `CheckCode`.
|
||||
pub static REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
||||
FxHashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U001", CheckCode::UP001),
|
||||
("U003", CheckCode::UP003),
|
||||
("U004", CheckCode::UP004),
|
||||
("U005", CheckCode::UP005),
|
||||
("U006", CheckCode::UP006),
|
||||
("U007", CheckCode::UP007),
|
||||
("U008", CheckCode::UP008),
|
||||
("U009", CheckCode::UP009),
|
||||
("U010", CheckCode::UP010),
|
||||
("U011", CheckCode::UP011),
|
||||
("U012", CheckCode::UP012),
|
||||
("U013", CheckCode::UP013),
|
||||
("U014", CheckCode::UP014),
|
||||
("U015", CheckCode::UP015),
|
||||
])
|
||||
});
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! File automatically generated by `examples/generate_check_code_prefix.rs`.
|
||||
|
||||
use colored::Colorize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::EnumString;
|
||||
|
||||
|
@ -348,6 +349,24 @@ pub enum CheckCodePrefix {
|
|||
T20,
|
||||
T201,
|
||||
T203,
|
||||
U,
|
||||
U0,
|
||||
U00,
|
||||
U001,
|
||||
U003,
|
||||
U004,
|
||||
U005,
|
||||
U006,
|
||||
U007,
|
||||
U008,
|
||||
U009,
|
||||
U01,
|
||||
U010,
|
||||
U011,
|
||||
U012,
|
||||
U013,
|
||||
U014,
|
||||
U015,
|
||||
UP,
|
||||
UP0,
|
||||
UP00,
|
||||
|
@ -1320,6 +1339,182 @@ impl CheckCodePrefix {
|
|||
CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T201 => vec![CheckCode::T201],
|
||||
CheckCodePrefix::T203 => vec![CheckCode::T203],
|
||||
CheckCodePrefix::U => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U0 => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U00 => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
],
|
||||
CheckCodePrefix::U001 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U001` has been renamed to `UP001`".bold()
|
||||
);
|
||||
vec![CheckCode::UP001]
|
||||
}
|
||||
CheckCodePrefix::U003 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U003` has been renamed to `UP003`".bold()
|
||||
);
|
||||
vec![CheckCode::UP003]
|
||||
}
|
||||
CheckCodePrefix::U004 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U004` has been renamed to `UP004`".bold()
|
||||
);
|
||||
vec![CheckCode::UP004]
|
||||
}
|
||||
CheckCodePrefix::U005 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U005` has been renamed to `UP005`".bold()
|
||||
);
|
||||
vec![CheckCode::UP005]
|
||||
}
|
||||
CheckCodePrefix::U006 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U006` has been renamed to `UP006`".bold()
|
||||
);
|
||||
vec![CheckCode::UP006]
|
||||
}
|
||||
CheckCodePrefix::U007 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U007` has been renamed to `UP007`".bold()
|
||||
);
|
||||
vec![CheckCode::UP007]
|
||||
}
|
||||
CheckCodePrefix::U008 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U008` has been renamed to `UP008`".bold()
|
||||
);
|
||||
vec![CheckCode::UP008]
|
||||
}
|
||||
CheckCodePrefix::U009 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U009` has been renamed to `UP009`".bold()
|
||||
);
|
||||
vec![CheckCode::UP009]
|
||||
}
|
||||
CheckCodePrefix::U01 => vec![
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U010 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U010` has been renamed to `UP010`".bold()
|
||||
);
|
||||
vec![CheckCode::UP010]
|
||||
}
|
||||
CheckCodePrefix::U011 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U011` has been renamed to `UP011`".bold()
|
||||
);
|
||||
vec![CheckCode::UP011]
|
||||
}
|
||||
CheckCodePrefix::U012 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U012` has been renamed to `UP012`".bold()
|
||||
);
|
||||
vec![CheckCode::UP012]
|
||||
}
|
||||
CheckCodePrefix::U013 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U013` has been renamed to `UP013`".bold()
|
||||
);
|
||||
vec![CheckCode::UP013]
|
||||
}
|
||||
CheckCodePrefix::U014 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U014` has been renamed to `UP014`".bold()
|
||||
);
|
||||
vec![CheckCode::UP014]
|
||||
}
|
||||
CheckCodePrefix::U015 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U015` has been renamed to `UP015`".bold()
|
||||
);
|
||||
vec![CheckCode::UP015]
|
||||
}
|
||||
CheckCodePrefix::UP => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
|
@ -1778,6 +1973,24 @@ impl CheckCodePrefix {
|
|||
CheckCodePrefix::T20 => SuffixLength::Two,
|
||||
CheckCodePrefix::T201 => SuffixLength::Three,
|
||||
CheckCodePrefix::T203 => SuffixLength::Three,
|
||||
CheckCodePrefix::U => SuffixLength::Zero,
|
||||
CheckCodePrefix::U0 => SuffixLength::One,
|
||||
CheckCodePrefix::U00 => SuffixLength::Two,
|
||||
CheckCodePrefix::U001 => SuffixLength::Three,
|
||||
CheckCodePrefix::U003 => SuffixLength::Three,
|
||||
CheckCodePrefix::U004 => SuffixLength::Three,
|
||||
CheckCodePrefix::U005 => SuffixLength::Three,
|
||||
CheckCodePrefix::U006 => SuffixLength::Three,
|
||||
CheckCodePrefix::U007 => SuffixLength::Three,
|
||||
CheckCodePrefix::U008 => SuffixLength::Three,
|
||||
CheckCodePrefix::U009 => SuffixLength::Three,
|
||||
CheckCodePrefix::U01 => SuffixLength::Two,
|
||||
CheckCodePrefix::U010 => SuffixLength::Three,
|
||||
CheckCodePrefix::U011 => SuffixLength::Three,
|
||||
CheckCodePrefix::U012 => SuffixLength::Three,
|
||||
CheckCodePrefix::U013 => SuffixLength::Three,
|
||||
CheckCodePrefix::U014 => SuffixLength::Three,
|
||||
CheckCodePrefix::U015 => SuffixLength::Three,
|
||||
CheckCodePrefix::UP => SuffixLength::Zero,
|
||||
CheckCodePrefix::UP0 => SuffixLength::One,
|
||||
CheckCodePrefix::UP00 => SuffixLength::Two,
|
||||
|
@ -1847,6 +2060,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
|
|||
CheckCodePrefix::RUF,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::UP,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
|
|
16
src/noqa.rs
16
src/noqa.rs
|
@ -8,7 +8,7 @@ use nohash_hasher::IntMap;
|
|||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::checks::{Check, CheckCode};
|
||||
use crate::checks::{Check, CheckCode, REDIRECTS};
|
||||
|
||||
static NO_QA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
|
@ -37,6 +37,7 @@ pub enum Directive<'a> {
|
|||
Codes(usize, usize, usize, Vec<&'a str>),
|
||||
}
|
||||
|
||||
/// Extract the noqa `Directive` from a line of Python source code.
|
||||
pub fn extract_noqa_directive(line: &str) -> Directive {
|
||||
match NO_QA_LINE_REGEX.captures(line) {
|
||||
Some(caps) => match caps.name("spaces") {
|
||||
|
@ -64,6 +65,19 @@ pub fn extract_noqa_directive(line: &str) -> Directive {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the string list of `codes` includes `code` (or an alias
|
||||
/// thereof).
|
||||
pub fn includes(needle: &CheckCode, haystack: &[&str]) -> bool {
|
||||
let needle: &str = needle.as_ref();
|
||||
haystack.iter().any(|candidate| {
|
||||
if let Some(candidate) = REDIRECTS.get(candidate) {
|
||||
needle == candidate.as_ref()
|
||||
} else {
|
||||
&needle == candidate
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_noqa(
|
||||
path: &Path,
|
||||
checks: &[Check],
|
||||
|
|
|
@ -74,4 +74,16 @@ mod tests {
|
|||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redirects() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/ruff/redirects.py"),
|
||||
&settings::Settings::for_rules(vec![CheckCode::UP007]),
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
6
src/rules/snapshots/ruff__rules__tests__redirects.snap
Normal file
6
src/rules/snapshots/ruff__rules__tests__redirects.snap
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/rules/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue