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

[ruff] Unformatted special comments (RUF037) #14111

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ac6ab76
[`ruff`] Unformatted special comments (`RUF102`)
InSyncWithFoo Nov 4, 2024
58c46ff
Rename to `RUF104`
InSyncWithFoo Nov 6, 2024
d7db816
Exempt doc comment from formatting checks
InSyncWithFoo Nov 6, 2024
dc2e866
Rewrite
InSyncWithFoo Nov 6, 2024
7c97af8
Fix
InSyncWithFoo Nov 6, 2024
43b24cd
Switch to `std::sync::LazyLock`
InSyncWithFoo Nov 7, 2024
0d0cc2b
Add a few tests
InSyncWithFoo Nov 7, 2024
189f265
Fix lint error
InSyncWithFoo Nov 7, 2024
7cfe1b4
Use different variant for each kind of special comments
InSyncWithFoo Nov 8, 2024
fd51044
Fix index bug
InSyncWithFoo Nov 8, 2024
e332e23
Add a few more tests
InSyncWithFoo Nov 9, 2024
48bba9d
Fix off-by-one error
InSyncWithFoo Nov 9, 2024
2c3efb7
Rewrite again
InSyncWithFoo Nov 9, 2024
66f77d2
Move `RUF014.py` to `preview_rules`
InSyncWithFoo Nov 10, 2024
60e9da3
Update snapshots
InSyncWithFoo Nov 10, 2024
1d9c9df
Oops
InSyncWithFoo Nov 10, 2024
0e11a3e
Exclude `RUF104` from implicit suppressions
InSyncWithFoo Nov 10, 2024
a050fc7
`rustfmt`
InSyncWithFoo Nov 10, 2024
42f9577
Update `# noqa` parsing algorithm to match that of `Directive::try_ex…
InSyncWithFoo Nov 10, 2024
268edc3
Require that `RUF104` be suppressed correctly
InSyncWithFoo Nov 10, 2024
e33b05c
`rustfmt`
InSyncWithFoo Nov 10, 2024
9c5ec19
Fix typo
InSyncWithFoo Nov 11, 2024
1c1b732
Address review
InSyncWithFoo Nov 11, 2024
ee6dc39
`rustfmt`
InSyncWithFoo Nov 11, 2024
78327f0
Fix failing testcase
InSyncWithFoo Nov 11, 2024
99606d8
Merge branch 'main' into RUF102
InSyncWithFoo Nov 12, 2024
c899ead
Rename to `RUF037`
InSyncWithFoo Nov 13, 2024
7cdf2d3
Missed two
InSyncWithFoo Nov 13, 2024
7bc3a84
Add `# yapf`
InSyncWithFoo Nov 13, 2024
2fbd4a2
Fix tests
InSyncWithFoo Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF104.py
InSyncWithFoo marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#foo
# foo
#ruff: foo-bar
# flake8:foo-bar
# black:skip

# FMT:OFF
# isort: On
# Mypy: disallow-subclassing-any
# PyRight: basic
# Type: ignore

# noqa
# noqa: A123
# noqa: A123, B456
# ruff: noqa
# ruff: noqa: A123
# ruff: noqa: A123, B456
# flake8: noqa
# flake8: noqa: A123
# flake8: noqa: A123, B456
# fmt: on
# fmt: off
# fmt: skip
# isort: on
# isort: off
# isort: split
# isort: skip
# isort: skip_file
# mypy: enable-error-codes=
# nopycln: file
# nopycln: import
# pyright: basic
# pyright: standard
# pyright: strict
# pyright: ignore
# pyright: ignore [reportFoo]
# ruff: isort: on
# ruff: isort: skip_file
# type: ignore
# type: int
# type: list[str]

# noqa:A123
#noqa: A123
# type:ignore
#type: int
# fmt:off
#fmt: on
# fmt: skip
# isort:skip
# isort:skip_file
# mypy: disallow-subclassing-any
# nopycln: file
# nopycln: import
#pyright:ignore[]
# pyright: ignore[]
# ruff: isort:skip
# ruff: isort:skip_file
# type: ignore
# type: int

# NoQA: A123, B456
# ruff: NoQA: A123, B456
# flake8: NoQA: A123, B456
# NoPyCLN: File
# NoPycln: Import
# nOpYcLn: iMpOrT

# noqa: A123 B456
# ruff: noqa: A123 B456
# flake8: noqa: A123 B456
# noqa: A123,B456
# ruff: noqa: A123,B456
# flake8: noqa: A123,B456
# noqa: A123,,B456
# noqa: A123 , , B456
# noqa: A123 B456
# noqa: A123 B456
# noqa: A123 B456
# noqa: A123 ,B456
# ruff: noqa: A123 B456
# flake8: noqa: A123 B456


# type: ignore # noqa: A123, B456

#pyright:ignore#noqa:A123

# nopycln:file# noqa: A123

# noqa:A123 - Lorem ipsum dolor sit amet
8 changes: 8 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF104_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
###
# Both of these should trigger RUF104
# as it is not explicitly suppressed.
###

# ruff:noqa

#noqa:A123
8 changes: 8 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF104_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
###
# None of these should trigger RUF104
# as it is explicitly suppressed for the entire file.
###

# flake8:noqa:RUF104

#noqa:A123
7 changes: 7 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF104_3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
###
# All of these should be reformatted.
###

