Skip to content

Commit

Permalink
Use Cursor
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Apr 10, 2024
1 parent 57324c5 commit fcc872c
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 91 deletions.
Expand Up @@ -10,9 +10,7 @@
# noqa: F401
# noqa: F401, W203



# Ok
# OK
x = 2 # noqa: X100
x = 2 # noqa:X100

Expand Down
69 changes: 41 additions & 28 deletions crates/ruff_linter/src/rules/pygrep_hooks/rules/blanket_noqa.rs
@@ -1,6 +1,7 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_index::Indexer;
use ruff_python_trivia::Cursor;
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange, TextSize};

Expand All @@ -27,6 +28,14 @@ use crate::noqa::Directive;
/// from .base import * # noqa: F403
/// ```
///
/// ## Fix safety
/// This rule will attempt to fix blanket `noqa` annotations that appear to
/// be unintentional. For example, given `# noqa F401`, the rule will suggest
/// inserting a colon, as in `# noqa: F401`.
///
/// While modifying `noqa` comments is generally safe, doing so may introduce
/// additional diagnostics.
///
/// ## References
/// - [Ruff documentation](https://docs.astral.sh/ruff/configuration/#error-suppression)
#[violation]
Expand All @@ -44,27 +53,31 @@ impl Violation for BlanketNOQA {
missing_colon,
space_before_colon,
} = self;
if *missing_colon {
return format!("Use a colon when specifying `noqa` rule codes");
}
if *space_before_colon {
return format!("Do not add spaces between `noqa` and its colon");

// This awkward branching is necessary to ensure that the generic message is picked up by
// `derive_message_formats`.
if !missing_colon && !space_before_colon {
format!("Use specific rule codes when using `noqa`")
} else if *missing_colon {
format!("Use a colon when specifying `noqa` rule codes")
} else {
format!("Do not add spaces between `noqa` and its colon")
}
format!("Use specific rule codes when using `noqa`")
}

fn fix_title(&self) -> Option<String> {
let BlanketNOQA {
missing_colon,
space_before_colon,
} = self;

if *missing_colon {
return Some("Add missing colon".to_string());
Some("Add missing colon".to_string())
} else if *space_before_colon {
Some("Remove space(s) before colon".to_string())
} else {
None
}
if *space_before_colon {
return Some("Remove space(s) before colon".to_string());
}
None
}
}

