Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# ruff: noqa: F841 -- intentional unused file directive; will be removed
8 changes: 8 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF100_7.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# flake8: noqa: F841, E501 -- used followed by unused code
# ruff: noqa: E701, F541 -- unused followed by used code


def a():
# Violating noqa'd F841 (unused) and F541 (no-placeholder f-string)
x = f"Hello, world"
return 6
61 changes: 48 additions & 13 deletions crates/ruff_linter/src/checkers/noqa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,29 @@ pub(crate) fn check_noqa(
&& !exemption.includes(Rule::UnusedNOQA)
&& !per_file_ignores.contains(Rule::UnusedNOQA)
{
for line in noqa_directives.lines() {
match &line.directive {
let directives: Vec<_> = if settings.preview.is_enabled() {
noqa_directives
.lines()
.iter()
.map(|line| (&line.directive, &line.matches, false))
.chain(
file_noqa_directives
.lines()
.iter()
.map(|line| (&line.parsed_file_exemption, &line.matches, true)),
)
.collect()
} else {
noqa_directives
.lines()
.iter()
.map(|line| (&line.directive, &line.matches, false))
.collect()
};
for (directive, matches, is_file_level) in directives {
match directive {
Directive::All(directive) => {
if line.matches.is_empty() {
if matches.is_empty() {
let edit = delete_comment(directive.range(), locator);
let mut diagnostic =
Diagnostic::new(UnusedNOQA { codes: None }, directive.range());
Expand All @@ -137,17 +156,21 @@ pub(crate) fn check_noqa(
break;
}

if !seen_codes.insert(original_code) {
duplicated_codes.push(original_code);
} else if line.matches.iter().any(|match_| *match_ == code)
|| settings
if seen_codes.insert(original_code) {
let is_code_used = if is_file_level {
diagnostics
.iter()
.any(|diag| diag.kind.rule().noqa_code() == code)
} else {
matches.iter().any(|match_| *match_ == code)
} || settings
.external
.iter()
.any(|external| code.starts_with(external))
{
valid_codes.push(original_code);
} else {
if let Ok(rule) = Rule::from_code(code) {
.any(|external| code.starts_with(external));

if is_code_used {
valid_codes.push(original_code);
} else if let Ok(rule) = Rule::from_code(code) {
if settings.rules.enabled(rule) {
unmatched_codes.push(original_code);
} else {
Expand All @@ -156,6 +179,8 @@ pub(crate) fn check_noqa(
} else {
unknown_codes.push(original_code);
}
} else {
duplicated_codes.push(original_code);
}
}

Expand All @@ -171,8 +196,18 @@ pub(crate) fn check_noqa(
let edit = if valid_codes.is_empty() {
delete_comment(directive.range(), locator)
} else {
let original_text = locator.slice(directive.range());
let prefix = if is_file_level {
if original_text.contains("flake8:") {
"# flake8: noqa: "
} else {
"# ruff: noqa: "
}
} else {
"# noqa: "
};
Edit::range_replacement(
format!("# noqa: {}", valid_codes.join(", ")),
format!("{}{}", prefix, valid_codes.join(", ")),
directive.range(),
)
};
Expand Down
31 changes: 31 additions & 0 deletions crates/ruff_linter/src/rules/ruff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,37 @@ mod tests {
Ok(())
}

#[test]
fn ruff_noqa_filedirective_unused() -> Result<()> {
let diagnostics = test_path(
Path::new("ruff/RUF100_6.py"),
&settings::LinterSettings {
preview: PreviewMode::Enabled,
..settings::LinterSettings::for_rules(vec![Rule::UnusedNOQA])
},
)?;
assert_messages!(diagnostics);
Ok(())
}

#[test]
fn ruff_noqa_filedirective_unused_last_of_many() -> Result<()> {
let diagnostics = test_path(
Path::new("ruff/RUF100_7.py"),
&settings::LinterSettings {
preview: PreviewMode::Enabled,
..settings::LinterSettings::for_rules(vec![
Rule::UnusedNOQA,
Rule::FStringMissingPlaceholders,
Rule::LineTooLong,
Rule::UnusedVariable,
])
},
)?;
assert_messages!(diagnostics);
Ok(())
}

#[test]
fn ruff_per_file_ignores() -> Result<()> {
let diagnostics = test_path(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
---
RUF100_6.py:1:1: RUF100 [*] Unused `noqa` directive (non-enabled: `F841`)
|
1 | # ruff: noqa: F841 -- intentional unused file directive; will be removed
| ^^^^^^^^^^^^^^^^^^ RUF100
|
= help: Remove unused `noqa` directive

ℹ Safe fix
1 |-# ruff: noqa: F841 -- intentional unused file directive; will be removed
1 |+
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
---
RUF100_7.py:1:1: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
1 | # flake8: noqa: F841, E501 -- used followed by unused code
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF100
2 | # ruff: noqa: E701, F541 -- unused followed by used code
|
= help: Remove unused `noqa` directive

ℹ Safe fix
1 |-# flake8: noqa: F841, E501 -- used followed by unused code
1 |+# flake8: noqa: F841 -- used followed by unused code
2 2 | # ruff: noqa: E701, F541 -- unused followed by used code
3 3 |
4 4 |

RUF100_7.py:2:1: RUF100 [*] Unused `noqa` directive (non-enabled: `E701`)
|
1 | # flake8: noqa: F841, E501 -- used followed by unused code
2 | # ruff: noqa: E701, F541 -- unused followed by used code
| ^^^^^^^^^^^^^^^^^^^^^^^^ RUF100
|
= help: Remove unused `noqa` directive

ℹ Safe fix
1 1 | # flake8: noqa: F841, E501 -- used followed by unused code
2 |-# ruff: noqa: E701, F541 -- unused followed by used code
2 |+# ruff: noqa: F541 -- unused followed by used code
3 3 |
4 4 |
5 5 | def a():
Loading