#noqa:
#ruff:noqa:
#flake8:noqa:
11 changes: 9 additions & 2 deletions crates/ruff_linter/src/checkers/noqa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,15 @@ pub(crate) fn check_noqa(

match &exemption {
FileExemption::All(_) => {
// If the file is exempted, ignore all diagnostics.
ignored_diagnostics.push(index);
// If the file is exempted, ignore all diagnostics,
// save for RUF104, which operates on `# noqa` comments
// and thus needs to be suppressed explicitly.
if !matches!(diagnostic.kind.rule(), Rule::UnformattedSpecialComment)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain the reasoning for this special handling? I think I would find this behavior surprising and I'm not sure if it justifies the added complexity

Copy link
Contributor Author

@InSyncWithFoo InSyncWithFoo Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blanket-noqa (PGH004) gets similar treatment (right above, line 48), and must either not be enabled or explicitly disabled using per-file-ignores/# ruff: noqa: PGH004 (line 223):

if matches!(diagnostic.kind.rule(), Rule::BlanketNOQA) {
    continue;
}
if settings.rules.enabled(Rule::BlanketNOQA)
    && !per_file_ignores.contains(Rule::BlanketNOQA)
    && !exemption.enumerates(Rule::BlanketNOQA)

RUF104 isn't a noqa-only rule, so unformatted_special_comment() shouldn't be called from check_noqa(). However, exemption is computed within check_noqa() using a function that might emit a warning if a # noqa: comment does not have any codes. This means the necessary information cannot be retrieved from functions other than check_noqa() without undesirable side-effects.

|| per_file_ignores.contains(Rule::UnformattedSpecialComment)
|| exemption.enumerates(Rule::UnformattedSpecialComment)
{
ignored_diagnostics.push(index);
}
continue;
}
FileExemption::Codes(codes) => {
Expand Down
4 changes: 4 additions & 0 deletions crates/ruff_linter/src/checkers/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ pub(crate) fn check_tokens(
pycodestyle::rules::too_many_newlines_at_end_of_file(&mut diagnostics, tokens);
}

if settings.rules.enabled(Rule::UnformattedSpecialComment) {
ruff::rules::unformatted_special_comment(&mut diagnostics, locator, comment_ranges);
}

diagnostics.retain(|diagnostic| settings.rules.enabled(diagnostic.kind.rule()));

diagnostics
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Ruff, "034") => (RuleGroup::Preview, rules::ruff::rules::UselessIfElse),
(Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA),
(Ruff, "101") => (RuleGroup::Stable, rules::ruff::rules::RedirectedNOQA),
(Ruff, "104") => (RuleGroup::Preview, rules::ruff::rules::UnformattedSpecialComment),

(Ruff, "200") => (RuleGroup::Stable, rules::ruff::rules::InvalidPyprojectToml),
#[cfg(any(feature = "test-rules", test))]
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ impl Rule {
| Rule::TrailingCommaOnBareTuple
| Rule::TypeCommentInStub
| Rule::UselessSemicolon
| Rule::UnformattedSpecialComment
| Rule::UTF8EncodingDeclaration => LintSource::Tokens,
Rule::IOError => LintSource::Io,
Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl AlwaysFixableViolation for FastApiRedundantResponseModel {
}
}

/// RUF102
/// FAST001
pub(crate) fn fastapi_redundant_response_model(
checker: &mut Checker,
function_def: &StmtFunctionDef,
Expand Down
6 changes: 5 additions & 1 deletion crates/ruff_linter/src/rules/ruff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ mod tests {
#[test_case(Rule::AssertWithPrintMessage, Path::new("RUF030.py"))]
#[test_case(Rule::IncorrectlyParenthesizedTupleInSubscript, Path::new("RUF031.py"))]
#[test_case(Rule::DecimalFromFloatLiteral, Path::new("RUF032.py"))]
#[test_case(Rule::PostInitDefault, Path::new("RUF033.py"))]
#[test_case(Rule::UselessIfElse, Path::new("RUF034.py"))]
#[test_case(Rule::RedirectedNOQA, Path::new("RUF101.py"))]
#[test_case(Rule::PostInitDefault, Path::new("RUF033.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Expand Down Expand Up @@ -384,6 +384,10 @@ mod tests {
}

#[test_case(Rule::ZipInsteadOfPairwise, Path::new("RUF007.py"))]
#[test_case(Rule::UnformattedSpecialComment, Path::new("RUF104.py"))]
#[test_case(Rule::UnformattedSpecialComment, Path::new("RUF104_1.py"))]
#[test_case(Rule::UnformattedSpecialComment, Path::new("RUF104_2.py"))]
#[test_case(Rule::UnformattedSpecialComment, Path::new("RUF104_3.py"))]
InSyncWithFoo marked this conversation as resolved.
Show resolved Hide resolved
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"preview__{}_{}",
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/ruff/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub(crate) use sort_dunder_slots::*;
pub(crate) use static_key_dict_comprehension::*;
#[cfg(any(feature = "test-rules", test))]
pub(crate) use test_rules::*;
pub(crate) use unformatted_special_comment::*;
pub(crate) use unnecessary_iterable_allocation_for_first_element::*;
pub(crate) use unnecessary_key_check::*;
pub(crate) use unused_async::*;
Expand Down Expand Up @@ -65,6 +66,7 @@ mod static_key_dict_comprehension;
mod suppression_comment_visitor;
#[cfg(any(feature = "test-rules", test))]
pub(crate) mod test_rules;
mod unformatted_special_comment;
mod unnecessary_iterable_allocation_for_first_element;
mod unnecessary_key_check;
mod unused_async;
Expand Down
Loading
Loading