Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pygrep-hooks] Improve blanket-noqa error message (PGH004) #10851

Merged
merged 2 commits into from Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -9,3 +9,24 @@
x = 1 # noqa: F401, W203
# noqa: F401
# noqa: F401, W203



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

# PGH004
x = 2 # noqa X100

# PGH004
x = 2 # noqa X100, X200

# PGH004
x = 2 # noqa : X300

# PGH004
x = 2 # noqa : X400

# PGH004
x = 2 # noqa :X500
2 changes: 1 addition & 1 deletion crates/ruff_linter/src/noqa.rs
Expand Up @@ -147,7 +147,7 @@ impl<'a> Directive<'a> {

/// Lex an individual rule code (e.g., `F401`).
#[inline]
fn lex_code(line: &str) -> Option<&str> {
pub(crate) fn lex_code(line: &str) -> Option<&str> {
// Extract, e.g., the `F` in `F401`.
let prefix = line.chars().take_while(char::is_ascii_uppercase).count();
// Extract, e.g., the `401` in `F401`.
Expand Down
85 changes: 80 additions & 5 deletions crates/ruff_linter/src/rules/pygrep_hooks/rules/blanket_noqa.rs
@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_index::Indexer;
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use ruff_text_size::{Ranged, TextRange, TextSize};

use crate::noqa::Directive;

Expand Down Expand Up @@ -30,13 +30,42 @@ use crate::noqa::Directive;
/// ## References
/// - [Ruff documentation](https://docs.astral.sh/ruff/configuration/#error-suppression)
#[violation]
pub struct BlanketNOQA;
pub struct BlanketNOQA {
missing_colon: bool,
space_before_colon: bool,
}

impl Violation for BlanketNOQA {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;

#[derive_message_formats]
fn message(&self) -> String {
let 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");
}
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());
}
if *space_before_colon {
return Some("Remove space(s) before colon".to_string());
}
None
}
}

/// PGH004
Expand All @@ -47,8 +76,54 @@ pub(crate) fn blanket_noqa(
) {
for range in indexer.comment_ranges() {
let line = locator.slice(*range);
if let Ok(Some(Directive::All(all))) = Directive::try_extract(line, range.start()) {
diagnostics.push(Diagnostic::new(BlanketNOQA, all.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..]);

// 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());
let mut diagnostic = Diagnostic::new(
BlanketNOQA {
missing_colon: false,
space_before_colon: true,
},
TextRange::new(noqa_start, end),
);
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();
let end = start + TextSize::new(1);
let mut diagnostic = Diagnostic::new(
BlanketNOQA {
missing_colon: true,
space_before_colon: false,
},
TextRange::new(noqa_start, end),
);
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(':'.to_string(), start)));
diagnostics.push(diagnostic);
} else {
diagnostics.push(Diagnostic::new(
BlanketNOQA {
missing_colon: false,
space_before_colon: false,
},
TextRange::new(noqa_start, noqa_end),
));
}
}
}
}

fn leading_whitespace_len(text: &str) -> usize {
text.find(|c: char| !c.is_whitespace()).unwrap_or(0)
}
Expand Up @@ -29,4 +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
|
19 | # PGH004
20 | x = 2 # noqa X100
| ^^^^^^^ PGH004
21 |
22 | # 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

PGH004_0.py:23:8: PGH004 [*] Use a colon when specifying `noqa` rule codes
|
22 | # PGH004
23 | x = 2 # noqa X100, X200
| ^^^^^^^ PGH004
24 |
25 | # 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

PGH004_0.py:26:8: PGH004 [*] Do not add spaces between `noqa` and its colon
|
25 | # PGH004
26 | x = 2 # noqa : X300
| ^^^^^^^ PGH004
27 |
28 | # 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

PGH004_0.py:29:8: PGH004 [*] Do not add spaces between `noqa` and its colon
|
28 | # PGH004
29 | x = 2 # noqa : X400
| ^^^^^^^^ PGH004
30 |
31 | # 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

PGH004_0.py:32:8: PGH004 [*] Do not add spaces between `noqa` and its colon
|
31 | # PGH004
32 | 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