diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF037.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF037.py new file mode 100644 index 0000000000000..0a8132b5aaffb --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF037.py @@ -0,0 +1,83 @@ +#foo +# foo +#ruff: foo-bar +# flake8:foo-bar +# black:skip +# fmt: foo +# isort: skip_entire +# ruff: isort: skipfile +# yapf: off +# FMT:OFF +# isort: On +# Type: ignore +# yapf: Disable +# Yapf: disable +#yapf : enable +# yapf : disable + +# noqa +# noqa: A123 +# noqa: A123, B456 +# ruff: noqa +# ruff: noqa: A123 +# ruff: noqa: A123, B456 +# flake8: noqa +# flake8: noqa: A123 +# flake8: noqa: A123, B456 +# fmt: on +# fmt: off +# fmt: skip +# isort: on +# isort: off +# isort: split +# isort: skip +# isort: skip_file +# ruff: isort: on +# ruff: isort: skip_file +# type: ignore +# type: int +# type: list[str] +# yapf: enable +# yapf: disable +# noqa:A123 +#noqa: A123 +# type:ignore +#type: int +# fmt:off +#fmt: on +# fmt: skip +# isort:skip +# isort:skip_file +# ruff: isort:skip +# ruff: isort:skip_file +# type: ignore +# type: int +# yapf: enable +# yapf: disable + +# NoQA: A123, B456 +# ruff: NoQA: A123, B456 +# flake8: NoQA: A123, B456 + +# noqa: A123 B456 +# ruff: noqa: A123 B456 +# flake8: noqa: A123 B456 +# noqa: A123,B456 +# ruff: noqa: A123,B456 +# flake8: noqa: A123,B456 +# noqa: A123,,B456 +# noqa: A123 , , B456 +# noqa: A123 B456 +# noqa: A123 B456 +# noqa: A123 B456 +# noqa: A123 ,B456 +# ruff: noqa: A123 B456 +# flake8: noqa: A123 B456 + + +# type: ignore # noqa: A123, B456 + +#isort:skip#noqa:A123 + +# fmt:off# noqa: A123 +# noqa:A123, B456 - Lorem ipsum dolor sit amet diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_empty_suppressions.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_empty_suppressions.py new file mode 100644 index 0000000000000..94129208af294 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_empty_suppressions.py @@ -0,0 +1,7 @@ +### +# All of these should be reformatted. +### + +#noqa: +#ruff:noqa: +#flake8:noqa: diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_explicit_suppression.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_explicit_suppression.py new file mode 100644 index 0000000000000..1efdf9830bd18 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_explicit_suppression.py @@ -0,0 +1,8 @@ +### +# None of these should trigger RUF037 +# as it is explicitly suppressed for the entire file. +### + +# flake8:noqa:RUF037 + +#noqa:A123 diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_implicit_suppression.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_implicit_suppression.py new file mode 100644 index 0000000000000..40401b1df92de --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF037_implicit_suppression.py @@ -0,0 +1,8 @@ +### +# Both of these should trigger RUF037 +# as it is not explicitly suppressed. +### + +# ruff:noqa + +#noqa:A123 diff --git a/crates/ruff_linter/src/checkers/noqa.rs b/crates/ruff_linter/src/checkers/noqa.rs index a2ecc7a34161f..9f318aa4e0853 100644 --- a/crates/ruff_linter/src/checkers/noqa.rs +++ b/crates/ruff_linter/src/checkers/noqa.rs @@ -51,8 +51,15 @@ pub(crate) fn check_noqa( match &exemption { FileExemption::All(_) => { - // If the file is exempted, ignore all diagnostics. - ignored_diagnostics.push(index); + // If the file is exempted, ignore all diagnostics, + // save for RUF037, which operates on `# noqa` comments + // and thus needs to be suppressed explicitly. + if !matches!(diagnostic.kind.rule(), Rule::UnformattedSpecialComment) + || per_file_ignores.contains(Rule::UnformattedSpecialComment) + || exemption.enumerates(Rule::UnformattedSpecialComment) + { + ignored_diagnostics.push(index); + } continue; } FileExemption::Codes(codes) => { diff --git a/crates/ruff_linter/src/checkers/tokens.rs b/crates/ruff_linter/src/checkers/tokens.rs index 7be23f8bfd035..ac6b63858e375 100644 --- a/crates/ruff_linter/src/checkers/tokens.rs +++ b/crates/ruff_linter/src/checkers/tokens.rs @@ -191,6 +191,10 @@ pub(crate) fn check_tokens( pycodestyle::rules::too_many_newlines_at_end_of_file(&mut diagnostics, tokens); } + if settings.rules.enabled(Rule::UnformattedSpecialComment) { + ruff::rules::unformatted_special_comment(&mut diagnostics, locator, comment_ranges); + } + diagnostics.retain(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())); diagnostics diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 58dc217e1388b..f0e20190f2c89 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -967,6 +967,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Ruff, "033") => (RuleGroup::Preview, rules::ruff::rules::PostInitDefault), (Ruff, "034") => (RuleGroup::Preview, rules::ruff::rules::UselessIfElse), (Ruff, "035") => (RuleGroup::Preview, rules::ruff::rules::UnsafeMarkupUse), + (Ruff, "037") => (RuleGroup::Preview, rules::ruff::rules::UnformattedSpecialComment), (Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA), (Ruff, "101") => (RuleGroup::Stable, rules::ruff::rules::RedirectedNOQA), diff --git a/crates/ruff_linter/src/registry.rs b/crates/ruff_linter/src/registry.rs index 1ee0cc5102add..664d2d493d36d 100644 --- a/crates/ruff_linter/src/registry.rs +++ b/crates/ruff_linter/src/registry.rs @@ -301,6 +301,7 @@ impl Rule { | Rule::TrailingCommaOnBareTuple | Rule::TypeCommentInStub | Rule::UselessSemicolon + | Rule::UnformattedSpecialComment | Rule::UTF8EncodingDeclaration => LintSource::Tokens, Rule::IOError => LintSource::Io, Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports, diff --git a/crates/ruff_linter/src/rules/fastapi/rules/fastapi_redundant_response_model.rs b/crates/ruff_linter/src/rules/fastapi/rules/fastapi_redundant_response_model.rs index 36f7fc1129a8f..a18749d05b5dd 100644 --- a/crates/ruff_linter/src/rules/fastapi/rules/fastapi_redundant_response_model.rs +++ b/crates/ruff_linter/src/rules/fastapi/rules/fastapi_redundant_response_model.rs @@ -73,7 +73,7 @@ impl AlwaysFixableViolation for FastApiRedundantResponseModel { } } -/// RUF102 +/// FAST001 pub(crate) fn fastapi_redundant_response_model( checker: &mut Checker, function_def: &StmtFunctionDef, diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index 19917696691b0..28cc02a7a672d 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -59,9 +59,9 @@ mod tests { #[test_case(Rule::AssertWithPrintMessage, Path::new("RUF030.py"))] #[test_case(Rule::IncorrectlyParenthesizedTupleInSubscript, Path::new("RUF031.py"))] #[test_case(Rule::DecimalFromFloatLiteral, Path::new("RUF032.py"))] + #[test_case(Rule::PostInitDefault, Path::new("RUF033.py"))] #[test_case(Rule::UselessIfElse, Path::new("RUF034.py"))] #[test_case(Rule::RedirectedNOQA, Path::new("RUF101.py"))] - #[test_case(Rule::PostInitDefault, Path::new("RUF033.py"))] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( @@ -388,6 +388,19 @@ mod tests { #[test_case(Rule::ZipInsteadOfPairwise, Path::new("RUF007.py"))] #[test_case(Rule::UnsafeMarkupUse, Path::new("RUF035.py"))] + #[test_case(Rule::UnformattedSpecialComment, Path::new("RUF037.py"))] + #[test_case( + Rule::UnformattedSpecialComment, + Path::new("RUF037_implicit_suppression.py") + )] + #[test_case( + Rule::UnformattedSpecialComment, + Path::new("RUF037_explicit_suppression.py") + )] + #[test_case( + Rule::UnformattedSpecialComment, + Path::new("RUF037_empty_suppressions.py") + )] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/ruff/rules/mod.rs b/crates/ruff_linter/src/rules/ruff/rules/mod.rs index 102174f8e32d2..f4df32278861e 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mod.rs @@ -26,6 +26,7 @@ pub(crate) use sort_dunder_slots::*; pub(crate) use static_key_dict_comprehension::*; #[cfg(any(feature = "test-rules", test))] pub(crate) use test_rules::*; +pub(crate) use unformatted_special_comment::*; pub(crate) use unnecessary_iterable_allocation_for_first_element::*; pub(crate) use unnecessary_key_check::*; pub(crate) use unsafe_markup_use::*; @@ -66,6 +67,7 @@ mod static_key_dict_comprehension; mod suppression_comment_visitor; #[cfg(any(feature = "test-rules", test))] pub(crate) mod test_rules; +mod unformatted_special_comment; mod unnecessary_iterable_allocation_for_first_element; mod unnecessary_key_check; mod unsafe_markup_use; diff --git a/crates/ruff_linter/src/rules/ruff/rules/unformatted_special_comment.rs b/crates/ruff_linter/src/rules/ruff/rules/unformatted_special_comment.rs new file mode 100644 index 0000000000000..ce38196fb12b2 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/rules/unformatted_special_comment.rs @@ -0,0 +1,518 @@ +use crate::Locator; +use regex::Regex; +use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_trivia::CommentRanges; +use ruff_text_size::TextRange; +use ruff_text_size::TextSize; +use std::ops::Range; +use std::sync::LazyLock; + +type HintRelativeRange = Range; +type SpecialCommentDescriptor<'a> = Option<(HintRelativeRange, &'a str, SpecialComment)>; + +#[derive(Debug, Eq, PartialEq)] +enum SpecialComment { + /// `# noqa`, `# noqa: A123, B456` + Noqa(Option>), + /// `# ruff: noqa`, `# ruff: noqa: A123, B456`, + /// `# flake8: noqa`, `# flake8: noqa: A123, B456` + FileLevelNoqa { + hint: String, + codes: Option>, + }, + + /// `# fmt: on`, `# fmt: off`, `fmt: skip` + Fmt(String), + /// `# isort: skip`, `# isort: skip_file` + Isort(String), + /// `# ruff: isort: skip`, `# ruff: isort: skip_file` + RuffIsort(String), + /// `# type: int`, `# type: ignore` + Type(String), + /// `# yapf: enable`, `# yapf: disable` + Yapf(String), +} + +impl SpecialComment { + fn formatted(&self) -> String { + match self { + SpecialComment::Noqa(None) => "# noqa".to_string(), + + SpecialComment::Noqa(Some(codes)) if codes.is_empty() => "# noqa:".to_string(), + + SpecialComment::Noqa(Some(codes)) => format!("# noqa: {}", codes.join(", ")), + + SpecialComment::FileLevelNoqa { hint, codes: None } => format!("# {hint}: noqa"), + + SpecialComment::FileLevelNoqa { + hint, + codes: Some(codes), + } if codes.is_empty() => format!("# {hint}: noqa:"), + + SpecialComment::FileLevelNoqa { + hint, + codes: Some(codes), + } => format!("# {hint}: noqa: {}", codes.join(", ")), + + SpecialComment::Fmt(rest) => format!("# fmt: {rest}"), + SpecialComment::Isort(rest) => format!("# isort: {rest}"), + SpecialComment::RuffIsort(rest) => format!("# ruff: isort: {rest}"), + SpecialComment::Type(rest) => format!("# type: {rest}"), + SpecialComment::Yapf(rest) => format!("# yapf: {rest}"), + } + } +} + +/// ## What it does +/// Checks special comments' formatting. +/// +/// ## Why is this bad? +/// Special comments are often written in the following format +/// (hash, space, directive, colon, space, directive body): +/// +/// ```python +/// # name: body +/// ``` +/// +/// ## Example +/// +/// ```python +/// # ruff: noqa:A123 B456 +/// # fmt:off +/// #type:ignore +/// ``` +/// +/// Use instead: +/// +/// ```python +/// # ruff: noqa: A123, B456 +/// # fmt: off +/// # type: ignore +/// ``` +#[violation] +pub(crate) struct UnformattedSpecialComment(SpecialComment); + +impl AlwaysFixableViolation for UnformattedSpecialComment { + #[derive_message_formats] + fn message(&self) -> String { + "Unformatted special comment".to_string() + } + + fn fix_title(&self) -> String { + "Format comment".to_string() + } +} + +fn add_diagnostic_if_applicable( + diagnostics: &mut Vec, + comment: SpecialComment, + comment_text: &str, + comment_range: TextRange, + hint_range: TextRange, +) { + let formatted = comment.formatted(); + + if comment_text == formatted { + return; + } + + let edit = Edit::range_replacement(formatted, comment_range); + let fix = Fix::safe_edit(edit); + + let violation = UnformattedSpecialComment(comment); + let diagnostic = Diagnostic::new(violation, hint_range).with_fix(fix); + + diagnostics.push(diagnostic); +} + +macro_rules! try_parse_common { + ($pattern:ident, $text:ident, $special_comment:expr) => {{ + let result = $pattern.captures($text)?; + + let comment = result.get(0).unwrap(); + let hint = result.name("hint").unwrap(); + let rest = result.name("rest").unwrap(); + + Some(( + hint.range(), + comment.as_str(), + $special_comment(rest.as_str().to_owned()), + )) + }}; +} + +fn try_parse_noqa(text: &str) -> SpecialCommentDescriptor { + fn parse_code_list(code_list: &str) -> Vec { + static PATTERN: LazyLock = LazyLock::new(|| Regex::new(r"[A-Z]+[0-9]+").unwrap()); + + PATTERN + .find_iter(code_list) + .map(|code| code.as_str().to_owned()) + .collect() + } + + // ruff_linter::noqa::Directive::try_extract + static PATTERN: LazyLock = LazyLock::new(|| { + Regex::new( + r"(?x) + ^ + \#\s* + (?(?i:noqa)) + (? + : + (?: + \s* + [A-Z]+[0-9]+ + (?:[\s,]+[A-Z]+[0-9]+)* + )? + )? + ", + ) + .unwrap() + }); + + let result = PATTERN.captures(text)?; + + let comment = result.get(0).unwrap(); + let hint = result.name("hint").unwrap(); + let codes = result + .name("code_list") + .map(|it| parse_code_list(it.as_str())); + + Some((hint.range(), comment.as_str(), SpecialComment::Noqa(codes))) +} + +fn try_parse_file_level_noqa(text: &str) -> SpecialCommentDescriptor { + fn parse_code_list(code_list: &str) -> Vec { + static PATTERN: LazyLock = + LazyLock::new(|| Regex::new(r"[A-Z]+[A-Za-z0-9]+").unwrap()); + + PATTERN + .find_iter(code_list) + .map(|code| code.as_str().to_owned()) + .collect() + } + + // ruff_linter::noqa::ParsedFileExemption::try_extract + static PATTERN: LazyLock = LazyLock::new(|| { + Regex::new( + r"(?x) + ^ + \#\s* + (?flake8|ruff)\s*:\s* + (?i:noqa)\s* + (? + : + (?: + \s* + [A-Z]+[A-Za-z0-9]+ + (?:[\s,]\s*[A-Z]+[A-Za-z0-9]+)* + )? + )? + ", + ) + .unwrap() + }); + + let result = PATTERN.captures(text)?; + + let comment = result.get(0).unwrap(); + let hint = result.name("hint").unwrap(); + let codes = result + .name("code_list") + .map(|it| parse_code_list(it.as_str())); + + let special_comment = SpecialComment::FileLevelNoqa { + hint: hint.as_str().to_owned(), + codes, + }; + + Some((hint.range(), comment.as_str(), special_comment)) +} + +fn try_parse_fmt(text: &str) -> SpecialCommentDescriptor { + static PATTERN: LazyLock = + LazyLock::new(|| Regex::new(r"^#\s*(?fmt):\s*(?on|off|skip)").unwrap()); + + try_parse_common!(PATTERN, text, SpecialComment::Fmt) +} + +fn try_parse_isort(text: &str) -> SpecialCommentDescriptor { + static PATTERN: LazyLock = + LazyLock::new(|| Regex::new(r"^# (?isort):(?skip_file|skip)").unwrap()); + + try_parse_common!(PATTERN, text, SpecialComment::Isort) +} + +fn try_parse_ruff_isort(text: &str) -> SpecialCommentDescriptor { + // ruff_linter::directives::extract_isort_directives + static PATTERN: LazyLock = + LazyLock::new(|| Regex::new(r"^# (?ruff): isort:(?skip_file|skip)").unwrap()); + + try_parse_common!(PATTERN, text, SpecialComment::RuffIsort) +} + +fn try_parse_type(text: &str) -> SpecialCommentDescriptor { + // https://github.com/python/cpython/blob/c222441fa7/Parser/lexer/lexer.c#L45-L47 + static PATTERN: LazyLock = + LazyLock::new(|| Regex::new(r"^#\s*(?type):\s*(?\S)").unwrap()); + + try_parse_common!(PATTERN, text, SpecialComment::Type) +} + +fn try_parse_yapf(text: &str) -> SpecialCommentDescriptor { + // https://github.com/astral-sh/ruff/blob/78e4753d74/crates/ruff_python_trivia/src/comments.rs#L18-L38 + static PATTERN: LazyLock = + LazyLock::new(|| Regex::new(r"#\s*(?yapf):\s*(?enable|disable)").unwrap()); + + try_parse_common!(PATTERN, text, SpecialComment::Yapf) +} + +fn text_range(start: usize, end: usize) -> Option { + let Ok(start) = TextSize::try_from(start) else { + return None; + }; + let Ok(end) = TextSize::try_from(end) else { + return None; + }; + + Some(TextRange::new(start, end)) +} + +macro_rules! parse_and_handle_comment { + ($parse:ident, $text:ident, $diagnostics:ident, $comment_start:ident) => { + if let Some((hint_relative_range, comment_text, comment)) = $parse($text) { + let comment_end = $comment_start + comment_text.len(); + let Some(comment_range) = text_range($comment_start, comment_end) else { + return; + }; + + let hint_start = $comment_start + hint_relative_range.start; + let hint_end = $comment_start + hint_relative_range.end; + let Some(hint_range) = text_range(hint_start, hint_end) else { + return; + }; + + add_diagnostic_if_applicable( + $diagnostics, + comment, + comment_text, + comment_range, + hint_range, + ); + return; + } + }; +} + +fn check_single_comment(diagnostics: &mut Vec, text: &str, start_index: usize) { + parse_and_handle_comment!(try_parse_noqa, text, diagnostics, start_index); + parse_and_handle_comment!(try_parse_file_level_noqa, text, diagnostics, start_index); + parse_and_handle_comment!(try_parse_fmt, text, diagnostics, start_index); + parse_and_handle_comment!(try_parse_isort, text, diagnostics, start_index); + parse_and_handle_comment!(try_parse_ruff_isort, text, diagnostics, start_index); + parse_and_handle_comment!(try_parse_type, text, diagnostics, start_index); + parse_and_handle_comment!(try_parse_yapf, text, diagnostics, start_index); +} + +fn check_composite_comment(diagnostics: &mut Vec, text: &str, range_start: usize) { + for (char_index, char) in text.char_indices() { + let next_char = text[char_index..].chars().nth(1); + + if char == '#' && !matches!(next_char, Some('#')) { + let subcomment_absolute_index = range_start + char_index; + + check_single_comment(diagnostics, &text[char_index..], subcomment_absolute_index); + } + } +} + +/// RUF037 +pub(crate) fn unformatted_special_comment( + diagnostics: &mut Vec, + locator: &Locator, + comment_ranges: &CommentRanges, +) { + for range in comment_ranges { + let text = locator.slice(range); + let range_start: usize = range.start().into(); + + check_composite_comment(diagnostics, text, range_start); + } +} + +#[cfg(test)] +mod tests { + use ruff_diagnostics::Diagnostic; + + use super::{check_composite_comment, check_single_comment}; + + fn check_single(text: &str) -> Vec { + let mut diagnostics = vec![]; + let start_index = 0; + + check_single_comment(&mut diagnostics, text, start_index); + + diagnostics + } + + fn check_composite(text: &str) -> Vec { + let mut diagnostics = vec![]; + let start_index = 0; + + check_composite_comment(&mut diagnostics, text, start_index); + + diagnostics + } + + fn has_unformatted(text: &str) { + let diagnostics = check_single(text); + + assert_eq!(diagnostics.len(), 1); + } + + fn no_unformatted(text: &str) { + let diagnostics = check_single(text); + + assert!(diagnostics.is_empty()); + } + + fn composite_has_unformatted(text: &str, count: usize) { + let diagnostics = check_composite(text); + + assert_eq!(diagnostics.len(), count); + } + + fn composite_no_unformatted(text: &str) { + composite_has_unformatted(text, 0); + } + + #[test] + fn unknown() { + no_unformatted("#foo"); + no_unformatted("# foo"); + + no_unformatted("#ruff: foo-bar"); + no_unformatted("# flake8:foo-bar"); + + no_unformatted("# black:skip"); + + no_unformatted("# fmt: foo"); + no_unformatted("# isort: skip_entire"); + no_unformatted("# ruff: isort: skipfile"); + no_unformatted("# yapf: off"); + } + + #[test] + fn incorrect_casing() { + no_unformatted("# FMT:OFF"); + no_unformatted("# isort: On"); + no_unformatted("# Type: ignore"); + no_unformatted("# yapf: Disable"); + no_unformatted("# Yapf: disable"); + } + + #[test] + fn incorrect_whitespace() { + no_unformatted("#yapf : enable"); + no_unformatted("# yapf : disable"); + } + + #[test] + fn already_formatted() { + no_unformatted("# noqa"); + no_unformatted("# noqa: A123"); + no_unformatted("# noqa: A123, B456"); + + no_unformatted("# ruff: noqa"); + no_unformatted("# ruff: noqa: A123"); + no_unformatted("# ruff: noqa: A123, B456"); + + no_unformatted("# flake8: noqa"); + no_unformatted("# flake8: noqa: A123"); + no_unformatted("# flake8: noqa: A123, B456"); + + no_unformatted("# fmt: on"); + no_unformatted("# fmt: off"); + no_unformatted("# fmt: skip"); + + no_unformatted("# isort: on"); + no_unformatted("# isort: off"); + no_unformatted("# isort: split"); + no_unformatted("# isort: skip"); + no_unformatted("# isort: skip_file"); + + no_unformatted("# ruff: isort: on"); + no_unformatted("# ruff: isort: skip_file"); + + no_unformatted("# type: ignore"); + no_unformatted("# type: int"); + no_unformatted("# type: list[str]"); + + no_unformatted("# yapf: enable"); + no_unformatted("# yapf: disable"); + } + + #[test] + fn whitespace() { + has_unformatted("# noqa:A123"); + has_unformatted("#noqa: A123"); + + has_unformatted("# type:ignore"); + has_unformatted("#type:\tint"); + + has_unformatted("# fmt:off"); + has_unformatted("#fmt: on"); + has_unformatted("# \t fmt:\t skip"); + + has_unformatted("# isort:skip"); + has_unformatted("# isort:skip_file"); + + has_unformatted("# ruff: isort:skip"); + has_unformatted("# ruff: isort:skip_file"); + + has_unformatted("# type:\t\t\tignore"); + has_unformatted("#\t \t \ttype:\t\t \tint"); + + has_unformatted("#\t \tyapf: \t \tenable"); + has_unformatted("#\t\tyapf: \t\tdisable"); + } + + #[test] + fn casing() { + has_unformatted("# NoQA: A123, B456"); + has_unformatted("# ruff: NoQA: A123, B456"); + has_unformatted("# flake8: NoQA: A123, B456"); + } + + #[test] + fn rule_code_separators() { + has_unformatted("# noqa: A123 B456"); + has_unformatted("# ruff: noqa: A123 B456"); + has_unformatted("# flake8: noqa: A123 B456"); + + has_unformatted("# noqa: A123,B456"); + has_unformatted("# ruff: noqa: A123,B456"); + has_unformatted("# flake8: noqa: A123,B456"); + + has_unformatted("# noqa: A123,,B456"); + has_unformatted("# noqa: A123 , \t,\t \tB456"); + has_unformatted("# noqa: A123\tB456"); + has_unformatted("# noqa: A123\t\t\tB456"); + has_unformatted("# noqa: A123\t\t\t\tB456"); + + has_unformatted("# noqa: A123 ,B456"); + has_unformatted("# ruff: noqa: A123\tB456"); + has_unformatted("# flake8: noqa: A123 B456"); + } + + #[test] + fn composite() { + composite_no_unformatted("# type: ignore # noqa: A123, B456"); + + composite_has_unformatted("# isort:skip#noqa:A123", 2); + composite_has_unformatted("# fmt:off# noqa: A123", 2); + composite_has_unformatted("# noqa:A123, B456 - Lorem ipsum dolor sit amet", 1); + } +} diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037.py.snap new file mode 100644 index 0000000000000..75f0a48537029 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037.py.snap @@ -0,0 +1,740 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +snapshot_kind: text +--- +RUF037.py:42:3: RUF037 [*] Unformatted special comment + | +40 | # yapf: enable +41 | # yapf: disable +42 | # noqa:A123 + | ^^^^ RUF037 +43 | #noqa: A123 +44 | # type:ignore + | + = help: Format comment + +ℹ Safe fix +39 39 | # type: list[str] +40 40 | # yapf: enable +41 41 | # yapf: disable +42 |-# noqa:A123 + 42 |+# noqa: A123 +43 43 | #noqa: A123 +44 44 | # type:ignore +45 45 | #type: int + +RUF037.py:43:2: RUF037 [*] Unformatted special comment + | +41 | # yapf: disable +42 | # noqa:A123 +43 | #noqa: A123 + | ^^^^ RUF037 +44 | # type:ignore +45 | #type: int + | + = help: Format comment + +ℹ Safe fix +40 40 | # yapf: enable +41 41 | # yapf: disable +42 42 | # noqa:A123 +43 |-#noqa: A123 + 43 |+# noqa: A123 +44 44 | # type:ignore +45 45 | #type: int +46 46 | # fmt:off + +RUF037.py:44:6: RUF037 [*] Unformatted special comment + | +42 | # noqa:A123 +43 | #noqa: A123 +44 | # type:ignore + | ^^^^ RUF037 +45 | #type: int +46 | # fmt:off + | + = help: Format comment + +ℹ Safe fix +41 41 | # yapf: disable +42 42 | # noqa:A123 +43 43 | #noqa: A123 +44 |-# type:ignore + 44 |+# type: ignore +45 45 | #type: int +46 46 | # fmt:off +47 47 | #fmt: on + +RUF037.py:45:2: RUF037 [*] Unformatted special comment + | +43 | #noqa: A123 +44 | # type:ignore +45 | #type: int + | ^^^^ RUF037 +46 | # fmt:off +47 | #fmt: on + | + = help: Format comment + +ℹ Safe fix +42 42 | # noqa:A123 +43 43 | #noqa: A123 +44 44 | # type:ignore +45 |-#type: int + 45 |+# type: int +46 46 | # fmt:off +47 47 | #fmt: on +48 48 | # fmt: skip + +RUF037.py:46:3: RUF037 [*] Unformatted special comment + | +44 | # type:ignore +45 | #type: int +46 | # fmt:off + | ^^^ RUF037 +47 | #fmt: on +48 | # fmt: skip + | + = help: Format comment + +ℹ Safe fix +43 43 | #noqa: A123 +44 44 | # type:ignore +45 45 | #type: int +46 |-# fmt:off + 46 |+# fmt: off +47 47 | #fmt: on +48 48 | # fmt: skip +49 49 | # isort:skip + +RUF037.py:47:2: RUF037 [*] Unformatted special comment + | +45 | #type: int +46 | # fmt:off +47 | #fmt: on + | ^^^ RUF037 +48 | # fmt: skip +49 | # isort:skip + | + = help: Format comment + +ℹ Safe fix +44 44 | # type:ignore +45 45 | #type: int +46 46 | # fmt:off +47 |-#fmt: on + 47 |+# fmt: on +48 48 | # fmt: skip +49 49 | # isort:skip +50 50 | # isort:skip_file + +RUF037.py:48:5: RUF037 [*] Unformatted special comment + | +46 | # fmt:off +47 | #fmt: on +48 | # fmt: skip + | ^^^ RUF037 +49 | # isort:skip +50 | # isort:skip_file + | + = help: Format comment + +ℹ Safe fix +45 45 | #type: int +46 46 | # fmt:off +47 47 | #fmt: on +48 |-# fmt: skip + 48 |+# fmt: skip +49 49 | # isort:skip +50 50 | # isort:skip_file +51 51 | # ruff: isort:skip + +RUF037.py:49:3: RUF037 [*] Unformatted special comment + | +47 | #fmt: on +48 | # fmt: skip +49 | # isort:skip + | ^^^^^ RUF037 +50 | # isort:skip_file +51 | # ruff: isort:skip + | + = help: Format comment + +ℹ Safe fix +46 46 | # fmt:off +47 47 | #fmt: on +48 48 | # fmt: skip +49 |-# isort:skip + 49 |+# isort: skip +50 50 | # isort:skip_file +51 51 | # ruff: isort:skip +52 52 | # ruff: isort:skip_file + +RUF037.py:50:3: RUF037 [*] Unformatted special comment + | +48 | # fmt: skip +49 | # isort:skip +50 | # isort:skip_file + | ^^^^^ RUF037 +51 | # ruff: isort:skip +52 | # ruff: isort:skip_file + | + = help: Format comment + +ℹ Safe fix +47 47 | #fmt: on +48 48 | # fmt: skip +49 49 | # isort:skip +50 |-# isort:skip_file + 50 |+# isort: skip_file +51 51 | # ruff: isort:skip +52 52 | # ruff: isort:skip_file +53 53 | # type: ignore + +RUF037.py:51:3: RUF037 [*] Unformatted special comment + | +49 | # isort:skip +50 | # isort:skip_file +51 | # ruff: isort:skip + | ^^^^ RUF037 +52 | # ruff: isort:skip_file +53 | # type: ignore + | + = help: Format comment + +ℹ Safe fix +48 48 | # fmt: skip +49 49 | # isort:skip +50 50 | # isort:skip_file +51 |-# ruff: isort:skip + 51 |+# ruff: isort: skip +52 52 | # ruff: isort:skip_file +53 53 | # type: ignore +54 54 | # type: int + +RUF037.py:52:3: RUF037 [*] Unformatted special comment + | +50 | # isort:skip_file +51 | # ruff: isort:skip +52 | # ruff: isort:skip_file + | ^^^^ RUF037 +53 | # type: ignore +54 | # type: int + | + = help: Format comment + +ℹ Safe fix +49 49 | # isort:skip +50 50 | # isort:skip_file +51 51 | # ruff: isort:skip +52 |-# ruff: isort:skip_file + 52 |+# ruff: isort: skip_file +53 53 | # type: ignore +54 54 | # type: int +55 55 | # yapf: enable + +RUF037.py:53:6: RUF037 [*] Unformatted special comment + | +51 | # ruff: isort:skip +52 | # ruff: isort:skip_file +53 | # type: ignore + | ^^^^ RUF037 +54 | # type: int +55 | # yapf: enable + | + = help: Format comment + +ℹ Safe fix +50 50 | # isort:skip_file +51 51 | # ruff: isort:skip +52 52 | # ruff: isort:skip_file +53 |-# type: ignore + 53 |+# type: ignore +54 54 | # type: int +55 55 | # yapf: enable +56 56 | # yapf: disable + +RUF037.py:54:7: RUF037 [*] Unformatted special comment + | +52 | # ruff: isort:skip_file +53 | # type: ignore +54 | # type: int + | ^^^^ RUF037 +55 | # yapf: enable +56 | # yapf: disable + | + = help: Format comment + +ℹ Safe fix +51 51 | # ruff: isort:skip +52 52 | # ruff: isort:skip_file +53 53 | # type: ignore +54 |-# type: int + 54 |+# type: int +55 55 | # yapf: enable +56 56 | # yapf: disable +57 57 | + +RUF037.py:55:6: RUF037 [*] Unformatted special comment + | +53 | # type: ignore +54 | # type: int +55 | # yapf: enable + | ^^^^ RUF037 +56 | # yapf: disable + | + = help: Format comment + +ℹ Safe fix +52 52 | # ruff: isort:skip_file +53 53 | # type: ignore +54 54 | # type: int +55 |-# yapf: enable + 55 |+# yapf: enable +56 56 | # yapf: disable +57 57 | +58 58 | # NoQA: A123, B456 + +RUF037.py:56:4: RUF037 [*] Unformatted special comment + | +54 | # type: int +55 | # yapf: enable +56 | # yapf: disable + | ^^^^ RUF037 +57 | +58 | # NoQA: A123, B456 + | + = help: Format comment + +ℹ Safe fix +53 53 | # type: ignore +54 54 | # type: int +55 55 | # yapf: enable +56 |-# yapf: disable + 56 |+# yapf: disable +57 57 | +58 58 | # NoQA: A123, B456 +59 59 | # ruff: NoQA: A123, B456 + +RUF037.py:58:3: RUF037 [*] Unformatted special comment + | +56 | # yapf: disable +57 | +58 | # NoQA: A123, B456 + | ^^^^ RUF037 +59 | # ruff: NoQA: A123, B456 +60 | # flake8: NoQA: A123, B456 + | + = help: Format comment + +ℹ Safe fix +55 55 | # yapf: enable +56 56 | # yapf: disable +57 57 | +58 |-# NoQA: A123, B456 + 58 |+# noqa: A123, B456 +59 59 | # ruff: NoQA: A123, B456 +60 60 | # flake8: NoQA: A123, B456 +61 61 | + +RUF037.py:59:3: RUF037 [*] Unformatted special comment + | +58 | # NoQA: A123, B456 +59 | # ruff: NoQA: A123, B456 + | ^^^^ RUF037 +60 | # flake8: NoQA: A123, B456 + | + = help: Format comment + +ℹ Safe fix +56 56 | # yapf: disable +57 57 | +58 58 | # NoQA: A123, B456 +59 |-# ruff: NoQA: A123, B456 + 59 |+# ruff: noqa: A123, B456 +60 60 | # flake8: NoQA: A123, B456 +61 61 | +62 62 | # noqa: A123 B456 + +RUF037.py:60:3: RUF037 [*] Unformatted special comment + | +58 | # NoQA: A123, B456 +59 | # ruff: NoQA: A123, B456 +60 | # flake8: NoQA: A123, B456 + | ^^^^^^ RUF037 +61 | +62 | # noqa: A123 B456 + | + = help: Format comment + +ℹ Safe fix +57 57 | +58 58 | # NoQA: A123, B456 +59 59 | # ruff: NoQA: A123, B456 +60 |-# flake8: NoQA: A123, B456 + 60 |+# flake8: noqa: A123, B456 +61 61 | +62 62 | # noqa: A123 B456 +63 63 | # ruff: noqa: A123 B456 + +RUF037.py:62:3: RUF037 [*] Unformatted special comment + | +60 | # flake8: NoQA: A123, B456 +61 | +62 | # noqa: A123 B456 + | ^^^^ RUF037 +63 | # ruff: noqa: A123 B456 +64 | # flake8: noqa: A123 B456 + | + = help: Format comment + +ℹ Safe fix +59 59 | # ruff: NoQA: A123, B456 +60 60 | # flake8: NoQA: A123, B456 +61 61 | +62 |-# noqa: A123 B456 + 62 |+# noqa: A123, B456 +63 63 | # ruff: noqa: A123 B456 +64 64 | # flake8: noqa: A123 B456 +65 65 | # noqa: A123,B456 + +RUF037.py:63:3: RUF037 [*] Unformatted special comment + | +62 | # noqa: A123 B456 +63 | # ruff: noqa: A123 B456 + | ^^^^ RUF037 +64 | # flake8: noqa: A123 B456 +65 | # noqa: A123,B456 + | + = help: Format comment + +ℹ Safe fix +60 60 | # flake8: NoQA: A123, B456 +61 61 | +62 62 | # noqa: A123 B456 +63 |-# ruff: noqa: A123 B456 + 63 |+# ruff: noqa: A123, B456 +64 64 | # flake8: noqa: A123 B456 +65 65 | # noqa: A123,B456 +66 66 | # ruff: noqa: A123,B456 + +RUF037.py:64:3: RUF037 [*] Unformatted special comment + | +62 | # noqa: A123 B456 +63 | # ruff: noqa: A123 B456 +64 | # flake8: noqa: A123 B456 + | ^^^^^^ RUF037 +65 | # noqa: A123,B456 +66 | # ruff: noqa: A123,B456 + | + = help: Format comment + +ℹ Safe fix +61 61 | +62 62 | # noqa: A123 B456 +63 63 | # ruff: noqa: A123 B456 +64 |-# flake8: noqa: A123 B456 + 64 |+# flake8: noqa: A123, B456 +65 65 | # noqa: A123,B456 +66 66 | # ruff: noqa: A123,B456 +67 67 | # flake8: noqa: A123,B456 + +RUF037.py:65:3: RUF037 [*] Unformatted special comment + | +63 | # ruff: noqa: A123 B456 +64 | # flake8: noqa: A123 B456 +65 | # noqa: A123,B456 + | ^^^^ RUF037 +66 | # ruff: noqa: A123,B456 +67 | # flake8: noqa: A123,B456 + | + = help: Format comment + +ℹ Safe fix +62 62 | # noqa: A123 B456 +63 63 | # ruff: noqa: A123 B456 +64 64 | # flake8: noqa: A123 B456 +65 |-# noqa: A123,B456 + 65 |+# noqa: A123, B456 +66 66 | # ruff: noqa: A123,B456 +67 67 | # flake8: noqa: A123,B456 +68 68 | # noqa: A123,,B456 + +RUF037.py:66:3: RUF037 [*] Unformatted special comment + | +64 | # flake8: noqa: A123 B456 +65 | # noqa: A123,B456 +66 | # ruff: noqa: A123,B456 + | ^^^^ RUF037 +67 | # flake8: noqa: A123,B456 +68 | # noqa: A123,,B456 + | + = help: Format comment + +ℹ Safe fix +63 63 | # ruff: noqa: A123 B456 +64 64 | # flake8: noqa: A123 B456 +65 65 | # noqa: A123,B456 +66 |-# ruff: noqa: A123,B456 + 66 |+# ruff: noqa: A123, B456 +67 67 | # flake8: noqa: A123,B456 +68 68 | # noqa: A123,,B456 +69 69 | # noqa: A123 , , B456 + +RUF037.py:67:3: RUF037 [*] Unformatted special comment + | +65 | # noqa: A123,B456 +66 | # ruff: noqa: A123,B456 +67 | # flake8: noqa: A123,B456 + | ^^^^^^ RUF037 +68 | # noqa: A123,,B456 +69 | # noqa: A123 , , B456 + | + = help: Format comment + +ℹ Safe fix +64 64 | # flake8: noqa: A123 B456 +65 65 | # noqa: A123,B456 +66 66 | # ruff: noqa: A123,B456 +67 |-# flake8: noqa: A123,B456 + 67 |+# flake8: noqa: A123, B456 +68 68 | # noqa: A123,,B456 +69 69 | # noqa: A123 , , B456 +70 70 | # noqa: A123 B456 + +RUF037.py:68:3: RUF037 [*] Unformatted special comment + | +66 | # ruff: noqa: A123,B456 +67 | # flake8: noqa: A123,B456 +68 | # noqa: A123,,B456 + | ^^^^ RUF037 +69 | # noqa: A123 , , B456 +70 | # noqa: A123 B456 + | + = help: Format comment + +ℹ Safe fix +65 65 | # noqa: A123,B456 +66 66 | # ruff: noqa: A123,B456 +67 67 | # flake8: noqa: A123,B456 +68 |-# noqa: A123,,B456 + 68 |+# noqa: A123, B456 +69 69 | # noqa: A123 , , B456 +70 70 | # noqa: A123 B456 +71 71 | # noqa: A123 B456 + +RUF037.py:69:3: RUF037 [*] Unformatted special comment + | +67 | # flake8: noqa: A123,B456 +68 | # noqa: A123,,B456 +69 | # noqa: A123 , , B456 + | ^^^^ RUF037 +70 | # noqa: A123 B456 +71 | # noqa: A123 B456 + | + = help: Format comment + +ℹ Safe fix +66 66 | # ruff: noqa: A123,B456 +67 67 | # flake8: noqa: A123,B456 +68 68 | # noqa: A123,,B456 +69 |-# noqa: A123 , , B456 + 69 |+# noqa: A123, B456 +70 70 | # noqa: A123 B456 +71 71 | # noqa: A123 B456 +72 72 | # noqa: A123 B456 + +RUF037.py:70:3: RUF037 [*] Unformatted special comment + | +68 | # noqa: A123,,B456 +69 | # noqa: A123 , , B456 +70 | # noqa: A123 B456 + | ^^^^ RUF037 +71 | # noqa: A123 B456 +72 | # noqa: A123 B456 + | + = help: Format comment + +ℹ Safe fix +67 67 | # flake8: noqa: A123,B456 +68 68 | # noqa: A123,,B456 +69 69 | # noqa: A123 , , B456 +70 |-# noqa: A123 B456 + 70 |+# noqa: A123, B456 +71 71 | # noqa: A123 B456 +72 72 | # noqa: A123 B456 +73 73 | # noqa: A123 ,B456 + +RUF037.py:71:3: RUF037 [*] Unformatted special comment + | +69 | # noqa: A123 , , B456 +70 | # noqa: A123 B456 +71 | # noqa: A123 B456 + | ^^^^ RUF037 +72 | # noqa: A123 B456 +73 | # noqa: A123 ,B456 + | + = help: Format comment + +ℹ Safe fix +68 68 | # noqa: A123,,B456 +69 69 | # noqa: A123 , , B456 +70 70 | # noqa: A123 B456 +71 |-# noqa: A123 B456 + 71 |+# noqa: A123, B456 +72 72 | # noqa: A123 B456 +73 73 | # noqa: A123 ,B456 +74 74 | # ruff: noqa: A123 B456 + +RUF037.py:72:3: RUF037 [*] Unformatted special comment + | +70 | # noqa: A123 B456 +71 | # noqa: A123 B456 +72 | # noqa: A123 B456 + | ^^^^ RUF037 +73 | # noqa: A123 ,B456 +74 | # ruff: noqa: A123 B456 + | + = help: Format comment + +ℹ Safe fix +69 69 | # noqa: A123 , , B456 +70 70 | # noqa: A123 B456 +71 71 | # noqa: A123 B456 +72 |-# noqa: A123 B456 + 72 |+# noqa: A123, B456 +73 73 | # noqa: A123 ,B456 +74 74 | # ruff: noqa: A123 B456 +75 75 | # flake8: noqa: A123 B456 + +RUF037.py:73:3: RUF037 [*] Unformatted special comment + | +71 | # noqa: A123 B456 +72 | # noqa: A123 B456 +73 | # noqa: A123 ,B456 + | ^^^^ RUF037 +74 | # ruff: noqa: A123 B456 +75 | # flake8: noqa: A123 B456 + | + = help: Format comment + +ℹ Safe fix +70 70 | # noqa: A123 B456 +71 71 | # noqa: A123 B456 +72 72 | # noqa: A123 B456 +73 |-# noqa: A123 ,B456 + 73 |+# noqa: A123, B456 +74 74 | # ruff: noqa: A123 B456 +75 75 | # flake8: noqa: A123 B456 +76 76 | + +RUF037.py:74:3: RUF037 [*] Unformatted special comment + | +72 | # noqa: A123 B456 +73 | # noqa: A123 ,B456 +74 | # ruff: noqa: A123 B456 + | ^^^^ RUF037 +75 | # flake8: noqa: A123 B456 + | + = help: Format comment + +ℹ Safe fix +71 71 | # noqa: A123 B456 +72 72 | # noqa: A123 B456 +73 73 | # noqa: A123 ,B456 +74 |-# ruff: noqa: A123 B456 + 74 |+# ruff: noqa: A123, B456 +75 75 | # flake8: noqa: A123 B456 +76 76 | +77 77 | + +RUF037.py:75:3: RUF037 [*] Unformatted special comment + | +73 | # noqa: A123 ,B456 +74 | # ruff: noqa: A123 B456 +75 | # flake8: noqa: A123 B456 + | ^^^^^^ RUF037 + | + = help: Format comment + +ℹ Safe fix +72 72 | # noqa: A123 B456 +73 73 | # noqa: A123 ,B456 +74 74 | # ruff: noqa: A123 B456 +75 |-# flake8: noqa: A123 B456 + 75 |+# flake8: noqa: A123, B456 +76 76 | +77 77 | +78 78 | # type: ignore # noqa: A123, B456 + +RUF037.py:80:13: RUF037 [*] Unformatted special comment + | +78 | # type: ignore # noqa: A123, B456 +79 | +80 | #isort:skip#noqa:A123 + | ^^^^ RUF037 +81 | +82 | # fmt:off# noqa: A123 + | + = help: Format comment + +ℹ Safe fix +77 77 | +78 78 | # type: ignore # noqa: A123, B456 +79 79 | +80 |-#isort:skip#noqa:A123 + 80 |+#isort:skip# noqa: A123 +81 81 | +82 82 | # fmt:off# noqa: A123 +83 83 | # noqa:A123, B456 - Lorem ipsum dolor sit amet + +RUF037.py:82:3: RUF037 [*] Unformatted special comment + | +80 | #isort:skip#noqa:A123 +81 | +82 | # fmt:off# noqa: A123 + | ^^^ RUF037 +83 | # noqa:A123, B456 - Lorem ipsum dolor sit amet + | + = help: Format comment + +ℹ Safe fix +79 79 | +80 80 | #isort:skip#noqa:A123 +81 81 | +82 |-# fmt:off# noqa: A123 + 82 |+# fmt: off# noqa: A123 +83 83 | # noqa:A123, B456 - Lorem ipsum dolor sit amet + +RUF037.py:82:14: RUF037 [*] Unformatted special comment + | +80 | #isort:skip#noqa:A123 +81 | +82 | # fmt:off# noqa: A123 + | ^^^^ RUF037 +83 | # noqa:A123, B456 - Lorem ipsum dolor sit amet + | + = help: Format comment + +ℹ Safe fix +79 79 | +80 80 | #isort:skip#noqa:A123 +81 81 | +82 |-# fmt:off# noqa: A123 + 82 |+# fmt:off# noqa: A123 +83 83 | # noqa:A123, B456 - Lorem ipsum dolor sit amet + +RUF037.py:83:3: RUF037 [*] Unformatted special comment + | +82 | # fmt:off# noqa: A123 +83 | # noqa:A123, B456 - Lorem ipsum dolor sit amet + | ^^^^ RUF037 + | + = help: Format comment + +ℹ Safe fix +80 80 | #isort:skip#noqa:A123 +81 81 | +82 82 | # fmt:off# noqa: A123 +83 |-# noqa:A123, B456 - Lorem ipsum dolor sit amet + 83 |+# noqa: A123, B456 - Lorem ipsum dolor sit amet diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_empty_suppressions.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_empty_suppressions.py.snap new file mode 100644 index 0000000000000..02390bb0c1eef --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_empty_suppressions.py.snap @@ -0,0 +1,56 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +snapshot_kind: text +--- +RUF037_empty_suppressions.py:5:2: RUF037 [*] Unformatted special comment + | +3 | ### +4 | +5 | #noqa: + | ^^^^ RUF037 +6 | #ruff:noqa: +7 | #flake8:noqa: + | + = help: Format comment + +ℹ Safe fix +2 2 | # All of these should be reformatted. +3 3 | ### +4 4 | +5 |-#noqa: + 5 |+# noqa: +6 6 | #ruff:noqa: +7 7 | #flake8:noqa: + +RUF037_empty_suppressions.py:6:2: RUF037 [*] Unformatted special comment + | +5 | #noqa: +6 | #ruff:noqa: + | ^^^^ RUF037 +7 | #flake8:noqa: + | + = help: Format comment + +ℹ Safe fix +3 3 | ### +4 4 | +5 5 | #noqa: +6 |-#ruff:noqa: + 6 |+# ruff: noqa: +7 7 | #flake8:noqa: + +RUF037_empty_suppressions.py:7:2: RUF037 [*] Unformatted special comment + | +5 | #noqa: +6 | #ruff:noqa: +7 | #flake8:noqa: + | ^^^^^^ RUF037 + | + = help: Format comment + +ℹ Safe fix +4 4 | +5 5 | #noqa: +6 6 | #ruff:noqa: +7 |-#flake8:noqa: + 7 |+# flake8: noqa: diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_explicit_suppression.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_explicit_suppression.py.snap new file mode 100644 index 0000000000000..b3a37c0921109 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_explicit_suppression.py.snap @@ -0,0 +1,5 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +snapshot_kind: text +--- + diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_implicit_suppression.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_implicit_suppression.py.snap new file mode 100644 index 0000000000000..31ee81c85c50d --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF037_RUF037_implicit_suppression.py.snap @@ -0,0 +1,39 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +snapshot_kind: text +--- +RUF037_implicit_suppression.py:6:3: RUF037 [*] Unformatted special comment + | +4 | ### +5 | +6 | # ruff:noqa + | ^^^^ RUF037 +7 | +8 | #noqa:A123 + | + = help: Format comment + +ℹ Safe fix +3 3 | # as it is not explicitly suppressed. +4 4 | ### +5 5 | +6 |-# ruff:noqa + 6 |+# ruff: noqa +7 7 | +8 8 | #noqa:A123 + +RUF037_implicit_suppression.py:8:2: RUF037 [*] Unformatted special comment + | +6 | # ruff:noqa +7 | +8 | #noqa:A123 + | ^^^^ RUF037 + | + = help: Format comment + +ℹ Safe fix +5 5 | +6 6 | # ruff:noqa +7 7 | +8 |-#noqa:A123 + 8 |+# noqa: A123 diff --git a/crates/ruff_python_trivia/src/cursor.rs b/crates/ruff_python_trivia/src/cursor.rs index c06d83156549d..3d95334f10ac3 100644 --- a/crates/ruff_python_trivia/src/cursor.rs +++ b/crates/ruff_python_trivia/src/cursor.rs @@ -21,7 +21,7 @@ impl<'a> Cursor<'a> { } } - /// Return the remaining input as a string slice. + /// Returns the remaining input as an iterator. pub fn chars(&self) -> Chars<'a> { self.chars.clone() } diff --git a/ruff.schema.json b/ruff.schema.json index e5be3b35a9057..0d8a68bb305bc 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3828,6 +3828,7 @@ "RUF033", "RUF034", "RUF035", + "RUF037", "RUF1", "RUF10", "RUF100", diff --git a/scripts/check_docs_formatted.py b/scripts/check_docs_formatted.py index c262722056267..10215a45cde7f 100755 --- a/scripts/check_docs_formatted.py +++ b/scripts/check_docs_formatted.py @@ -81,6 +81,7 @@ "under-indentation", "unexpected-indentation-comment", "unexpected-spaces-around-keyword-parameter-equals", + "unformatted-special-comment", "unicode-kind-prefix", "unnecessary-class-parentheses", "unnecessary-escaped-quote",