Skip to content

Commit

Permalink
Gate logical line behaviors under a feature
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Feb 5, 2023
1 parent 775f5fe commit 362d9e9
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 90 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ jobs:
- name: "Run tests (Ubuntu)"
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
cargo insta test --all --delete-unreferenced-snapshots
cargo insta test --all --all-features --delete-unreferenced-snapshots
git diff --exit-code
- name: "Run tests (Windows)"
if: ${{ matrix.os == 'windows-latest' }}
shell: bash
run: |
cargo insta test --all
cargo insta test --all --all-features
git diff --exit-code
- run: cargo test --package ruff_cli --test black_compatibility_test -- --ignored
# Check for broken links in the documentation.
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ thiserror = { version = "1.0" }
titlecase = { version = "2.2.1" }
toml = { version = "0.6.0" }

[features]
default = []
logical_lines = []

# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
# For (future) wasm-pack support
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
Expand Down
14 changes: 0 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -701,20 +701,6 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| E101 | mixed-spaces-and-tabs | Indentation contains mixed spaces and tabs | |
| E111 | indentation-with-invalid-multiple | Indentation is not a multiple of {indent_size} | |
| E112 | no-indented-block | Expected an indented block | |
| E113 | unexpected-indentation | Unexpected indentation | |
| E114 | indentation-with-invalid-multiple-comment | Indentation is not a multiple of {indent_size} (comment) | |
| E115 | no-indented-block-comment | Expected an indented block (comment) | |
| E116 | unexpected-indentation-comment | Unexpected indentation (comment) | |
| E117 | over-indented | Over-indented | |
| E201 | whitespace-after-open-bracket | Whitespace after '(' | |
| E202 | whitespace-before-close-bracket | Whitespace before ')' | |
| E203 | whitespace-before-punctuation | Whitespace before ',', ';', or ':' | |
| E221 | multiple-spaces-before-operator | Multiple spaces before operator | |
| E222 | multiple-spaces-after-operator | Multiple spaces after operator | |
| E223 | tab-before-operator | Tab before operator | |
| E224 | tab-after-operator | Tab after operator | |
| E401 | multiple-imports-on-one-line | Multiple imports on one line | |
| E402 | module-import-not-at-top-of-file | Module level import not at top of file | |
| E501 | line-too-long | Line too long ({length} > {limit} characters) | |
Expand Down
14 changes: 0 additions & 14 deletions ruff.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1447,23 +1447,9 @@
"E10",
"E101",
"E11",
"E111",
"E112",
"E113",
"E114",
"E115",
"E116",
"E117",
"E2",
"E20",
"E201",
"E202",
"E203",
"E22",
"E221",
"E222",
"E223",
"E224",
"E4",
"E40",
"E401",
Expand Down
2 changes: 1 addition & 1 deletion ruff_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ doctest = false
once_cell = { version = "1.17.0" }
proc-macro2 = { version = "1.0.47" }
quote = { version = "1.0.21" }
syn = { version = "1.0.103", features = ["derive", "parsing"] }
syn = { version = "1.0.103", features = ["derive", "parsing", "extra-traits"] }
textwrap = { version = "0.16.0" }
43 changes: 27 additions & 16 deletions ruff_macros/src/define_rule_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashMap;
use proc_macro2::Span;
use quote::quote;
use syn::parse::Parse;
use syn::{Ident, LitStr, Path, Token};
use syn::{Attribute, Ident, LitStr, Path, Token};

pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
let mut rule_variants = quote!();
Expand All @@ -18,25 +18,32 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
let mut diagkind_commit_match_arms = quote!();
let mut from_impls_for_diagkind = quote!();

