Skip to content

Conversation

@amyreese
Copy link
Contributor

@amyreese amyreese commented Nov 14, 2025

Summary

This adds a new suppression module to the ruff_linter crate, similar to the suppression
module for ty, to parse comments for ruff suppression directives, such as # ruff: disable[CODE].

This is just the piece for parsing and storing an IMR of the comments themselves. Next step
is to interpret parsed comments and map them to ranges, codes, etc that can be referenced
later when determining which diagnostics should be kept/filtered/whatever.

Test Plan

  • A bunch of new snapshot tests
  • Added to the beginning of check_path so that the parsing runs on all checked files
    for performance regression testing.

Issue #3711

@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 14, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@amyreese
Copy link
Contributor Author

Updated with logic to match disable/enable suppression comments and transform them into ranged suppression objects. Currently just looks for matching indentation and codes, to get a simplified implementation that works, since looking at tokens is complicated by where dedent tokens appear relative to own-line comments at the end of blocks (playground example).

@MichaReiser
Copy link
Member

Updated with logic to match disable/enable suppression comments and transform them into ranged suppression objects. Currently just looks for matching indentation and codes, to get a simplified implementation that works, since looking at tokens is complicated by where dedent tokens appear relative to own-line comments at the end of blocks (playground example).

Yeah, I think we have to "ignore" Dedents if all that comes between the comment token and the Dedent token are trivia tokens (newline, comment,)

@amyreese
Copy link
Contributor Author

Yeah, I think we have to "ignore" Dedents if all that comes between the comment token and the Dedent token are trivia tokens (newline, comment,)

Plus we still need to do indentation checking to make a reasonable guess as to which scope the comment should be associated:

def foo():
    if True:
        pass
        # here?
    # here?
# or here?

@amyreese amyreese force-pushed the suppression-parsing branch from 5ebddd7 to 34f19b1 Compare November 21, 2025 02:10
@amyreese amyreese force-pushed the suppression-parsing branch 2 times, most recently from 7c1629a to 88a71a0 Compare November 21, 2025 21:49
@amyreese amyreese force-pushed the suppression-parsing branch from dfd4fa3 to ef8f399 Compare November 24, 2025 16:27
@amyreese amyreese closed this Nov 25, 2025
@amyreese amyreese deleted the suppression-parsing branch November 25, 2025 01:58
@amyreese amyreese restored the suppression-parsing branch November 25, 2025 01:59
@amyreese amyreese reopened this Nov 25, 2025
@amyreese amyreese marked this pull request as ready for review November 25, 2025 02:13
@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 25, 2025

Diagnostic diff on typing conformance tests

No changes detected when running ty on typing conformance tests ✅

@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 25, 2025

mypy_primer results

