Compare commits
2 Commits
cli/previe
...
charlie/cl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffbbb8d912 | ||
|
|
b9f0ade746 |
@@ -4,6 +4,7 @@ use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use ruff::registry::Linter;
|
||||
use ruff::settings::types::PreviewMode;
|
||||
use ruff::RuleSelector;
|
||||
|
||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
@@ -331,7 +332,7 @@ pub(crate) fn infer_plugins_from_codes(selectors: &HashSet<RuleSelector>) -> Vec
|
||||
.filter(|plugin| {
|
||||
for selector in selectors {
|
||||
if selector
|
||||
.into_iter()
|
||||
.rules(PreviewMode::Disabled)
|
||||
.any(|rule| Linter::from(plugin).rules().any(|r| r == rule))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::codes::RuleCodePrefix;
|
||||
use crate::codes::RuleIter;
|
||||
use crate::registry::{Linter, Rule, RuleNamespace};
|
||||
use crate::rule_redirects::get_redirect;
|
||||
use crate::settings::types::PreviewMode;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum RuleSelector {
|
||||
@@ -29,6 +30,11 @@ pub enum RuleSelector {
|
||||
prefix: RuleCodePrefix,
|
||||
redirected_from: Option<&'static str>,
|
||||
},
|
||||
/// Select an individual rule with a given prefix.
|
||||
Rule {
|
||||
prefix: RuleCodePrefix,
|
||||
redirected_from: Option<&'static str>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<Linter> for RuleSelector {
|
||||
@@ -61,16 +67,42 @@ impl FromStr for RuleSelector {
|
||||
return Ok(Self::Linter(linter));
|
||||
}
|
||||
|
||||
Ok(Self::Prefix {
|
||||
prefix: RuleCodePrefix::parse(&linter, code)
|
||||
.map_err(|_| ParseError::Unknown(s.to_string()))?,
|
||||
redirected_from,
|
||||
})
|
||||
// Does the selector select a single rule?
|
||||
let prefix = RuleCodePrefix::parse(&linter, code)
|
||||
.map_err(|_| ParseError::Unknown(s.to_string()))?;
|
||||
if is_single_rule_selector(&prefix) {
|
||||
Ok(Self::Rule {
|
||||
prefix,
|
||||
redirected_from,
|
||||
})
|
||||
} else {
|
||||
Ok(Self::Prefix {
|
||||
prefix,
|
||||
redirected_from,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the [`RuleCodePrefix`] matches a single rule exactly
|
||||
/// (e.g., `E225`, as opposed to `E2`).
|
||||
fn is_single_rule_selector(prefix: &RuleCodePrefix) -> bool {
|
||||
let mut rules = prefix.rules();
|
||||
|
||||
// The selector must match a single rule.
|
||||
let Some(rule) = rules.next() else {
|
||||
return false;
|
||||
};
|
||||
if rules.next().is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The rule must match the selector exactly.
|
||||
rule.noqa_code().suffix() == prefix.short_code()
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ParseError {
|
||||
#[error("Unknown rule selector: `{0}`")]
|
||||
@@ -86,7 +118,7 @@ impl RuleSelector {
|
||||
RuleSelector::Preview => ("", "PREVIEW"),
|
||||
RuleSelector::C => ("", "C"),
|
||||
RuleSelector::T => ("", "T"),
|
||||
RuleSelector::Prefix { prefix, .. } => {
|
||||
RuleSelector::Prefix { prefix, .. } | RuleSelector::Rule { prefix, .. } => {
|
||||
(prefix.linter().common_prefix(), prefix.short_code())
|
||||
}
|
||||
RuleSelector::Linter(l) => (l.common_prefix(), ""),
|
||||
@@ -137,20 +169,9 @@ impl Visitor<'_> for SelectorVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RuleCodePrefix> for RuleSelector {
|
||||
fn from(prefix: RuleCodePrefix) -> Self {
|
||||
Self::Prefix {
|
||||
prefix,
|
||||
redirected_from: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for &RuleSelector {
|
||||
type IntoIter = RuleSelectorIter;
|
||||
type Item = Rule;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
impl RuleSelector {
|
||||
/// Return all matching rules, regardless of whether they're in preview.
|
||||
pub fn all_rules(&self) -> impl Iterator<Item = Rule> + '_ {
|
||||
match self {
|
||||
RuleSelector::All => RuleSelectorIter::All(Rule::iter()),
|
||||
RuleSelector::Preview => {
|
||||
@@ -167,9 +188,18 @@ impl IntoIterator for &RuleSelector {
|
||||
.chain(Linter::Flake8Print.rules()),
|
||||
),
|
||||
RuleSelector::Linter(linter) => RuleSelectorIter::Vec(linter.rules()),
|
||||
RuleSelector::Prefix { prefix, .. } => RuleSelectorIter::Vec(prefix.clone().rules()),
|
||||
RuleSelector::Prefix { prefix, .. } | RuleSelector::Rule { prefix, .. } => {
|
||||
RuleSelectorIter::Vec(prefix.clone().rules())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns rules matching the selector, taking into account whether preview mode is enabled.
|
||||
pub fn rules(&self, preview: PreviewMode) -> impl Iterator<Item = Rule> + '_ {
|
||||
self.all_rules().filter(move |rule| {
|
||||
matches!(self, RuleSelector::Rule { .. }) || preview.is_enabled() || !rule.is_preview()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RuleSelectorIter {
|
||||
@@ -270,14 +300,14 @@ impl RuleSelector {
|
||||
RuleSelector::T => Specificity::LinterGroup,
|
||||
RuleSelector::C => Specificity::LinterGroup,
|
||||
RuleSelector::Linter(..) => Specificity::Linter,
|
||||
RuleSelector::Rule { .. } => Specificity::Rule,
|
||||
RuleSelector::Prefix { prefix, .. } => {
|
||||
let prefix: &'static str = prefix.short_code();
|
||||
match prefix.len() {
|
||||
1 => Specificity::Code1Char,
|
||||
2 => Specificity::Code2Chars,
|
||||
3 => Specificity::Code3Chars,
|
||||
4 => Specificity::Code4Chars,
|
||||
5 => Specificity::Code5Chars,
|
||||
1 => Specificity::Prefix1Char,
|
||||
2 => Specificity::Prefix2Chars,
|
||||
3 => Specificity::Prefix3Chars,
|
||||
4 => Specificity::Prefix4Chars,
|
||||
_ => panic!("RuleSelector::specificity doesn't yet support codes with so many characters"),
|
||||
}
|
||||
}
|
||||
@@ -287,14 +317,22 @@ impl RuleSelector {
|
||||
|
||||
#[derive(EnumIter, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
|
||||
pub enum Specificity {
|
||||
/// The specificity when selecting all rules (e.g., `--select ALL`).
|
||||
All,
|
||||
/// The specificity when selecting a linter group (e.g., `--select PL`).
|
||||
LinterGroup,
|
||||
/// The specificity when selecting a linter (e.g., `--select PLE` or `--select UP`).
|
||||
Linter,
|
||||
Code1Char,
|
||||
Code2Chars,
|
||||
Code3Chars,
|
||||
Code4Chars,
|
||||
Code5Chars,
|
||||
/// The specificity when selecting via a rule prefix at one-character depth (e.g., `--select PLE1`).
|
||||
Prefix1Char,
|
||||
/// The specificity when selecting via a rule prefix at two-character depth (e.g., `--select PLE12`).
|
||||
Prefix2Chars,
|
||||
/// The specificity when selecting via a rule prefix at one-character depth (e.g., `--select PLE120`).
|
||||
Prefix3Chars,
|
||||
/// The specificity when selecting via a rule prefix at one-character depth (e.g., `--select PLE120`).
|
||||
Prefix4Chars,
|
||||
/// The specificity when selecting an individual rule (e.g., `--select PLE1205`).
|
||||
Rule,
|
||||
}
|
||||
|
||||
#[cfg(feature = "clap")]
|
||||
|
||||
@@ -70,7 +70,10 @@ pub static INCLUDE: Lazy<Vec<FilePattern>> = Lazy::new(|| {
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
rules: PREFIXES.iter().flat_map(IntoIterator::into_iter).collect(),
|
||||
rules: PREFIXES
|
||||
.iter()
|
||||
.flat_map(|selector| selector.rules(PreviewMode::default()))
|
||||
.collect(),
|
||||
allowed_confusables: FxHashSet::from_iter([]),
|
||||
builtins: vec![],
|
||||
dummy_variable_rgx: DUMMY_VARIABLE_RGX.clone(),
|
||||
|
||||
@@ -194,7 +194,7 @@ pub struct PerFileIgnore {
|
||||
|
||||
impl PerFileIgnore {
|
||||
pub fn new(pattern: String, prefixes: &[RuleSelector], project_root: Option<&Path>) -> Self {
|
||||
let rules: RuleSet = prefixes.iter().flat_map(IntoIterator::into_iter).collect();
|
||||
let rules: RuleSet = prefixes.iter().flat_map(RuleSelector::all_rules).collect();
|
||||
let path = Path::new(&pattern);
|
||||
let absolute = match project_root {
|
||||
Some(project_root) => fs::normalize_path_to(path, project_root),
|
||||
|
||||
@@ -8,7 +8,7 @@ use syn::{
|
||||
Ident, ItemFn, LitStr, Pat, Path, Stmt, Token,
|
||||
};
|
||||
|
||||
use crate::rule_code_prefix::{get_prefix_ident, if_all_same, is_preview};
|
||||
use crate::rule_code_prefix::{get_prefix_ident, if_all_same};
|
||||
|
||||
/// A rule entry in the big match statement such a
|
||||
/// `(Pycodestyle, "E112") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::NoIndentedBlock),`
|
||||
@@ -156,7 +156,7 @@ pub(crate) fn map_codes(func: &ItemFn) -> syn::Result<TokenStream> {
|
||||
|
||||
output.extend(quote! {
|
||||
impl #linter {
|
||||
pub fn rules(self) -> ::std::vec::IntoIter<Rule> {
|
||||
pub fn rules(&self) -> ::std::vec::IntoIter<Rule> {
|
||||
match self { #prefix_into_iter_match_arms }
|
||||
}
|
||||
}
|
||||
@@ -172,7 +172,7 @@ pub(crate) fn map_codes(func: &ItemFn) -> syn::Result<TokenStream> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rules(self) -> ::std::vec::IntoIter<Rule> {
|
||||
pub fn rules(&self) -> ::std::vec::IntoIter<Rule> {
|
||||
match self {
|
||||
#(RuleCodePrefix::#linter_idents(prefix) => prefix.clone().rules(),)*
|
||||
}
|
||||
@@ -195,26 +195,12 @@ fn rules_by_prefix(
|
||||
// TODO(charlie): Why do we do this here _and_ in `rule_code_prefix::expand`?
|
||||
let mut rules_by_prefix = BTreeMap::new();
|
||||
|
||||
for (code, rule) in rules {
|
||||
// Nursery rules have to be explicitly selected, so we ignore them when looking at
|
||||
// prefix-level selectors (e.g., `--select SIM10`), but add the rule itself under
|
||||
// its fully-qualified code (e.g., `--select SIM101`).
|
||||
if is_preview(&rule.group) {
|
||||
rules_by_prefix.insert(code.clone(), vec![(rule.path.clone(), rule.attrs.clone())]);
|
||||
continue;
|
||||
}
|
||||
|
||||
for code in rules.keys() {
|
||||
for i in 1..=code.len() {
|
||||
let prefix = code[..i].to_string();
|
||||
let rules: Vec<_> = rules
|
||||
.iter()
|
||||
.filter_map(|(code, rule)| {
|
||||
// Nursery rules have to be explicitly selected, so we ignore them when
|
||||
// looking at prefixes.
|
||||
if is_preview(&rule.group) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if code.starts_with(&prefix) {
|
||||
Some((rule.path.clone(), rule.attrs.clone()))
|
||||
} else {
|
||||
@@ -336,12 +322,10 @@ fn generate_iter_impl(
|
||||
let mut linter_rules_match_arms = quote!();
|
||||
let mut linter_all_rules_match_arms = quote!();
|
||||
for (linter, map) in linter_to_rules {
|
||||
let rule_paths = map.values().filter(|rule| !is_preview(&rule.group)).map(
|
||||
|Rule { attrs, path, .. }| {
|
||||
let rule_name = path.segments.last().unwrap();
|
||||
quote!(#(#attrs)* Rule::#rule_name)
|
||||
},
|
||||
);
|
||||
let rule_paths = map.values().map(|Rule { attrs, path, .. }| {
|
||||
let rule_name = path.segments.last().unwrap();
|
||||
quote!(#(#attrs)* Rule::#rule_name)
|
||||
});
|
||||
linter_rules_match_arms.extend(quote! {
|
||||
Linter::#linter => vec![#(#rule_paths,)*].into_iter(),
|
||||
});
|
||||
|
||||
@@ -12,22 +12,14 @@ pub(crate) fn expand<'a>(
|
||||
let mut prefix_to_codes: BTreeMap<String, BTreeSet<String>> = BTreeMap::default();
|
||||
let mut code_to_attributes: BTreeMap<String, &[Attribute]> = BTreeMap::default();
|
||||
|
||||
for (variant, group, attr) in variants {
|
||||
for (variant, .., attr) in variants {
|
||||
let code_str = variant.to_string();
|
||||
// Nursery rules have to be explicitly selected, so we ignore them when looking at prefixes.
|
||||
if is_preview(group) {
|
||||
for i in 1..=code_str.len() {
|
||||
let prefix = code_str[..i].to_string();
|
||||
prefix_to_codes
|
||||
.entry(code_str.clone())
|
||||
.entry(prefix)
|
||||
.or_default()
|
||||
.insert(code_str.clone());
|
||||
} else {
|
||||
for i in 1..=code_str.len() {
|
||||
let prefix = code_str[..i].to_string();
|
||||
prefix_to_codes
|
||||
.entry(prefix)
|
||||
.or_default()
|
||||
.insert(code_str.clone());
|
||||
}
|
||||
}
|
||||
|
||||
code_to_attributes.insert(code_str, attr);
|
||||
@@ -125,14 +117,3 @@ pub(crate) fn get_prefix_ident(prefix: &str) -> Ident {
|
||||
};
|
||||
Ident::new(&prefix, Span::call_site())
|
||||
}
|
||||
|
||||
/// Returns true if the given group is the "preview" group.
|
||||
pub(crate) fn is_preview(group: &Path) -> bool {
|
||||
let group = group
|
||||
.segments
|
||||
.iter()
|
||||
.map(|segment| segment.ident.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("::");
|
||||
group == "RuleGroup::Preview"
|
||||
}
|
||||
|
||||
@@ -440,14 +440,18 @@ impl Configuration {
|
||||
}
|
||||
|
||||
pub fn as_rule_table(&self) -> RuleTable {
|
||||
let preview = self.preview.unwrap_or_default();
|
||||
|
||||
// The select_set keeps track of which rules have been selected.
|
||||
let mut select_set: RuleSet = defaults::PREFIXES.iter().flatten().collect();
|
||||
// The fixable set keeps track of which rules are fixable.
|
||||
let mut fixable_set: RuleSet = RuleSelector::All
|
||||
.into_iter()
|
||||
.chain(&RuleSelector::Preview)
|
||||
let mut select_set: RuleSet = defaults::PREFIXES
|
||||
.iter()
|
||||
.map(|selector| selector.rules(preview))
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
// The fixable set keeps track of which rules are fixable.
|
||||
let mut fixable_set: RuleSet = RuleSelector::All.rules(preview).collect();
|
||||
|
||||
// Ignores normally only subtract from the current set of selected
|
||||
// rules. By that logic the ignore in `select = [], ignore = ["E501"]`
|
||||
// would be effectless. Instead we carry over the ignores to the next
|
||||
@@ -474,9 +478,6 @@ impl Configuration {
|
||||
let carriedover_unfixables = carryover_unfixables.take();
|
||||
|
||||
for spec in Specificity::iter() {
|
||||
let include_preview_rules =
|
||||
self.preview.is_some_and(|preview| preview.is_enabled());
|
||||
|
||||
// Iterate over rule selectors in order of specificity.
|
||||
for selector in selection
|
||||
.select
|
||||
@@ -485,10 +486,7 @@ impl Configuration {
|
||||
.chain(selection.extend_select.iter())
|
||||
.filter(|s| s.specificity() == spec)
|
||||
{
|
||||
for rule in selector {
|
||||
if spec == Specificity::All && !include_preview_rules && rule.is_preview() {
|
||||
continue;
|
||||
}
|
||||
for rule in selector.rules(preview) {
|
||||
select_map_updates.insert(rule, true);
|
||||
}
|
||||
}
|
||||
@@ -498,7 +496,7 @@ impl Configuration {
|
||||
.chain(carriedover_ignores.into_iter().flatten())
|
||||
.filter(|s| s.specificity() == spec)
|
||||
{
|
||||
for rule in selector {
|
||||
for rule in selector.rules(preview) {
|
||||
select_map_updates.insert(rule, false);
|
||||
}
|
||||
}
|
||||
@@ -510,7 +508,7 @@ impl Configuration {
|
||||
.chain(selection.extend_fixable.iter())
|
||||
.filter(|s| s.specificity() == spec)
|
||||
{
|
||||
for rule in selector {
|
||||
for rule in selector.rules(preview) {
|
||||
fixable_map_updates.insert(rule, true);
|
||||
}
|
||||
}
|
||||
@@ -520,7 +518,7 @@ impl Configuration {
|
||||
.chain(carriedover_unfixables.into_iter().flatten())
|
||||
.filter(|s| s.specificity() == spec)
|
||||
{
|
||||
for rule in selector {
|
||||
for rule in selector.rules(preview) {
|
||||
fixable_map_updates.insert(rule, false);
|
||||
}
|
||||
}
|
||||
|
||||
25
ruff.schema.json
generated
25
ruff.schema.json
generated
@@ -1797,6 +1797,8 @@
|
||||
"COM818",
|
||||
"COM819",
|
||||
"CPY",
|
||||
"CPY0",
|
||||
"CPY00",
|
||||
"CPY001",
|
||||
"D",
|
||||
"D1",
|
||||
@@ -1883,6 +1885,7 @@
|
||||
"E1",
|
||||
"E10",
|
||||
"E101",
|
||||
"E11",
|
||||
"E111",
|
||||
"E112",
|
||||
"E113",
|
||||
@@ -1890,10 +1893,14 @@
|
||||
"E115",
|
||||
"E116",
|
||||
"E117",
|
||||
"E2",
|
||||
"E20",
|
||||
"E201",
|
||||
"E202",
|
||||
"E203",
|
||||
"E21",
|
||||
"E211",
|
||||
"E22",
|
||||
"E221",
|
||||
"E222",
|
||||
"E223",
|
||||
@@ -1902,15 +1909,20 @@
|
||||
"E226",
|
||||
"E227",
|
||||
"E228",
|
||||
"E23",
|
||||
"E231",
|
||||
"E24",
|
||||
"E241",
|
||||
"E242",
|
||||
"E25",
|
||||
"E251",
|
||||
"E252",
|
||||
"E26",
|
||||
"E261",
|
||||
"E262",
|
||||
"E265",
|
||||
"E266",
|
||||
"E27",
|
||||
"E271",
|
||||
"E272",
|
||||
"E273",
|
||||
@@ -2051,7 +2063,10 @@
|
||||
"FLY00",
|
||||
"FLY002",
|
||||
"FURB",
|
||||
"FURB1",
|
||||
"FURB11",
|
||||
"FURB113",
|
||||
"FURB13",
|
||||
"FURB131",
|
||||
"FURB132",
|
||||
"G",
|
||||
@@ -2196,6 +2211,9 @@
|
||||
"PLC04",
|
||||
"PLC041",
|
||||
"PLC0414",
|
||||
"PLC1",
|
||||
"PLC19",
|
||||
"PLC190",
|
||||
"PLC1901",
|
||||
"PLC3",
|
||||
"PLC30",
|
||||
@@ -2288,6 +2306,9 @@
|
||||
"PLR55",
|
||||
"PLR550",
|
||||
"PLR5501",
|
||||
"PLR6",
|
||||
"PLR63",
|
||||
"PLR630",
|
||||
"PLR6301",
|
||||
"PLW",
|
||||
"PLW0",
|
||||
@@ -2315,12 +2336,16 @@
|
||||
"PLW1509",
|
||||
"PLW151",
|
||||
"PLW1510",
|
||||
"PLW16",
|
||||
"PLW164",
|
||||
"PLW1641",
|
||||
"PLW2",
|
||||
"PLW29",
|
||||
"PLW290",
|
||||
"PLW2901",
|
||||
"PLW3",
|
||||
"PLW32",
|
||||
"PLW320",
|
||||
"PLW3201",
|
||||
"PLW33",
|
||||
"PLW330",
|
||||
|
||||
Reference in New Issue
Block a user