for (code, path, name) in &mapping.entries {
for (code, path, name, attr) in &mapping.entries {
let code_str = LitStr::new(&code.to_string(), Span::call_site());
rule_variants.extend(quote! {
#[doc = #code_str]
#(#attr)*
#name,
});
diagkind_variants.extend(quote! {#name(#path),});
diagkind_variants.extend(quote! {#(#attr)* #name(#path),});

// Apply the `attrs` to each arm, like `[cfg(feature = "foo")]`.
rule_message_formats_match_arms
.extend(quote! {Self::#name => <#path as Violation>::message_formats(),});
rule_autofixable_match_arms.extend(quote! {Self::#name => <#path as Violation>::AUTOFIX,});
rule_code_match_arms.extend(quote! {Self::#name => #code_str,});
rule_from_code_match_arms.extend(quote! {#code_str => Ok(Rule::#name), });
diagkind_code_match_arms.extend(quote! {Self::#name(..) => &Rule::#name, });
diagkind_body_match_arms.extend(quote! {Self::#name(x) => Violation::message(x), });
.extend(quote! {#(#attr)* Self::#name => <#path as Violation>::message_formats(),});
rule_autofixable_match_arms
.extend(quote! {#(#attr)* Self::#name => <#path as Violation>::AUTOFIX,});
rule_code_match_arms.extend(quote! {#(#attr)* Self::#name => #code_str,});
rule_from_code_match_arms.extend(quote! {#(#attr)* #code_str => Ok(Rule::#name), });
diagkind_code_match_arms.extend(quote! {#(#attr)* Self::#name(..) => &Rule::#name, });
diagkind_body_match_arms
.extend(quote! {#(#attr)* Self::#name(x) => Violation::message(x), });
diagkind_fixable_match_arms
.extend(quote! {Self::#name(x) => x.autofix_title_formatter().is_some(),});
diagkind_commit_match_arms
.extend(quote! {Self::#name(x) => x.autofix_title_formatter().map(|f| f(x)), });
.extend(quote! {#(#attr)* Self::#name(x) => x.autofix_title_formatter().is_some(),});
diagkind_commit_match_arms.extend(
quote! {#(#attr)* Self::#name(x) => x.autofix_title_formatter().map(|f| f(x)), },
);
from_impls_for_diagkind.extend(quote! {
#(#attr)*
impl From<#path> for DiagnosticKind {
fn from(x: #path) -> Self {
DiagnosticKind::#name(x)
Expand All @@ -48,14 +55,15 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
let code_to_name: HashMap<_, _> = mapping
.entries
.iter()
.map(|(code, _, name)| (code.to_string(), name))
.map(|(code, _, name, _)| (code.to_string(), name))
.collect();

let rulecodeprefix = super::rule_code_prefix::expand(
&Ident::new("Rule", Span::call_site()),
&Ident::new("RuleCodePrefix", Span::call_site()),
mapping.entries.iter().map(|(code, ..)| code),
|code| code_to_name[code],
mapping.entries.iter().map(|(.., attr)| attr),
);

quote! {
Expand Down Expand Up @@ -104,7 +112,6 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
}
}


impl DiagnosticKind {
/// The rule of the diagnostic.
pub fn rule(&self) -> &'static Rule {
Expand Down Expand Up @@ -134,19 +141,23 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
}

pub struct Mapping {
entries: Vec<(Ident, Path, Ident)>,
entries: Vec<(Ident, Path, Ident, Vec<Attribute>)>,
}

impl Parse for Mapping {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut entries = Vec::new();
while !input.is_empty() {
// Grab the `#[cfg(...)]` attributes.
let attrs = input.call(Attribute::parse_outer)?;

// Parse the `RuleCodePrefix::... => ...` part.
let code: Ident = input.parse()?;
let _: Token![=>] = input.parse()?;
let path: Path = input.parse()?;
let name = path.segments.last().unwrap().ident.clone();
let _: Token![,] = input.parse()?;
entries.push((code, path, name));
entries.push((code, path, name, attrs));
}
Ok(Self { entries })
}
Expand Down
49 changes: 35 additions & 14 deletions ruff_macros/src/rule_code_prefix.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeMap;

use proc_macro2::Span;
use quote::quote;
use syn::Ident;
use syn::{Attribute, Ident};

pub fn expand<'a>(
rule_type: &Ident,
prefix_ident: &Ident,
variants: impl Iterator<Item = &'a Ident>,
variant_name: impl Fn(&str) -> &'a Ident,
attr: impl Iterator<Item = &'a Vec<Attribute>>,
) -> proc_macro2::TokenStream {
// Build up a map from prefix to matching RuleCodes.
let mut prefix_to_codes: BTreeMap<String, BTreeSet<String>> = BTreeMap::default();
let mut prefix_to_codes: BTreeMap<String, BTreeMap<String, Vec<Attribute>>> =
BTreeMap::default();

let mut all_codes = BTreeSet::new();
let mut pl_codes = BTreeSet::new();
let mut pl_codes = BTreeMap::new();

for variant in variants {
for (variant, attr) in variants.zip(attr) {
let code_str = variant.to_string();
let code_prefix_len = code_str
.chars()
Expand All @@ -28,19 +29,25 @@ pub fn expand<'a>(
prefix_to_codes
.entry(prefix)
.or_default()
.insert(code_str.clone());
.entry(code_str.clone())
.or_insert_with(|| attr.clone());
}
if code_str.starts_with("PL") {
pl_codes.insert(code_str.to_string());
pl_codes.insert(code_str, attr.clone());
}
all_codes.insert(code_str);
}

prefix_to_codes.insert("PL".to_string(), pl_codes);

let prefix_variants = prefix_to_codes.keys().map(|prefix| {
let prefix_variants = prefix_to_codes.iter().map(|(prefix, codes)| {
let prefix = Ident::new(prefix, Span::call_site());
let attr = if codes.len() == 1 {
codes.values().next().unwrap().clone()
} else {
vec![]
};
quote! {
#(#attr)*
#prefix
}
});
Expand Down Expand Up @@ -74,24 +81,32 @@ pub fn expand<'a>(
fn generate_impls<'a>(
rule_type: &Ident,
prefix_ident: &Ident,
prefix_to_codes: &BTreeMap<String, BTreeSet<String>>,
prefix_to_codes: &BTreeMap<String, BTreeMap<String, Vec<Attribute>>>,
variant_name: impl Fn(&str) -> &'a Ident,
) -> proc_macro2::TokenStream {
let into_iter_match_arms = prefix_to_codes.iter().map(|(prefix_str, codes)| {
let codes = codes.iter().map(|code| {
let prefix = Ident::new(prefix_str, Span::call_site());
let attr = if codes.len() == 1 {
codes.values().next().unwrap().clone()
} else {
vec![]
};

let codes = codes.iter().map(|(code, attr)| {
let rule_variant = variant_name(code);
quote! {
#(#attr)*
#rule_type::#rule_variant
}
});
let prefix = Ident::new(prefix_str, Span::call_site());

quote! {
#(#attr)*
#prefix_ident::#prefix => vec![#(#codes),*].into_iter(),
}
});

let specificity_match_arms = prefix_to_codes.keys().map(|prefix_str| {
let specificity_match_arms = prefix_to_codes.iter().map(|(prefix_str, codes)| {
let prefix = Ident::new(prefix_str, Span::call_site());
let mut num_numeric = prefix_str.chars().filter(|char| char.is_numeric()).count();
if prefix_str != "PL" && prefix_str.starts_with("PL") {
Expand All @@ -106,7 +121,13 @@ fn generate_impls<'a>(
5 => quote! { Specificity::Code5Chars },
_ => panic!("Invalid prefix: {prefix}"),
};
let attr = if codes.len() == 1 {
codes.values().next().unwrap().clone()
} else {
vec![]
};
quote! {
#(#attr)*
#prefix_ident::#prefix => #suffix_len,
}
});
Expand Down
45 changes: 30 additions & 15 deletions src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,33 @@ use crate::violation::Violation;
ruff_macros::define_rule_mapping!(
// pycodestyle errors
E101 => rules::pycodestyle::rules::MixedSpacesAndTabs,
#[cfg(feature = "logical_lines")]
E111 => rules::pycodestyle::rules::IndentationWithInvalidMultiple,
#[cfg(feature = "logical_lines")]
E112 => rules::pycodestyle::rules::NoIndentedBlock,
#[cfg(feature = "logical_lines")]
E113 => rules::pycodestyle::rules::UnexpectedIndentation,
E114 => rules::pycodestyle::rules::IndentationWithInvalidMultipleComment,
#[cfg(feature = "logical_lines")]
E114 => rules::pycodestyle::rules::IndentationWithInvalidMultipleComment,
#[cfg(feature = "logical_lines")]
E115 => rules::pycodestyle::rules::NoIndentedBlockComment,
#[cfg(feature = "logical_lines")]
E116 => rules::pycodestyle::rules::UnexpectedIndentationComment,
#[cfg(feature = "logical_lines")]
E117 => rules::pycodestyle::rules::OverIndented,
#[cfg(feature = "logical_lines")]
E201 => rules::pycodestyle::rules::WhitespaceAfterOpenBracket,
#[cfg(feature = "logical_lines")]
E202 => rules::pycodestyle::rules::WhitespaceBeforeCloseBracket,
#[cfg(feature = "logical_lines")]
E203 => rules::pycodestyle::rules::WhitespaceBeforePunctuation,
#[cfg(feature = "logical_lines")]
E221 => rules::pycodestyle::rules::MultipleSpacesBeforeOperator,
#[cfg(feature = "logical_lines")]
E222 => rules::pycodestyle::rules::MultipleSpacesAfterOperator,
#[cfg(feature = "logical_lines")]
E223 => rules::pycodestyle::rules::TabBeforeOperator,
#[cfg(feature = "logical_lines")]
E224 => rules::pycodestyle::rules::TabAfterOperator,
E401 => rules::pycodestyle::rules::MultipleImportsOnOneLine,
E402 => rules::pycodestyle::rules::ModuleImportNotAtTopOfFile,
Expand Down Expand Up @@ -698,20 +712,6 @@ impl Rule {
pub const fn lint_source(&self) -> &'static LintSource {
match self {
Rule::UnusedNOQA => &LintSource::NoQa,
Rule::IndentationWithInvalidMultiple
| Rule::IndentationWithInvalidMultipleComment
| Rule::MultipleSpacesAfterOperator
| Rule::MultipleSpacesBeforeOperator
| Rule::NoIndentedBlock
| Rule::NoIndentedBlockComment
| Rule::OverIndented
| Rule::TabAfterOperator
| Rule::TabBeforeOperator
| Rule::UnexpectedIndentation
| Rule::UnexpectedIndentationComment
| Rule::WhitespaceAfterOpenBracket
| Rule::WhitespaceBeforeCloseBracket
| Rule::WhitespaceBeforePunctuation => &LintSource::LogicalLines,
Rule::BlanketNOQA
| Rule::BlanketTypeIgnore
| Rule::DocLineTooLong
Expand Down Expand Up @@ -742,6 +742,21 @@ impl Rule {
Rule::IOError => &LintSource::Io,
Rule::UnsortedImports | Rule::MissingRequiredImport => &LintSource::Imports,
Rule::ImplicitNamespacePackage => &LintSource::Filesystem,
#[cfg(feature = "logical_lines")]
Rule::IndentationWithInvalidMultiple
| Rule::IndentationWithInvalidMultipleComment
| Rule::MultipleSpacesAfterOperator
| Rule::MultipleSpacesBeforeOperator
| Rule::NoIndentedBlock
| Rule::NoIndentedBlockComment
| Rule::OverIndented
| Rule::TabAfterOperator
| Rule::TabBeforeOperator
| Rule::UnexpectedIndentation
| Rule::UnexpectedIndentationComment
| Rule::WhitespaceAfterOpenBracket
| Rule::WhitespaceBeforeCloseBracket
| Rule::WhitespaceBeforePunctuation => &LintSource::LogicalLines,
_ => &LintSource::Ast,
}
}
Expand Down
Loading

0 comments on commit 362d9e9

Please sign in to comment.