2783 Commits

Author SHA1 Message Date
Brent Westbrook
fd9f87d02d Bump 0.14.13 (#22604) 2026-01-15 12:53:20 -05:00
Brent Westbrook
f9dd973b13 Bump 0.14.12 (#22602) 2026-01-15 10:41:03 -05:00
Илья Любавский
5d99ef9d95 [flake8-blind-except] Allow more logging methods (BLE001) (#22057)
## Summary
Fix issue #21889 by checking that the logging method is one of the
``debug``, ``info``, ``warning``, ``error``, ``exception``,
``critical``, ``log`` methods that support ``exc_info`` passing. Also
fixed the behavior in which ``exc_info`` was considered passed only when
it was equal to the literal ``True``, now the ``Truthiness`` of the
expression is checked (we will leave additional checks to type checkers)

## Test Plan
Additional snapshot tests have been added for all logging functions, as
well as tests in which an exception object is passed as ``exc_info``

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2026-01-14 21:18:13 +00:00
liam
1bd5ff77d0 [ruff] Respect lint.pydocstyle.property-decorators in RUF066 (#22515)
Resolves #22216.

This diff makes `RUF066` (`property-without-return`) respect the
`lint.pydocstyle.property-decorators` setting. `RUF066` is now
consistent with other rules that check for property methods like
`PLR0206`, `D401`, `PLR6301`, and `DOC201`
2026-01-14 16:02:24 -05:00
Bhuminjay Soni
d9028a098b [isort] Insert imports in alphabetical order (I002) (#22493)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
This PR fixes #20811 , current approach reverses the order in `BtreeSet`
however as pointed in
https://github.com/astral-sh/ruff/issues/20811#issuecomment-3398958832
here we cannot use I`IndexSet` to preserve config order since Settings
derives `CacheKey` which isn't implemented for `IndexSet`, another
approach to preserve the original order might be to use `Vec` however
lookup time complexity might get affected as a result.

<!-- How was it tested? -->
I have tested it locally its working as expected ,
<img width="2200" height="1071" alt="image"
src="https://github.com/user-attachments/assets/7d97b488-1552-4a42-9d90-92acf55ec493"
/>

---------

Signed-off-by: Bhuminjay <bhuminjaysoni@gmail.com>
2026-01-13 21:21:18 +00:00
Harutaka Kawamura
c98ea1bc24 [flake8-pytest-style] Add check parameter example to PT017 docs (#22546)
## Summary

- Adds an alternative example to the PT017 (`pytest-assert-in-except`)
rule documentation showing pytest's `check` parameter for validating
exceptions, available since pytest 8.4.0

Closes #22529

## Test plan

Documentation-only change. Verified with `uvx prek run -a`.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 15:45:53 -05:00
GiGaGon
9a2990b2a1 [ruff] Make example error out-of-the-box (RUF103) (#22558)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Part of #18972

This PR makes [invalid-suppression-comment
(RUF103)](https://docs.astral.sh/ruff/rules/invalid-suppression-comment/#invalid-suppression-comment-ruf103)'s
example error out-of-the-box.

[Old example](https://play.ruff.rs/3ff757f3-04ae-4d27-986d-49972338fa24)
```py
ruff: disable # missing codes
```

[New example](https://play.ruff.rs/4a9970c4-3b33-4533-8ffa-f15d481b1e6f)
```py
# ruff: disable # missing codes
```

## Test Plan

<!-- How was it tested? -->

N/A, no functionality/tests affected
2026-01-13 15:06:54 -05:00
Manuel Jacob
cb31883c5f Correct comment about public functions starting with an underscore. (#22550) 2026-01-13 14:04:19 +00:00
Jason K Hall
f40c578ffb [ruff] document RUF100 trailing comment fix behavior (#22479)
## Summary
Ruff's `--fix` for `RUF100` can inadvertently remove trailing comments
(e.g., `pylint` or `mypy` suppressions) by interpreting them as
descriptions. This PR adds a "Conflict with other linters" section to
the rule documentation to clarify this behavior and provide the
double-hash (`# noqa # pylint`) workaround.

## Fixes
Fixes #20762
2026-01-09 09:01:09 -08:00
Dylan
c920cf8cdb Bump 0.14.11 (#22462) 2026-01-08 12:51:47 -06:00
Amethyst Reese
805503c19a [ruff] Improve fix title for RUF102 invalid rule code (#22100)
## Summary

Updates the fix title for RUF102 to either specify which rule code to
remove, or clarify
that the entire suppression comment should be removed.

## Test Plan

Updated test snapshots.
2026-01-07 17:23:18 -08:00
Jelle Zijlstra
c02d164357 Check required-version before parsing rules (#22410)
Co-authored-by: Micha Reiser <micha@reiser.io>
2026-01-07 17:29:27 +00:00
Jason K Hall
3b61da0da3 Allow Python 3.15 as valid target-version value in preview (#22419) 2026-01-07 09:38:36 +01:00
Amethyst Reese
21c5cfe236 Consolidate diagnostics for matched disable/enable suppression comments (#22099)
## Summary

Combines diagnostics for matched suppression comments, so that ranges
and autofixes for both
the `#ruff:disable` and `#ruff:enable` comments will be reported as a
single diagnostic.

## Test Plan

Snapshot changes, added new snapshot for full output from preview mode
rather than just a diff.

Issue #3711
2026-01-06 18:42:51 -08:00
Brent Westbrook
a10e42294b [pylint] Demote PLW1510 fix to display-only (#22318)
Summary
--

Closes #17091. `PLW1510` checks for `subprocess.run` calls without a
`check`
keyword argument and previously had a safe fix to add `check=False`.
That's the
default value, so technically it preserved the code's behavior, but as
discussed
in #17091 and #17087, Ruff can't actually know what the author intended.

I don't think it hurts to keep this as a display-only fix instead of
removing it
entirely, but it definitely shouldn't be safe at the very least.

Test Plan
--

Existing tests
2026-01-05 19:36:16 -05:00
Amethyst Reese
12a4ca003f [flake8_print] better suggestion for basicConfig in T201 docs (#22101)
`logging.basicConfig` should not be called at a global module scope,
as that produces a race condition to configure logging based on which
module gets imported first.  Logging should instead be initialized
in an entrypoint to the program, either in a `main()` or in the
typical `if __name__ == "__main__"` block.
2026-01-05 11:42:47 -08:00
Jack O'Connor
4712503c6d [ty] cargo insta test --force-update-snapshots (#22313)
Snapshot tests recently started reporting this warning:

> Snapshot test passes but the existing value is in a legacy format.
> Please run cargo insta test --force-update-snapshots to update to a
> newer format.

This PR is the result of that forced update.

One file (crates/ruff_db/src/diagnostic/render/full.rs) seems to get
corrupted, because it contains strings with unprintable characters that
trigger some bug in cargo-insta. I've manually reverted that file, and
also manually reverted the `input_file:` lines, which we like.
2026-01-05 07:55:47 -08:00
Jason K Hall
24580e2ee8 flake8-simplify: avoid unnecessary builtins import for SIM105 (#22358) 2026-01-05 10:58:46 +01:00
Alex Waygood
d0f841bff2 Add help: subdiagnostics for several Ruff rules that can sometimes appear to disagree with ty (#22331) 2026-01-02 22:10:39 +00:00
Matthew Mckee
a2e0ff57c3 Run cargo sort (#22310) 2026-01-02 19:58:15 +00:00
Nikolas Hearp
0804030ee9 [pylint] Ignore identical members (PLR1714) (#22220)
## Summary

This PR closes #21692. `PLR1714` will no longer flag if all members are
identical. I iterate through the equality comparisons and if they are
all equal the rule does not flag.

## Test Plan

Additional tests were added with identical members.
2026-01-02 12:56:17 -05:00
Brent Westbrook
15aa74206e [pylint] Improve diagnostic range for PLC0206 (#22312)
Summary
--

This PR fixes #14900 by:

- Restricting the diagnostic range from the whole `for` loop to only the
`target in iter` part
- Adding secondary annotations to each use of the `dict[key]` accesses
- Adding a `fix_title` suggesting to use `for key in dict.items()`

I thought this approach sounded slightly nicer than the alternative of
renaming the rule to focus on each indexing operation mentioned in
https://github.com/astral-sh/ruff/issues/14900#issuecomment-2543923625,
but I don't feel too strongly. This was easy to implement with our new
diagnostic infrastructure too.

This produces an example annotation like this:

```
PLC0206 Extracting value from dictionary without calling `.items()`
  --> dict_index_missing_items.py:59:5
   |
58 | # A case with multiple uses of the value to show off the secondary annotations
59 | for instrument in ORCHESTRA:
   |     ^^^^^^^^^^^^^^^^^^^^^^^
60 |     data = json.dumps(
61 |         {
62 |             "instrument": instrument,
63 |             "section": ORCHESTRA[instrument],
   |                        ---------------------
64 |         }
65 |     )
66 |
67 |     print(f"saving data for {instrument} in {ORCHESTRA[instrument]}")
   |                                              ---------------------
68 |
69 |     with open(f"{instrument}/{ORCHESTRA[instrument]}.txt", "w") as f:
   |                               ---------------------
70 |         f.write(data)
   |
help: Use `for instrument, value in ORCHESTRA.items()` instead
```

which I think is a big improvement over:

```
PLC0206 Extracting value from dictionary without calling `.items()`
  --> dict_index_missing_items.py:59:1
   |
58 |   # A case with multiple uses of the value to show off the secondary annotations
59 | / for instrument in ORCHESTRA:
60 | |     data = json.dumps(
61 | |         {
62 | |             "instrument": instrument,
63 | |             "section": ORCHESTRA[instrument],
64 | |         }
65 | |     )
66 | |
67 | |     print(f"saving data for {instrument} in {ORCHESTRA[instrument]}")
68 | |
69 | |     with open(f"{instrument}/{ORCHESTRA[instrument]}.txt", "w") as f:
70 | |         f.write(data)
   | |_____________________^
   |
```

The secondary annotation feels a bit bare without a message, but I
thought it
might be too busy to include one. Something like `value extracted here`
or
`indexed here` might work if we do want to include a brief message.

To avoid collecting a `Vec` of annotation ranges, I added a `&Checker`
to the
rule's visitor to emit diagnostics as we go instead of at the end.

Test Plan
--

Existing tests, plus a new case showing off multiple secondary
annotations
2025-12-31 13:54:58 -05:00
ValdonVitijaa
77c2f4c6cb [flake8-unused-arguments] Mark **kwargs in TypeVar as used (ARG001) (#22214)
## Summary

Fixes false positive in ARG001 when `**kwargs` is passed to
`typing.TypeVar`

Closes #22178

When `**kwargs` is used in a `typing.TypeVar` call, the checker was not
recognizing it as a usage, leading to false positive "unused function
argument" warnings.

### Root Cause

In the AST, keyword arguments are represented by the `Keyword` struct
with an `arg` field of type `Option<Identifier>`:
- Named keywords like `bound=int` have `arg = Some("bound")`
- Dictionary unpacking like `**kwargs` has `arg = None`

The existing code only handled the `Some(id)` case, never visiting the
expression when `arg` was `None`, so `**kwargs` was never marked as
used.

### Changes

Added an `else` branch to handle `**kwargs` unpacking by calling
`visit_non_type_definition(value)` when `arg` is `None`. This ensures
the `kwargs` variable is properly visited and marked as used by the
semantic model.

## Test Plan

Tested with the following code:

```python
import typing

def f(
    *args: object,
    default: object = None,
    **kwargs: object,
) -> None:
    typing.TypeVar(*args, **kwargs)
```

Before :

`ARG001 Unused function argument: kwargs
`

After : 

`All checks passed!`

Run the example with the following command(from the root of ruff and
please change the path to the module that contains the code example):

`cargo run -p ruff -- check /path/to/file.py --isolated --select=ARG
--no-cache`
2025-12-31 11:07:56 -05:00
Kevin Yang
b2b9d91859 [airflow] Passing positional argument into airflow.lineage.hook.HookLineageCollector.create_asset is not allowed (AIR303) (#22046)
## Summary

This is a follow up PR to https://github.com/astral-sh/ruff/pull/21096

The new code AIR303 is added for checking function signature change in
Airflow 3.0. The new rule added to AIR303 will check if positional
argument is passed into
`airflow.lineage.hook.HookLineageCollector.create_asset`. Since this
method is updated to accept only keywords argument, passing positional
argument into it is not allowed, and will raise an error. The test is
done by checking whether positional argument with 0 index can be found.

## Test Plan

A new test file is added to the fixtures for the code AIR303. Snapshot
test is updated accordingly.

<img width="1444" height="513" alt="Screenshot from 2025-12-17 20-54-48"
src="https://github.com/user-attachments/assets/bc235195-e986-4743-9bf7-bba65805fb87"
/>

<img width="981" height="433" alt="Screenshot from 2025-12-17 21-34-29"
src="https://github.com/user-attachments/assets/492db71f-58f2-40ba-ad2f-f74852fa5a6b"
/>
2025-12-30 11:39:08 -05:00
Brent Westbrook
c483b59ddd [ruff] Add non-empty-init-module (RUF067) (#22143)
Summary
--

This PR adds a new rule, `non-empty-init-module`, which restricts the
kind of
code that can be included in an `__init__.py` file. By default,
docstrings,
imports, and assignments to `__all__` are allowed. When the new
configuration
option `lint.ruff.strictly-empty-init-modules` is enabled, no code at
all is
allowed.

This closes #9848, where these two variants correspond to different
rules in the

[`flake8-empty-init-modules`](https://github.com/samueljsb/flake8-empty-init-modules/)
linter. The upstream rules are EIM001, which bans all code, and EIM002,
which
bans non-import/docstring/`__all__` code. Since we discussed folding
these into
one rule on [Discord], I just added the rule to the `RUF` group instead
of
adding a new `EIM` plugin.

I'm not really sure we need to flag docstrings even when the strict
setting is
enabled, but I just followed upstream for now. Similarly, as I noted in
a TODO
comment, we could also allow more statements involving `__all__`, such
as
`__all__.append(...)` or `__all__.extend(...)`. The current version only
allows
assignments, like upstream, as well as annotated and augmented
assignments,
unlike upstream.

I think when we discussed this previously, we considered flagging the
module
itself as containing code, but for now I followed the upstream
implementation of
flagging each statement in the module that breaks the rule (actually the
upstream linter flags each _line_, including comments). This will
obviously be a
bit noisier, emitting many diagnostics for the same module. But this
also seems
preferable because it flags every statement that needs to be fixed up
front
instead of only emitting one diagnostic for the whole file that persists
as you
keep removing more lines. It was also easy to implement in
`analyze::statement`
without a separate visitor.

The first commit adds the rule and baseline tests, the second commit
adds the
option and a diff test showing the additional diagnostics when the
setting is
enabled.

I noticed a small (~2%) performance regression on our largest benchmark,
so I also added a cached `Checker::in_init_module` field and method
instead of the `Checker::path` method. This was almost the only reason
for the `Checker::path` field at all, but there's one remaining
reference in a `warn_user!`
[call](https://github.com/astral-sh/ruff/blob/main/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs#L188).

Test Plan
--

New tests adapted from the upstream linter

## Ecosystem Report

I've spot-checked the ecosystem report, and the results look "correct."
This is obviously a very noisy rule if you do include code in
`__init__.py` files. We could make it less noisy by adding more
exceptions (e.g. allowing `if TYPE_CHECKING` blocks, allowing
`__getattr__` functions, allowing imports from `importlib` assignments),
but I'm sort of inclined just to start simple and see what users need.

[Discord]:
https://discord.com/channels/1039017663004942429/1082324250112823306/1440086001035771985

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-30 11:32:10 -05:00
Brent Westbrook
2ada8b6634 Document options for more rules (#22295)
Summary
--

This is a follow up to #22198 documenting more rule options I found
while going
through all of our rules.

The second commit renames the internal
`flake8_gettext::Settings::functions_names` field to `function_names` to
match
the external configuration option. I guess this is technically breaking
because
it's exposed to users via `--show-settings`, but I don't think we
consider that
part of our stable API. I can definitely revert that if needed, though.

The other changes are just like #22198, adding new `## Options` sections
to
rules to document the settings they use. I missed these in the previous
PR
because they were used outside the rule implementations themselves. Most
of
these settings are checked where the rules' implementation functions are
called
instead.

Oh, the last commit also updates the removal date for
`typing.ByteString`, which
got pushed back in the 3.14 release. I snuck that in today since I never
opened
this PR last week.

I also fixed one reference link in RUF041.

Test Plan
--

Docs checks in CI
2025-12-30 08:44:11 -05:00
renovate[bot]
a1c3f16358 Update pre-commit dependencies (#22281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-29 17:11:28 +00:00
Harutaka Kawamura
6730350bbd [refurb] Mark FURB192 fix as always unsafe (#22210)
## Summary

Close #22204

## Test Plan

Existing checks
2025-12-29 09:17:33 -05:00
Brent Westbrook
95a532f9fd [pylint] Restore the fix safety docs for PLW0133 (#22211)
Summary
--

Noticed while responding to #22201 that the last sentence here just ends
abruptly. It turns out that I missed this change when reviewing #21382.

Test Plan
--

CI
2025-12-26 11:45:15 -05:00
Brent Westbrook
c842de5c4c Document the options used by more rules (#22198)
Summary
--

While analyzing our rules, I wanted to know which of them use
configuration options but noticed that some of them were not documented
(or at least not documented in a separate `## Options` section).

I had Claude generate an initial list of candidate rules, but it
contained a lot of false positives that I filtered out, and I ended up
adding all of these sections myself. I'm not claiming that the options
lists are exhaustive (as in the rules may use additional options beyond
what I found), but this will at least help with my goal of determining
whether or not a rule is configurable at all and also hopefully be
helpful in general.

I mostly just tacked on an `## Options` section without any commentary,
but I added a couple lines of explanation when I felt that the meaning
of the options wasn't obvious from the context.

I also noticed a bit of variation in the `flake8-simplify` rules from
doing this. Some of them offer a diagnostic but no fix depending on the
resulting line length of the suggestion, while others offer neither. I'm
not sure we need to do anything different here, but it seemed worth
mentioning.

Test Plan
--

Docs tests to make sure the links are right
2025-12-26 11:24:49 -05:00
Brent Westbrook
5e7fc9a4e1 Render the entire diagnostic message in all output formats (#22164)
Summary
--

This PR fixes https://github.com/astral-sh/ty/issues/2186 by replacing
uses of
`Diagnostic::body` with [`Diagnostic::concise_message`][d]. The initial
report
was only about ty's GitHub and GitLab output formats, but I think it
makes sense
to prefer `concise_message` in the other output formats too. Ruff
currently only
sets the primary message on its diagnostics, which is why this has no
effect on
Ruff, and ty currently only supports the GitHub and GitLab formats that
used
`body`, but removing `body` should help to avoid this problem as Ruff
starts to
use more diagnostic features or ty gets new output formats.

Test Plan
--

Updated existing GitLab and GitHub CLI tests to have `reveal_type`
diagnostics

[d]:
https://github.com/astral-sh/ruff/blob/395bf106ab/crates/ruff_db/src/diagnostic/mod.rs#L185
[t]:
https://github.com/astral-sh/ruff/blob/395bf106ab/crates/ruff/tests/cli/lint.rs#L3509
2025-12-24 10:28:24 -05:00
Chris Bachhuber
b342f60b40 Update T201 suggestion to not use root logger to satisfy LOG015 (#22059)
## Summary

Currently, the proposed fix for https://docs.astral.sh/ruff/rules/print/
violates https://docs.astral.sh/ruff/rules/root-logger-call/. Thus,
let's change the proposal to make LOG015 happy as well.

## Test Plan

Test manually in a project that has both T201 and LOG015 enabled and run
them over the previous and proposed code. Is there continuous testing of
the code snippets from the docs?
2025-12-19 11:08:12 -08:00
Amethyst Reese
b7fbd986bc [ruff] fix preview-since values for RUF103 and RUF104 (#22061)
Missed including this in the follow-up on #21908
2025-12-18 13:18:04 -08:00
Amethyst Reese
3d334a313e Report diagnostics for invalid/unmatched range suppression comments (#21908)
## Summary

- Adds new RUF103 and RUF104 diagnostics for invalid and unmatched
suppression comments
- Reports RUF100 for any unused range suppression
- Reports RUF102 for range suppression comment with invalid rule codes
- Reports RUF103 for range suppression comment with invalid suppression syntax
- Reports RUF104 diagnostics for any unmatched range suppression comment (disable w/o enable)


## Test Plan

Updated snapshots from test cases with unmatched suppression comments

Issue #3711
Fixes #21878
Fixes #21875
2025-12-18 12:58:58 -08:00
Dylan
45bbb4cbff Bump 0.14.10 (#22058) 2025-12-18 13:08:17 -06:00
mahiro
10748b2fdb [flake8-pytest-style] Allow match and check keyword arguments without an expected exception type (PT010) (#21964)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Updates PT010(`pytest-raises-without-exception`) to recognize `match`
and `check` keyword arguments as valid alternatives to specifying an
exception class.

As of pytest 8.4.0, `pytest.raises()` can be called with only `match` or
`check` keyword arguments without an expected exception.

Fixes #18653

## Test Plan

<!-- How was it tested? -->

- Added test cases for `match`-only, `check`-only, and both arguments.
- `cargo test -p ruff_linter -- "pytestraiseswithoutexception"` passes
2025-12-18 10:42:06 -05:00
Aria Desires
8d32ad1cab [ty] Add support for attribute docstrings (#22036)
## Summary

I should have factored this better but this includes a drive-by move of
find_node to ruff_python_ast so ty_python_semantic can use it too.

* Fixes https://github.com/astral-sh/ty/issues/2017 

## Test Plan

Snapshots galore
2025-12-18 12:18:20 +00:00
Amethyst Reese
9cc132f098 [eradicate] ignore ruff:disable and ruff:enable comments in ERA001 (#22038)
## Summary

Don't flag `# ruff: disable` or `# ruff: enable` comments as
commented-out code.

## Test Plan

New test cases.

Issue #3711
2025-12-17 17:04:49 -08:00
Shantanu
cf8d2e35a8 New rule to prevent implicit string concatenation in collections (#21972)
This is a common footgun, see the example in
https://github.com/astral-sh/ruff/issues/13014#issuecomment-3411496519

Fixes #13014 , fixes #13031
2025-12-17 17:37:01 -05:00
Brent Westbrook
0290f5dc3b [flake8-bandit] Fix broken link (S704) (#22039)
Summary
--

While going through all the rules I noticed a broken link in the S704
docs. (I then got really confused because it appeared to be correct in
the _other_ `unsafe_markup_use.rs` file for the removed RUF version of
the rule).

Test Plan
--

[Before](https://docs.astral.sh/ruff/rules/unsafe-markup-use/):

<img width="537" height="171" alt="image"
src="https://github.com/user-attachments/assets/01007cab-6673-48e5-b3a5-6006bc78a027"
/>


After:

<img width="451" height="189" alt="image"
src="https://github.com/user-attachments/assets/4e5d0e0d-76be-4f66-b747-e209f11ab11a"
/>

I also did at least a cursory grep for other cases with escaped link
brackets (`\[`) and only turned up this rule on main.
2025-12-17 17:35:04 -05:00
chiri
883701ae88 [flake8-use-pathlib] Make fixes unsafe when types change in compound statements (PTH104, PTH105, PTH109, PTH115) (#22009)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/21794

## Test Plan

`cargo nextest run flake8_use_pathlib`
2025-12-17 12:31:27 -05:00
Bhuminjay Soni
421f88bb32 [refurb] Extend support for Path.open (FURB101, FURB103) (#21080)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
This PR fixes https://github.com/astral-sh/ruff/issues/18409

## Test Plan

<!-- How was it tested? -->
I have added tests in FURB103.

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <bhuminjaysoni@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-12-17 09:18:13 -05:00
Bhuminjay Soni
52849a5e68 [syntax-errors] Annotated name cannot be global (#20868)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
This PR implements a new semantic syntax error where annotated name
can't be global
example
```
x: int = 1

def f():
    global x
    x: str = "foo"  # SyntaxError: annotated name 'x' can't be global
 ```

## Test Plan

<!-- How was it tested? -->
I have written tests as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <bhuminjaysoni@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-12-17 08:39:47 -05:00
Phong Do
b0bc990cbf [pyupgrade] Fix parsing named Unicode escape sequences (UP032) (#21901)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/19771

Fixes incorrect parsing of Unicode named escape sequences like `Hey
\N{snowman}` in `FormatString`, which were being incorrectly split into
separate literal and field parts instead of being treated as a single
literal unit.

## Problem

The `FormatString` parser incorrectly handles Unicode named escape
sequences:
- **Current**: `Hey \N{snowman}` is parsed into 2 parts `Literal("Hey
\N")` & `Field("snowman")`
- **Expected**: `Hey \N{snowman}` should be parsed into 1 part
`Literal("Hey \N{snowman}")`

This affects f-string conversion rules when fixing `UP032` that rely on
proper format string parsing.

## Solution

I modified `parse_literal` to detect and handle Unicode named escape
sequences before parsing single characters:
- Introduced a flag to track when a backslash is "available" to escape
something.
- When the flag is `true`, and the text starts with `N{`, try to parse
the complete Unicode escape sequence as one unit, and set the flag to
`false` after parsing successfully.
- Set the flag to `false` when the backslash is already consumed.

## Manual Verification

`"\N{angle}AOB = {angle}°".format(angle=180)` 

**Result**

```bash
 def foo():
-    "\N{angle}AOB = {angle}°".format(angle=180)
+    f"\N{angle}AOB = {180}°"

Would fix 1 error.
```

`"\N{snowman} {snowman}".format(snowman=1)`

**Result**
```bash
 def foo():
-    "\N{snowman} {snowman}".format(snowman=1)
+    f"\N{snowman} {1}"

Would fix 1 error.
```

`"\\N{snowman} {snowman}".format(snowman=1)`

**Result**
```bash
 def foo():
-    "\\N{snowman} {snowman}".format(snowman=1)
+    f"\\N{1} {1}"

Would fix 1 error.
```

## Test Plan

- Added test cases (happy case, invalid case, edge case) for
`FormatString` when parsing Unicode escape sequence.
- Updated snapshots.
2025-12-16 16:33:39 -05:00
Micha Reiser
d08e414179 Update MSRV to 1.90 (#21987) 2025-12-15 14:29:11 +01:00
Amethyst Reese
3f63ea4b50 Prepare 0.14.9 release (#21927)
- **Changelog and docs**
- **metadata**
2025-12-11 13:17:52 -08:00
Amethyst Reese
7a578ce833 Ignore ruff:isort like ruff:noqa in new suppressions (#21922)
## Summary

Ignores `#ruff:isort` when parsing suppressions similar to `#ruff:noqa`.
Should clear up ecosystem issues in #21908

## Test Plan

cargo tests
2025-12-11 11:04:28 -08:00
Denys Zhak
27912d46b1 Remove BackwardsTokenizer based parenthesized_range references in ruff_linter (#21836)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-12-11 13:04:57 +01:00
Loïc Riegel
9490fbf1e1 [pylint] Detect subclasses of builtin exceptions (PLW0133) (#21382)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
Closes #17347

Goal is to detect the useless exception statement not just for builtin
exceptions but also custom (user defined) ones.

## Test Plan

<!-- How was it tested? -->
I added test cases in the rule fixture and updated the insta snapshot.
Note that I first moved up a test case case which was at the bottom to
the correct "violation category".
I wasn't sure if I should create new test cases or just insert inside
those tests. I know that ideally each test case should test only one
thing, but here, duplicating twice 12 test cases seemed very verbose,
and actually less maintainable in the future. The drawback is that the
diff in the snapshot is hard to review, sorry. But you can see that the
snapshot gives 38 diagnostics, which is what we expect.

Alternatively, I also created this file for manual testing.
```py
# tmp/test_error.py

class MyException(Exception):
    ...
class MyBaseException(BaseException):
    ...
class MyValueError(ValueError):
    ...
class MyExceptionCustom(Exception):
    ...
class MyBaseExceptionCustom(BaseException):
    ...
class MyValueErrorCustom(ValueError):
    ...
class MyDeprecationWarning(DeprecationWarning):
    ...
class MyDeprecationWarningCustom(MyDeprecationWarning):
    ...
class MyExceptionGroup(ExceptionGroup):
    ...
class MyExceptionGroupCustom(MyExceptionGroup):
    ...
class MyBaseExceptionGroup(ExceptionGroup):
    ...
class MyBaseExceptionGroupCustom(MyBaseExceptionGroup):
    ...


def foo():
    Exception("...")
    BaseException("...")
    ValueError("...")
    RuntimeError("...")
    DeprecationWarning("...")
    GeneratorExit("...")
    SystemExit("...")
    ExceptionGroup("eg", [ValueError(1), TypeError(2), OSError(3), OSError(4)])
    BaseExceptionGroup("eg", [ValueError(1), TypeError(2), OSError(3), OSError(4)])
    MyException("...")
    MyBaseException("...")
    MyValueError("...")
    MyExceptionCustom("...")
    MyBaseExceptionCustom("...")
    MyValueErrorCustom("...")
    MyDeprecationWarning("...")
    MyDeprecationWarningCustom("...")
    MyExceptionGroup("...")
    MyExceptionGroupCustom("...")
    MyBaseExceptionGroup("...")
    MyBaseExceptionGroupCustom("...")

```

and you can run this to check the PR:
```sh
target/debug/ruff check tmp/test_error.py --select PLW0133 --unsafe-fixes --diff --no-cache --isolated --target-version py310
target/debug/ruff check tmp/test_error.py --select PLW0133 --unsafe-fixes --diff --no-cache --isolated --target-version py314
```
2025-12-09 13:49:55 -05:00
Amethyst Reese
4e4d018344 New diagnostics for unused range suppressions (#21783)
Issue #3711
2025-12-09 08:30:27 -08:00