Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions crates/ruff/tests/cli/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3720,6 +3720,48 @@ def func(t: _T) -> _T:
);
}

/// Test that `noqa` comments with rule codes
/// 1. Get replaced with Ruff-specific suppression comments (RUF105)
/// 2. Use human-readable rule names instead of codes (RUF106)
#[test]
fn noqa_comments_to_human_readable_ruff_ignores() -> Result<()> {
let fixture = CliTest::new()?;
let source = "# ruff: noqa: F401
import os

def foo():
value = 1 # noqa: F841
";

assert_cmd_snapshot!(
fixture
.check_command()
.args([
"--select=F401,F841,RUF105,RUF106",
"--stdin-filename=test.py",
"--fix",
"--preview",
"-",
])
.pass_stdin(source),
@"
success: true
exit_code: 0
----- stdout -----
# ruff:file-ignore[unused-import]
import os

def foo():
value = 1 # ruff:ignore[unused-variable]

----- stderr -----
Found 4 errors (4 fixed, 0 remaining).
",
);

Ok(())
}

/// Test that we do not rename two different type parameters to the same name
/// in one execution of Ruff (autofixing this to `class Foo[T, T]: ...` would
/// introduce invalid syntax)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
# `rule-codes-in-suppression-comments` (`RUF106`)

```toml
[lint]
preview = true
select = ["RUF106"]
external = ["EXT"]
```

## `ruff:ignore`

Each Ruff rule code receives a separate diagnostic. Rule names and external or unknown codes are
preserved:

```py
# snapshot: rule-codes-in-suppression-comments
# snapshot: rule-codes-in-suppression-comments
# ruff:ignore[F401, undefined-name, EXT001, UNKNOWN, F841]
value = 1
```

```snapshot
error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:3:15
|
3 | # ruff:ignore[F401, undefined-name, EXT001, UNKNOWN, F841]
| ^^^^
|
help: Replace rule code with name
|
2 | # snapshot: rule-codes-in-suppression-comments
- # ruff:ignore[F401, undefined-name, EXT001, UNKNOWN, F841]
3 + # ruff:ignore[unused-import, undefined-name, EXT001, UNKNOWN, F841]
4 | value = 1
|


error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:3:54
|
3 | # ruff:ignore[F401, undefined-name, EXT001, UNKNOWN, F841]
| ^^^^
|
help: Replace rule code with name
|
2 | # snapshot: rule-codes-in-suppression-comments
- # ruff:ignore[F401, undefined-name, EXT001, UNKNOWN, F841]
3 + # ruff:ignore[F401, undefined-name, EXT001, UNKNOWN, unused-variable]
4 | value = 1
|
```

Valid human-readable names are unaffected:

```py
# snapshot: rule-codes-in-suppression-comments
# snapshot: rule-codes-in-suppression-comments
# ruff:ignore[F401, undefined-name, F841]
value = 1
```

```snapshot
error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:7:15
|
7 | # ruff:ignore[F401, undefined-name, F841]
| ^^^^
|
help: Replace rule code with name
|
6 | # snapshot: rule-codes-in-suppression-comments
- # ruff:ignore[F401, undefined-name, F841]
7 + # ruff:ignore[unused-import, undefined-name, F841]
8 | value = 1
|


error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:7:37
|
7 | # ruff:ignore[F401, undefined-name, F841]
| ^^^^
|
help: Replace rule code with name
|
6 | # snapshot: rule-codes-in-suppression-comments
- # ruff:ignore[F401, undefined-name, F841]
7 + # ruff:ignore[F401, undefined-name, unused-variable]
8 | value = 1
|
```

## `ruff:file-ignore`

```py
# snapshot: rule-codes-in-suppression-comments
# snapshot: rule-codes-in-suppression-comments
# ruff:file-ignore[F401, F841]
```

```snapshot
error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:3:20
|
3 | # ruff:file-ignore[F401, F841]
| ^^^^
|
help: Replace rule code with name
|
2 | # snapshot: rule-codes-in-suppression-comments
- # ruff:file-ignore[F401, F841]
3 + # ruff:file-ignore[unused-import, F841]
|


error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:3:26
|
3 | # ruff:file-ignore[F401, F841]
| ^^^^
|
help: Replace rule code with name
|
2 | # snapshot: rule-codes-in-suppression-comments
- # ruff:file-ignore[F401, F841]
3 + # ruff:file-ignore[F401, unused-variable]
|
```

## Matched `ruff:disable` and `ruff:enable`

Matching comments are reported and fixed together:

```py
# snapshot: rule-codes-in-suppression-comments
# snapshot: rule-codes-in-suppression-comments
# ruff:disable[F401, undefined-name, F841]
value = 1
# ruff:enable[F401, undefined-name, F841]
```

