Respect per-file ignores for blanket and redirected noqa rules (#11728)

## Summary

Ensures that we respect per-file ignores and exemptions for these rules.
Specifically, we allow:

```python
# ruff: noqa: PGH004
```

...to ignore `PGH004`.
This commit is contained in:
Charlie Marsh 2024-06-03 23:57:59 -04:00 committed by GitHub
parent b56a577f25
commit 0c75548146
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 46 additions and 17 deletions

View file

@ -0,0 +1,8 @@
# noqa
# ruff : noqa
# ruff: noqa: F401
# ruff: noqa: PGH004
# flake8: noqa
import math as m

View file

@ -55,7 +55,7 @@ pub(crate) fn check_noqa(
}
match &exemption {
FileExemption::All => {
FileExemption::All(_) => {
// If the file is exempted, ignore all diagnostics.
ignored_diagnostics.push(index);
continue;
@ -213,7 +213,17 @@ pub(crate) fn check_noqa(
}
}
if settings.rules.enabled(Rule::BlanketNOQA) {
if settings.rules.enabled(Rule::RedirectedNOQA)
&& !per_file_ignores.contains(Rule::RedirectedNOQA)
&& !exemption.includes(Rule::RedirectedNOQA)
{
ruff::rules::redirected_noqa(diagnostics, &noqa_directives);
}
if settings.rules.enabled(Rule::BlanketNOQA)
&& !per_file_ignores.contains(Rule::BlanketNOQA)
&& !exemption.enumerates(Rule::BlanketNOQA)
{
pygrep_hooks::rules::blanket_noqa(
diagnostics,
&noqa_directives,
@ -223,10 +233,6 @@ pub(crate) fn check_noqa(
);
}
if settings.rules.enabled(Rule::RedirectedNOQA) {
ruff::rules::redirected_noqa(diagnostics, &noqa_directives);
}
ignored_diagnostics.sort_unstable();
ignored_diagnostics
}

View file

@ -280,7 +280,7 @@ pub(crate) fn rule_is_ignored(
#[derive(Debug)]
pub(crate) enum FileExemption<'a> {
/// The file is exempt from all rules.
All,
All(Vec<&'a NoqaCode>),
/// The file is exempt from the given rules.
Codes(Vec<&'a NoqaCode>),
}
@ -290,28 +290,38 @@ impl<'a> FileExemption<'a> {
pub(crate) fn includes(&self, needle: Rule) -> bool {
let needle = needle.noqa_code();
match self {
FileExemption::All => true,
FileExemption::All(_) => true,
FileExemption::Codes(codes) => codes.iter().any(|code| needle == **code),
}
}
/// Returns `true` if the file exemption lists the rule directly, rather than via a blanket
/// exemption.
pub(crate) fn enumerates(&self, needle: Rule) -> bool {
let needle = needle.noqa_code();
let codes = match self {
FileExemption::All(codes) => codes,
FileExemption::Codes(codes) => codes,
};
codes.iter().any(|code| needle == **code)
}
}
impl<'a> From<&'a FileNoqaDirectives<'a>> for FileExemption<'a> {
fn from(directives: &'a FileNoqaDirectives) -> Self {
let codes = directives
.lines()
.iter()
.flat_map(|line| &line.matches)
.collect();
if directives
.lines()
.iter()
.any(|line| ParsedFileExemption::All == line.parsed_file_exemption)
{
FileExemption::All
FileExemption::All(codes)
} else {
FileExemption::Codes(
directives
.lines()
.iter()
.flat_map(|line| &line.matches)
.collect(),
)
FileExemption::Codes(codes)
}
}
}
@ -717,7 +727,7 @@ fn find_noqa_comments<'a>(
// Mark any non-ignored diagnostics.
for diagnostic in diagnostics {
match &exemption {
FileExemption::All => {
FileExemption::All(_) => {
// If the file is exempted, don't add any noqa directives.
comments_by_line.push(None);
continue;

View file

@ -19,6 +19,7 @@ mod tests {
#[test_case(Rule::BlanketNOQA, Path::new("PGH004_0.py"))]
#[test_case(Rule::BlanketNOQA, Path::new("PGH004_1.py"))]
#[test_case(Rule::BlanketNOQA, Path::new("PGH004_2.py"))]
#[test_case(Rule::BlanketNOQA, Path::new("PGH004_3.py"))]
#[test_case(Rule::InvalidMockAccess, Path::new("PGH005_0.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/pygrep_hooks/mod.rs
---