Changes were detected when running on open source projects
beartype (https://github.com/beartype/beartype)
- beartype/claw/_package/clawpkgtrie.py:66:29: warning[unsupported-base] Unsupported class base with type `<class 'dict[str, PackagesTrieBlacklist]'> | <class 'dict[str, Divergent]'>`
- beartype/claw/_package/clawpkgtrie.py:247:29: warning[unsupported-base] Unsupported class base with type `<class 'dict[str, PackagesTrieWhitelist]'> | <class 'dict[str, Divergent]'>`
- Found 498 diagnostics
+ Found 496 diagnostics

scikit-build-core (https://github.com/scikit-build/scikit-build-core)
+ src/scikit_build_core/_logging.py:153:13: warning[unsupported-base] Unsupported class base with type `<class 'Mapping[str, Style]'> | <class 'Mapping[str, Divergent]'>`
- src/scikit_build_core/build/_pathutil.py:25:38: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `str | PathLike[str]`, found `DirEntry[Path]`
- src/scikit_build_core/build/_pathutil.py:27:24: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `str | PathLike[str]`, found `DirEntry[Path]`
- src/scikit_build_core/build/wheel.py:98:20: error[no-matching-overload] No overload of bound method `__init__` matches arguments
- Found 44 diagnostics
+ Found 42 diagnostics

pydantic (https://github.com/pydantic/pydantic)
- pydantic/fields.py:943:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
+ pydantic/fields.py:943:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
- pydantic/fields.py:983:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
+ pydantic/fields.py:983:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
- pydantic/fields.py:1026:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
+ pydantic/fields.py:1026:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
- pydantic/fields.py:1066:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
+ pydantic/fields.py:1066:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
- pydantic/fields.py:1109:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
+ pydantic/fields.py:1109:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
- pydantic/fields.py:1148:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
+ pydantic/fields.py:1148:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
- pydantic/fields.py:1188:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
+ pydantic/fields.py:1188:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
- pydantic/fields.py:1567:13: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`, found `Top[dict[Unknown, Unknown]] | (((dict[str, Divergent], /) -> None) & ~Top[dict[Unknown, Unknown]]) | None`
+ pydantic/fields.py:1567:13: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`, found `Top[dict[Unknown, Unknown]] | (((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) & ~Top[dict[Unknown, Unknown]]) | None`

No memory usage changes detected ✅

@MichaReiser MichaReiser changed the title [ruff] new module for parsing ranged suppressions new module for parsing ranged suppressions Nov 26, 2025
@amyreese amyreese force-pushed the suppression-parsing branch from ce14617 to 607325e Compare December 1, 2025 21:17
@amyreese amyreese requested a review from MichaReiser December 2, 2025 00:04
@amyreese
Copy link
Contributor Author

amyreese commented Dec 2, 2025

Updated with most of the suggestions, including use of &str, better testing snapshots, etc. Performance work to bridge comment ranges and tokens was left for a future PR. I think this should be ready to land assuming I haven't introduced other nits.

@amyreese amyreese force-pushed the suppression-parsing branch from a09899f to ea5b48d Compare December 2, 2025 00:39
Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

Thanks, this looks good.

I suggest keeping disable comments without an explicit comment as valid suppressions and instead creating a new ruff dedicated lint rule that warns about this. This way, it gives users a way to opt-out from this behavior.

// for each pending comment, search for matching comments at the same indentation level,
// generate range suppressions for any matches, and then discard and unmatched comments
// from the outgoing indentation block
while comment_index < self.pending.len() {
Copy link
Member

Choose a reason for hiding this comment

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

I still think that this might be a nice simplification but up to you

@amyreese amyreese force-pushed the suppression-parsing branch from ea5b48d to 288d431 Compare December 2, 2025 22:46
@amyreese amyreese merged commit abaa49f into main Dec 2, 2025
41 checks passed
@amyreese amyreese deleted the suppression-parsing branch December 2, 2025 23:40
@amyreese amyreese linked an issue Dec 2, 2025 that may be closed by this pull request
2 tasks
dcreager added a commit that referenced this pull request Dec 3, 2025
* origin/main:
  new module for parsing ranged suppressions (#21441)
  [ty] `type[T]` is assignable to an inferable typevar (#21766)
  Fix syntax error false positives for `await` outside functions (#21763)
  [ty] Improve diagnostics for unsupported comparison operations (#21737)
dcreager added a commit that referenced this pull request Dec 3, 2025
* origin/main:
  [ty] Improve `@override`, `@final` and Liskov checks in cases where there are multiple reachable definitions (#21767)
  [ty] Extend `invalid-explicit-override` to also cover properties decorated with `@override` that do not override anything (#21756)
  [ty] Enable LRU collection for parsed module (#21749)
  [ty] Support typevar-specialized dynamic types in generic type aliases (#21730)
  Add token based `parenthesized_ranges` implementation (#21738)
  [ty] Default-specialization of generic type aliases (#21765)
  [ty] Suppress false positives when `dataclasses.dataclass(...)(cls)` is called imperatively (#21729)
  [syntax-error] Default type parameter followed by non-default type parameter (#21657)
  new module for parsing ranged suppressions (#21441)
  [ty] `type[T]` is assignable to an inferable typevar (#21766)
  Fix syntax error false positives for `await` outside functions (#21763)
  [ty] Improve diagnostics for unsupported comparison operations (#21737)
  Move `Token`, `TokenKind` and `Tokens` to `ruff-python-ast` (#21760)
  [ty] Don't confuse multiple occurrences of `typing.Self` when binding bound methods (#21754)
  Use our org-wide Renovate preset (#21759)
  Delete `my-script.py` (#21751)
  [ty] Move `all_members`, and related types/routines, out of `ide_support.rs` (#21695)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal An internal refactor or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants