From 35c6dfe4819663b237dbeec184b4b530d17d05e9 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sat, 2 Nov 2024 16:24:59 -0400 Subject: [PATCH] Avoid parsing joint rule codes as distinct codes in `# noqa` (#12809) ## Summary We should enable warnings for unsupported codes, but this at least fixes the parsing for `# noqa: F401F841`. Closes https://github.com/astral-sh/ruff/issues/12808. --- .../resources/test/fixtures/ruff/RUF100_3.py | 3 + crates/ruff_linter/src/noqa.rs | 24 +++- ..._linter__rules__ruff__tests__ruf100_3.snap | 131 +++++++++++++----- ...linter__noqa__tests__noqa_empty_comma.snap | 23 +++ ...__noqa__tests__noqa_empty_comma_space.snap | 23 +++ ...ter__noqa__tests__noqa_squashed_codes.snap | 19 +++ 6 files changed, 186 insertions(+), 37 deletions(-) create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_empty_comma.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_empty_comma_space.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_squashed_codes.snap diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF100_3.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF100_3.py index 17e6367ad0..2269552c3b 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF100_3.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF100_3.py @@ -23,6 +23,9 @@ print(a) # noqa: E501, F821 # comment print(a) # noqa: E501, F821 # comment print(a) # noqa: E501, F821 comment print(a) # noqa: E501, F821 comment +print(a) # noqa: E501,,F821 comment +print(a) # noqa: E501, ,F821 comment +print(a) # noqa: E501 F821 comment print(a) # comment with unicode µ # noqa: E501 print(a) # comment with unicode µ # noqa: E501, F821 diff --git a/crates/ruff_linter/src/noqa.rs b/crates/ruff_linter/src/noqa.rs index 948cd534f9..f734448c79 100644 --- a/crates/ruff_linter/src/noqa.rs +++ b/crates/ruff_linter/src/noqa.rs @@ -183,7 +183,7 @@ impl<'a> Directive<'a> { // Extract, e.g., the `401` in `F401`. let suffix = line[prefix..] .chars() - .take_while(char::is_ascii_digit) + .take_while(char::is_ascii_alphanumeric) .count(); if prefix > 0 && suffix > 0 { Some(&line[..prefix + suffix]) @@ -549,7 +549,7 @@ impl<'a> ParsedFileExemption<'a> { // Extract, e.g., the `401` in `F401`. let suffix = line[prefix..] .chars() - .take_while(char::is_ascii_digit) + .take_while(char::is_ascii_alphanumeric) .count(); if prefix > 0 && suffix > 0 { Some(&line[..prefix + suffix]) @@ -895,7 +895,7 @@ pub(crate) struct NoqaDirectiveLine<'a> { pub(crate) directive: Directive<'a>, /// The codes that are ignored by the directive. pub(crate) matches: Vec, - // Whether the directive applies to range.end + /// Whether the directive applies to `range.end`. pub(crate) includes_end: bool, } @@ -1191,6 +1191,24 @@ mod tests { assert_debug_snapshot!(Directive::try_extract(source, TextSize::default())); } + #[test] + fn noqa_squashed_codes() { + let source = "# noqa: F401F841"; + assert_debug_snapshot!(Directive::try_extract(source, TextSize::default())); + } + + #[test] + fn noqa_empty_comma() { + let source = "# noqa: F401,,F841"; + assert_debug_snapshot!(Directive::try_extract(source, TextSize::default())); + } + + #[test] + fn noqa_empty_comma_space() { + let source = "# noqa: F401, ,F841"; + assert_debug_snapshot!(Directive::try_extract(source, TextSize::default())); + } + #[test] fn noqa_invalid_suffix() { let source = "# noqa[F401]"; diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__ruf100_3.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__ruf100_3.snap index 1ccfc73987..187117c3e0 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__ruf100_3.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__ruf100_3.snap @@ -344,7 +344,7 @@ RUF100_3.py:23:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) 23 |+print(a) # noqa: F821 # comment 24 24 | print(a) # noqa: E501, F821 comment 25 25 | print(a) # noqa: E501, F821 comment -26 26 | +26 26 | print(a) # noqa: E501,,F821 comment RUF100_3.py:24:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) | @@ -353,6 +353,7 @@ RUF100_3.py:24:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) 24 | print(a) # noqa: E501, F821 comment | ^^^^^^^^^^^^^^^^^^ RUF100 25 | print(a) # noqa: E501, F821 comment +26 | print(a) # noqa: E501,,F821 comment | = help: Remove unused `noqa` directive @@ -363,8 +364,8 @@ RUF100_3.py:24:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) 24 |-print(a) # noqa: E501, F821 comment 24 |+print(a) # noqa: F821 comment 25 25 | print(a) # noqa: E501, F821 comment -26 26 | -27 27 | print(a) # comment with unicode µ # noqa: E501 +26 26 | print(a) # noqa: E501,,F821 comment +27 27 | print(a) # noqa: E501, ,F821 comment RUF100_3.py:25:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) | @@ -372,8 +373,8 @@ RUF100_3.py:25:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) 24 | print(a) # noqa: E501, F821 comment 25 | print(a) # noqa: E501, F821 comment | ^^^^^^^^^^^^^^^^^^ RUF100 -26 | -27 | print(a) # comment with unicode µ # noqa: E501 +26 | print(a) # noqa: E501,,F821 comment +27 | print(a) # noqa: E501, ,F821 comment | = help: Remove unused `noqa` directive @@ -383,48 +384,110 @@ RUF100_3.py:25:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) 24 24 | print(a) # noqa: E501, F821 comment 25 |-print(a) # noqa: E501, F821 comment 25 |+print(a) # noqa: F821 comment -26 26 | -27 27 | print(a) # comment with unicode µ # noqa: E501 -28 28 | print(a) # comment with unicode µ # noqa: E501, F821 +26 26 | print(a) # noqa: E501,,F821 comment +27 27 | print(a) # noqa: E501, ,F821 comment +28 28 | print(a) # noqa: E501 F821 comment -RUF100_3.py:27:7: F821 Undefined name `a` +RUF100_3.py:26:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) + | +24 | print(a) # noqa: E501, F821 comment +25 | print(a) # noqa: E501, F821 comment +26 | print(a) # noqa: E501,,F821 comment + | ^^^^^^^^^^^^^^^^^^ RUF100 +27 | print(a) # noqa: E501, ,F821 comment +28 | print(a) # noqa: E501 F821 comment + | + = help: Remove unused `noqa` directive + +ℹ Safe fix +23 23 | print(a) # noqa: E501, F821 # comment +24 24 | print(a) # noqa: E501, F821 comment +25 25 | print(a) # noqa: E501, F821 comment +26 |-print(a) # noqa: E501,,F821 comment + 26 |+print(a) # noqa: F821 comment +27 27 | print(a) # noqa: E501, ,F821 comment +28 28 | print(a) # noqa: E501 F821 comment +29 29 | + +RUF100_3.py:27:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) | 25 | print(a) # noqa: E501, F821 comment -26 | -27 | print(a) # comment with unicode µ # noqa: E501 - | ^ F821 -28 | print(a) # comment with unicode µ # noqa: E501, F821 - | - -RUF100_3.py:27:39: RUF100 [*] Unused `noqa` directive (unused: `E501`) - | -25 | print(a) # noqa: E501, F821 comment -26 | -27 | print(a) # comment with unicode µ # noqa: E501 - | ^^^^^^^^^^^^ RUF100 -28 | print(a) # comment with unicode µ # noqa: E501, F821 +26 | print(a) # noqa: E501,,F821 comment +27 | print(a) # noqa: E501, ,F821 comment + | ^^^^^^^^^^^^^^^^^^^ RUF100 +28 | print(a) # noqa: E501 F821 comment | = help: Remove unused `noqa` directive ℹ Safe fix 24 24 | print(a) # noqa: E501, F821 comment 25 25 | print(a) # noqa: E501, F821 comment -26 26 | -27 |-print(a) # comment with unicode µ # noqa: E501 - 27 |+print(a) # comment with unicode µ -28 28 | print(a) # comment with unicode µ # noqa: E501, F821 +26 26 | print(a) # noqa: E501,,F821 comment +27 |-print(a) # noqa: E501, ,F821 comment + 27 |+print(a) # noqa: F821 comment +28 28 | print(a) # noqa: E501 F821 comment +29 29 | +30 30 | print(a) # comment with unicode µ # noqa: E501 -RUF100_3.py:28:39: RUF100 [*] Unused `noqa` directive (unused: `E501`) +RUF100_3.py:28:11: RUF100 [*] Unused `noqa` directive (unused: `E501`) | -27 | print(a) # comment with unicode µ # noqa: E501 -28 | print(a) # comment with unicode µ # noqa: E501, F821 - | ^^^^^^^^^^^^^^^^^^ RUF100 +26 | print(a) # noqa: E501,,F821 comment +27 | print(a) # noqa: E501, ,F821 comment +28 | print(a) # noqa: E501 F821 comment + | ^^^^^^^^^^^^^^^^^ RUF100 +29 | +30 | print(a) # comment with unicode µ # noqa: E501 | = help: Remove unused `noqa` directive ℹ Safe fix 25 25 | print(a) # noqa: E501, F821 comment -26 26 | -27 27 | print(a) # comment with unicode µ # noqa: E501 -28 |-print(a) # comment with unicode µ # noqa: E501, F821 - 28 |+print(a) # comment with unicode µ # noqa: F821 +26 26 | print(a) # noqa: E501,,F821 comment +27 27 | print(a) # noqa: E501, ,F821 comment +28 |-print(a) # noqa: E501 F821 comment + 28 |+print(a) # noqa: F821 comment +29 29 | +30 30 | print(a) # comment with unicode µ # noqa: E501 +31 31 | print(a) # comment with unicode µ # noqa: E501, F821 + +RUF100_3.py:30:7: F821 Undefined name `a` + | +28 | print(a) # noqa: E501 F821 comment +29 | +30 | print(a) # comment with unicode µ # noqa: E501 + | ^ F821 +31 | print(a) # comment with unicode µ # noqa: E501, F821 + | + +RUF100_3.py:30:39: RUF100 [*] Unused `noqa` directive (unused: `E501`) + | +28 | print(a) # noqa: E501 F821 comment +29 | +30 | print(a) # comment with unicode µ # noqa: E501 + | ^^^^^^^^^^^^ RUF100 +31 | print(a) # comment with unicode µ # noqa: E501, F821 + | + = help: Remove unused `noqa` directive + +ℹ Safe fix +27 27 | print(a) # noqa: E501, ,F821 comment +28 28 | print(a) # noqa: E501 F821 comment +29 29 | +30 |-print(a) # comment with unicode µ # noqa: E501 + 30 |+print(a) # comment with unicode µ +31 31 | print(a) # comment with unicode µ # noqa: E501, F821 + +RUF100_3.py:31:39: RUF100 [*] Unused `noqa` directive (unused: `E501`) + | +30 | print(a) # comment with unicode µ # noqa: E501 +31 | print(a) # comment with unicode µ # noqa: E501, F821 + | ^^^^^^^^^^^^^^^^^^ RUF100 + | + = help: Remove unused `noqa` directive + +ℹ Safe fix +28 28 | print(a) # noqa: E501 F821 comment +29 29 | +30 30 | print(a) # comment with unicode µ # noqa: E501 +31 |-print(a) # comment with unicode µ # noqa: E501, F821 + 31 |+print(a) # comment with unicode µ # noqa: F821 diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_empty_comma.snap b/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_empty_comma.snap new file mode 100644 index 0000000000..7a83ed9db2 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_empty_comma.snap @@ -0,0 +1,23 @@ +--- +source: crates/ruff_linter/src/noqa.rs +expression: "Directive::try_extract(source, TextSize::default())" +--- +Ok( + Some( + Codes( + Codes { + range: 0..18, + codes: [ + Code { + code: "F401", + range: 8..12, + }, + Code { + code: "F841", + range: 14..18, + }, + ], + }, + ), + ), +) diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_empty_comma_space.snap b/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_empty_comma_space.snap new file mode 100644 index 0000000000..c2a215c16f --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_empty_comma_space.snap @@ -0,0 +1,23 @@ +--- +source: crates/ruff_linter/src/noqa.rs +expression: "Directive::try_extract(source, TextSize::default())" +--- +Ok( + Some( + Codes( + Codes { + range: 0..19, + codes: [ + Code { + code: "F401", + range: 8..12, + }, + Code { + code: "F841", + range: 15..19, + }, + ], + }, + ), + ), +) diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_squashed_codes.snap b/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_squashed_codes.snap new file mode 100644 index 0000000000..8a6adc1cc7 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__noqa__tests__noqa_squashed_codes.snap @@ -0,0 +1,19 @@ +--- +source: crates/ruff_linter/src/noqa.rs +expression: "Directive::try_extract(source, TextSize::default())" +--- +Ok( + Some( + Codes( + Codes { + range: 0..16, + codes: [ + Code { + code: "F401F841", + range: 8..16, + }, + ], + }, + ), + ), +)