Expand All @@ -78,16 +91,19 @@ pub(crate) fn blanket_noqa(
let line = locator.slice(*range);
let offset = range.start();
if let Ok(Some(Directive::All(all))) = Directive::try_extract(line, TextSize::new(0)) {
let noqa_start = offset + all.range().start();
let noqa_end = offset + all.range().end();
let post_noqa_start = all.range().end().to_usize();
let mut cursor = post_noqa_start;
cursor += leading_whitespace_len(&line[cursor..]);
// The `all` range is that of the `noqa` directive in, e.g., `# noqa` or `# noqa F401`.
let noqa_start = offset + all.start();
let noqa_end = offset + all.end();

// Skip the `# noqa`, plus any trailing whitespace.
let mut cursor = Cursor::new(&line[all.end().to_usize()..]);
cursor.eat_while(char::is_whitespace);

// Check for extraneous space before colon
if matches!(line[cursor..].chars().next(), Some(':')) {
let start = offset + all.range().end();
let end = offset + TextSize::new(u32::try_from(cursor).unwrap());
// Check for extraneous spaces before the colon.
// Ex) `# noqa : F401`
if cursor.first() == ':' {
let start = offset + all.end();
let end = start + cursor.token_len();
let mut diagnostic = Diagnostic::new(
BlanketNOQA {
missing_colon: false,
Expand All @@ -97,10 +113,10 @@ pub(crate) fn blanket_noqa(
);
diagnostic.set_fix(Fix::unsafe_edit(Edit::deletion(start, end)));
diagnostics.push(diagnostic);
}
// Check for missing colon
else if Directive::lex_code(&line[cursor..]).is_some() {
let start = offset + all.range().end();
} else if Directive::lex_code(cursor.chars().as_str()).is_some() {
// Check for a missing colon.
// Ex) `# noqa F401`
let start = offset + all.end();
let end = start + TextSize::new(1);
let mut diagnostic = Diagnostic::new(
BlanketNOQA {
Expand All @@ -112,6 +128,7 @@ pub(crate) fn blanket_noqa(
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(':'.to_string(), start)));
diagnostics.push(diagnostic);
} else {
// Otherwise, it looks like an intentional blanket `noqa` annotation.
diagnostics.push(Diagnostic::new(
BlanketNOQA {
missing_colon: false,
Expand All @@ -123,7 +140,3 @@ pub(crate) fn blanket_noqa(
}
}
}

fn leading_whitespace_len(text: &str) -> usize {
text.find(|c: char| !c.is_whitespace()).unwrap_or(0)
}
Expand Up @@ -29,97 +29,97 @@ PGH004_0.py:4:1: PGH004 Use specific rule codes when using `noqa`
6 | # noqa:F401,W203
|

PGH004_0.py:20:8: PGH004 [*] Use a colon when specifying `noqa` rule codes
PGH004_0.py:18:8: PGH004 [*] Use a colon when specifying `noqa` rule codes
|
19 | # PGH004
20 | x = 2 # noqa X100
17 | # PGH004
18 | x = 2 # noqa X100
| ^^^^^^^ PGH004
21 |
22 | # PGH004
19 |
20 | # PGH004
|
= help: Add missing colon

Unsafe fix
17 17 | x = 2 # noqa:X100
18 18 |
19 19 | # PGH004
20 |-x = 2 # noqa X100
20 |+x = 2 # noqa: X100
21 21 |
22 22 | # PGH004
23 23 | x = 2 # noqa X100, X200
15 15 | x = 2 # noqa:X100
16 16 |
17 17 | # PGH004
18 |-x = 2 # noqa X100
18 |+x = 2 # noqa: X100
19 19 |
20 20 | # PGH004
21 21 | x = 2 # noqa X100, X200

PGH004_0.py:23:8: PGH004 [*] Use a colon when specifying `noqa` rule codes
PGH004_0.py:21:8: PGH004 [*] Use a colon when specifying `noqa` rule codes
|
22 | # PGH004
23 | x = 2 # noqa X100, X200
20 | # PGH004
21 | x = 2 # noqa X100, X200
| ^^^^^^^ PGH004
24 |
25 | # PGH004
22 |
23 | # PGH004
|
= help: Add missing colon

Unsafe fix
20 20 | x = 2 # noqa X100
21 21 |
22 22 | # PGH004
23 |-x = 2 # noqa X100, X200
23 |+x = 2 # noqa: X100, X200
24 24 |
25 25 | # PGH004
26 26 | x = 2 # noqa : X300
18 18 | x = 2 # noqa X100
19 19 |
20 20 | # PGH004
21 |-x = 2 # noqa X100, X200
21 |+x = 2 # noqa: X100, X200
22 22 |
23 23 | # PGH004
24 24 | x = 2 # noqa : X300

PGH004_0.py:26:8: PGH004 [*] Do not add spaces between `noqa` and its colon
PGH004_0.py:24:8: PGH004 [*] Do not add spaces between `noqa` and its colon
|
25 | # PGH004
26 | x = 2 # noqa : X300
23 | # PGH004
24 | x = 2 # noqa : X300
| ^^^^^^^ PGH004
27 |
28 | # PGH004
25 |
26 | # PGH004
|
= help: Remove space(s) before colon

Unsafe fix
23 23 | x = 2 # noqa X100, X200
24 24 |
25 25 | # PGH004
26 |-x = 2 # noqa : X300
26 |+x = 2 # noqa: X300
27 27 |
28 28 | # PGH004
29 29 | x = 2 # noqa : X400
21 21 | x = 2 # noqa X100, X200
22 22 |
23 23 | # PGH004
24 |-x = 2 # noqa : X300
24 |+x = 2 # noqa: X300
25 25 |
26 26 | # PGH004
27 27 | x = 2 # noqa : X400

PGH004_0.py:29:8: PGH004 [*] Do not add spaces between `noqa` and its colon
PGH004_0.py:27:8: PGH004 [*] Do not add spaces between `noqa` and its colon
|
28 | # PGH004
29 | x = 2 # noqa : X400
26 | # PGH004
27 | x = 2 # noqa : X400
| ^^^^^^^^ PGH004
30 |
31 | # PGH004
28 |
29 | # PGH004
|
= help: Remove space(s) before colon

Unsafe fix
26 26 | x = 2 # noqa : X300
27 27 |
28 28 | # PGH004
29 |-x = 2 # noqa : X400
29 |+x = 2 # noqa: X400
30 30 |
31 31 | # PGH004
32 32 | x = 2 # noqa :X500
24 24 | x = 2 # noqa : X300
25 25 |
26 26 | # PGH004
27 |-x = 2 # noqa : X400
27 |+x = 2 # noqa: X400
28 28 |
29 29 | # PGH004
30 30 | x = 2 # noqa :X500

PGH004_0.py:32:8: PGH004 [*] Do not add spaces between `noqa` and its colon
PGH004_0.py:30:8: PGH004 [*] Do not add spaces between `noqa` and its colon
|
31 | # PGH004
32 | x = 2 # noqa :X500
29 | # PGH004
30 | x = 2 # noqa :X500
| ^^^^^^^ PGH004
|
= help: Remove space(s) before colon

Unsafe fix
29 29 | x = 2 # noqa : X400
30 30 |
31 31 | # PGH004
32 |-x = 2 # noqa :X500
32 |+x = 2 # noqa:X500
27 27 | x = 2 # noqa : X400
28 28 |
29 29 | # PGH004
30 |-x = 2 # noqa :X500
30 |+x = 2 # noqa:X500

0 comments on commit fcc872c

Please sign in to comment.