```snapshot
error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:3:16
|
3 | # ruff:disable[F401, undefined-name, F841]
| ^^^^
4 | value = 1
5 | # ruff:enable[F401, undefined-name, F841]
| ----
|
help: Replace rule code with name
|
2 | # snapshot: rule-codes-in-suppression-comments
- # ruff:disable[F401, undefined-name, F841]
3 + # ruff:disable[unused-import, undefined-name, F841]
4 | value = 1
- # ruff:enable[F401, undefined-name, F841]
5 + # ruff:enable[unused-import, undefined-name, F841]
|


error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:3:38
|
3 | # ruff:disable[F401, undefined-name, F841]
| ^^^^
4 | value = 1
5 | # ruff:enable[F401, undefined-name, F841]
| ----
|
help: Replace rule code with name
|
2 | # snapshot: rule-codes-in-suppression-comments
- # ruff:disable[F401, undefined-name, F841]
3 + # ruff:disable[F401, undefined-name, unused-variable]
4 | value = 1
- # ruff:enable[F401, undefined-name, F841]
5 + # ruff:enable[F401, undefined-name, unused-variable]
|
```

## Unmatched `ruff:disable`

An unmatched disable comment is still an effective suppression through the end of its indentation
level:

```py
# snapshot: rule-codes-in-suppression-comments
# ruff:disable[F401]
```

```snapshot
error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:2:16
|
2 | # ruff:disable[F401]
| ^^^^
|
help: Replace rule code with name
|
1 | # snapshot: rule-codes-in-suppression-comments
- # ruff:disable[F401]
2 + # ruff:disable[unused-import]
|
```

## Unmatched `ruff:enable`

An unmatched enable comment is invalid and is left to `invalid-suppression-comment`:

```py
# ruff:enable[F401]
```

## Redirected codes

Redirected codes are replaced with the name of their canonical rule:

```py
# snapshot: rule-codes-in-suppression-comments
# ruff:ignore[PGH001]
value = 1
```

```snapshot
error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:2:15
|
2 | # ruff:ignore[PGH001]
| ^^^^^^
|
help: Replace rule code with name
|
1 | # snapshot: rule-codes-in-suppression-comments
- # ruff:ignore[PGH001]
2 + # ruff:ignore[suspicious-eval-usage]
3 | value = 1
|
```

## Nested suppression comments

Only the rule codes within a nested suppression comment are replaced:

```py
# snapshot: rule-codes-in-suppression-comments
# snapshot: rule-codes-in-suppression-comments
value = 1 # explanation # ruff:ignore[F401, F841] reason # another
```

```snapshot
error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:3:40
|
3 | value = 1 # explanation # ruff:ignore[F401, F841] reason # another
| ^^^^
|
help: Replace rule code with name
|
2 | # snapshot: rule-codes-in-suppression-comments
- value = 1 # explanation # ruff:ignore[F401, F841] reason # another
3 + value = 1 # explanation # ruff:ignore[unused-import, F841] reason # another
|


error[RUF106]: Rule code used instead of name in suppression comment
--> src/mdtest_snippet.py:3:46
|
3 | value = 1 # explanation # ruff:ignore[F401, F841] reason # another
| ^^^^
|
help: Replace rule code with name
|
2 | # snapshot: rule-codes-in-suppression-comments
- value = 1 # explanation # ruff:ignore[F401, F841] reason # another
3 + value = 1 # explanation # ruff:ignore[F401, unused-variable] reason # another
|
```

## Comments without Ruff rule codes

Comments containing only names and external or unknown codes are unchanged:

```py
# ruff:ignore[unused-import, EXT001, UNKNOWN]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you add a test for an ignore that uses human readable names and codes? I don't think this is covered yet

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this is covered by:

## `ruff:ignore`
Rule names and external or unknown codes are preserved while Ruff rule codes are replaced:
```py
# snapshot: rule-codes-in-suppression-comments
# ruff:ignore[F401, undefined-name, EXT001, UNKNOWN, F841]
value = 1
```
```snapshot

if I understand your suggestion. Or do you mean just human-readable names and valid codes, without the external codes?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yes, I mean one with valid human readable names and valid codes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a version without the external codes:

Valid human-readable names are unaffected:
```py
# snapshot: rule-codes-in-suppression-comments
# snapshot: rule-codes-in-suppression-comments
# ruff:ignore[F401, undefined-name, F841]
value = 1
```

value = 1
```

## Self-suppression

The rule can be suppressed by its code or name:

```py
# ruff:ignore[F401, RUF106]
value = 1
```

```py
# ruff:ignore[F401, rule-codes-in-suppression-comments]
value = 1
```

The diagnostic can also be suppressed with a `noqa` comment:

```py
value = 1 # ruff:ignore[F401] # noqa: RUF106
```
Loading
Loading