Commit Graph

6616 Commits

Author SHA1 Message Date
Micha Reiser 0073fd4945
[red-knot] Playground improvements (#17109)
## Summary

A few smaller editor improvements that felt worth pulling out of my
other feature PRs:

* Load the `Editor` lazily: This allows splitting the entire monaco
javascript into a separate async bundle, drastically reducing the size
of the `index.js`
* Fix the name of `to_range` and `text_range` to the more idiomatic js
names `toRange` and `textRange`
* Use one indexed values for `Position::line` and `Position::column`,
which is the same as monaco (reduces the need for `+1` and `-1`
operations spread all over the place)
* Preserve the editor state when navigating between tabs. This ensures
that selections are preserved even when switching between tabs.
* Stop the default handling of the `Enter` key press event when renaming
a file because it resulted in adding a newline in the editor
2025-04-01 10:04:51 +02:00
Micha Reiser b57c62e6b3
[red-knot] IDE crate (#17045)
## Summary

This PR adds a new but so far empty and unused `red_knot_ide` crate. 

This new crate's purpose is to implement IDE-specific functionality,
such as go to definition, hover, completion, etc., which are used by
both the LSP and the playground.

The crate itself doesn't depend on `lsptypes`. The idea is that the
facade crates (e.g., `red_knot_server`) convert external to internal
types.
Not only allows this to share the logic between server and playground,
it also ensures that the core functionality is easier to test because it
can be tested without needing a full LSP.



## Test Plan

`cargo build`
2025-04-01 09:36:00 +02:00
Trevor Manz 53cfaaebc4
[red-knot] Add redundant-cast error (#17100)
## Summary

Following up from earlier discussion on Discord, this PR adds logic to
flag casts as redundant when the inferred type of the expression is the
same as the target type. It should follow the semantics from
[mypy](https://github.com/python/mypy/pull/1705).

Example:

```python
def f() -> int:
    return 10

# error: [redundant-cast] "Value is already of type `int`"
cast(int, f())
```
2025-04-01 00:37:25 +00:00
Matthew Mckee 3ad123bc23
[red-knot] Narrowing on `in tuple[...]` and `in str` (#17059)
## Summary

Part of #13694

Seems there a bit more to cover regarding `in` and other types, but i
can cover them in different PRs

## Test Plan
Add `in.md` file in narrowing conditionals folder

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-31 23:38:09 +00:00
Micha Reiser a1535fbdbd
[red-knot] Change venv discovery (#17099)
## Summary

Rewrites the virtual env discovery to:

* Only use of `System` APIs, this ensures that the discovery will also
work when using a memory file system (testing or WASM)
* Don't traverse ancestor directories. We're not convinced that this is
necessary. Let's wait until someone shows us a use case where it is
needed
* Start from the project root and not from the current working
directory. This ensures that Red Knot picks up the right venv even when
using `knot --project ../other-dir`

## Test Plan

Existing tests, @ntBre tested that the `file_watching` tests no longer
pick up his virtual env in a parent directory
2025-03-31 19:39:05 +02:00
Matthew Mckee 4a6fa5fc27
[red-knot] Add assignability of function literals to callables (#17095)
## Summary

Part  of #16953

## Test Plan

Update is_assignable_to.md

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-03-31 15:42:42 +00:00
Aarni Koskela 491a51960e
[`ruff`] Support slices in `RUF005` (#17078)
## Summary

Teaches `RUF005` to also consider slices for concatenation. Other
indexing (`foo[0] + [7, 8, 9] + bar[1]`) is explicitly not considered.

```diff
 foo = [4, 5, 6]
-bar = [1, 2, 3] + foo
-slicing1 = foo[:1] + [7, 8, 9]
-slicing2 = [7, 8, 9] + bar[1:]
-slicing3 = foo[:1] + [7, 8, 9] + bar[1:]
+bar = [1, 2, 3, *foo]
+slicing1 = [*foo[:1], 7, 8, 9]
+slicing2 = [7, 8, 9, *bar[1:]]
+slicing3 = [*foo[:1], 7, 8, 9, *bar[1:]]
```

## Test Plan

Manually tested (diff above from `ruff check --diff`), snapshot updated.
2025-03-31 09:09:39 -04:00
David Peter 2d7f118f52
[red-knot] Binary operator inference: generalize code for non-instances (#17081)
## Summary

Generalize the rich-comparison fallback code for binary operator
inference. This gets rid of one `todo_type!(…)` and implements the last
remaining failing case from
https://github.com/astral-sh/ruff/issues/14200.

closes https://github.com/astral-sh/ruff/issues/14200

## Test Plan

New Markdown tests.
2025-03-31 13:01:25 +02:00
David Peter 3d1e5676fb
[red-knot] Add `ParamSpecArgs` and `ParamSpecKwargs` as `KnownClass` (#17086)
## Summary

In preparation for #17017, where we will need them to suppress new false
positives (once we understand the `ParamSpec.args`/`ParamSpec.kwargs`
properties).

## Test Plan

Tested on branch #17017
2025-03-31 10:52:23 +00:00
Wei Lee 0b1ab8fd5a
[airflow] Combine AIR302 matches (#17080)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

* Combine AIR302 matches
* Found a few errors. Will be fixed in another PR

## Test Plan

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

This PR does not change anything. The existing testing fixture should
work as it used to be
2025-03-31 12:17:18 +02:00
David Peter d9616e61b0
[red-knot] Disallow `todo_type!` without custom message (#17083)
## Summary

Disallow empty `todo_type!()`s without a custom message. They can lead
to spurious diffs in `mypy_primer` where the only thing that's changed
is the file/line information.
2025-03-31 10:49:19 +02:00
Wei Lee fa80e10aac
[airflow] fix typos in AIR302 implementation and test cases (#17082)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

* The following paths are wrong 
* `airflow.providers.amazon.auth_manager.avp.entities` should be
`airflow.providers.amazon.aws.auth_manager.avp.entities`
* `["airflow", "datasets", "manager", "dataset_manager"]` should be
fixed as `airflow.assets.manager` but not
`airflow.assets.manager.asset_manager`
* `["airflow", "datasets.manager", "DatasetManager"]` should be `
["airflow", "datasets", "manager", "DatasetManager"]` instead

## Test Plan

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

the test fixture is updated accordingly
2025-03-31 10:42:04 +02:00
Wei Lee 30a5f69913
[airflow] fix missing or wrong test cases (AIR302) (#16968)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

## Test Plan

<!-- How was it tested? -->
test fixtures have been updated accordingly
2025-03-31 10:22:45 +02:00
renovate[bot] a192d96880
Update pre-commit dependencies (#17073)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[abravalheri/validate-pyproject](https://redirect.github.com/abravalheri/validate-pyproject)
| repository | patch | `v0.24` -> `v0.24.1` |
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.11.0` -> `v0.11.2` |
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) |
repository | minor | `v1.30.2` -> `v1.31.0` |
|
[python-jsonschema/check-jsonschema](https://redirect.github.com/python-jsonschema/check-jsonschema)
| repository | minor | `0.31.3` -> `0.32.1` |
|
[woodruffw/zizmor-pre-commit](https://redirect.github.com/woodruffw/zizmor-pre-commit)
| repository | patch | `v1.5.1` -> `v1.5.2` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>abravalheri/validate-pyproject
(abravalheri/validate-pyproject)</summary>

###
[`v0.24.1`](https://redirect.github.com/abravalheri/validate-pyproject/releases/tag/v0.24.1)

[Compare
Source](https://redirect.github.com/abravalheri/validate-pyproject/compare/v0.24...v0.24.1)

#### What's Changed

- Fixed multi plugin id was read from the wrong place by
[@&#8203;henryiii](https://redirect.github.com/henryiii) in
[https://github.com/abravalheri/validate-pyproject/pull/240](https://redirect.github.com/abravalheri/validate-pyproject/pull/240)
- Implemented alternative plugin sorting,
[https://github.com/abravalheri/validate-pyproject/pull/243](https://redirect.github.com/abravalheri/validate-pyproject/pull/243)

**Full Changelog**:
https://github.com/abravalheri/validate-pyproject/compare/v0.24...v0.24.1

</details>

<details>
<summary>astral-sh/ruff-pre-commit (astral-sh/ruff-pre-commit)</summary>

###
[`v0.11.2`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.11.2)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.11.1...v0.11.2)

See: https://github.com/astral-sh/ruff/releases/tag/0.11.2

###
[`v0.11.1`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.11.1)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.1)

See: https://github.com/astral-sh/ruff/releases/tag/0.11.1

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.31.0`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.31.0)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.30.3...v1.31.0)

#### \[1.31.0] - 2025-03-28

##### Features

- Updated the dictionary with the [March
2025](https://redirect.github.com/crate-ci/typos/issues/1266) changes

###
[`v1.30.3`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.30.3)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.30.2...v1.30.3)

#### \[1.30.3] - 2025-03-24

##### Features

-   Support detecting `go.work` and `go.work.sum` files

</details>

<details>
<summary>python-jsonschema/check-jsonschema
(python-jsonschema/check-jsonschema)</summary>

###
[`v0.32.1`](https://redirect.github.com/python-jsonschema/check-jsonschema/blob/HEAD/CHANGELOG.rst#0321)

[Compare
Source](https://redirect.github.com/python-jsonschema/check-jsonschema/compare/0.32.0...0.32.1)

-   Fix the `check-meltano` hook to use `types_or`. Thanks
    :user:`edgarrmondragon`! (:pr:`543`)

###
[`v0.32.0`](https://redirect.github.com/python-jsonschema/check-jsonschema/blob/HEAD/CHANGELOG.rst#0320)

[Compare
Source](https://redirect.github.com/python-jsonschema/check-jsonschema/compare/0.31.3...0.32.0)

- Update vendored schemas: circle-ci, compose-spec, dependabot,
github-workflows,
    gitlab-ci, mergify, renovate, taskfile (2025-03-25)
- Add Meltano schema and pre-commit hook. Thanks
:user:`edgarrmondragon`! (:issue:`540`)
- Add Snapcraft schema and pre-commit hook. Thanks :user:`fabolhak`!
(:issue:`535`)

</details>

<details>
<summary>woodruffw/zizmor-pre-commit
(woodruffw/zizmor-pre-commit)</summary>

###
[`v1.5.2`](https://redirect.github.com/woodruffw/zizmor-pre-commit/releases/tag/v1.5.2)

[Compare
Source](https://redirect.github.com/woodruffw/zizmor-pre-commit/compare/v1.5.1...v1.5.2)

See: https://github.com/woodruffw/zizmor/releases/tag/v1.5.2

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-31 07:42:15 +00:00
Mike Perlov 5c1fab0661
Fix member lookup for unions & intersections ignoring policy (#17066)
## Summary

A quick fix for how union/intersection member search ins performed in
Knot.

## Test Plan

* Added a dunder method call test for Union, which exhibits the error
* Also added an intersection error, but it is not triggering currently
due to `call` logic not being fully implemented for intersections.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-03-31 09:40:47 +02:00
cake-monotone c6efa93cf7
[red-knot] Handle special case returning `NotImplemented` (#17034)
## Summary

Closes #16661

This PR includes two changes:

- `NotImplementedType` is now a member of `KnownClass`
- We skip `is_assignable_to` checks for `NotImplemented` when checking
return types

### Limitation

```py
def f(cond: bool) -> int:
    return 1 if cond else NotImplemented
```

The implementation covers cases where `NotImplemented` appears inside a
`Union`.
However, for more complex types (ex. `Intersection`) it will not worked.
In my opinion, supporting such complexity is unnecessary at this point.

## Test Plan

Two `mdtest` files were updated:

- `mdtest/function/return_type.md`
- `mdtest/type_properties/is_singleton.md`

To test `KnownClass`, run:
```bash
cargo test -p red_knot_python_semantic -- types::class::
```
2025-03-30 11:06:12 -07:00
Brent Westbrook ab1011ce70
[syntax-errors] Single starred assignment target (#17024)
Summary
--

Detects starred assignment targets outside of tuples and lists like `*a
= (1,)`.

This PR only considers assignment statements. I also checked annotated
assigment statements, but these give a separate error that we already
catch, so I think they're okay not to consider:

```pycon
>>> *a: list[int] = []
  File "<python-input-72>", line 1
    *a: list[int] = []
      ^
SyntaxError: invalid syntax
```

Fixes #13759

Test Plan
--

New inline tests, plus a new `SemanticSyntaxError` for an existing
parser test. I also removed a now-invalid case from an otherwise-valid
test fixture.

The new semantic error leads to two errors for the case below:

```python
*foo() = 42
```

but this matches [pyright] too.

[pyright]: https://pyright-play.net/?code=FQMw9mAUCUAEC8sAsAmAUEA
2025-03-29 12:35:47 -04:00
Brent Westbrook a0819f0c51
[syntax-errors] Store to or delete `__debug__` (#16984)
Summary
--

Detect setting or deleting `__debug__`. Assigning to `__debug__` was a
`SyntaxError` on the earliest version I tested (3.8). Deleting
`__debug__` was made a `SyntaxError` in [BPO 45000], which said it was
resolved in Python 3.10. However, `del __debug__` was also a runtime
error (`NameError`) when I tested in Python 3.9.6, so I thought it was
worth including 3.9 in this check.

I don't think it was ever a *good* idea to try `del __debug__`, so I
think there's also an argument for not making this version-dependent at
all. That would only simplify the implementation very slightly, though.

[BPO 45000]: https://github.com/python/cpython/issues/89163

Test Plan
--

New inline tests. This also required adding a `PythonVersion` field to
the `TestContext` that could be taken from the inline `ParseOptions` and
making the version field on the options accessible.
2025-03-29 12:07:20 -04:00
Alex Waygood 93052331b0
[red-knot] Allow `CallableTypeFromFunction` to display the signatures of callable types that are not function literals (#17047)
I found this helpful for understanding some of the stuff that was going
on in https://github.com/astral-sh/ruff/pull/17005.
2025-03-28 20:23:04 +00:00
Micha Reiser e07741e553
Add `as_group` methods to `AnyNodeRef` (#17048)
## Summary

This PR adds `as_<group>` methods to `AnyNodeRef` to e.g. convert an
`AnyNodeRef` to an `ExprRef`.

I need this for go to definition where the fallback is to test if
`AnyNodeRef` is an expression and then call `inferred_type` (listing
this mapping at every call site where we need to convert `AnyNodeRef` to
an `ExprRef` is a bit painful ;))

Split out from https://github.com/astral-sh/ruff/pull/16901

## Test Plan

`cargo test`
2025-03-28 19:42:45 +00:00
Micha Reiser 050f332771
Rename `visit_preorder` to `visit_source_order` (#17046)
## Summary

We renamed the `PreorderVisitor` to `SourceOrderVisitor` a long time ago
but it seems that we missed to rename the `visit_preorder` functions to
`visit_source_order`.
This PR renames `visit_preorder` to `visit_source_order`

## Test Plan

`cargo test`
2025-03-28 19:40:26 +00:00
Micha Reiser 6b02c39321
[red-knot] Incorporate recent ruff server improvements into red knot's LSP (#17044) 2025-03-28 18:39:18 +00:00
Eric Mark Martin 78b0b5a3ab
[red-knot] Factor out shared unpacking logic (#16595)
## Summary

This PR refactors the common logic for unpacking in assignment, for loops, and with items.

## Test Plan

Make sure existing tests pass.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-03-28 23:52:51 +05:30
Matthew Mckee 0e48940ea4
[red-knot] Discover local venv folder in cli (#16917)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes #16744 

Code from 

bbf4f830b5/crates/uv-python/src/virtualenv.rs (L124-L144)

## Test Plan

Manual testing

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-28 17:59:49 +00:00
Shunsuke Shibayama aca6254e82
[red-knot] fix eager nested scopes handling (#16916)
## Summary

From #16861, and the continuation of #16915.

This PR fixes the incorrect behavior of
`TypeInferenceBuilder::infer_name_load` in eager nested scopes.

And this PR closes #16341.

## Test Plan

New test cases are added in `annotations/deferred.md`.
2025-03-28 11:11:56 -04:00
Eric Mark Martin 64171744dc
[red-knot] support narrowing on or patterns in matches (#17030)
## Summary

Part of #13694

Narrow in or-patterns by taking the type union of the type constraints
in each disjunct pattern.

## Test Plan

Add new tests to narrow/match.md

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-28 14:27:09 +00:00
Eric Mark Martin 3acf4e716d
[red-knot] support narrowing on constants in matches (#16974)
## Summary

Part of #13694

The implementation here was suspiciously straightforward so please lmk
if I missed something

Also some drive-by changes to DRY things up a bit

## Test Plan

Add new tests to narrow/match.md

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-28 02:36:51 +00:00
Alex Waygood 992a1af4c2
[red-knot] Reduce false positives on `super()` and enum-class attribute accesses (#17004)
## Summary

This PR adds some branches so that we infer `Todo` types for attribute
access on instances of `super()` and subtypes of `type[Enum]`. It reduces
false positives in the short term until we implement full support for
these features.

## Test Plan

New mdtests added + mypy_primer report
2025-03-27 17:30:56 -04:00
Micha Reiser 4067a7e50c
[red-knot] Don't check non-python files (#17021)
## Summary

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

## Test Plan

I renamed a python file to `knot.toml` and verified that there are no
diagnostics. Renaming back the file to `*.py` brings back the
diagnostics
2025-03-27 19:45:04 +00:00
InSync 6ef522159d
Check `pyproject.toml` correctly when it is passed via stdin (#16971)
## Summary

Resolves #16950 and [a 1.5-year-old TODO
comment](8d16a5c8c9/crates/ruff/src/diagnostics.rs (L380)).

After this change, a `pyproject.toml` will be linted the same as any
Python files would when passed via stdin.

## Test Plan

Integration tests.
2025-03-27 16:01:45 +00:00
Matthew Mckee b9a7328789
[red-knot] Make every type a subtype of object (#16960)
## Summary

Mainly for partially fixing #16953

## Test Plan

Update is_subtype tests. And should maybe do these checks for many other
types (is subtype of object but object is not subtype)

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-03-27 14:24:18 +00:00
Brent Westbrook d70a3e6753
[syntax-errors] Multiple assignments in `case` pattern (#16957)
Summary
--

This PR detects multiple assignments to the same name in `case` patterns
by recursively visiting each pattern.

Test Plan
--

New inline tests.
2025-03-26 13:02:42 -04:00
Brent Westbrook 5697d21fca
[syntax-errors] Irrefutable case pattern before final case (#16905)
Summary
--

Detects irrefutable `match` cases before the final case using a modified
version
of the existing `Pattern::is_irrefutable` method from the AST crate. The
modified method helps to retrieve a more precise diagnostic range to
match what
Python 3.13 shows in the REPL.

Test Plan
--

New inline tests, as well as some updates to existing tests that had
irrefutable
patterns before the last block.
2025-03-26 12:27:16 -04:00
Wei Lee 58350ec93b
[`airflow`] refactor: remove unnecessary `Some` in `check_method`, `check_class_attribute` (`AIR302`) (#16975)
## Summary

remove unnecessary `Some`

## Test Plan

It's a refactoring change. Existing test cases won't be affected
2025-03-26 12:17:34 -04:00
Matthew Mckee aae4d0f3eb
[red-knot] A `FunctionType` can be a subtype of `Callable` (but never the other way around) (#16970)
## Summary

Partially fixes #16953

## Test Plan

Update is_subtype_of.md

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-25 22:04:34 +00:00
Micha Reiser 8d16a5c8c9
[red-knot] Use `web-time` instead of `FileTime::now` (#16967)
## Summary

`std::time::now` isn't available on `wasm32-unknown-unknown` but it is
used by `FileTime::now`.

This PR replaces the usages of `FileTime::now` with a target specific
helper function that we already had in the memory file system.
Fixes https://github.com/astral-sh/ruff/issues/16966

## Test Plan

Tested that the playground no longer crash when adding an extra-path
2025-03-25 13:03:30 +00:00
Alex Waygood 4975c2f027
[red-knot] Fix panic on cyclic `*` imports (#16958)
## Summary

Further work towards https://github.com/astral-sh/ruff/issues/14169.

We currently panic on encountering cyclic `*` imports. This is easily
fixed using fixpoint iteration.

## Test Plan

Added a test that panics on `main`, but passes with this PR
2025-03-24 18:23:02 +00:00
Dhruv Manilawala dd5b02aaa2
[red-knot] Fix gradual equivalence for callable types (#16887)
## Summary

As mentioned in
https://github.com/astral-sh/ruff/pull/16698#discussion_r2004920075,
part of #15382, this PR updates the `is_gradual_equivalent_to`
implementation between callable types to be similar to
`is_equivalent_to` and checks other attributes of parameters like name,
optionality, and parameter kind.

## Test Plan

Expand the existing test cases to consider other properties but not all
similar to how the tests are structured for subtyping and assignability.
2025-03-24 23:46:06 +05:30
Aleksei Latyshev 68ea2b8b5b
[red-knot] simplify "removing" in UnionBuilder::add (#16947)
## Summary

Simplify "removing" in UnionBuilder::add
It's now O(m) instead of O(n + m) and easier to read.

## Test Plan

cargo test (incl. mdtest)
2025-03-24 14:04:03 -04:00
Alex Waygood e87fee4b3b
[red-knot] Add initial support for `*` imports (#16923)
## Summary

This PR adds initial support for `*` imports to red-knot. The approach
is to implement a standalone query, called from semantic indexing, that
visits the module referenced by the `*` import and collects all
global-scope public names that will be imported by the `*` import. The
`SemanticIndexBuilder` then adds separate definitions for each of these
names, all keyed to the same `ast::Alias` node that represents the `*`
import.

There are many pieces of `*`-import semantics that are still yet to be
done, even with this PR:
- This PR does not attempt to implement any of the semantics to do with
`__all__`. (If a module defines `__all__`, then only the symbols
included in `__all__` are imported, _not_ all public global-scope
symbols.
- With the logic implemented in this PR as it currently stands, we
sometimes incorrectly consider a symbol bound even though it is defined
in a branch that is statically known to be dead code, e.g. (assuming the
target Python version is set to 3.11):

  ```py
  # a.py

  import sys

  if sys.version_info < (3, 10):
      class Foo: ...

  ```

  ```py
  # b.py

  from a import *

  print(Foo)  # this is unbound at runtime on 3.11,
# but we currently consider it bound with the logic in this PR
  ```

Implementing these features is important, but is for now deferred to
followup PRs.

Many thanks to @ntBre, who contributed to this PR in a pairing session
on Friday!

## Test Plan

Assertions in existing mdtests are adjusted, and several new ones are
added.
2025-03-24 17:15:58 +00:00
Alex Waygood 66d0cf2a72
[red-knot] Add more tests for `*` imports (#16955)
## Summary

This PR separates out the entirely new tests from
https://github.com/astral-sh/ruff/pull/16923 into a standalone PR. I'll
rebase https://github.com/astral-sh/ruff/pull/16923 on top of this
branch.

The reasons for separating it out are:
- It should make it clearer to see in
<https://github.com/astral-sh/ruff/pull/16923> exactly how the
functionality is changing (we can see the assertions in the tests
_change_, which isn't so obvious if the tests are entirely new)
- The diff on <https://github.com/astral-sh/ruff/pull/16923> is getting
pretty big; this should reduce the diff on that PR somewhat
- These tests seem useful in and of themselves, so even if we need to do
a wholesale revert of <https://github.com/astral-sh/ruff/pull/16923> for
whatever reason, it'll be nice to keep the tests

## Test Plan

`cargo test -p red_knot_python_semantic`
2025-03-24 16:39:16 +00:00
Alex Waygood 888a910925
[red-knot] Demote the `negation_reverses_subtype_order` test back to flaky (#16951)
Fixes #16913. See my analysis in the issue for the rationale
2025-03-24 11:37:03 -04:00
Wei Lee 581b7005dc
[airflow] refactor: combine similar case condition (AIR302) (#16944)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

combine similar case condition in AIR302 

## Test Plan

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

nothing should be changed. existing test case should already cover it
2025-03-24 15:36:33 +00:00
Brent Westbrook 2711e08eb8
[syntax-errors] Fix false positive for parenthesized tuple index (#16948)
Summary
--

Fixes #16943 by checking if the tuple is not parenthesized before
emitting an error.

Test Plan
--

New inline test based on the initial report
2025-03-24 10:34:38 -04:00
Micha Reiser f5cdf23545
[red-knot] Add settings support to playground (#16929)
## Summary

This PR extends the Red Knot playground by adding configuration support
by adding a `knot.json` file.

<img width="1679" alt="Screenshot 2025-03-23 at 21 12 16"
src="https://github.com/user-attachments/assets/81ff1588-a07a-4847-97d8-61250aa2feda"
/>
2025-03-24 01:38:48 +00:00
John Stilley 3899f7156f
Fixing more spelling errors (#16926)
## Summary

Here I fix the last English spelling errors I could find in the repo.

Again, I am trying not to touch variable/function names, or anything
that might be misspelled in the API. The goal is to make this PR safe
and easy to merge.

## Test Plan

I have run all the unit tests. Though, again, all of the changes I make
here are to docs and docstrings. I make no code changes, which I believe
should greatly mitigate the testing concerns.
2025-03-23 10:55:14 -07:00
InSync 902d86e79e
[red-knot] Do not emit `invalid-return-type` for abstract functions (#16900)
## Summary

Resolves #16895.

`abstractmethod` is now a `KnownFunction`. When a function is decorated
by `abstractmethod` or when the parent class inherits directly from
`Protocol`, `invalid-return-type` won't be emitted for that function.

## Test Plan

Markdown tests.

---------

Co-authored-by: Carl Meyer <carl@oddbird.net>
2025-03-23 17:51:10 +00:00
Daniel Wilton 9fe89ddfba
[`refurb`] Document why `UserDict`, `UserList`, `UserString` are preferred over `dict`, `list`, `str` (`FURB189`) (#16927)
## Summary

This PR addresses docs issue
https://github.com/astral-sh/ruff/issues/14328.
2025-03-23 13:24:39 -04:00
Matthew Mckee 08a0995108
[red-knot] Disambiguate display for intersection types (#16914)
## Summary

Fixes #16912 

Create a new type `DisplayMaybeParenthesizedType` that is now used in
Union and Intersection display

## Test Plan

Update callable annotations
2025-03-23 07:18:30 -07:00
InSync 2d892bc9f7
Fix typos (#16908)
## Summary

The noun is spelled "descend<strong><em>a</em></strong>nt" and the
adjective "descend<strong><em>e</em></strong>nt".

## Test Plan

[From the English
Wiktionary](https://en.wiktionary.org/wiki/descendent#Usage_notes):

> The adjective, "descending from a biological ancestor", may be spelt
either with an <i>[a](https://en.wiktionary.org/wiki/-ant)</i> or with
an <i>[e](https://en.wiktionary.org/wiki/-ent)</i> in the final syllable
(see [descendant](https://en.wiktionary.org/wiki/descendant)). However,
the noun <i>descendant</i>, "one who is the progeny of someone", may be
spelt only with an <i>[a](https://en.wiktionary.org/wiki/-ant)</i>.
Compare also
<i>[dependent](https://en.wiktionary.org/wiki/dependent#English)</i> and
<i>[dependant](https://en.wiktionary.org/wiki/dependant#English)</i>.
2025-03-23 07:15:56 -07:00
Shunsuke Shibayama ee51c2a389
[red-knot] fix ordering of `ClassDef` semantic index building (#16915)
## Summary

From #16861

This PR fixes the incorrect `ClassDef` handling of
`SemanticIndexBuilder::visit_stmt`, which fixes some of the incorrect
behavior of referencing the class itself in the class scope (a complete
fix requires a different fix, which will be done in the another PR).

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-23 13:23:12 +00:00
Vasco Schiavo bb07ccd783
[`pylint`] Fix typo in documentation of PLC1802 (#16920) 2025-03-23 06:17:33 -05:00
John Stilley c35f2bfe32
Fixing various spelling errors (#16924)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

This is a cleanup PR. I am fixing various English language spelling
errors. This is mostly in docs and docstrings.

## Test Plan

The usual CI tests were run. I tried to build the docs (though I had
some troubles there). The testing needs here are, I trust, very low
impact. (Though I would happily test more.)
2025-03-23 08:08:40 +00:00
Micha Reiser 7fb765d9b6
[red-knot] Log sys-prefix origin for easier debugging (#16921)
## Summary

Log the origin of the sys path prefix. This should help with debugging
if someone doesn't understand
why Red Knot picks up a certain venv.

## Test Plan

Ran the CLI and tested that it logs the origin
2025-03-23 08:06:04 +00:00
Dhruv Manilawala 0360c6b219
[red-knot] Support calling a `typing.Callable` (#16888)
## Summary

Part of #15382, this PR adds support for calling a variable that's
annotated with `typing.Callable`.

## Test Plan

Add test cases in a new `call/annotation.md` file.
2025-03-23 02:39:33 +05:30
Dhruv Manilawala 1cffb323bc
[red-knot] Check assignability for two callable types (#16845)
## Summary

Part of #15382

This PR adds support for checking the assignability of two general
callable types.

This is built on top of #16804 by including the gradual parameters check
and accepting a function that performs the check between the two types.

## Test Plan

Update `is_assignable_to.md` with callable types section.
2025-03-23 02:28:44 +05:30
Matthew Mckee 92028efe3d
[red-knot] Fix disambiguate display for union types (#16907)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

When callables are displayed in unions, like:
```py
from typing import Callable


def foo(x: Callable[[], int] | None):
    # red-knot: Revealed type is `() -> int | None` [revealed-type]
    reveal_type(x)
```

This leaves the type rather ambiguous, to fix this we can add
parenthesis to callable type in union

Fixes #16893

## Test Plan

Update callable annotations tests

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-22 13:08:51 +01:00
Matthew Mckee 7b86f54c4c
[red-knot] Add line number to mdtest panic message about language tag mismatch (#16906)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes #16898 

## Test Plan

Update test for lang mismatch panic
2025-03-22 13:05:31 +01:00
Brent Westbrook e4f5fe8cf7
[syntax-errors] Duplicate type parameter names (#16858)
Summary
--

Detects duplicate type parameter names in function definitions, class
definitions, and type alias statements.

I also boxed the `type_params` field on `StmtTypeAlias` to make it
easier to
`match` with functions and classes. (That's the reason for the red-knot
code
owner review requests, sorry!)

Test Plan
--

New `ruff_python_syntax_errors` unit tests.

Fixes #11119.
2025-03-21 15:06:22 -04:00
Brent Westbrook 2baaedda6c
[syntax-errors] Start detecting compile-time syntax errors (#16106)
## Summary

This PR implements the "greeter" approach for checking the AST for
syntax errors emitted by the CPython compiler. It introduces two main
infrastructural changes to support all of the compile-time errors:
1. Adds a new `semantic_errors` module to the parser crate with public
`SemanticSyntaxChecker` and `SemanticSyntaxError` types
2. Embeds a `SemanticSyntaxChecker` in the `ruff_linter::Checker` for
checking these errors in ruff

As a proof of concept, it also implements detection of two syntax
errors:
1. A reimplementation of
[`late-future-import`](https://docs.astral.sh/ruff/rules/late-future-import/)
(`F404`)
2. Detection of rebound comprehension iteration variables
(https://github.com/astral-sh/ruff/issues/14395)

## Test plan
Existing F404 tests, new inline tests in the `ruff_python_parser` crate,
and a linter CLI test showing an example of the `Message` output.

I also tested in VS Code, where `preview = false` and turning off syntax
errors both disable the new errors:


![image](https://github.com/user-attachments/assets/cf453d95-04f7-484b-8440-cb812f29d45e)

And on the playground, where `preview = false` also disables the errors:


![image](https://github.com/user-attachments/assets/a97570c4-1efa-439f-9d99-a54487dd6064)


Fixes #14395

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-21 14:45:25 -04:00
Ash Berlin-Taylor b1deab83d9
Update replacement paths for AIR302 (#16876)
I am one of the core developers of Airflow and working on the
"airflow.sdk"
package, and this updates the recommended replacments to the correct
user-facing imports.[^1]

cc @Lee-W @uranusjr 

[^1]:
33f0f1d639/task-sdk/src/airflow/sdk/__init__.py (L68-L93)

<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

## Test Plan

Hope and pray? 😉 

I'm sure there are some snapshot files I'm supposed to fix first.


<!-- How was it tested? -->
2025-03-21 18:46:56 +01:00
Alex Waygood d21d639ee0
[red-knot] Avoid false-positive diagnostics on `*` import statements (#16899)
## Summary

This PR removes false-positive diagnostics for `*` imports. Currently we
always emit a diagnostic for these statements unless the module we're
importing from has a symbol named `"*"` in its symbol table for the
global scope. (And if we were doing everything correctly, no module ever
would have a symbol named `"*"` in its global scope!)

The fix here is sort-of hacky and won't be what we'll want to do
long-term. However, I think it's useful to do this as a first step
since:
- It significantly reduces false positives when running on code that
uses `*` imports
- It "resets" the tests to a cleaner state with many fewer TODOs, making
it easier to see what the hard work is that's still to be done.

## Test Plan

`cargo test -p red_knot_python_semantic`
2025-03-21 14:41:49 +00:00
Alex Waygood 14eb4cac88
[red-knot] Add failing tests for `*` imports (#16873)
## Summary

This PR adds a suite of tests for wildcard (`*`) imports. The tests
nearly all fail for now, and those that don't, ahem, pass for the wrong
reasons...

I've tried to add TODO comments in all instances for places where we are
currently inferring the incorrect thing, incorrectly emitting a
diagnostic, or emitting a diagnostic with a bad error message.

## Test Plan

`cargo test -p red_knot_python_semantic`
2025-03-21 14:17:15 +00:00
Douglas Creager c03c28d199
[red-knot] Break up call binding into two phases (#16546)
This breaks up call binding into two phases:

- **_Matching parameters_** just looks at the names and kinds
(positional/keyword) of each formal and actual parameters, and matches
them up. Most of the current call binding errors happen during this
phase.

- Once we have matched up formal and actual parameters, we can **_infer
types_** of each actual parameter, and **_check_** that each one is
assignable to the corresponding formal parameter type.

As part of this, we add information to each formal parameter about
whether it is a type form or not. Once [PEP
747](https://peps.python.org/pep-0747/) is finalized, we can hook that
up to this internal type form representation. This replaces the
`ParameterExpectations` type, which did the same thing in a more ad hoc
way.

While we're here, we add a new fluent API for building `Parameter`s,
which makes our signature constructors a bit nicer to read. We also
eliminate a TODO where we were consuming types from the argument list
instead of the bound parameter list when evaluating our special-case
known functions.

Closes #15460

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-21 09:38:11 -04:00
Brent Westbrook 4773878ee7
Bump 0.11.2 (#16896)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-03-21 09:17:07 -04:00
Junhson Jean-Baptiste 2a4d835132
Use the common `OperatorPrecedence` for the parser (#16747)
## Summary

This change continues to resolve #16071 (and continues the work started
in #16162). Specifically, this PR changes the code in the parser so that
it uses the `OperatorPrecedence` struct from `ruff_python_ast` instead
of its own version. This is part of an effort to get rid of the
redundant definitions of `OperatorPrecedence` throughout the codebase.

Note that this PR only makes this change for `ruff_python_parser` -- we
still want to make a similar change for the formatter (namely the
`OperatorPrecedence` defined in the expression part of the formatter,
the pattern one is different). I separated the work to keep the PRs
small and easily reviewable.

## Test Plan

Because this is an internal change, I didn't add any additional tests.
Existing tests do pass.
2025-03-21 09:40:37 +05:30
Dhruv Manilawala 04a8756379
[red-knot] Check subtype relation between callable types (#16804)
## Summary

Part of #15382

This PR adds support for checking the subtype relationship between the
two callable types.

The main source of reference used for implementation is
https://typing.python.org/en/latest/spec/callables.html#assignability-rules-for-callables.

The implementation is split into two phases:
1. Check all the positional parameters which includes positional-only,
standard (positional or keyword) and variadic kind
2. Collect all the keywords in a `HashMap` to do the keyword parameters
check via name lookup

For (1), there's a helper struct which is similar to `.zip_longest`
(from `itertools`) except that it allows control over one of the
iterator as that's required when processing a variadic parameter. This
is required because positional parameters needs to be checked as per
their position between the two callable types. The struct also keeps
track of the current iteration element because when the loop is exited
(to move on to the phase 2) the current iteration element would be
carried over to the phase 2 check.

This struct is internal to the `is_subtype_of` method as I don't think
it makes sense to expose it outside. It also allows me to use "self" and
"other" suffixed field names as that's only relevant in that context.

## Test Plan

Add extensive tests in markdown.

Converted all of the code snippets from
https://typing.python.org/en/latest/spec/callables.html#assignability-rules-for-callables
to use `knot_extensions.is_subtype_of` and verified the result.
2025-03-21 03:27:22 +00:00
Dhruv Manilawala 193c38199e
[red-knot] Check whether two callable types are equivalent (#16698)
## Summary

This PR checks whether two callable types are equivalent or not.

This is required because for an equivalence relationship, the default
value does not necessarily need to be the same but if the parameter in
one of the callable has a default value then the corresponding parameter
in the other callable should also have a default value. This is the main
reason a manual implementation is required.

And, as per https://typing.python.org/en/latest/spec/callables.html#id4,
the default _type_ doesn't participate in a subtype relationship, only
the optionality (required or not) participates. This means that the
following two callable types are equivalent:

```py
def f1(a: int = 1) -> None: ...
def f2(a: int = 2) -> None: ...
```

Additionally, the name of positional-only, variadic and keyword-variadic
are not required to be the same for an equivalence relation.

A potential solution to avoid the manual implementation would be to only
store whether a parameter has a default value or not but the type is
currently required to check for assignability.

## Test plan

Add tests for callable types in `is_equivalent_to.md`
2025-03-21 03:19:07 +00:00
Matthew Mckee 63e78b41cd
[red-knot] Ban most `Type::Instance` types in type expressions (#16872)
## Summary

Catch some Instances, but raise type error for the rest of them
Fixes #16851 

## Test Plan

Extend invalid.md in annotations

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-03-20 15:19:56 -07:00
Alex Waygood 296d67a496
Special-case value-expression inference of special form subscriptions (#16877)
## Summary

Currently for something like `X = typing.Tuple[str, str]`, we infer the
value of `X` as `object`. That's because `Tuple` (like many of the
symbols in the typing module) is annotated as a `_SpecialForm` instance
in typeshed's stubs:


23382f5f8c/crates/red_knot_vendored/vendor/typeshed/stdlib/typing.pyi (L215)

and we don't understand implicit type aliases yet, and the stub for
`_SpecialForm.__getitem__` says it always returns `object`:


23382f5f8c/crates/red_knot_vendored/vendor/typeshed/stdlib/typing.pyi (L198-L200)

We have existing false positives in our test suite due to this:


23382f5f8c/crates/red_knot_python_semantic/resources/mdtest/annotations/annotated.md?plain=1#L76-L78

and it's causing _many_ new false positives in #16872, which tries to
make our annotation-expression parsing stricter in some ways.

This PR therefore adds some small special casing for `KnownInstanceType`
variants that fallback to `_SpecialForm`, so that these false positives
can be avoided.

## Test Plan

Existing mdtest altered.

Cc. @MatthewMckee4
2025-03-20 21:46:02 +00:00
Brent Westbrook 42cbce538b
[syntax-errors] Fix star annotation before Python 3.11 (#16878)
Summary
--

Fixes #16874. I previously emitted a syntax error when starred
annotations were _allowed_ rather than when they were actually used.
This caused false positives for any starred parameter name because these
are allowed to have starred annotations but not required to. The fix is
to check if the annotation is actually starred after parsing it.

Test Plan
--

New inline parser tests derived from the initial report and more
examples from the comments, although I think the first case should cover
them all.
2025-03-20 17:44:52 -04:00
Shunsuke Shibayama 23382f5f8c
[red-knot] add test cases result in false positive errors (#16856)
## Summary

From #16641

The previous PR attempted to fix the errors presented in this PR, but as
discussed in the conversation, it was concluded that the approach was
undesirable and that further work would be needed to fix the errors with
a correct general solution.

In this PR, I instead add the test cases from the previous PR as TODOs,
as a starting point for future work.

## Test Plan

---------

Co-authored-by: Carl Meyer <carl@oddbird.net>
2025-03-20 17:17:54 +00:00
Dylan c1971fdde2
Bump 0.11.1 (#16871) 2025-03-20 09:50:46 -05:00
Matthew Mckee cdafd8e32b
Allow discovery of venv in VIRTUAL_ENV env variable (#16853)
## Summary

Fixes #16744 

Allows the cli to find a virtual environment from the VIRTUAL_ENV
environment variable if no `--python` is set

## Test Plan

Manual testing, of:
- Virtual environments explicitly activated using `source .venv/bin/activate`
- Virtual environments implicilty activated via `uv run`
- Broken virtual environments with no `pyvenv.cfg` file
2025-03-20 13:55:35 +00:00
Junhson Jean-Baptiste 47c4ccff5d
Separate `BitXorOr` into `BitXor` and `BitOr` precedence (#16844)
## Summary

This change follows up on the bug-fix requested in #16747 --
`ruff_python_ast::OperatorPrecedence` had an enum variant, `BitXorOr`,
which which gave the same precedence to the `|` and `^` operators. This
goes against [Python's documentation for operator
precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence),
so this PR changes the code so that it's correct.

This is part of the overall effort to unify redundant definitions of
`OperatorPrecedence` throughout the codebase (#16071)

## Test Plan

Because this is an internal change, I only ran existing tests to ensure
nothing was broken.
2025-03-20 16:13:47 +05:30
Dylan 74f64d3f96
Server: Allow `FixAll` action in presence of version-specific syntax errors (#16848)
The single flag `has_syntax_error` on `LinterResult` is replaced with
two (private) flags: `has_valid_syntax` and
`has_no_unsupported_syntax_errors`, which record whether there are
`ParseError`s or `UnsupportedSyntaxError`s, respectively. Only the
former is used to prevent a `FixAll` action.

An attempt has been made to make consistent the usage of the phrases
"valid syntax" (which seems to be used to refer only to _parser_ errors)
and "syntax error" (which refers to both _parser_ errors and
version-specific syntax errors).

Closes #16841
2025-03-20 05:09:14 -05:00
Vasco Schiavo 999fd4f885
[`refurb`] Fix starred expressions fix (`FURB161`) (#16550)
The PR partially solves issue #16457

Specifically, it solves the following problem:

```text
$ cat >furb161_1.py <<'# EOF'
print(bin(*[123]).count("1"))
# EOF

$ python furb161_1.py
6

$ ruff --isolated check --target-version py310 --preview --select FURB161 furb161_1.py --diff 2>&1 | grep error:
error: Fix introduced a syntax error. Reverting all changes.
```

Now starred expressions are corrected handled.
2025-03-19 17:43:58 -04:00
Dylan 433a342656
[`flake8-executable`] Add pytest and uv run to help message for `shebang-missing-python` (`EXE003`) (#16855)
Followup to #16849 per
https://github.com/astral-sh/ruff/pull/16849#issuecomment-2737316564
2025-03-19 13:12:32 -05:00
Matthew Mckee 4ed93b4311
Show more precise messages in invalid type expressions (#16850)
## Summary

Some error messages were not very specific; this PR improves them

## Test Plan

New mdtests added; existing mdtests tweaked
2025-03-19 17:00:30 +00:00
Dylan 98fdc0ebae
[`flake8-executables`] Allow `uv run` in shebang line for `shebang-missing-python` (`EXE003`) (#16849)
Skip the lint for [shebang-missing-python
(EXE003)](https://docs.astral.sh/ruff/rules/shebang-missing-python/#shebang-missing-python-exe003)
if we find `uv run` on the shebang line.

Closes #13021
2025-03-19 10:35:07 -05:00
Josh Cannon 861931795c
Add `--exit-non-zero-on-format` (#16009)
## Summary

Fixes #8191 by introducing `--exit-non-zero-on-format` to `ruff format`
which pretty much does what it says on the tin.

## Test Plan

Added a new test!

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-03-19 10:55:05 -04:00
Alex Waygood a3f3d734a1
[red-knot] Ban list literals in most contexts in type expressions (#16847)
## Summary

This PR reworks `TypeInferenceBuilder::infer_type_expression()` so that
we emit diagnostics when encountering a list literal in a type
expression. The only place where a list literal is allowed in a type
expression is if it appears as the first argument to `Callable[]`, and
`Callable` is already heavily special-cased in our type-expression
parsing.

In order to ensure that list literals are _always_ allowed as the
_first_ argument to `Callabler` (but never allowed as the second, third,
etc. argument), I had to do some refactoring of our type-expression
parsing for `Callable` annotations.

## Test Plan

New mdtests added, and existing ones updated
2025-03-19 14:42:42 +00:00
Matthew Mckee 3a5f1d46c0
[red-knot] Make' Type::in_type_expression()' exhaustive for Type::KnownInstance (#16836)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

fixes #15048 
We want to handle more types from Type::KnownInstance 

## Test Plan

Add tests for each type added explicitly in the match

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-03-19 07:36:28 -07:00
Alex Waygood f3f3e55d97
[red-knot] Minor cleanup to `infer_parameterized_known_instance_type_expression` (#16846)
## Summary

These are just cosmetic changes, but I'm separating them out into a
standalone PR to make a branch I have stacked on top of this easier to
review

## Test Plan

Existing tests all pass
2025-03-19 14:19:13 +00:00
Brent Westbrook 22de00de16 [internal] Return `Message`s from `check_path` (#16837)
Summary
--

This PR updates `check_path` in the `ruff_linter` crate to return a
`Vec<Message>` instead of a `Vec<Diagnostic>`. The main motivation for
this is to make it easier to convert semantic syntax errors directly
into `Message`s rather than `Diagnostic`s in #16106. However, this also
has the benefit of keeping the preview check on unsupported syntax
errors in `check_path`, as suggested in
https://github.com/astral-sh/ruff/pull/16429#discussion_r1974748024.

All of the interesting changes are in the first commit. The second
commit just renames variables like `diagnostics` to `messages`, and the
third commit is a tiny import fix.

I also updated the `ExpandedMessage::location` field name, which caused
a few extra commits tidying up the playground code. I thought it was
nicely symmetric with `end_location`, but I'm happy to revert that too.

Test Plan
--

Existing tests. I also tested the playground and server manually.
2025-03-19 10:08:07 -04:00
Dhruv Manilawala a69f6240cc
[red-knot] Infer `lambda` return type as `Unknown` (#16695)
## Summary

Part of #15382

This PR infers the return type `lambda` expression as `Unknown`. In the
future, it would be more useful to infer the expression type considering
the surrounding context (#16696).

## Test Plan

Update existing test cases from `@todo` to the (verified) return type.
2025-03-18 22:48:10 +05:30
Dhruv Manilawala c3d429ddd8
[red-knot] Move `name` field on parameter kind (#16830)
## Summary

Previously, the `name` field was on `Parameter` which required it to be
always optional regardless of the parameter kind because a
`typing.Callable` signature does not have name for the parameters. This
is the case for positional-only parameters. This wasn't enforced at the
type level which meant that downstream usages would have to unwrap on
`name` even though it's guaranteed to be present.

This commit moves the `name` field from `Parameter` to the
`ParameterKind` variants and makes it optional only for
`ParameterKind::PositionalOnly` variant while required for all other
variants.

One change that's now required is that a `Callable` form using a gradual
form for parameter types (`...`) would have a default `args` and
`kwargs` name used for variadic and keyword-variadic parameter kind
respectively. This is also the case for invalid `Callable` type forms. I
think this is fine as names are not relevant in this context but happy
to make it optional even in variadic variants.

## Test Plan

No new tests; make sure existing tests are passing.
2025-03-18 22:47:44 +05:30
Matthew Mckee ab3ec4de6a
[red-knot] Emit errors for more AST nodes that are invalid (or only valid in specific contexts) in type expressions (#16822)
## Summary

Add error messages for invalid nodes in type expressions

Fixes #16816 

## Test Plan

Extend annotations/invalid.md to handle these invalid AST nodes error
messages
2025-03-18 17:16:50 +00:00
Micha Reiser c027979851
Red Knot Playground (#12681)
## Summary

This PR adds a playground for Red Knot

[Screencast from 2024-08-14
10-33-54.webm](https://github.com/user-attachments/assets/ae81d85f-74a3-4ba6-bb61-4a871b622f05)

Sharing does work 😆 I just forgot to start wrangler. 


It supports:

* Multiple files
* Showing the AST
* Showing the tokens
* Sharing
* Persistence to local storage

Future extensions:

* Configuration support: The `pyproject.toml` would *just* be another
file.
* Showing type information on hover

## Blockers

~~Salsa uses `catch_unwind` to break cycles, which Red Knot uses
extensively when inferring types in the standard library.
However, WASM (at least `wasm32-unknown-unknown`) doesn't support
`catch_unwind` today, so the playground always crashes when the type
inference encounters a cycle.~~

~~I created a discussion in the [salsa
zulip](https://salsa.zulipchat.com/#narrow/stream/333573-salsa-3.2E0/topic/WASM.20support)
to see if it would be possible to **not** use catch unwind to break
cycles.~~

~~[Rust tracking issue for WASM catch unwind
support](https://github.com/rust-lang/rust/issues/118168)~~

~~I tried to build the WASM with the nightly compiler option but ran
into problems because wasm-bindgen doesn't support WASM-exceptions. We
could try to write the binding code by hand.~~

~~Another alternative is to use `wasm32-unknown-emscripten` but it's
rather painful to build~~
2025-03-18 17:17:11 +01:00
Brent Westbrook dcf31c9348
[syntax-errors] PEP 701 f-strings before Python 3.12 (#16543)
## Summary

This PR detects the use of PEP 701 f-strings before 3.12. This one
sounded difficult and ended up being pretty easy, so I think there's a
good chance I've over-simplified things. However, from experimenting in
the Python REPL and checking with [pyright], I think this is correct.
pyright actually doesn't even flag the comment case, but Python does.

I also checked pyright's implementation for
[quotes](98dc4469cc/packages/pyright-internal/src/analyzer/checker.ts (L1379-L1398))
and
[escapes](98dc4469cc/packages/pyright-internal/src/analyzer/checker.ts (L1365-L1377))
and think I've approximated how they do it.

Python's error messages also point to the simple approach of these
characters simply not being allowed:

```pycon
Python 3.11.11 (main, Feb 12 2025, 14:51:05) [Clang 19.1.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f'''multiline {
... expression # comment
... }'''
  File "<stdin>", line 3
    }'''
        ^
SyntaxError: f-string expression part cannot include '#'
>>> f'''{not a line \
... continuation}'''
  File "<stdin>", line 2
    continuation}'''
                    ^
SyntaxError: f-string expression part cannot include a backslash
>>> f'hello {'world'}'
  File "<stdin>", line 1
    f'hello {'world'}'
              ^^^^^
SyntaxError: f-string: expecting '}'
```

And since escapes aren't allowed, I don't think there are any tricky
cases where nested quotes or comments can sneak in.

It's also slightly annoying that the error is repeated for every nested
quote character, but that also mirrors pyright, although they highlight
the whole nested string, which is a little nicer. However, their check
is in the analysis phase, so I don't think we have such easy access to
the quoted range, at least without adding another mini visitor.

## Test Plan

New inline tests

[pyright]:
https://pyright-play.net/?pythonVersion=3.11&strict=true&code=EYQw5gBAvBAmCWBjALgCgO4gHaygRgEoAoEaCAIgBpyiiBiCLAUwGdknYIBHAVwHt2LIgDMA5AFlwSCJhwAuCAG8IoMAG1Rs2KIC6EAL6iIxosbPmLlq5foRWiEAAcmERAAsQAJxAomnltY2wuSKogA6WKIAdABWfPBYqCAE%2BuSBVqbpWVm2iHwAtvlMWMgB2ekiolUAgq4FjgA2TAAeEMieSADWCsoV5qoaqrrGDJ5MiDz%2B8ABuLqosAIREhlXlaybrmyYMXsDw7V4AnoysyAmQ5SIhwYo3d9cheADUeKlv5O%2BpQA
2025-03-18 11:12:15 -04:00
cake-monotone 4ab529803f
[red-knot] Refactor `property_tests.rs` into `property_tests` module structure (#16827)
## Summary

For now, `property_tests.rs` has grown larger and larger, making the
file difficult to read and maintain.

Although the code has been split, the test paths and full names remain
unchanged. There are no changes affecting test execution.
2025-03-18 12:59:14 +00:00
Alex Waygood 23b7df9b29
[red-knot] Simplify `IterationError` and `ContextManagerError` (#16820)
## Summary

This PR simplifies `IterationError` and `ContextManagerError` so that
they no longer "remember" what type it was that was (respectively) not
iterable or not valid as a context manager. Instead, the type that was
iterated over (or was used as a context manager) is passed back in when
calling the error struct's `report_diagnostic` method.

The motivations for this are:
- It significantly simplifies the code
- It reduces the size of these types on the stack

## Test Plan

`cargo test -p red_knot_python_semantic`
2025-03-18 11:30:41 +00:00
cake-monotone 3e2cf5d7c4
[red-knot] Improve property test performance by cloning db instead of holding `MutexGuard` (#16823)
## Summary

This PR brings an optimization.

- `get_cached_db` no longer returns a `MutexGuard`; instead, it returns
a cloned database.

### `get_cached_db`

Previously, the `MutexGuard` was held inside the property test function
(defined in the macro), which prevented multiple property tests from
running in parallel. More specifically, the program could only test one
random test case at a time, which likely caused a significant
bottleneck.

On my local machine, running:

```
QUICKCHECK_TESTS=100000 cargo test --release -p red_knot_python_semantic -- --ignored stable
```

showed about **a 75% speedup** (from \~60s to \~15s).
2025-03-18 09:09:57 +01:00
Peter Hill 433879d852
[ruff] Fix `--statistics` reporting for unsafe fixes (#16756)
Fixes #16751

## Summary

Previously, unsafe fixes were counted as "fixable" in
`Printer::write_statistics`, in contrast to the behaviour in
`Printer::write_once`. This changes the behaviour to align with
`write_once`, including them only if `--unsafe-fixes` is set.

We now also reuse `Printer::write_summary` to avoid duplicating the
logic for whether or not to report if there are hidden fixes.

## Test Plan

Existing tests modified to use an unsafe-fixable rule, and new ones
added to cover the case with `--unsafe-fixes`
2025-03-18 08:03:14 +01:00
Kaxil Naik b7d232cf89
[`airflow`] Add `chain`, `chain_linear` and `cross_downstream` for `AIR302` (#16647)
## Summary

Similar to https://github.com/astral-sh/ruff/pull/16014. PR on Airflow
side: https://github.com/apache/airflow/pull/47639

## Test Plan

A test fixture has been updated
2025-03-18 11:08:45 +05:30
Andrew Gallant bd9eab059f red_knot: update diagnostic output snapshots
These should all be minor cosmetic changes. To summarize:

* In many cases, `-` was replaced with `^` for primary annotations.
This is because, previously, whether `-` or `^` was used depended
on the severity. But in the new data model, it's based on whether
the annotation is "primary" or not. We could of course change this
in whatever way we want, but I think we should roll with this for now.

* The "secondary messages" in the old API are rendered as
sub-diagnostics. This in turn results in a small change in the output
format, since previously, the secondary messages were represented as
just another snippet. We use sub-diagnostics because that's the intended
way to enforce relative ordering between messages within a diagnostic.

* The "info:" prefix used in some annotation messages has been dropped.
We could re-add this, but I think I like it better without this prefix.

I believe those 3 cover all of the snapshot changes here.
2025-03-17 12:46:49 -04:00
Andrew Gallant 6883c1dde7 ruff_db: delete old diagnostic renderer
... and switch to the new one.

We do this switch by converting the old diagnostics to a
`Diagnostic`, and then rendering that.

This does not quite emit identical output. There are some
changes. They *could* be fixed to remain the same, but the
changes aren't obviously worse to me and I think the right
way to *improve* them is to move Red Knot to the new `Diagnostic`
API.

The next commit will have the snapshot changes.
2025-03-17 12:46:49 -04:00
Andrew Gallant 9291074ba6 ruff_db: tweak main diagnostic message
In our existing diagnostics, our message is just the diagnostic
ID, and the message goes to the annotation. In reality, the
diagnostic can have its own message distinct from the optional
messages associated with an annotation.

In order to make the outputs match, we do a small tweak here:
when the main diagnostic message is empty, we drop the colon
after the diagnostic ID.

I expect that we'll want to rejigger this output format more
in the future, but for now this was a very simple change to
preserve the status quo.
2025-03-17 12:46:49 -04:00
Andrew Gallant 602a27c4e3 ruff_db: tweak number of line terminators emitted in new diagnostic renderer
When moving over to the new renderer, I noticed that it
was emitting an extra line terminator compared to the status
quo. This removes it by turning the line terminator into a
line delimiter between diagnostics.
2025-03-17 12:46:49 -04:00
Andrew Gallant ff548b1272 ruff_db: clarify the error conditions of `Diagnostic::print` 2025-03-17 12:46:49 -04:00
Brent Westbrook b2e0ae6416
[`flake8-gettext`] Swap `format-` and `printf-in-get-text-func-call` examples (`INT002`, `INT003`) (#16769)
Summary
--
Fixes #16735. I also checked `INT001`, and it correctly has an f-string
example.

Test Plan
--
None
2025-03-17 14:37:38 +00:00
Douglas Creager 23ccb52fa6
[red-knot] Handle unions of callables better (#16716)
This cleans up how we handle calling unions of types. #16568 adding a
three-level structure for callable signatures (`Signatures`,
`CallableSignature`, and `Signature`) to handle unions and overloads.

This PR updates the bindings side to mimic that structure. What used to
be called `CallOutcome` is now `Bindings`, and represents the result of
binding actual arguments against a possible union of callables.
`CallableBinding` is the result of binding a single, possibly
overloaded, callable type. `Binding` is the result of binding a single
overload.

While we're here, this also cleans up `CallError` greatly. It was
previously extracting error information from the bindings and storing it
in the error result. It is now a simple enum, carrying no data, that's
used as a status code to talk about whether the overall binding was
successful or not. We are now more consistent about walking the binding
itself to get detailed information about _how_ the binding was
unsucessful.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-17 10:35:52 -04:00
Dhruv Manilawala 3ccc8dbbf9
[red-knot] Fix fully static check for callable type (#16803)
## Summary

This PR fixes a bug in the check for fully static callable type where we
would skip unannotated parameter type.

## Test Plan

Add tests using the new `CallableTypeFromFunction` special form.
2025-03-17 20:01:30 +05:30
Brent Westbrook 75a562d313
[syntax-errors] Parenthesized context managers before Python 3.9 (#16523)
Summary
--

I thought this was very complicated based on the comment here:
https://github.com/astral-sh/ruff/pull/16106#issuecomment-2653505671 and
on some of the discussion in the CPython issue here:
https://github.com/python/cpython/issues/56991. However, after a little
bit of experimentation, I think it boils down to this example:

```python
with (x as y): ...
```

The issue is parentheses around a `with` item with an `optional_var`, as
we (and
[Python](https://docs.python.org/3/library/ast.html#ast.withitem)) call
the trailing variable name (`y` in this case). It's not actually about
line breaks after all, except that line breaks are allowed in
parenthesized expressions, which explains the validity of cases like


```pycon
>>> with (
...     x,
...     y
... ) as foo:
...     pass
... 
```

even on Python 3.8.

I followed [pyright]'s example again here on the diagnostic range (just
the opening paren) and the wording of the error.


Test Plan
--
Inline tests

[pyright]:
https://pyright-play.net/?pythonVersion=3.7&strict=true&code=FAdwlgLgFgBAFAewA4FMB2cBEAzBCB0EAHhJgJQwCGAzjLgmQFwz6tA
2025-03-17 08:54:55 -04:00
Alex Waygood 50b66dc025
[red-knot] Stabilize `negation_reverses_subtype_order` property test (#16801)
## Summary

This is a re-creation of https://github.com/astral-sh/ruff/pull/16764 by
@mtshiba, which I closed meaning to immediately reopen (GitHub wasn't
updating the PR with the latest pushed changes), and which GitHub will
not allow me to reopen for some reason. Pasting the summary from that PR
below:

> From https://github.com/astral-sh/ruff/pull/16641
> 
> As stated in this comment
(https://github.com/astral-sh/ruff/pull/16641#discussion_r1996153702),
the current ordering implementation for intersection types is incorrect.
So, I will introduce lexicographic ordering for intersection types.

## Test Plan

One property test stabilised (tested locally with
`QUICKCHECK_TESTS=2000000 cargo test --release -p
red_knot_python_semantic -- --ignored
types::property_tests::stable::negation_reverses_subtype_order`), and
existing mdtests that previously failed now pass.

Primarily-authored-by:
[mtshiba](https://github.com/astral-sh/ruff/commits?author=mtshiba)

---------

Co-authored-by: Shunsuke Shibayama <sbym1346@gmail.com>
2025-03-17 12:33:38 +00:00
Matthew Mckee 24707777af
[red-knot] Emit error if int/float/complex/bytes/boolean literals appear in type expressions outside `typing.Literal[]` (#16765)
## Summary
Fixes https://github.com/astral-sh/ruff/issues/16532

## Test Plan

New mdtest assertions added

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-03-17 11:56:16 +00:00
Alex Waygood 38bfda94ce
[syntax-errors] Improve error message and range for pre-PEP-614 decorator syntax errors (#16581)
## Summary

A small followup to https://github.com/astral-sh/ruff/pull/16386. We now
tell the user exactly what it was about their decorator that constituted
invalid syntax on Python <3.9, and the range now highlights the specific
sub-expression that is invalid rather than highlighting the whole
decorator

## Test Plan

Inline snapshots are updated, and new ones are added.
2025-03-17 11:17:27 +00:00
Mauro Fontana 4da6936ec4
[`flake8-bandit`] Allow raw strings in `suspicious-mark-safe-usage` (`S308`) #16702 (#16770)
## Summary
Stop flagging each invocation of `django.utils.safestring.mark_safe`
(also available at, `django.utils.html.mark_safe`) as an error.

Instead, allow string literals as valid uses for `mark_safe`.

Also, update the documentation, pointing at
`django.utils.html.format_html` for dynamic content generation use
cases.

Closes #16702 

## Test Plan
I verified several possible uses, but string literals, are still
flagged.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-17 11:29:07 +01:00
Dylan 238ec39c56
[`refurb`] Avoid panicking `unwrap` in `verbose-decimal-constructor` (`FURB157`) (#16777) 2025-03-17 05:09:07 -05:00
Micha Reiser b04103fa1d
[red-knot] Add `--color` CLI option (#16758)
## Summary

This PR adds a new `--color` CLI option that controls whether the output
should be colorized or not.

This is implements part of
https://github.com/astral-sh/ruff/issues/16727 except that it doesn't
implement the persistent configuration support as initially proposed in
the CLI document. I realized, that having this as a persistent
configuration is somewhat awkward because we may end up writing tracing
logs **before** we loaded and resolved the settings. Arguably, it's
probably fine to color the output up to that point, but it feels like a
somewhat broken experience. That's why I decided not to add the
persistent configuration option for now.


## Test Plan

I tested this change manually by running Red Knot with `--color=always`,
`--color=never`, and `--color=auto` (or no argument) and verified that:

* The diagnostics are or aren't colored
* The tracing output is or isn't colored.

---------

Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-03-17 10:06:34 +00:00
Micha Reiser c100d519e9
[internal]: Upgrade salsa (#16794)
## Summary

Another salsa upgrade. 

The main motivation is to stay on a recent salsa version because there
are still a lot of breaking changes happening.
The most significant changes in this update:

* Salsa no longer derives `Debug` by default. It now requires
`interned(debug)` (or similar)
* This version ships the foundation for garbage collecting interned
values. However, this comes at the cost that queries now track which
interned values they created (or read). The micro benchmarks in the
salsa repo showed a significant perf regression. Will see if this also
visible in our benchmarks.

## Test Plan

`cargo test`
2025-03-17 11:05:54 +01:00
Micha Reiser 6f5a68608e
[ci]: Fixup codspeed upgrade (#16790)
## Summary

Benchmark isn't a required build step. That's why
https://github.com/astral-sh/ruff/pull/16784/ got merged with the step
failing.

This PR fixes up the benchmarking step
2025-03-17 09:14:22 +01:00
Carl Meyer 2de8455e43
[red-knot] LSP: only emit WARN logs from non-red-knot sources (#16760)
Currently the red-knot LSP server emits any log messages of level `INFO`
or higher from non-red-knot crates. This makes its output quite verbose,
because Salsa emits an `INFO` level message every time it executes a
query. I use red-knot as LSP with neovim, and this spams the log file
quite a lot.

It seems like a better default to only emit `WARN` or higher messages
from non-red-knot sources.

I confirmed that this fixes the nvim LSP log spam.
2025-03-15 08:47:50 -07:00
github-actions[bot] 1fab292ec1
Sync vendored typeshed stubs (#16762)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2025-03-15 00:38:58 +00:00
David Peter ebcad6e641
[red-knot] Use `try_call_dunder` for augmented assignment (#16717)
## Summary

Uses the `try_call_dunder` infrastructure for augmented assignment and
fixes the logic to work for types other than `Type::Instance(…)`. This
allows us to infer the correct type here:
```py
x = (1, 2)
x += (3, 4)
reveal_type(x)  # revealed: tuple[Literal[1], Literal[2], Literal[3], Literal[4]]
```
Or in this (extremely weird) scenario:
```py
class Meta(type):
    def __iadd__(cls, other: int) -> str:
        return ""

class C(metaclass=Meta): ...

cls = C
cls += 1

reveal_type(cls)  # revealed: str
```

Union and intersection handling could also be improved here, but I made
no attempt to do so in this PR.

## Test Plan

New MD tests
2025-03-14 20:36:09 +01:00
David Peter fe275725e0
[red-knot] Document current state of attribute assignment diagnostics (#16746)
## Summary

A follow-up to https://github.com/astral-sh/ruff/pull/16705 which
documents various kinds of diagnostics that can appear when assigning to
an attribute.

## Test Plan

New snapshot tests.
2025-03-14 20:34:43 +01:00
Micha Reiser a467e7c8d3
[red-knot] Case sensitive module resolver (#16521)
## Summary

This PR implements the first part of
https://github.com/astral-sh/ruff/discussions/16440. It ensures that Red
Knot's module resolver is case sensitive on all systems.

This PR combines a few approaches:

1. It uses `canonicalize` on non-case-sensitive systems to get the real
casing of a path. This works for as long as no symlinks or mapped
network drives (the windows `E:\` is mapped to `\\server\share` thingy).
This is the same as what Pyright does
2. If 1. fails, fall back to recursively list the parent directory and
test if the path's file name matches the casing exactly as listed in by
list dir. This is the same approach as CPython takes in its module
resolver. The main downside is that it requires more syscalls because,
unlike CPython, we Red Knot needs to invalidate its caches if a file
name gets renamed (CPython assumes that the folders are immutable).

It's worth noting that the file watching test that I added that renames
`lib.py` to `Lib.py` currently doesn't pass on case-insensitive systems.
Making it pass requires some more involved changes to `Files`. I plan to
work on this next. There's the argument that landing this PR on its own
isn't worth it without this issue being addressed. I think it's still a
good step in the right direction even when some of the details on how
and where the path case sensitive comparison is implemented.

## Test plan

I added multiple integration tests (including a failing one). I tested
that the `case-sensitivity` detection works as expected on Windows,
MacOS and Linux and that the fast-paths are taken accordingly.
2025-03-14 19:16:44 +00:00
Micha Reiser a128ca761f
[red-knot] Very minor simplification of the render tests (#16759) 2025-03-14 19:13:07 +00:00
Brent Westbrook 3a32e56445
[syntax-errors] Unparenthesized assignment expressions in sets and indexes (#16404)
## Summary
This PR detects unparenthesized assignment expressions used in set
literals and comprehensions and in sequence indexes. The link to the
release notes in https://github.com/astral-sh/ruff/issues/6591 just has
this entry:
> * Assignment expressions can now be used unparenthesized within set
literals and set comprehensions, as well as in sequence indexes (but not
slices).

with no other information, so hopefully the test cases I came up with
cover all of the changes. I also tested these out in the Python REPL and
they actually worked in Python 3.9 too. I'm guessing this may be another
case that was "formally made part of the language spec in Python 3.10,
but usable -- and commonly used -- in Python >=3.9" as @AlexWaygood
added to the body of #6591 for context managers. So we may want to
change the version cutoff, but I've gone along with the release notes
for now.

## Test Plan

New inline parser tests and linter CLI tests.
2025-03-14 15:06:42 -04:00
Andrew Gallant b9d7c36a23 ruff_db: add a new diagnostic renderer
We don't actually hook this up to anything in this PR, but we do
go to some trouble to granularly unit test it. The unit tests caught
plenty of bugs after I initially wrote down the implementation, so they
were very much worth it.

Closes #16506
2025-03-14 14:59:33 -04:00
Andrew Gallant ef9a825827 ruff_db: add `context` configuration
Instead of hard-coding a specific context window,
it seemed prudent to make this configurable. That
makes it easier to test different context window
sizes as well.

I am not totally convinced that this is the right
place for this configuration. I could see the context
window size being a property of `Diagnostic` instead,
since we might want to change the context window
size based not just on some end user configuration,
but perhaps also the specific diagnostic.

But for now, I think it's fine for it to live here,
and all of the rendering logic doesn't care where
it lives. So it should be relatively easy to change
in the future.
2025-03-14 14:59:33 -04:00
Andrew Gallant 2bcd2b4147 red_knot: plumb through `DiagnosticFormat` to the CLI
The CLI calls this `OutputFormat`, and so does the type where the CLI is
defined. But it's called `DiagnosticFormat` in `ruff_db` to be
consistent with `DisplayDiagnosticConfig`.

Ref https://github.com/astral-sh/ruff/issues/15697#issuecomment-2706477278
2025-03-14 14:46:17 -04:00
Andrew Gallant eb6871d209 ruff_db: add concise diagnostic mode
This adds a new configuration knob to diagnostic rendering that, when
enabled, will make diagnostic rendering much more terse. Specifically,
it will guarantee that each diagnostic will only use one line.

This doesn't actually hook the concise output option up to anything.
We'll do that plumbing in the next commit.
2025-03-14 14:46:17 -04:00
Brent Westbrook 6311412373
[syntax-errors] Star annotations before Python 3.11 (#16545)
Summary
--

This is closely related to (and stacked on)
https://github.com/astral-sh/ruff/pull/16544 and detects star
annotations in function definitions.

I initially called the variant `StarExpressionInAnnotation` to mirror
`StarExpressionInIndex`, but I realized it's not really a "star
expression" in this position and renamed it. `StarAnnotation` seems in
line with the PEP.

Test Plan
--

Two new inline tests. It looked like there was pretty good existing
coverage of this syntax, so I just added simple examples to test the
version cutoff.
2025-03-14 15:20:44 +00:00
Brent Westbrook 4f2851982d
[syntax-errors] Star expression in index before Python 3.11 (#16544)
Summary
--

This PR detects tuple unpacking expressions in index/subscript
expressions before Python 3.11.

Test Plan
--

New inline tests
2025-03-14 14:51:34 +00:00
Micha Reiser 2cd25ef641
Ruff 0.11.0 (#16723)
## Summary

Follow-up release for Ruff v0.10 that now includes the following two
changes that we intended to ship but slipped:

* Changes to how the Python version is inferred when a `target-version`
is not specified (#16319)
* `blanket-noqa` (`PGH004`): Also detect blanked file-level noqa
comments (and not just line level comments).

## Test plan

I verified that the binary built on this branch respects the
`requires-python` setting
([logs](https://www.diffchecker.com/qyJWYi6W/), left: v0.10, right:
v0.11)
2025-03-14 13:57:56 +01:00
David Peter a22d206db2
[red-knot] Preliminary tests for typing.Final (#15917)
## Summary

WIP.

Adds some preliminary tests for `typing.Final`.

## Test Plan

New MD tests
2025-03-14 12:30:13 +01:00
cake-monotone 270318c2e0
[red-knot] fix: improve type inference for binary ops on tuples (#16725)
## Summary

This PR includes minor improvements to binary operation inference,
specifically for tuple concatenation.

### Before

```py
reveal_type((1, 2) + (3, 4))  # revealed: @Todo(return type of decorated function)
# If TODO is ignored, the revealed type would be `tuple[1|2|3|4, ...]`
```

The `builtins.tuple` type stub defines `__add__`, but it appears to only
work for homogeneous tuples. However, I think this limitation is not
ideal for many use cases.

### After

```py
reveal_type((1, 2) + (3, 4))  # revealed: tuple[Literal[1], Literal[2], Literal[3], Literal[4]]
```

## Test Plan

### Added
- `mdtest/binary/tuples.md`

### Affected
- `mdtest/slots.md` (a test have been moved out of the `False-Negative`
block.)
2025-03-14 12:29:57 +01:00
David Peter d03b12e711
[red-knot] Assignments to attributes (#16705)
## Summary

This changeset adds proper support for assignments to attributes:
```py
obj.attr = value
```

In particular, the following new features are now available:

* We previously didn't raise any errors if you tried to assign to a
non-existing attribute `attr`. This is now fixed.
* If `type(obj).attr` is a data descriptor, we now call its `__set__`
method instead of trying to assign to the load-context type of
`obj.attr`, which can be different for data descriptors.
* An initial attempt was made to support unions and intersections, as
well as possibly-unbound situations. There are some remaining TODOs in
tests, but they only affect edge cases. Having nested diagnostics would
be one way that could help solve the remaining cases, I believe.

## Follow ups

The following things are planned as follow-ups:

- Write a test suite with snapshot diagnostics for various attribute
assignment errors
- Improve the diagnostics. An easy improvement would be to highlight the
right hand side of the assignment as a secondary span (with the rhs type
as additional information). Some other ideas are mentioned in TODO
comments in this PR.
- Improve the union/intersection/possible-unboundness handling
- Add support for calling custom `__setattr__` methods (see new false
positive in the ecosystem results)

## Ecosystem changes

Some changes are related to assignments on attributes with a custom
`__setattr__` method (see above). Since we didn't notice missing
attributes at all in store context previously, these are new.

The other changes are related to properties. We previously used their
read-context type to test the assignment. That results in weird error
messages, as we often see assignments to `self.property` and then we
think that those are instance attributes *and* descriptors, leading to
union types. Now we properly look them up on the meta type, see the
decorated function, and try to overwrite it with the new value (as we
don't understand decorators yet). Long story short: the errors are still
weird, we need to understand decorators to make them go away.

## Test Plan

New Markdown tests
2025-03-14 12:15:41 +01:00
Micha Reiser 14c5ed5d7d
[`pygrep-hooks`]: Detect file-level suppressions comments without rul… (#16720)
## Summary

I accidentially dropped this commit from the Ruff 0.10 release. See
https://github.com/astral-sh/ruff/pull/16699
2025-03-14 09:37:16 +01:00
Micha Reiser 595565015b
Fallback to requires-python in certain cases when target-version is not found (#16721)
## Summary

Restores https://github.com/astral-sh/ruff/pull/16319 after it got
dropped from the 0.10 release branch :(

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-03-14 09:36:51 +01:00
Brent Westbrook 2382fe1f25
[syntax-errors] Tuple unpacking in `for` statement iterator clause before Python 3.9 (#16558)
Summary
--

This PR reuses a slightly modified version of the
`check_tuple_unpacking` method added for detecting unpacking in `return`
and `yield` statements to detect the same issue in the iterator clause
of `for` loops.

I ran into the same issue with a bare `for x in *rest: ...` example
(invalid even on Python 3.13) and added it as a comment on
https://github.com/astral-sh/ruff/issues/16520.

I considered just making this an additional `StarTupleKind` variant as
well, but this change was in a different version of Python, so I kept it
separate.

Test Plan
--

New inline tests.
2025-03-13 15:55:17 -04:00
Micha Reiser 27e9d1fe3e
Ruff v0.10 Release (#16708)
Co-authored-by: dylwil3 <dylwil3@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-03-13 13:53:11 -04:00
Dylan b9b256209b
describe requires-python fallback in docs (#16704)
Adds description of `requires-python` fallback to documentation for
configuration file discovery.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-03-13 11:48:12 -05:00
Carl Meyer abaa18993b
[red-knot] handle cycles in MRO/bases resolution (#16693)
There can be semi-cyclic inheritance patterns (e.g. recursive generics)
that are not technically inheritance cycles, but that can cause us to
hit Salsa query cycles in evaluating a type's MRO. Add fixed-point
handling to these MRO-related queries so we don't panic on these cycles.

The details of what queries we hit in what order in this case will
change as we implement support for generics, but ultimately we will
probably need cycle handling for all queries that can re-enter type
inference, otherwise we are susceptible to small changes in query
execution order causing panics.

Fixes #14333
Further reduces the panicking set of seeds in #14737
2025-03-13 08:16:03 -07:00
Shaygan Hooshyari 360ba095ff
[red-knot] Auto generate statement nodes (#16645)
<!--
Thank you for contributing to Ruff! 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?
- 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 #15655 

Replaced statement nodes with autogenerated ones. Reused the stuff we
introduced in #16285. Nothing except for copying the nodes to new
format.

## Test Plan

Tests run without any changes. Also moved the test that checks size of
AST nodes to `generated.rs` since all of the structs that it tests are
now there.
<!-- How was it tested? -->
2025-03-13 15:43:48 +01:00
Micha Reiser d8159e816f [`pylint`] Better inference for `str.strip` (`PLE310`) (#16671)
## Summary
This PR stabilizes the behavior introduced in
https://github.com/astral-sh/ruff/pull/15985

The new behavior improves the inference of `str.strip` calls:

* before: The rule only considered calls on string or byte literals
(`"abcd".strip`)
* now: The rule also catches calls to `strip` on object where the type
is known to be a `str` or `bytes` (e.g. `a = "abc"; a.strip("//")`)


The new behavior shipped as part of Ruff 0.9.6 on the 10th of Feb which
is a little more than a month ago.
There have been now new issues or PRs related to the new behavior.
2025-03-13 15:37:37 +01:00
Micha Reiser 04ad562afd [`pylint`] Improve `repeated-equality-comparison` fix to use a `set` when all elements are hashable (`PLR1714`) (#16685)
## Summary

This PR promotes the fix improvements for `PLR1714` that were introduced
in https://github.com/astral-sh/ruff/pull/14372/ to stable.

The improvement is that the fix now proposes to use a set if all
elements are hashable:

```
foo == "bar" or foo == "baz" or foo == "qux"
```

Gets fixed to 

```py
foo in {"bar", "baz", "qux"}
```

where it previously always got fixed to a tuple.

The new fix was first released in ruff 0.8.0 (Nov last year). This is
not a breaking change. The change was preview gated only to get some
extra test coverage.


There are no open issues or PRs related to this changed fix behavior.
2025-03-13 15:37:37 +01:00
Micha Reiser 91674718c4 [`pylint`/`pep8-naming`] Check `__new__` argument name in `bad-staticmethod-argument` and not `invalid-first-argument-name-for-class-method` (`PLW0211`/`N804`) (#16676)
## Summary

This PR stabilizes the behavior changes introduced by
https://github.com/astral-sh/ruff/pull/13305 that were gated behind
preview.
The change is that `__new__` methods are now no longer flagged by
`invalid-first-argument-name-for-class-method` (`N804`) but instead by
`bad-staticmethod-argument` (`PLW0211`)

> __new__ methods are technically static methods, with cls as their
first argument. However, Ruff currently classifies them as classmethod,
which causes two issues:

## Test Plan

There have been no new issues or PRs related to `N804` or `PLW0211`
since the behavior change was released in Ruff 0.9.7 (about 3 weeks
ago).
This is a somewhat recent change but I don't think it's necessary to
leave this in preview for another 2 months. The main reason why it was
in preview
is that it is breaking, not because it is a risky change.
2025-03-13 15:37:37 +01:00
Micha Reiser 348815d6d6 [`flake8-pyi`] Stabilize fix for `unused-private-type-var` (`PYI018`) (#16682)
## Summary

This PR stabilizes the fix for `PYI018` introduced in
https://github.com/astral-sh/ruff/pull/15999/ (first released with Ruff
0.9.5 early February)

There are no known issues with the fix or open PRs.
2025-03-13 15:37:37 +01:00
Micha Reiser 1326d55c29 [`flake8-bandit`] Deprecate `suspicious-xmle-tree-usage` (`S320`) (#16680)
## Summary
Deprecate `S320` because defusedxml has deprecated there `lxml` module
and `lxml` has been hardened since.

flake8-bandit has removed their implementation as well
(https://github.com/PyCQA/bandit/pull/1212).

Addresses https://github.com/astral-sh/ruff/issues/13707


## Test Plan

I verified that selecting `S320` prints a warning and fails if the
preview mode is enabled.
2025-03-13 15:37:37 +01:00
Micha Reiser c19cd58670 [`flake8-simplify`] Avoid double negation in fixes (`SIM103`) (#16684)
## Summary

This PR stabilizes the fixes improvements made in
https://github.com/astral-sh/ruff/pull/15562 (released with ruff 0.9.3
in mid January).

There's no open issue or PR related to the changed fix behavior.

This is not a breaking change. The fix was only gated behind preview to
get some more test coverage before releasing.
2025-03-13 15:37:37 +01:00
Micha Reiser 8155197549 [`pyupgrade`]: Improve diagnostic range for `redundant-open-mode` (`UP015`) (#16672)
## Summary

This PR stabilizes the behavior change introduced in
https://github.com/astral-sh/ruff/pull/15872/

The diagnostic range is now the range of the redundant `mode` argument
where it previously was the range of the entire `open` call:

Before:

```
UP015.py:2:1: UP015 [*] Unnecessary mode argument
  |
1 | open("foo", "U")
2 | open("foo", "Ur")
  | ^^^^^^^^^^^^^^^^^ UP015
3 | open("foo", "Ub")
4 | open("foo", "rUb")
  |
  = help: Remove mode argument
```


Now:

```
UP015.py:2:13: UP015 [*] Unnecessary mode argument
  |
1 | open("foo", "U")
2 | open("foo", "Ur")
  |             ^^^^ UP015
3 | open("foo", "Ub")
4 | open("foo", "rUb")
  |
  = help: Remove mode argument
```

This is a breaking change because it may require moving a `noqa` comment
onto a different line, e.g if you have

```py
open(
    "foo",
    "Ur",
) # noqa: UP015
```

Needs to be rewritten to 

```py
open(
    "foo",
    "Ur", # noqa: UP015
)
```

There have been now new issues or PRs since the new preview behavior was
implemented. It first was released as part of Ruff 0.9.5 on the 5th of
Feb (a little more than a month ago)

## Test Plan

I reviewed the snapshot tests
2025-03-13 15:37:37 +01:00
Micha Reiser 92193a3254 Consider all `TYPE_CHECKING` symbols for type-checking blocks (#16669)
## Summary

This PR stabilizes the preview behavior introduced in
https://github.com/astral-sh/ruff/pull/15719 to recognize all symbols
named `TYPE_CHECKING` as type-checking
checks in `if TYPE_CHECKING` conditions. This ensures compatibility with
mypy and pyright.

This PR also stabilizes the new behavior that removes `if 0:` and `if
False` to be no longer considered type checking blocks.
Since then, this syntax has been removed from the typing spec and was
only used for Python modules that don't have a `typing` module
([comment](https://github.com/astral-sh/ruff/pull/15719#issuecomment-2612787793)).

The preview behavior was first released with Ruff 0.9.5 (6th of
February), which was about a month ago. There are no open issues or PRs
for the changed behavior


## Test Plan

The snapshots for `SIM108` change because `SIM108` ignored type checking
blocks but it can no
simplify `if 0` or `if False` blocks again because they're no longer
considered type checking blocks.

The changes in the `TC005` snapshot or only due to that `if 0` and `if
False` are no longer recognized as type checking blocks

<!-- How was it tested? -->
2025-03-13 15:37:37 +01:00
Micha Reiser 3d2f2a2f8d [`pep8-naming`]: Ignore methods decorated with `@typing.override` (`invalid-argument-name`) (#16667)
## Summary

This PR stabilizes the preview behavior for `invalid-argument-name`
(`N803`)
to ignore argument names of functions decorated with `typing.override`
because
these methods are *out of the authors* control. 

This behavior was introduced in
https://github.com/astral-sh/ruff/pull/15954
and released as part of Ruff 0.9.5 (6th of February). 

There have been no new issues or PRs since this behavior change
(preview) was introduced.
2025-03-13 15:37:37 +01:00
Micha Reiser 0aded52c40 Stabilize FURB169 preview behavior (#16666)
## Summary

This PR stabilizes the preview behavior introduced in
https://github.com/astral-sh/ruff/pull/15905

The behavior change is that the rule now also recognizes `type(expr) is
type(None)` comparisons where `expr` isn't a name expression.
For example, the rule now detects `type(a.b) is type(None)` and suggests
rewriting the comparison to `a.b is None`.

The new behavior was introduced with Ruff 0.9.5 (6th of February), about
a month ago. There are no open issues or PRs related to this rule (or
behavior change).
2025-03-13 15:37:37 +01:00
Micha Reiser 7af5b98606 [`pylint`] Detect invalid default value type for `os.environ.get` (`PLW1508`) (#16674)
## Summary
This PR stabilizes the new behavior introduced in
https://github.com/astral-sh/ruff/pull/14512 to also detect defalut
value arguemnts to `os.environ.get` that have an invalid type (not
`str`).
There's an upstream issue for this behavior change
https://github.com/pylint-dev/pylint/issues/10092 that was accepted and
a PR, but it hasn't been merged yet.

This behavior change was first shipped with Ruff 0.8.1 (Nov 22). 

There has only be one PR since the new behavior was introduced but it
was unrelated to the scope increase
(https://github.com/astral-sh/ruff/pull/14841).
2025-03-13 15:37:37 +01:00
Micha Reiser b9ed3e3876 [`flake8-pytest-style`] Allow for loops with empty bodies (`PT012`, `PT031`) (#16678)
## Summary

This PR stabilizes the behavior change introduced in
https://github.com/astral-sh/ruff/pull/15542 to allow
for statements with an empty body in `pytest.raises` and `pytest.warns`
with statements.

This raised an error before but is now allowed:

```py
with pytest.raises(KeyError, match='unknown'):
    async for _ in gpt.generate(gpt_request):
        pass
```

The same applies to 

```py
with pytest.raises(KeyError, match='unknown'):
    async for _ in gpt.generate(gpt_request):
        ...
```


There have been now new issues or PRs related to PT012 or PT031 since
this behavior change was introduced in ruff 0.9.3 (January 23rd).
2025-03-13 15:37:37 +01:00
Micha Reiser e740286bbd [`pyupgrade`]: Deprecate `non-pep604-isinstance` (`UP038`) (#16681)
## Summary

This PR deprecates UP038. Using PEP 604 syntax in `isinstance` and
`issubclass` calls isn't a recommended pattern (or community agreed best
practice)
and it negatively impacts performance. 

Resolves https://github.com/astral-sh/ruff/issues/7871

## Test Plan

I tested that selecting `UP038` results in a warning in no-preview mode
and an error in preview mode
2025-03-13 15:37:37 +01:00
Brent Westbrook 776a401703 [`flake8-type-checking`] Stabilize `runtime-cast-value` (`TC006`) (#16637)
Summary
--

Stabilizes TC006. The test was already in the right place.

Test Plan
--

No open issues or PRs. The last related [issue] was closed on
2025-02-09.

[issue]: https://github.com/astral-sh/ruff/issues/16037
2025-03-13 15:37:37 +01:00
Brent Westbrook 5c3d555950 [`flake8-bandit`] Stabilize `unsafe-markup-use` (`S704`) (#16643)
Summary
--

Stabilizes S704, which is also being recoded from RUF035 in 0.10.

Test Plan
--
Existing tests with `PreviewMode` removed from the settings.

There was one issue closed on 2024-12-20 calling the rule noisy and
asking for a config option, but the option was added and then there were
no more issues or PRs.
2025-03-13 15:37:37 +01:00
Brent Westbrook ba37c7cdba [`flake8-datetimez`] Stabilize `datetime-min-max` (`DTZ901`) (#16635)
Summary
--

Stabilizes DTZ901, renames the rule function to match the rule name,
removes the `preview_rules` test, and handles some nits in the docs
(mention `min` first to match the rule name too).

Test Plan
--

1 closed issue on 2024-11-12, 4 days after the rule was added. No issues
since
2025-03-13 15:37:37 +01:00
InSync c605ce6fe2 Use inline snapshots in `# noqa` unit tests (#16687)
## Summary

Follow-up to #16677.

This change converts all unit tests (69 of them) in `noqa.rs` to use
inline snapshots instead. It extends the file by more than 1000 lines,
but the tests are now much easier to read and reason about.

## Test Plan

`cargo insta test`.
2025-03-13 15:37:37 +01:00
Brent Westbrook 16b0902e28 [`ruff`] Stabilize `unnecessary-nested-literal` (`RUF041`) (#16648)
Summary
--

Stabilizes RUF041. The tests are already in the right place, and the
docs look good.

Test Plan
--

0 issues, 1 [PR] fixing nested literals and unions the day after the
rule was added. No changes since then

I wonder if the fix in that PR could be relevant for
https://github.com/astral-sh/ruff/pull/16639, where I noticed a
potential issue with `Union`. It could be unrelated, though.

[PR]: https://github.com/astral-sh/ruff/pull/14641
2025-03-13 15:37:37 +01:00
Brent Westbrook 5bcc0c460b [`flake8-use-pathlib`] Stabilize `invalid-pathlib-with-suffix` (`PTH210`) (#16656)
Summary
--

Stabilizes PTH210. Tests and docs looked good.

Test Plan
--

Mentioned in 1 open issue around Python 3.14 support (`"."` becomes a
valid suffix in 3.14). Otherwise no issues or PRs since 2024-12-12, 6
days after the rule was added.
2025-03-13 15:37:37 +01:00
InSync 62e9c9e506 Add missing unit tests for `# noqa: A`-like cases (#16677)
## Summary

Follow-up to #16659.

This change adds tests for these three cases, which are (also) not
covered by existing tests:

* `# noqa: A` (lone incomplete code)
* `# noqa: A123, B` (complete codes, last one incomplete)
* `# noqa: A123B` (squashed codes, last one incomplete)
2025-03-13 15:37:37 +01:00
Brent Westbrook a6d4e5aae4 [`ruff`] Stabilize `if-key-in-dict-del` (`RUF051`) (#16658)
Summary
--

Stabilizes RUF051. The tests and docs looked good.

Test Plan
--

1 closed documentation issue from 4 days after the rule was added and 1
typo fix from the same day it was added, but no other issues or PRs.
2025-03-13 15:37:37 +01:00
Brent Westbrook 692b651776 [`flake8-bugbear`] Stabilize `batched-without-explicit-strict` (`B911`) (#16655)
Summary
--

Stabilizes B911. Tests and docs looked good.

Test Plan
--

0 issues or PRs, open or closed
2025-03-13 15:37:37 +01:00
Brent Westbrook 2f2d72dc95 [`flake8-logging`] Stabilize `root-logger-call` (`LOG015`) (#16654)
Summary
--

Stabilizes LOG015. The tests and docs looked good.

Test Plan
--

1 closed documentation issue from 4 days after the rule was added, but
no other issues or PRs.
2025-03-13 15:37:37 +01:00
Brent Westbrook d15641faea [`ruff`] Stabilize `map-int-version-parsing` (`RUF048`) (#16653)
Summary
--

Stabilizes RUF048 and moves its test to the right place. The docs look
good.

Test Plan
--

0 closed or open issues. There was 1 [PR] related to an extension to the
rule, but it was closed without comment.

[PR]: https://github.com/astral-sh/ruff/pull/14701
2025-03-13 15:37:37 +01:00
Brent Westbrook 9b1b3c1859 [`ruff`] Stabilize `unnecessary-cast-to-int` (`RUF046`) (#16649)
Summary
--

Stabilizes RUF046 and moves its test to the right place. The docs look
good.

Test Plan
--

2 closed newline/whitespace issues from early January and 1 closed issue
about really being multiple rules, but otherwise no recent issues or
PRs.
2025-03-13 15:37:37 +01:00
Brent Westbrook e4b46913d2 [`ruff`] Stabilize `invalid-assert-message-literal-argument` (`RUF040`) (#16646)
Summary
--

Stabilizes RUF040 and fixes a very minor typo in the docs. The tests are
already in the right place.

Test Plan
--

0 issues or PRs
2025-03-13 15:37:37 +01:00
Brent Westbrook f48bc3aea1 [`flake8-use-pathlib`] Stabilize `os-listdir` (`PTH208`) (#16642)
Summary
--

Stabilizes PTH208. The test was already in the right place, and the docs
look good.

Test Plan
--

0 issues and PRs, open or closed
2025-03-13 15:37:37 +01:00
Brent Westbrook 64b248ccfc [`flake8-type-checking`] Stabilize `unquoted-type-alias` (`TC007`) (#16638)
Summary
--

Stabilizes TC007. The test was already in the right place.

Test Plan
--

No open issues or PRs.
2025-03-13 15:37:37 +01:00
InSync c236be4320 Add missing unit tests for `# noqa:`-like cases (#16659) 2025-03-13 15:37:37 +01:00
Dylan 8bd140c99d Make noqa parsing consistent and more robust (#16483)
# Summary
The goal of this PR is to address various issues around parsing
suppression comments by

1. Unifying the logic used to parse in-line (`# noqa`) and file-level
(`# ruff: noqa`) noqa comments
2. Recovering from certain errors and surfacing warnings in these cases

Closes #15682 
Supersedes #12811 
Addresses
https://github.com/astral-sh/ruff/pull/14229#discussion_r1835481018
Related: #14229 , #12809
2025-03-13 15:37:37 +01:00
Brent Westbrook a04347b7a3 [`flake8-builtins`] Default to non-strict checking (`A005`) (#16125)
## Summary

This PR changes the default value of
`lint.flake8-builtins.builtins-strict-checking` added in
https://github.com/astral-sh/ruff/pull/15951 from `true` to `false`.
This also allows simplifying the default option logic and removes the
dependence on preview mode.

https://github.com/astral-sh/ruff/issues/15399 was already closed by
#15951, but this change will finalize the behavior mentioned in
https://github.com/astral-sh/ruff/issues/15399#issuecomment-2587017147.

As an example, strict checking flags modules based on their last
component, so `utils/logging.py` triggers A005. Non-strict checking
checks the path to the module, so `utils/logging.py` is allowed (this is
the example and desired behavior from #15399 exactly) but a top-level
`logging.py` or `logging/__init__.py` is still disallowed.

## Test Plan

Existing tests from #15951 and #16006, with the snapshot updated in
`a005_module_shadowing_strict_default` to reflect the new default.
2025-03-13 15:37:37 +01:00
Brent Westbrook 958e1177ce [`pyupgrade`] Stabilize `non-pep646-unpack` (`UP044`) (#16632)
Summary
--

Stabilizes UP044, renames the module to match the rule name, and removes
the `PreviewMode` from the test settings.

Test Plan
--

2 closed issues in November, just after the rule was added, otherwise no
issues
2025-03-13 15:37:37 +01:00
Brent Westbrook fce5d892c1 [`flake8-simplify`] Stabilize `split-static-string` (`SIM905`) (#16631)
Summary
--

Stabilizes SIM905 and adds a small addition to the docs. The test was
already in the right place.

Test Plan
--

No issues except 2 recent, general issues about whitespace
normalization.
2025-03-13 15:37:37 +01:00
Alex Waygood 66cae0a3ec [ruff-0.10] [`flake8-pyi`] Stabilize preview-mode behaviours for `custom-type-var-for-self`(`PYI019`) (#16607)
## Summary

This PR stabilizes several preview-only behaviours for
`custom-typevar-for-self` (`PYI019`). Namely:
- A new, more accurate technique is now employed for detecting custom
TypeVars that are replaceable with `Self`. See
https://github.com/astral-sh/ruff/pull/15888 for details.
- The range of the diagnostic is now the full function header rather
than just the return annotation. (Previously, the rule only applied to
methods with return annotations, but this is no longer true due to the
changes in the first bullet point.)
- The fix is now available even when preview mode is not enabled.

## Test Plan

- Existing snapshots that do not have preview mode enabled are updated
- Preview-specific snapshots are removed
- I'll check the ecosystem report on this PR to verify everything's as
expected
2025-03-13 15:37:37 +01:00
Brent Westbrook bbcddf7e79 [`pylint`] Stabilize `len-test` (`PLC1802`) (#16626)
Summary
--

Stabilizes PLC1802. The tests were already in the right place, and I
just tidied the docs a little bit.

Test Plan
--

1 issue closed 4 days after the rule was added, no other issues
2025-03-13 15:37:37 +01:00
Brent Westbrook c387a51cad [`pylint`] Stabilize `shallow-copy-environ` (`PLW1507`) (#16627)
Summary
--

Stabilizes PLW1507. The tests were already in the right place, and I
just tidied the docs a little bit.

Test Plan
--

1 issue from 2 weeks ago but just suggesting to mark the fix unsafe. The
shallow vs deep copy *does* change the program behavior, just usually in
a preferable way.
2025-03-13 15:37:37 +01:00
Brent Westbrook 5285e3fcbc [`FastAPI`] Stabilize `fast-api-unused-path-parameter` (`FAST003`) (#16625)
## Summary

Stabilizes FAST003, completing the group with FAST001 and FAST002.

## Test Plan

Last bug fix (false positive) was fixed on 2025-01-13, almost 2 months
ago.

The test case was already in the right place.
2025-03-13 15:37:37 +01:00
Brent Westbrook ed4152dec6 [`flake8-comprehensions`] Stabilize `unnecessary-dict-comprehension-for-iterable` (`C420`) (#16624)
## Summary

Stabilizes C420 for the 0.10 release.

## Test Plan

No open issues or PRs (except a general issue about [string
normalization](https://github.com/astral-sh/ruff/issues/16579)). The
last (and only) false-negative bug fix was over a month ago.

The tests for this rule were already not on the `preview_rules` test, so
I just changed the `RuleGroup`. The documentation looked okay to me.
2025-03-13 15:37:37 +01:00
InSync 24ec94562c [`flake8-builtins`] Remove `builtins-` prefix from option names (#16092)
## Summary

Resolves #15368.

The following options have been renamed:

* `builtins-allowed-modules` &rarr; `allowed-modules`
* `builtins-ignorelist` &rarr; `ignorelist`
* `builtins-strict-checking` &rarr; `strict-checking`

To preserve compatibility, the old names are kept as Serde aliases.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-13 15:37:37 +01:00
David Salvisberg c0b1413ecd [`flake8-bandit`] Move `unsafe-markup-use` from `RUF035` to `S704` (#15957)
## Summary

`RUF035` has been backported into bandit as `S704` in this
[PR](https://github.com/PyCQA/bandit/pull/1225)

This moves the rule and its corresponding setting to the `flake8-bandit`
category

## Test Plan

`cargo nextest run`

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-13 15:37:37 +01:00
Dhruv Manilawala 798fa47c2e Server: Remove log notification for `printDebugInformation` command (#16617)
## Summary

For context, the initial implementation started out by sending a log
notification to the client to include this information in the client
channel. This is a bit ineffective because it doesn't allow the client
to display this information in a more obvious way. In addition to that,
it isn't obvious from a users perspective as to where the information is
being printed unless they actually open the output channel.

The change was to actually return this formatted string that contains
the information and let the client handle how it should display this
information. For example, in the Ruff VS Code extension we open a split
window and show this information which is similar to what rust-analyzer
does.

The notification request was kept as a precaution in case there are
users who are actually utilizing this way. If they exists, it should a
minority as it requires the user to actually dive into the code to
understand how to hook into this notification. With 0.10, we're removing
the old way as it only clobbers the output channel with a long message.

fixes: #16225

## Test Plan

Tested it out locally that the information is not being logged to the
output channel of VS Code.
2025-03-13 15:37:37 +01:00
Micha Reiser a4b7c4ef70 [formatter] Stabilize fix for single-with-item formatting with trailing comment (#16603)
## Summary

This PR stabilizies the fix for
https://github.com/astral-sh/ruff/issues/14001

We try to only make breaking formatting changes once a year. However,
the plan was to release this fix as part of Ruff 0.9 but I somehow
missed it when promoting all other formatter changes.
I think it's worth making an exception here considering that this is a
bug fix, it improves readability, and it should be rare
(very few files in a single project). Our version policy explicitly
allows breaking formatter changes in any minor release and the idea of
only making breaking formatter changes once a year is mainly to avoid
multiple releases throughout the year that introduce large formatter
changes

Closes https://github.com/astral-sh/ruff/issues/14001

## Test Plan

Updated snapshot
2025-03-13 15:37:37 +01:00
Peter Hill a90cf9d59c
[ruff] Fix `last_tag`/`commits_since_last_tag` for `version` command (#16686)
## Summary

Since Ruff changed to GitHub releases, tags are no longer annotated and
`git describe` no longer picks them up. Instead, it's necessary to also
search lightweight tags.

This changes fixes the `version` command to give more accurate
`last_tag`/`commits_since_last_tag` information. This only affects
development builds, as this information is not present in releases.

## Test Plan

Testing is a little tricky because this information changes on every
commit. Running manually on current `main` and my branch:

`main`:

```
# cargo run --bin ruff -- version --output-format=text
ruff 0.9.10+2547 (dd2313ab0 2025-03-12)

# cargo run --bin ruff -- version --output-format=json
{
  "version": "0.9.10",
  "commit_info": {
    "short_commit_hash": "dd2313ab0",
    "commit_hash": "dd2313ab0faea90abf66a75f1b5c388e728d9d0a",
    "commit_date": "2025-03-12",
    "last_tag": "v0.4.10",
    "commits_since_last_tag": 2547
  }
}
```

This PR:

```
# cargo run --bin ruff -- version --output-format=text
ruff 0.9.10+46 (11f39f616 2025-03-12)

# cargo run --bin ruff -- version --output-format=json
{
  "version": "0.9.10",
  "commit_info": {
    "short_commit_hash": "11f39f616",
    "commit_hash": "11f39f6166c3d7a521725b938a166659f64abb59",
    "commit_date": "2025-03-12",
    "last_tag": "0.9.10",
    "commits_since_last_tag": 46
  }
}
```
2025-03-13 11:59:54 +00:00
Dhruv Manilawala 58d5fe982e
[red-knot] Check gradual equivalence between callable types (#16634) 2025-03-13 08:16:51 +05:30
Dhruv Manilawala 08fa9b4a90
[red-knot] Add `CallableTypeFromFunction` special form (#16683)
## Summary

This PR adds a new `CallableTypeFromFunction` special form to allow
extracting the abstract signature of a function literal i.e., convert a
`Type::Function` into a `Type::Callable` (`CallableType::General`).

This is done to support testing the `is_gradual_equivalent_to` type
relation specifically the case we want to make sure that a function that
has parameters with no annotations and does not have a return type
annotation is gradual equivalent to `Callable[[Any, Any, ...], Any]`
where the number of parameters should match between the function literal
and callable type.

Refer
https://github.com/astral-sh/ruff/pull/16634#discussion_r1989976692

### Bikeshedding

The name `CallableTypeFromFunction` is a bit too verbose. A possibly
alternative from Carl is `CallableTypeOf` but that would be similar to
`TypeOf` albeit with a limitation that the former only accepts function
literal types and errors on other types.

Some other alternatives:
* `FunctionSignature`
* `SignatureOf` (similar issues as `TypeOf`?)
* ...

## Test Plan

Update `type_api.md` with a new section that tests this special form,
both invalid and valid forms.
2025-03-13 07:49:34 +05:30
David Peter dd2313ab0f
[red-knot] Add mypy_primer usage documentation (#16679)
## Summary

Add documentation on how to run mypy_primer locally.
2025-03-12 16:47:10 +01:00
David Peter 083df0cf84
[red-knot] Support custom `__getattr__` methods (#16668)
## Summary

Add support for calling custom `__getattr__` methods in case an
attribute is not otherwise found. This allows us to get rid of many
ecosystem false positives where we previously emitted errors when
accessing attributes on `argparse.Namespace`.

closes #16614

## Test Plan

* New Markdown tests
* Observed expected ecosystem changes (the changes for `arrow` also look
fine, since the `Arrow` class has a custom [`__getattr__`
here](1d70d00919/arrow/arrow.py (L802-L815)))
2025-03-12 13:44:11 +01:00
Carl Meyer a176c1ac80
[red-knot] use fixpoint iteration for all cycles (#14029)
Pulls in the latest Salsa main branch, which supports fixpoint
iteration, and uses it to handle all query cycles.

With this, we no longer need to skip any corpus files to avoid panics.

Latest perf results show a 6% incremental and 1% cold-check regression.
This is not a "no cycles" regression, as tomllib and typeshed do trigger
some definition cycles (previously handled by our old
`infer_definition_types` fallback to `Unknown`). We don't currently have
a benchmark we can use to measure the pure no-cycles regression, though
I expect there would still be some regression; the fixpoint iteration
feature in Salsa does add some overhead even for non-cyclic queries.

I think this regression is within the reasonable range for this feature.
We can do further optimization work later, but I don't think it's the
top priority right now. So going ahead and acknowledging the regression
on CodSpeed.

Mypy primer is happy, so this doesn't regress anything on our
currently-checked projects. I expect it probably unlocks adding a number
of new projects to our ecosystem check that previously would have
panicked.

Fixes #13792
Fixes #14672
2025-03-12 12:41:40 +00:00
David Peter a6572a57c4
[red-knot] Attribute access on intersection types (#16665)
## Summary

Implements attribute access on intersection types, which didn't
previously work. For example:

```py
from typing import Any

class P: ...
class Q: ...

class A:
    x: P = P()

class B:
    x: Any = Q()

def _(obj: A):
    if isinstance(obj, B):
        reveal_type(obj.x)  # revealed: P & Any
```

Refers to [this comment].

[this comment]:
https://github.com/astral-sh/ruff/pull/16416#discussion_r1985040363

## Test Plan

New Markdown tests
2025-03-12 13:20:17 +01:00
Joey Bar b250304ad3
[red-knot] Improve is_disjoint for two intersections (#16636)
## Summary

Background - as a follow up to #16611 I noticed that there's a lot of
code duplicated between the `is_assignable_to` and `is_subtype_of`
functions and considered trying to merge them.

[A subtype and an assignable type are pretty much the
same](https://typing.python.org/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation),
except that subtypes are by definition fully static, so I think we can
replace the whole of `is_subtype_of` with:

```
if !self.is_fully_static(db) || !target.is_fully_static(db) {
    return false;
}
return self.is_assignable_to(target)
```

if we move all of the logic to is_assignable_to and delete duplicate
code. Then we can discuss if it even makes sense to have a separate
is_subtype_of function (I think the answer is yes since it's used by a
bunch of other places, but we may be able to basically rip out the
concept).

Anyways while playing with combining the functions I noticed is that the
handling of Intersections in `is_subtype_of` has a special case for two
intersections, which I didn't include in the last PR - rather I first
handled right hand intersections before left hand, which should properly
handle double intersections (hand-wavy explanation I can justify if
needed - (A & B & C) is assignable to (A & B) because the left is
assignable to both A and B, but none of A, B, or C is assignable to (A &
B)).

I took a look at what breaks if I remove the handling for double
intersections, and the reason it is needed is because is_disjoint does
not properly handle intersections with negative conditions (so instead
`is_subtype_of` basically implements the check correctly).

This PR adds support to is_disjoint for properly checking negative
branches, which also lets us simplify `is_subtype_of`, bringing it in
line with `is_assignable_to`

## Test Plan

Added a bunch of tests, most of which failed before this fix

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-12 12:13:04 +00:00
David Peter 11b5cbcd2f
[red-knot] Restructure attribute-access and descriptor-protocol test suites. (#16664)
## Summary

This is a pure restructuring of the `attributes.md` and
`descriptor_protocol.md` test suites. They have grown organically and I
didn't want to make major structural changes in my recent PR to keep the
diff clean.
2025-03-12 09:52:21 +01:00
David Peter 3228545598
[red-knot] Minor optimization/cleanup in member lookup (#16663)
## Summary

A follow up to address [this comment]:

> Similarly here, it might be a little more performant to have a single
`Type::instance()` branch with an inner match over `class.known()`
rather than having multiple branches with `if class.is_known()` guards

[this comment]:
https://github.com/astral-sh/ruff/pull/16416#discussion_r1985159037
2025-03-12 09:11:05 +01:00
David Peter 860b95a318
[red-knot] Binary operator inference for union types (#16601)
## Summary

Properly handle binary operator inference for union types.

This fixes a bug I noticed while looking at ecosystem results. The MRE
version of it is this:

```py
def sub(x: float, y: float):
    # Red Knot: Operator `-` is unsupported between objects of type `int | float` and `int | float`
    return x - y
```

## Test Plan

- New Markdown tests.
- Expected diff in the ecosystem checks
2025-03-12 08:21:54 +01:00
Dhruv Manilawala 6de2b2873b
[red-knot] Check if callable type is fully static (#16633)
## Summary

Part of #15382 

This PR adds the check for whether a callable type is fully static or
not.

A callable type is fully static if all of the parameter types are fully
static _and_ the return type is fully static _and_ if it does not use
the gradual form (`...`) for its parameters.

## Test Plan

Update `is_fully_static.md` with callable types.

It seems that currently this test is grouped into either fully static or
not, I think it would be useful to split them up in groups like
callable, etc. I intentionally avoided that in this PR but I'll put up a
PR for an appropriate split.

Note: I've an explicit goal of updating the property tests with the new
callable types once all relations are implemented.
2025-03-12 12:13:22 +05:30
Dhruv Manilawala 6b84253679
[red-knot] Callable member lookup, meta type impl (#16618)
## Summary

This PR is a follow-up to https://github.com/astral-sh/ruff/pull/16493
that implements member lookup for the general callable type.

Based on the discussion around [member lookup
here](https://github.com/astral-sh/ruff/pull/16493#discussion_r1982041180)
and [`.to_meta_type()`
here](https://github.com/astral-sh/ruff/pull/16493#discussion_r1985104664).

## Test Plan

Add a new test cases.
2025-03-12 12:01:38 +05:30
Carl Meyer 0340e23395
[red-knot] remove redundant sentence in test (#16660)
Removes a redundant sentence I accidentally left in the test suite from
in #16540 (my mistake).
2025-03-12 04:20:31 +00:00
Shunsuke Shibayama 78b5f0b165
[red-knot] detect invalid return type (#16540)
## Summary

This PR closes #16248.

If the return type of the function isn't assignable to the one
specified, an `invalid-return-type` error occurs.
I thought it would be better to report this as a different kind of error
than the `invalid-assignment` error, so I defined this as a new error.

## Test Plan

All type inconsistencies in the test cases have been replaced with
appropriate ones.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-12 01:58:59 +00:00
Douglas Creager e17cd350b6
[red-knot] Support multiple overloads when binding parameters at call sites (#16568)
This updates the `Signature` and `CallBinding` machinery to support
multiple overloads for a callable. This is currently only used for
`KnownFunction`s that we special-case in our type inference code. It
does **_not_** yet update the semantic index builder to handle
`@overload` decorators and construct a multi-signature `Overloads`
instance for real Python functions.

While I was here, I updated many of the `try_call` special cases to use
signatures (possibly overloaded ones now) and `bind_call` to check
parameter lists. We still need some of the mutator methods on
`OverloadBinding` for the special cases where we need to update return
types based on some Rust code.
2025-03-11 15:08:17 -04:00
Alex Waygood c16237ddc0
[red-knot] Rework `Type::to_instance()` to return `Option<Type>` (#16428)
## Summary

This PR fixes https://github.com/astral-sh/ruff/issues/16302.

The PR reworks `Type::to_instance()` to return `Option<Type>` rather
than `Type`. This reflects more accurately the fact that some variants
cannot be "turned into an instance", since they _already_ represent
instances of some kind. On `main`, we silently fallback to `Unknown` for
these variants, but this implicit behaviour can be somewhat surprising
and lead to unexpected bugs.

Returning `Option<Type>` rather than `Type` means that each callsite has
to account for the possibility that the type might already represent an
instance, and decide what to do about it.
In general, I think this increases the robustness of the code. Working
on this PR revealed two latent bugs in the code:
- One which has already been fixed by
https://github.com/astral-sh/ruff/pull/16427
- One which is fixed as part of https://github.com/astral-sh/ruff/pull/16608

I added special handling to `KnownClass::to_instance()`: If we fail to find one of these classes and the `test` feature is
_not_ enabled, we log a warning to the terminal saying that we failed to
find the class in typeshed and that we will be falling back to
`Type::Unknown`. A cache is maintained so that we record all classes
that we have already logged a warning for; we only log a warning for
failing to lookup a `KnownClass` if we know that it's the first time
we're looking it up.

## Test Plan

- All existing tests pass
- I ran the property tests via `QUICKCHECK_TESTS=1000000 cargo test
--release -p red_knot_python_semantic -- --ignored
types::property_tests::stable`

I also manually checked that warnings are appropriately printed to the
terminal when `KnownClass::to_instance()` falls back to `Unknown` and
the `test` feature is not enabled. To do this, I applied this diff to
the PR branch:

<details>
<summary>Patch deleting `int` and `str` from buitins</summary>

```diff
diff --git a/crates/red_knot_vendored/vendor/typeshed/stdlib/builtins.pyi b/crates/red_knot_vendored/vendor/typeshed/stdlib/builtins.pyi
index 0a6dc57b0..86636a05b 100644
--- a/crates/red_knot_vendored/vendor/typeshed/stdlib/builtins.pyi
+++ b/crates/red_knot_vendored/vendor/typeshed/stdlib/builtins.pyi
@@ -228,111 +228,6 @@ _PositiveInteger: TypeAlias = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
 _NegativeInteger: TypeAlias = Literal[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20]
 _LiteralInteger = _PositiveInteger | _NegativeInteger | Literal[0]  # noqa: Y026  # TODO: Use TypeAlias once mypy bugs are fixed
 
-class int:
-    @overload
-    def __new__(cls, x: ConvertibleToInt = ..., /) -> Self: ...
-    @overload
-    def __new__(cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self: ...
-    def as_integer_ratio(self) -> tuple[int, Literal[1]]: ...
-    @property
-    def real(self) -> int: ...
-    @property
-    def imag(self) -> Literal[0]: ...
-    @property
-    def numerator(self) -> int: ...
-    @property
-    def denominator(self) -> Literal[1]: ...
-    def conjugate(self) -> int: ...
-    def bit_length(self) -> int: ...
-    if sys.version_info >= (3, 10):
-        def bit_count(self) -> int: ...
-
-    if sys.version_info >= (3, 11):
-        def to_bytes(
-            self, length: SupportsIndex = 1, byteorder: Literal["little", "big"] = "big", *, signed: bool = False
-        ) -> bytes: ...
-        @classmethod
-        def from_bytes(
-            cls,
-            bytes: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer,
-            byteorder: Literal["little", "big"] = "big",
-            *,
-            signed: bool = False,
-        ) -> Self: ...
-    else:
-        def to_bytes(self, length: SupportsIndex, byteorder: Literal["little", "big"], *, signed: bool = False) -> bytes: ...
-        @classmethod
-        def from_bytes(
-            cls,
-            bytes: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer,
-            byteorder: Literal["little", "big"],
-            *,
-            signed: bool = False,
-        ) -> Self: ...
-
-    if sys.version_info >= (3, 12):
-        def is_integer(self) -> Literal[True]: ...
-
-    def __add__(self, value: int, /) -> int: ...
-    def __sub__(self, value: int, /) -> int: ...
-    def __mul__(self, value: int, /) -> int: ...
-    def __floordiv__(self, value: int, /) -> int: ...
-    def __truediv__(self, value: int, /) -> float: ...
-    def __mod__(self, value: int, /) -> int: ...
-    def __divmod__(self, value: int, /) -> tuple[int, int]: ...
-    def __radd__(self, value: int, /) -> int: ...
-    def __rsub__(self, value: int, /) -> int: ...
-    def __rmul__(self, value: int, /) -> int: ...
-    def __rfloordiv__(self, value: int, /) -> int: ...
-    def __rtruediv__(self, value: int, /) -> float: ...
-    def __rmod__(self, value: int, /) -> int: ...
-    def __rdivmod__(self, value: int, /) -> tuple[int, int]: ...
-    @overload
-    def __pow__(self, x: Literal[0], /) -> Literal[1]: ...
-    @overload
-    def __pow__(self, value: Literal[0], mod: None, /) -> Literal[1]: ...
-    @overload
-    def __pow__(self, value: _PositiveInteger, mod: None = None, /) -> int: ...
-    @overload
-    def __pow__(self, value: _NegativeInteger, mod: None = None, /) -> float: ...
-    # positive __value -> int; negative __value -> float
-    # return type must be Any as `int | float` causes too many false-positive errors
-    @overload
-    def __pow__(self, value: int, mod: None = None, /) -> Any: ...
-    @overload
-    def __pow__(self, value: int, mod: int, /) -> int: ...
-    def __rpow__(self, value: int, mod: int | None = None, /) -> Any: ...
-    def __and__(self, value: int, /) -> int: ...
-    def __or__(self, value: int, /) -> int: ...
-    def __xor__(self, value: int, /) -> int: ...
-    def __lshift__(self, value: int, /) -> int: ...
-    def __rshift__(self, value: int, /) -> int: ...
-    def __rand__(self, value: int, /) -> int: ...
-    def __ror__(self, value: int, /) -> int: ...
-    def __rxor__(self, value: int, /) -> int: ...
-    def __rlshift__(self, value: int, /) -> int: ...
-    def __rrshift__(self, value: int, /) -> int: ...
-    def __neg__(self) -> int: ...
-    def __pos__(self) -> int: ...
-    def __invert__(self) -> int: ...
-    def __trunc__(self) -> int: ...
-    def __ceil__(self) -> int: ...
-    def __floor__(self) -> int: ...
-    def __round__(self, ndigits: SupportsIndex = ..., /) -> int: ...
-    def __getnewargs__(self) -> tuple[int]: ...
-    def __eq__(self, value: object, /) -> bool: ...
-    def __ne__(self, value: object, /) -> bool: ...
-    def __lt__(self, value: int, /) -> bool: ...
-    def __le__(self, value: int, /) -> bool: ...
-    def __gt__(self, value: int, /) -> bool: ...
-    def __ge__(self, value: int, /) -> bool: ...
-    def __float__(self) -> float: ...
-    def __int__(self) -> int: ...
-    def __abs__(self) -> int: ...
-    def __hash__(self) -> int: ...
-    def __bool__(self) -> bool: ...
-    def __index__(self) -> int: ...
-
 class float:
     def __new__(cls, x: ConvertibleToFloat = ..., /) -> Self: ...
     def as_integer_ratio(self) -> tuple[int, int]: ...
@@ -437,190 +332,6 @@ class _FormatMapMapping(Protocol):
 class _TranslateTable(Protocol):
     def __getitem__(self, key: int, /) -> str | int | None: ...
 
-class str(Sequence[str]):
-    @overload
-    def __new__(cls, object: object = ...) -> Self: ...
-    @overload
-    def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
-    @overload
-    def capitalize(self: LiteralString) -> LiteralString: ...
-    @overload
-    def capitalize(self) -> str: ...  # type: ignore[misc]
-    @overload
-    def casefold(self: LiteralString) -> LiteralString: ...
-    @overload
-    def casefold(self) -> str: ...  # type: ignore[misc]
-    @overload
-    def center(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ...
-    @overload
-    def center(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ...  # type: ignore[misc]
-    def count(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
-    def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ...
-    def endswith(
-        self, suffix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /
-    ) -> bool: ...
-    @overload
-    def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ...
-    @overload
-    def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ...  # type: ignore[misc]
-    def find(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
-    @overload
-    def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ...
-    @overload
-    def format(self, *args: object, **kwargs: object) -> str: ...
-    def format_map(self, mapping: _FormatMapMapping, /) -> str: ...
-    def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
-    def isalnum(self) -> bool: ...
-    def isalpha(self) -> bool: ...
-    def isascii(self) -> bool: ...
-    def isdecimal(self) -> bool: ...
-    def isdigit(self) -> bool: ...
-    def isidentifier(self) -> bool: ...
-    def islower(self) -> bool: ...
-    def isnumeric(self) -> bool: ...
-    def isprintable(self) -> bool: ...
-    def isspace(self) -> bool: ...
-    def istitle(self) -> bool: ...
-    def isupper(self) -> bool: ...
-    @overload
-    def join(self: LiteralString, iterable: Iterable[LiteralString], /) -> LiteralString: ...
-    @overload
-    def join(self, iterable: Iterable[str], /) -> str: ...  # type: ignore[misc]
-    @overload
-    def ljust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ...
-    @overload
-    def ljust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ...  # type: ignore[misc]
-    @overload
-    def lower(self: LiteralString) -> LiteralString: ...
-    @overload
-    def lower(self) -> str: ...  # type: ignore[misc]
-    @overload
-    def lstrip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ...
-    @overload
-    def lstrip(self, chars: str | None = None, /) -> str: ...  # type: ignore[misc]
-    @overload
-    def partition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ...
-    @overload
-    def partition(self, sep: str, /) -> tuple[str, str, str]: ...  # type: ignore[misc]
-    if sys.version_info >= (3, 13):
-        @overload
-        def replace(
-            self: LiteralString, old: LiteralString, new: LiteralString, /, count: SupportsIndex = -1
-        ) -> LiteralString: ...
-        @overload
-        def replace(self, old: str, new: str, /, count: SupportsIndex = -1) -> str: ...  # type: ignore[misc]
-    else:
-        @overload
-        def replace(
-            self: LiteralString, old: LiteralString, new: LiteralString, count: SupportsIndex = -1, /
-        ) -> LiteralString: ...
-        @overload
-        def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ...  # type: ignore[misc]
-    if sys.version_info >= (3, 9):
-        @overload
-        def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ...
-        @overload
-        def removeprefix(self, prefix: str, /) -> str: ...  # type: ignore[misc]
-        @overload
-        def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ...
-        @overload
-        def removesuffix(self, suffix: str, /) -> str: ...  # type: ignore[misc]
-
-    def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
-    def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
-    @overload
-    def rjust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ...
-    @overload
-    def rjust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ...  # type: ignore[misc]
-    @overload
-    def rpartition(self: LiteralString, sep: LiteralString, /) -> tuple[LiteralString, LiteralString, LiteralString]: ...
-    @overload
-    def rpartition(self, sep: str, /) -> tuple[str, str, str]: ...  # type: ignore[misc]
-    @overload
-    def rsplit(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ...
-    @overload
-    def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ...  # type: ignore[misc]
-    @overload
-    def rstrip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ...
-    @overload
-    def rstrip(self, chars: str | None = None, /) -> str: ...  # type: ignore[misc]
-    @overload
-    def split(self: LiteralString, sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString]: ...
-    @overload
-    def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ...  # type: ignore[misc]
-    @overload
-    def splitlines(self: LiteralString, keepends: bool = False) -> list[LiteralString]: ...
-    @overload
-    def splitlines(self, keepends: bool = False) -> list[str]: ...  # type: ignore[misc]
-    def startswith(
-        self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /
-    ) -> bool: ...
-    @overload
-    def strip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ...
-    @overload
-    def strip(self, chars: str | None = None, /) -> str: ...  # type: ignore[misc]
-    @overload
-    def swapcase(self: LiteralString) -> LiteralString: ...
-    @overload
-    def swapcase(self) -> str: ...  # type: ignore[misc]
-    @overload
-    def title(self: LiteralString) -> LiteralString: ...
-    @overload
-    def title(self) -> str: ...  # type: ignore[misc]
-    def translate(self, table: _TranslateTable, /) -> str: ...
-    @overload
-    def upper(self: LiteralString) -> LiteralString: ...
-    @overload
-    def upper(self) -> str: ...  # type: ignore[misc]
-    @overload
-    def zfill(self: LiteralString, width: SupportsIndex, /) -> LiteralString: ...
-    @overload
-    def zfill(self, width: SupportsIndex, /) -> str: ...  # type: ignore[misc]
-    @staticmethod
-    @overload
-    def maketrans(x: dict[int, _T] | dict[str, _T] | dict[str | int, _T], /) -> dict[int, _T]: ...
-    @staticmethod
-    @overload
-    def maketrans(x: str, y: str, /) -> dict[int, int]: ...
-    @staticmethod
-    @overload
-    def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ...
-    @overload
-    def __add__(self: LiteralString, value: LiteralString, /) -> LiteralString: ...
-    @overload
-    def __add__(self, value: str, /) -> str: ...  # type: ignore[misc]
-    # Incompatible with Sequence.__contains__
-    def __contains__(self, key: str, /) -> bool: ...  # type: ignore[override]
-    def __eq__(self, value: object, /) -> bool: ...
-    def __ge__(self, value: str, /) -> bool: ...
-    @overload
-    def __getitem__(self: LiteralString, key: SupportsIndex | slice, /) -> LiteralString: ...
-    @overload
-    def __getitem__(self, key: SupportsIndex | slice, /) -> str: ...  # type: ignore[misc]
-    def __gt__(self, value: str, /) -> bool: ...
-    def __hash__(self) -> int: ...
-    @overload
-    def __iter__(self: LiteralString) -> Iterator[LiteralString]: ...
-    @overload
-    def __iter__(self) -> Iterator[str]: ...  # type: ignore[misc]
-    def __le__(self, value: str, /) -> bool: ...
-    def __len__(self) -> int: ...
-    def __lt__(self, value: str, /) -> bool: ...
-    @overload
-    def __mod__(self: LiteralString, value: LiteralString | tuple[LiteralString, ...], /) -> LiteralString: ...
-    @overload
-    def __mod__(self, value: Any, /) -> str: ...
-    @overload
-    def __mul__(self: LiteralString, value: SupportsIndex, /) -> LiteralString: ...
-    @overload
-    def __mul__(self, value: SupportsIndex, /) -> str: ...  # type: ignore[misc]
-    def __ne__(self, value: object, /) -> bool: ...
-    @overload
-    def __rmul__(self: LiteralString, value: SupportsIndex, /) -> LiteralString: ...
-    @overload
-    def __rmul__(self, value: SupportsIndex, /) -> str: ...  # type: ignore[misc]
-    def __getnewargs__(self) -> tuple[str]: ...
-
 class bytes(Sequence[int]):
```

</details>

And then ran red-knot on my
[typeshed-stats](https://github.com/AlexWaygood/typeshed-stats) project
using the command

```
cargo run -p red_knot -- check --project ../typeshed-stats --python-version="3.12" --verbose
```

I observed that the following logs were printed to the terminal, but
that each warning was only printed once (the desired behaviour):

```
INFO Python version: Python 3.12, platform: all
INFO Indexed 15 file(s)
INFO Could not find class `builtins.int` in typeshed on Python 3.12. Falling back to `Unknown` for the symbol instead.
INFO Could not find class `builtins.str` in typeshed on Python 3.12. Falling back to `Unknown` for the symbol instead.
```
2025-03-11 16:42:44 +00:00
Alex Waygood 989075dc16
[red-knot] Add tests asserting that `KnownClass::to_instance()` doesn't unexpectedly fallback to `Type::Unknown` with full typeshed stubs (#16608)
## Summary

One of the motivations in https://github.com/astral-sh/ruff/pull/16428
for panicking when the `test` or `debug_assertions` features are enabled
and a lookup of a `KnownClass` fails is that we've had some latent bugs
in our code where certain variants have been silently falling back to
`Unknown` in every typeshed lookup without us realising. But that in
itself isn't a great motivation for panicking in
`KnownClass::to_instance()`, since we can fairly easily add some tests
that assert that we don't unexpectedly fallback to `Unknown` for any
`KnownClass` variant. This PR adds those tests.

## Test Plan

`cargo test -p red_knot_python_semantic`
2025-03-11 16:12:44 +00:00
Joey Bar e8e24310fb
[red-knot] Handle gradual intersection types in assignability (#16611)
## Summary

This mostly fixes #14899

My motivation was similar to the last comment by @sharkdp there. I ran
red_knot on a codebase and the most common error was patterns like this
failing:

```
def foo(x: str): ...

x: Any = ...
if isinstance(x, str):
    foo(x) # Object of type `Any & str` cannot be assigned to parameter 1 (`x`) of function `foo`; expected type `str`
```

The desired behavior is pretty much to ignore Any/Unknown when resolving
intersection assignability - `Any & str` should be assignable to `str`,
and `str` should be assignable to `str & Any`
 
The fix is actually very similar to the existing code in
`is_subtype_of`, we need to correctly handle intersections on either
side, while being careful to handle dynamic types as desired.

This does not fix the second test case from that issue:

```
static_assert(is_assignable_to(Intersection[Unrelated, Any], Not[tuple[Unrelated, Any]]))
```

but that's misleading because the root cause there has nothing to do
with gradual types. I added a simpler test case that also fails:

```
static_assert(is_assignable_to(Unrelated, Not[tuple[Unrelated]]))
```
This is because we don't determine that Unrelated does not subclass from
tuple so we can't rule out this relation. If that logic is improved then
this fix should also handle the case of the intersection

## Test Plan

Added a bunch of is_assignable_to tests, most of which failed before
this fix.
2025-03-11 07:58:56 -07:00
Dhruv Manilawala da069aa00c
[red-knot] Infer `lambda` expression (#16547)
## Summary

Part of https://github.com/astral-sh/ruff/issues/15382

This PR adds support for inferring the `lambda` expression and return
the `CallableType`.

Currently, this is only limited to inferring the parameters and a todo
type for the return type.

For posterity, I tried using the `file_expression_type` to infer the
return type of lambda but it would always lead to cycle. The main reason
is that in `infer_parameter_definition`, the default expression is being
inferred using `file_expression_type`, which is correct, but it then

Take the following source code as an example:
```py
lambda x=1: x
```

Here's how the code will flow:
* `infer_scope_types` for the global scope
* `infer_lambda_expression`
* `infer_expression` for the default value `1`
* `file_expression_type` for the return type using the body expression.
This is because the body creates it's own scope
* `infer_scope_types` (lambda body scope)
* `infer_name_load` for the symbol `x` whose visible binding is the
lambda parameter `x`
* `infer_parameter_definition` for parameter `x`
* `file_expression_type` for the default value `1`
* `infer_scope_types` for the global scope because of the default
expression

This will then reach to `infer_definition` for the parameter `x` again
which then creates the cycle.

## Test Plan

Add tests around `lambda` expression inference.
2025-03-11 11:25:20 +05:30
David Peter c60e8a037a
[red-knot] Add support for calling `type[…]` (#16597)
## Summary

This fixes the non-diagnostics part of #15948.

## Test Plan

New Markdown tests.

Negative diff on the ecosystem checks:

```diff
zipp (https://github.com/jaraco/zipp)
- error: lint:call-non-callable
-    --> /tmp/mypy_primer/projects/zipp/zipp/__init__.py:393:16
-     |
- 392 |     def _next(self, at):
- 393 |         return self.__class__(self.root, at)
-     |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Object of type `type[Unknown]` is not callable
- 394 |
- 395 |     def is_dir(self):
-     |
- 
- Found 9 diagnostics
+ Found 8 diagnostics

arrow (https://github.com/arrow-py/arrow)
+     |
+     |
+ warning: lint:unused-ignore-comment
+    --> /tmp/mypy_primer/projects/arrow/arrow/arrow.py:576:66
+ 574 |                 values.append(1)
+ 575 |
+ 576 |             floor = self.__class__(*values, tzinfo=self.tzinfo)  # type: ignore[misc]
+     |                                                                  -------------------- Unused blanket `type: ignore` directive
+ 577 |
+ 578 |             if frame_absolute == "week":
- error: lint:call-non-callable
-     --> /tmp/mypy_primer/projects/arrow/arrow/arrow.py:1080:16
-      |
- 1078 |           dt = self._datetime.astimezone(tz)
- 1079 |
- 1080 |           return self.__class__(
-      |  ________________^
- 1081 | |             dt.year,
- 1082 | |             dt.month,
- 1083 | |             dt.day,
- 1084 | |             dt.hour,
- 1085 | |             dt.minute,
- 1086 | |             dt.second,
- 1087 | |             dt.microsecond,
- 1088 | |             dt.tzinfo,
- 1089 | |             fold=getattr(dt, "fold", 0),
- 1090 | |         )
-      | |_________^ Object of type `type[Unknown]` is not callable
- 1091 |
- 1092 |       # string output and formatting
-      |

black (https://github.com/psf/black)
- 
-     |
-     |
- error: lint:call-non-callable
-    --> /tmp/mypy_primer/projects/black/src/blib2to3/pgen2/grammar.py:135:15
- 133 |         Copy the grammar.
- 134 |         """
- 135 |         new = self.__class__()
-     |               ^^^^^^^^^^^^^^^^ Object of type `type[@Todo]` is not callable
- 136 |         for dict_attr in (
- 137 |             "symbol2number",
- Found 328 diagnostics
+ Found 327 diagnostics
```
2025-03-10 13:24:13 +01:00
David Peter ca974706dd
[red-knot] Do not ignore typeshed stubs for 'venv' module (#16596)
## Summary

We currently fail to add the stubs for the `venv` stdlib module because
there is a `venv/` ignore pattern in the top-level `.gitignore` file.

## Test Plan

Ran the typeshed sync workflow manually once to see if the `venv/`
folder is now correctly added.
2025-03-10 09:07:48 +01:00
Alex Waygood b6c7ba4f8e
[red-knot] Reduce Salsa lookups in `Type::find_name_in_mro` (#16582)
## Summary

Theoretically this should be slightly more performant, since the
`class.is_known()` calls each do a separate Salsa lookup, which we can
avoid if we do a single `match` on the value of `class.known()`. It also
ends up being two lines less code overall!

## Test Plan

`cargo test -p red_knot_python_semantic`
2025-03-10 07:55:22 +01:00
Alex Waygood c970b794d0
Fix broken red-knot property tests (#16574)
## Summary

Fixes #16566, fixes #16575

The semantics of `Type::class_member` changed in
https://github.com/astral-sh/ruff/pull/16416, but the property-test
infrastructure was not updated. That means that the property tests were
panicking on the second `expect_type` call here:


0361021863/crates/red_knot_python_semantic/src/types/property_tests.rs (L151-L158)

With the somewhat unhelpful message:

```
Expected a (possibly unbound) type, not an unbound symbol
```

Applying this patch, and then running `QUICKCHECK_TESTS=1000000 cargo
test --release -p red_knot_python_semantic -- --ignored
types::property_tests::stable::equivalent_to_is_reflexive` showed
clearly that it was no longer able to find _any_ methods on _any_
classes due to the change in semantics of `Type::class_member`:

```diff
--- a/crates/red_knot_python_semantic/src/types/property_tests.rs
+++ b/crates/red_knot_python_semantic/src/types/property_tests.rs
@@ -27,7 +27,7 @@
 use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
 
 use crate::db::tests::{setup_db, TestDb};
-use crate::symbol::{builtins_symbol, known_module_symbol};
+use crate::symbol::{builtins_symbol, known_module_symbol, Symbol};
 use crate::types::{
     BoundMethodType, CallableType, IntersectionBuilder, KnownClass, KnownInstanceType,
     SubclassOfType, TupleType, Type, UnionType,
@@ -150,10 +150,11 @@ impl Ty {
             Ty::BuiltinsFunction(name) => builtins_symbol(db, name).symbol.expect_type(),
             Ty::BuiltinsBoundMethod { class, method } => {
                 let builtins_class = builtins_symbol(db, class).symbol.expect_type();
-                let function = builtins_class
-                    .class_member(db, method.into())
-                    .symbol
-                    .expect_type();
+                let Symbol::Type(function, ..) =
+                    builtins_class.class_member(db, method.into()).symbol
+                else {
+                    panic!("no method `{method}` on class `{class}`");
+                };
 
                 create_bound_method(db, function, builtins_class)
             }
```

This PR updates the property-test infrastructure to use `Type::member`
rather than `Type::class_member`.

## Test Plan

- Ran `QUICKCHECK_TESTS=1000000 cargo test --release -p
red_knot_python_semantic -- --ignored types::property_tests::stable`
successfully
- Checked that there were no remaining uses of `Type::class_member` in
`property_tests.rs`
2025-03-09 17:40:08 +00:00
Alex Waygood 335b264fe2
[red-knot] Consistent spelling of "metaclass" and "meta-type" (#16576)
## Summary

Fixes a small nit of mine -- we are currently inconsistent in our
spelling between "metaclass" and "meta class", and between "meta type"
and "meta-type". This PR means that we consistently use "metaclass" and
"meta-type".

## Test Plan

`uvx pre-commit run -a`
2025-03-09 12:30:32 +00:00
Dhruv Manilawala 0361021863
[red-knot] Understand `typing.Callable` (#16493)
## Summary

Part of https://github.com/astral-sh/ruff/issues/15382

This PR implements a general callable type that wraps around a
`Signature` and it uses that new type to represent `typing.Callable`.

It also implements `Display` support for `Callable`. The format is as:
```
([<arg name>][: <arg type>][ = <default type>], ...) -> <return type>
```

The `/` and `*` separators are added at the correct boundary for
positional-only and keyword-only parameters. Now, as `typing.Callable`
only has positional-only parameters, the rendered signature would be:

```py
Callable[[int, str], None]
# (int, str, /) -> None
```

The `/` separator represents that all the arguments are positional-only.

The relationship methods that check assignability, subtype relationship,
etc. are not yet implemented and will be done so as a follow-up.

## Test Plan

Add test cases for display support for `Signature` and various mdtest
for `typing.Callable`.
2025-03-08 03:58:52 +00:00
Eric Mark Martin 24c8b1242e
[red-knot] Support unpacking `with` target (#16469)
## Summary

Resolves #16365

Add support for unpacking `with` statement targets.

## Test Plan

Added some test cases, alike the ones added by #15058.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-08 02:36:35 +00:00
David Peter 820a31af5d
[red-knot] Attribute access and the descriptor protocol (#16416)
## Summary

* Attributes/method are now properly looked up on metaclasses, when
called on class objects
* We properly distinguish between data descriptors and non-data
descriptors (but we do not yet support them in store-context, i.e.
`obj.data_descr = …`)
* The descriptor protocol is now implemented in a single unified place
for instances, classes and dunder-calls. Unions and possibly-unbound
symbols are supported in all possible stages of the process by creating
union types as results.
* In general, the handling of "possibly-unbound" symbols has been
improved in a lot of places: meta-class attributes, attributes,
descriptors with possibly-unbound `__get__` methods, instance
attributes, …
* We keep track of type qualifiers in a lot more places. I anticipate
that this will be useful if we import e.g. `Final` symbols from other
modules (see relevant change to typing spec:
https://github.com/python/typing/pull/1937).
* Detection and special-casing of the `typing.Protocol` special form in
order to avoid lots of changes in the test suite due to new `@Todo`
types when looking up attributes on builtin types which have `Protocol`
in their MRO. We previously
looked up attributes in a wrong way, which is why this didn't come up
before.

closes #16367
closes #15966

## Context

The way attribute lookup in `Type::member` worked before was simply
wrong (mostly my own fault). The whole instance-attribute lookup should
probably never have been integrated into `Type::member`. And the
`Type::static_member` function that I introduced in my last descriptor
PR was the wrong abstraction. It's kind of fascinating how far this
approach took us, but I am pretty confident that the new approach
proposed here is what we need to model this correctly.

There are three key pieces that are required to implement attribute
lookups:

- **`Type::class_member`**/**`Type::find_in_mro`**: The
`Type::find_in_mro` method that can look up attributes on class bodies
(and corresponding bases). This is a partial function on types, as it
can not be called on instance types like`Type::Instance(…)` or
`Type::IntLiteral(…)`. For this reason, we usually call it through
`Type::class_member`, which is essentially just
`type.to_meta_type().find_in_mro(…)` plus union/intersection handling.
- **`Type::instance_member`**: This new function is basically the
type-level equivalent to `obj.__dict__[name]` when called on
`Type::Instance(…)`. We use this to discover instance attributes such as
those that we see as declarations on class bodies or as (annotated)
assignments to `self.attr` in methods of a class.
- The implementation of the descriptor protocol. It works slightly
different for instances and for class objects, but it can be described
by the general framework:
- Call `type.class_member("attribute")` to look up "attribute" in the
MRO of the meta type of `type`. Call the resulting `Symbol` `meta_attr`
(even if it's unbound).
- Use `meta_attr.class_member("__get__")` to look up `__get__` on the
*meta type* of `meta_attr`. Call it with `__get__(meta_attr, self,
self.to_meta_type())`. If this fails (either the lookup or the call),
just proceed with `meta_attr`. Otherwise, replace `meta_attr` in the
following with the return type of `__get__`. In this step, we also probe
if a `__set__` or `__delete__` method exists and store it in
`meta_attr_kind` (can be either "data descriptor" or "normal attribute
or non-data descriptor").
  - Compute a `fallback` type.
    - For instances, we use `self.instance_member("attribute")`
- For class objects, we use `class_attr =
self.find_in_mro("attribute")`, and then try to invoke the descriptor
protocol on `class_attr`, i.e. we look up `__get__` on the meta type of
`class_attr` and call it with `__get__(class_attr, None, self)`. This
additional invocation of the descriptor protocol on the fallback type is
one major asymmetry in the otherwise universal descriptor protocol
implementation.
- Finally, we look at `meta_attr`, `meta_attr_kind` and `fallback`, and
handle various cases of (possible) unboundness of these symbols.
- If `meta_attr` is bound and a data descriptor, just return `meta_attr`
- If `meta_attr` is not a data descriptor, and `fallback` is bound, just
return `fallback`
- If `meta_attr` is not a data descriptor, and `fallback` is unbound,
return `meta_attr`
- Return unions of these three possibilities for partially-bound
symbols.

This allows us to handle class objects and instances within the same
framework. There is a minor additional detail where for instances, we do
not allow the fallback type (the instance attribute) to completely
shadow the non-data descriptor. We do this because we (currently) don't
want to pretend that we can statically infer that an instance attribute
is always set.

Dunder method calls can also be embedded into this framework. The only
thing that changes is that *there is no fallback type*. If a dunder
method is called on an instance, we do not fall back to instance
variables. If a dunder method is called on a class object, we only look
it up on the meta class, never on the class itself.

## Test Plan

New Markdown tests.
2025-03-07 22:03:28 +01:00
InSync a18d8bfa7d
[`pep8-naming`] Add links to `ignore-names` options in various rules' documentation (#16557)
## Summary

Resolves #16551.

All rules using
[`lint.pep8-naming.ignore-names`](https://docs.astral.sh/ruff/settings/#lint_pep8-naming_ignore-names)
and
[`lint.pep8-naming.extend-ignore-names`](https://docs.astral.sh/ruff/settings/#lint_pep8-naming_extend-ignore-names)
now have their documentation linked to these two options.

## Test Plan

None.
2025-03-07 14:49:08 -05:00
Shunsuke Shibayama 348c196cb3
[red-knot] avoid inferring types if unpacking fails (#16530)
## Summary

This PR closes #15199.

The change I just made is to set all variables to type `Unknown` if
unpacking fails, but in some cases this may be excessive.
For example:

```py
a, b, c = "ab"
reveal_type(a)  # Unknown, but it would be reasonable to think of it as LiteralString
reveal_type(c)  # Unknown
```

```py
# Failed to unpack before the starred expression
(a, b, *c, d, e) = (1,)
reveal_type(a)  # Unknown
reveal_type(b)  # Unknown
...
# Failed to unpack after the starred expression
(a, b, *c, d, e) = (1, 2, 3)
reveal_type(a)  # Unknown, but should it be Literal[1]?
reveal_type(b)  # Unknown, but should it be Literal[2]?
reveal_type(c)  # Todo
reveal_type(d)  # Unknown
reveal_type(e)  # Unknown
```

I will modify it if you think it would be better to make it a different
type than just `Unknown`.

## Test Plan

I have made appropriate modifications to the test cases affected by this
change, and also added some more test cases.
2025-03-07 11:04:44 -08:00
Vasco Schiavo 6d6e524b90
[flake8-bandit] Fix mixed-case hash algorithm names (S324) (#16552)
The PR solves issue #16525
2025-03-07 15:21:07 +00:00
Dylan 0dfa810e9a
Bump 0.9.10 (#16556) 2025-03-07 09:00:08 -06:00
Micha Reiser 9cd0cdefd3
Assert that formatted code doesn't introduce any new unsupported syntax errors (#16549)
## Summary

This should give us better coverage for the unsupported syntax error
features and
increases our confidence that the formatter doesn't accidentially
introduce new unsupported
syntax errors. 

A feature like this would have been very useful when working on f-string
formatting
where it took a lot of iteration to find all Python 3.11 or older
incompatibilities.

## Test Plan

I applied my changes on top of
https://github.com/astral-sh/ruff/pull/16523 and
removed the target version check in the with-statement formatting code.
As expected,
the integration tests now failed
2025-03-07 09:12:00 +01:00
Eric Mark Martin 05a4c29344
print MDTEST_TEST_FILTER value in single-quotes (and escaped) (#16548)
<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

If an mdtest fails, the error output will include an example command
that can be run to re-run just the failing test, e.g

```
To rerun this specific test, set the environment variable: MDTEST_TEST_FILTER="sync.md - With statements - Context manager with non-callable `__exit__` attribute"
MDTEST_TEST_FILTER="sync.md - With statements - Context manager with non-callable `__exit__` attribute" cargo test -p red_knot_python_semantic --test mdtest -- mdtest__with_sync
```
This is very helpful, but because we're printing the envvar value
surrounded in double-quotes, the bits between backticks in this example
get interpreted as a shell interpolation. When running this in zsh, for
example, I see

```console
❯ MDTEST_TEST_FILTER="sync.md - With statements - Context manager with non-callable `__exit__` attribute" cargo test -p red_knot_python_semantic --test mdtest -- mdtest__with_sync  
zsh: command not found: __exit__
   Compiling red_knot_python_semantic v0.0.0 (/home/ericmarkmartin/Development/ruff/crates/red_knot_python_semantic)
   Compiling red_knot_test v0.0.0 (/home/ericmarkmartin/Development/ruff/crates/red_knot_test)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 6.09s
     Running tests/mdtest.rs (target/debug/deps/mdtest-149b8f9d937e36bc)

running 1 test
test mdtest__with_sync ... ok
```
[^1]

This is a minor annoyance which we can solve by using single-quotes
instead of double-quotes for this string. To do so safely, we also
escape single-quotes possibly contained within the string.

There is a [shell-quote](https://github.com/allenap/shell-quote) crate,
which seems to handle all this escaping stuff for you but fixing this
issue perfectly isn't a big deal (if there are more things to escape we
can deal with it then), so adding a new dependency (even a dev one)
seemed overkill.

[^1]: The filter does still work---it turns out that the filter
`MDTEST_TEST_FILTER="sync.md - With statements - Context manager with
non-callable attribute"` (what you get after the failed interpolation)
is still good enough

## Test Plan
<!-- How was it tested? -->

I broke the ``## Context manager with non-callable `__exit__`
attribute`` test by deleting the error assertion, then successfully ran
the new command it printed out.
2025-03-07 09:04:52 +01:00
Brent Westbrook b3c884f4f3
[syntax-errors] Parenthesized keyword argument names after Python 3.8 (#16482)
Summary
--

Unlike the other syntax errors detected so far, parenthesized keyword
arguments are only allowed *before* 3.8. It sounds like they were only
accidentally allowed before that [^1].

As an aside, you get a pretty confusing error from Python for this, so
it's nice that we can catch it:

```pycon
>>> def f(**kwargs): ...
... f((a)=1)
...
  File "<python-input-0>", line 2
    f((a)=1)
       ^^^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
>>>
```
Test Plan
--
Inline tests.

[^1]: https://github.com/python/cpython/issues/78822
2025-03-06 12:18:13 -05:00
Brent Westbrook 6c14225c66
[syntax-errors] Tuple unpacking in `return` and `yield` before Python 3.8 (#16485)
Summary
--

Checks for tuple unpacking in `return` and `yield` statements before
Python 3.8, as described [here].

Test Plan
--
Inline tests.

[here]: https://github.com/python/cpython/issues/76298
2025-03-06 11:57:20 -05:00
David Peter 0a627ef216
[red-knot] Never is callable and iterable. Arbitrary attributes can be accessed. (#16533)
## Summary

- `Never` is callable
- `Never` is iterable
- Arbitrary attributes can be accessed on `Never`

Split out from #16416 that is going to be required.

## Test Plan

Tests for all properties above.
2025-03-06 15:59:19 +00:00
Micha Reiser a25be4610a
Clarify that D417 only checks docstrings with an arguments section (#16494)
## Summary

This came up in https://github.com/astral-sh/ruff/issues/16477

It's not obvious from the D417 rule's documentation that it only checks
docstrings
with an arguments section. Functions without such a section aren't
checked.

This PR tries to make this clearer in the documentation.
2025-03-06 09:49:35 +00:00
Micha Reiser ce0018c3cb
Add `OsSystem` support to mdtests (#16518)
## Summary

This PR introduces a new mdtest option `system` that can either be
`in-memory` or `os`
where `in-memory` is the default.

The motivation for supporting `os` is so that we can write OS/system
specific tests
with mdtests. Specifically, I want to write mdtests for the module
resolver,
testing that module resolution is case sensitive. 

## Test Plan

I tested that the case-sensitive module resolver test start failing when
setting `system = "os"`
2025-03-06 10:41:40 +01:00
Micha Reiser 48f906e06c
Add tests for case-sensitive module resolution (#16517)
## Summary

Python's module resolver is case sensitive. 

This PR adds mdtests that assert that our module resolution is case
sensitive.

The tests currently all pass because our in memory file system is case
sensitive.
I'll add support for using the real file system to the mdtest framework
in a separate PR.

This PR also adds support for specifying extra search paths to the
mdtest framework.

## Test Plan
The tests fail when running them using the real file system.
2025-03-06 10:19:23 +01:00
Douglas Creager ebd172e732
[red-knot] Several failing tests for generics (#16509)
To kick off the work of supporting generics, this adds many new
(currently failing) tests, showing the behavior we plan to support.

This is still missing a lot!  Not included:

- typevar tuples
- param specs
- variance
- `Self`

But it's a good start! We can add more failing tests for those once we
tackle these.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-05 17:21:19 -05:00
Carl Meyer 114abc7cfb
[red-knot] support empty TypeInference with fallback type (#16510)
This is split out of https://github.com/astral-sh/ruff/pull/14029, to
reduce the size of that PR, and to validate that this "fallback type"
support in `TypeInference` doesn't come with a performance cost. It also
improves the reliability and debuggability of our current (temporary)
cycle handling.

In order to recover from a cycle, we have to be able to construct a
"default" `TypeInference` where all expressions and definitions have
some "default" type. In our current cycle handling, this "default" type
is just unknown or a todo type. With fixpoint iteration, the "default"
type will be `Type::Never`, which is the "bottom" type that fixpoint
iteration starts from.

Since it would be costly (both in space and time) to actually enumerate
all expressions and definitions in a scope, just to insert the same
default type for all of them, instead we add an optional "missing type"
fallback to `TypeInference`, which (if set) is the fallback type for any
expression or definition which doesn't have an explicit type set.

With this change, cycles can no longer result in the dreaded "Missing
key" errors looking up the type of some expression.
2025-03-05 09:12:27 -08:00
Brent Westbrook 318f503714
[syntax-errors] Named expressions in decorators before Python 3.9 (#16386)
Summary
--

This PR detects the relaxed grammar for decorators proposed in [PEP
614](https://peps.python.org/pep-0614/) on Python 3.8 and lower.

The 3.8 grammar for decorators is
[here](https://docs.python.org/3.8/reference/compound_stmts.html#grammar-token-decorators):

```
decorators                ::=  decorator+
decorator                 ::=  "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE
dotted_name               ::=  identifier ("." identifier)*
```

in contrast to the current grammar
[here](https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-decorators)

```
decorators                ::= decorator+
decorator                 ::= "@" assignment_expression NEWLINE
assignment_expression ::= [identifier ":="] expression
```

Test Plan
--

New inline parser tests.
2025-03-05 17:08:18 +00:00
Brent Westbrook d0623888b3
[syntax-errors] Positional-only parameters before Python 3.8 (#16481)
Summary
--

Detect positional-only parameters before Python 3.8, as marked by the
`/` separator in a parameter list.

Test Plan
--
Inline tests.
2025-03-05 13:46:43 +00:00
Shaygan Hooshyari 23fd4927ae
Auto generate ast expression nodes (#16285)
## Summary

Part of https://github.com/astral-sh/ruff/issues/15655

- Auto generate AST nodes using definitions in `ast.toml`. I added
attributes similar to
[`Field`](https://github.com/python/cpython/blob/main/Parser/asdl.py#L67)
in ASDL to hold field information

## Test Plan

Nothing outside the `ruff_python_ast` package should change.

---------

Co-authored-by: Douglas Creager <dcreager@dcreager.net>
2025-03-05 08:25:55 -05:00
Andrew Gallant cc324abcc2 ruff_db: add new `Diagnostic` type
... with supporting types. This is meant to give us a base to work with
in terms of our new diagnostic data model. I expect the representations
to be tweaked over time, but I think this is a decent start.

I would also like to add doctest examples, but I think it's better if we
wait until an initial version of the renderer is done for that.
2025-03-05 08:23:02 -05:00
Andrew Gallant 80be0a0115 ruff_db: move `ParseDiagnostic` to `old` submodule too
This should have been with the previous two commits, but I missed it.
2025-03-05 08:23:02 -05:00
Andrew Gallant b2e90c3f5c ruff_db: rename `ParseDiagnostic` to `OldParseDiagnostic`
I missed this in the previous commits.
2025-03-05 08:23:02 -05:00
Andrew Gallant d7cbe6b7df ruff_db: move old types into their own sub-module
This puts them out of the way so that they can hopefully be removed more
easily in the (near) future, and so that they don't get in the way of
the new types. This also makes the intent of the migration a bit clearer
in the code and hopefully results in less confusion.
2025-03-05 08:23:02 -05:00
Andrew Gallant 021640a7a6 ruff_db: rename `Diagnostic` to `OldDiagnosticTrait`
This trait should eventually go away, so we rename it (and supporting
types) to make room for a new concrete `Diagnostic` type.

This commit is just the rename. In the next commit, we'll move it to a
different module.
2025-03-05 08:23:02 -05:00
Brent Westbrook 81bcdcebd3
[syntax-errors] Type parameter lists before Python 3.12 (#16479)
Summary
--

Another simple one, just detect type parameter lists in functions
and classes. Like pyright, we don't emit a second diagnostic for
`type` alias statements, which were also introduced in 3.12.

Test Plan
--
Inline tests.
2025-03-05 13:19:09 +00:00
Dhruv Manilawala d94a78a134
[red-knot] De-duplicate symbol table query (#16515)
## Summary

This PR does a small refactor to avoid double
`symbol_table(...).symbol(...)` call to check for `__slots__` and
`TYPE_CHECKING`. It merges them into a single call.

I noticed this while looking at
https://github.com/astral-sh/ruff/pull/16468.
2025-03-05 07:36:21 +00:00
Shunsuke Shibayama bb44926ca5
[red-knot] Add rule `invalid-type-checking-constant` (#16501)
## Summary

This PR adds more features to #16468.

* Adds a new error rule `invalid-type-checking-constant`, which occurs
when we try to assign a value other than `False` to a user-defined
`TYPE_CHECKING` variable (it is possible to assign `...` in a stub
file).
* Allows annotated assignment to `TYPE_CHECKING`. Only types that
`False` can be assigned to are allowed. However, the type of
`TYPE_CHECKING` will be inferred to be `Literal[True]` regardless of
what the type is specified.

## Test plan

I ran the tests with `cargo test -p red_knot_python_semantic` and
confirmed that all tests passed.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-04 19:49:34 +00:00
Brent Westbrook 32c66ec4b7
[syntax-errors] `type` alias statements before Python 3.12 (#16478)
Summary
--
Another simple one, just detect standalone `type` statements. I limited
the diagnostic to `type` itself like [pyright]. That probably makes the
most sense for more complicated examples.

Test Plan
--
Inline tests.

[pyright]:
https://pyright-play.net/?pythonVersion=3.8&strict=true&code=C4TwDgpgBAHlC8UCWA7YQ
2025-03-04 17:20:10 +00:00
Micha Reiser 087d92cbf4
Formatter: Fix syntax error location in notebooks (#16499)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/16476
fixes: #11453

We format notebooks cell by cell. That means, that offsets in parse
errors are relative
to the cell and not the entire document. We didn't account for this fact
when emitting syntax errors for notebooks in the formatter. 

This PR ensures that we correctly offset parse errors by the cell
location.

## Test Plan

Added test (it panicked before)
2025-03-04 18:00:31 +01:00
Brent Westbrook e7b93f93ef
[syntax-errors] Type parameter defaults before Python 3.13 (#16447)
Summary
--

Detects the presence of a [PEP 696] type parameter default before Python
3.13.

Test Plan
--

New inline parser tests for type aliases, generic functions and generic
classes.

[PEP 696]: https://peps.python.org/pep-0696/#grammar-changes
2025-03-04 16:53:38 +00:00
Brent Westbrook c8a06a9be8
[syntax-errors] Limit `except*` range to `*` (#16473)
Summary
--
This is a follow-up to #16446 to fix the diagnostic range to point to
the `*` like `pyright` does
(https://github.com/astral-sh/ruff/pull/16446#discussion_r1976900643).

Storing the range in the `ExceptClauseKind::Star` variant feels slightly
awkward, but we don't store the star itself anywhere on the
`ExceptHandler`. And we can't just take `ExceptHandler.start() +
"except".text_len()` because this code appears to be valid:

```python
try: ...
except    *    Error: ...
```

Test Plan
--
Existing tests.
2025-03-04 16:50:09 +00:00
Shunsuke Shibayama 1977dda079
[red-knot] respect `TYPE_CHECKING` even if not imported from typing (#16468)
## Summary

This PR closes #15722.

The change is that if the variable `TYPE_CHECKING` is defined/imported,
the type of the variable is interpreted as `Literal[True]` regardless of
what the value is.
This is compatible with the behavior of other type checkers (e.g. mypy,
pyright).

## Test Plan

I ran the tests with `cargo test -p red_knot_python_semantic` and
confirmed that all tests passed.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-03-04 07:58:29 -08:00
Brent Westbrook 37fbe58b13
Document `LinterResult::has_syntax_error` and add `Parsed::has_no_syntax_errors` (#16443)
Summary
--

This is a follow up addressing the comments on #16425. As @dhruvmanila
pointed out, the naming is a bit tricky. I went with `has_no_errors` to
try to differentiate it from `is_valid`. It actually ends up negated in
most uses, so it would be more convenient to have `has_any_errors` or
`has_errors`, but I thought it would sound too much like the opposite of
`is_valid` in that case. I'm definitely open to suggestions here.

Test Plan
--

Existing tests.
2025-03-04 08:35:38 -05:00
InSync a3ae76edc0
[`pyupgrade`] Do not offer fix when at least one target is `global`/`nonlocal` (`UP028`) (#16451)
## Summary

Resolves #16445.

`UP028` is now no longer always fixable: it will not offer a fix when at
least one `ExprName` target is bound to either a `global` or a
`nonlocal` declaration.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-03-04 11:28:01 +01:00
Brent Westbrook d93ed293eb
Escape template filenames in glob patterns (#16407)
## Summary

Fixes #9381. This PR fixes errors like 

```
Cause: error parsing glob '/Users/me/project/{{cookiecutter.project_dirname}}/__pycache__': nested alternate groups are not allowed
```

caused by glob special characters in filenames like
`{{cookiecutter.project_dirname}}`. When the user is matching that
directory exactly, they can use the workaround given by
https://github.com/astral-sh/ruff/issues/7959#issuecomment-1764751734,
but that doesn't work for a nested config file with relative paths. For
example, the directory tree in the reproduction repo linked
[here](https://github.com/astral-sh/ruff/issues/9381#issuecomment-2677696408):

```
.
├── README.md
├── hello.py
├── pyproject.toml
├── uv.lock
└── {{cookiecutter.repo_name}}
    ├── main.py
    ├── pyproject.toml
    └── tests
        └── maintest.py
```

where the inner `pyproject.toml` contains a relative glob:

```toml
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["F811"]
```

## Test Plan

A new CLI test in both the linter and formatter. The formatter test may
not be necessary because I didn't have to modify any additional code to
pass it, but the original report mentioned both `check` and `format`, so
I wanted to be sure both were fixed.
2025-03-03 09:29:58 -05:00
Vasco Schiavo 4d92e20e81
[`pylint`] Convert a `code` keyword argument to a positional argument (`PLR1722`) (#16424)
The PR addresses issue #16396 .

Specifically:

- If the exit statement contains a code keyword argument, it is
converted into a positional argument.
- If retrieving the code from the exit statement is not possible, a
violation is raised without suggesting a fix.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-03-03 09:20:57 -05:00
Micha Reiser c4578162d5
[red-knot] Add support for `knot check <paths>` (#16375)
## Summary

This PR adds support for an optional list of paths that should be
checked to `knot check`.

E.g. to only check the `src` directory

```sh
knot check src
```

The default is to check all files in the project but users can reduce
the included files by specifying one or multiple optional paths.

The main two challenges with adding this feature were:

* We now need to show an error when one of the provided paths doesn't
exist. That's why this PR now collects errors from the project file
indexing phase and adds them to the output diagnostics. The diagnostic
looks similar to ruffs (see CLI test)
* The CLI should pick up new files added to included folders. For
example, `knot check src --watch` should pick up new files that are
added to the `src` folder. This requires that we now filter the files
before adding them to the project. This is a good first step to
supporting `include` and `exclude`.


The PR makes two simplifications:

1. I didn't test the changes with case-insensitive file systems. We may
need to do some extra path normalization to support those well. See
https://github.com/astral-sh/ruff/issues/16400
2. Ideally, we'd accumulate the IO errors from the initial indexing
phase and subsequent incremental indexing operations. For example, we
should preserve the IO diagnostic for a non existing `test.py` if it was
specified as an explicit CLI argument until the file gets created and we
should show it again when the file gets deleted. However, this is
somewhat complicated because we'd need to track which files we revisited
(or were removed because the entire directory is gone). I considered
this too low a priority as it's worth dealing with right now.

The implementation doesn't support symlinks within the project but that
is the same as Ruff and is unchanged from before this PR.



Closes https://github.com/astral-sh/ruff/issues/14193

## Test Plan

Added CLI and file watching integration tests. Manually testing.
2025-03-03 12:59:56 +00:00
Vasco Schiavo 5d56c2e877
[flake8-builtins] Ignore variables matching module attribute names (A001) (#16454)
This PR (partially) addresses issue #16373
2025-03-03 11:10:23 +01:00
Jelle Zijlstra c80678a1c0
Add new rule RUF059: Unused unpacked assignment (#16449)
Split from F841 following discussion in #8884.

Fixes #8884.

<!--
Thank you for contributing to Ruff! 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?
- Does this pull request include references to any relevant issues?
-->

## Summary

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

Add a new rule for unused assignments in tuples. Remove similar behavior
from F841.

## Test Plan

Adapt F841 tests and move them over to the new rule.

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

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-03 10:51:36 +01:00
renovate[bot] 79a2c7eaa2
Update pre-commit dependencies (#16465)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.9.6` -> `v0.9.9` |
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) |
repository | minor | `v1.29.7` -> `v1.30.0` |
|
[python-jsonschema/check-jsonschema](https://redirect.github.com/python-jsonschema/check-jsonschema)
| repository | patch | `0.31.1` -> `0.31.2` |
|
[rbubley/mirrors-prettier](https://redirect.github.com/rbubley/mirrors-prettier)
| repository | patch | `v3.5.1` -> `v3.5.2` |
|
[woodruffw/zizmor-pre-commit](https://redirect.github.com/woodruffw/zizmor-pre-commit)
| repository | minor | `v1.3.1` -> `v1.4.1` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>astral-sh/ruff-pre-commit (astral-sh/ruff-pre-commit)</summary>

###
[`v0.9.9`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.9.9)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.9.8...v0.9.9)

See: https://github.com/astral-sh/ruff/releases/tag/0.9.9

###
[`v0.9.8`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.9.8)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.9.7...v0.9.8)

See: https://github.com/astral-sh/ruff/releases/tag/0.9.8

###
[`v0.9.7`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.9.7)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.9.6...v0.9.7)

See: https://github.com/astral-sh/ruff/releases/tag/0.9.7

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.30.0`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.30.0)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.29.10...v1.30.0)

#### \[1.30.0] - 2025-03-01

##### Features

- Updated the dictionary with the [February
2025](https://redirect.github.com/crate-ci/typos/issues/1221) changes

###
[`v1.29.10`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.29.10)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.29.9...v1.29.10)

#### \[1.29.10] - 2025-02-25

##### Fixes

-   Also correct `contaminent` as `contaminant`

###
[`v1.29.9`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.29.9)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.29.8...v1.29.9)

#### \[1.29.9] - 2025-02-20

##### Fixes

-   *(action)* Correctly get binary for some aarch64 systems

###
[`v1.29.8`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.29.8)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.29.7...v1.29.8)

#### \[1.29.8] - 2025-02-19

##### Features

-   Attempt to build Linux aarch64 binaries

</details>

<details>
<summary>python-jsonschema/check-jsonschema
(python-jsonschema/check-jsonschema)</summary>

###
[`v0.31.2`](https://redirect.github.com/python-jsonschema/check-jsonschema/blob/HEAD/CHANGELOG.rst#0312)

[Compare
Source](https://redirect.github.com/python-jsonschema/check-jsonschema/compare/0.31.1...0.31.2)

- Update vendored schemas: dependabot, github-workflows, gitlab-ci,
mergify, renovate,
    woodpecker-ci (2025-02-19)

</details>

<details>
<summary>rbubley/mirrors-prettier (rbubley/mirrors-prettier)</summary>

###
[`v3.5.2`](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.5.1...v3.5.2)

[Compare
Source](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.5.1...v3.5.2)

</details>

<details>
<summary>woodruffw/zizmor-pre-commit
(woodruffw/zizmor-pre-commit)</summary>

###
[`v1.4.1`](https://redirect.github.com/woodruffw/zizmor-pre-commit/releases/tag/v1.4.1)

[Compare
Source](https://redirect.github.com/woodruffw/zizmor-pre-commit/compare/v1.4.0...v1.4.1)

See: https://github.com/woodruffw/zizmor/releases/tag/v1.4.1

###
[`v1.4.0`](https://redirect.github.com/woodruffw/zizmor-pre-commit/releases/tag/v1.4.0)

[Compare
Source](https://redirect.github.com/woodruffw/zizmor-pre-commit/compare/v1.3.1...v1.4.0)

See: https://github.com/woodruffw/zizmor/releases/tag/v1.4.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xNzYuMiIsInVwZGF0ZWRJblZlciI6IjM5LjE3Ni4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-03-03 13:10:46 +05:30
Brent Westbrook e924ecbdac
[syntax-errors] `except*` before Python 3.11 (#16446)
Summary
--

One of the simpler ones, just detect the use of `except*` before 3.11.

Test Plan
--

New inline tests.
2025-03-02 18:20:18 +00:00
github-actions[bot] 0d615b8765
Sync vendored typeshed stubs (#16448)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2025-03-01 08:21:03 +01:00
Brent Westbrook 4431978262
[syntax-errors] Assignment expressions before Python 3.8 (#16383)
## Summary
This PR is the first in a series derived from
https://github.com/astral-sh/ruff/pull/16308, each of which add support
for detecting one version-related syntax error from
https://github.com/astral-sh/ruff/issues/6591. This one should be
the largest because it also includes the addition of the 
`Parser::add_unsupported_syntax_error` method

Otherwise I think the general structure will be the same for each syntax
error:
* Detecting the error in the parser
* Inline parser tests for the new error
* New ruff CLI tests for the new error

## Test Plan
As noted above, there are new inline parser tests, as well as new ruff
CLI
tests. Once https://github.com/astral-sh/ruff/pull/16379 is resolved,
there should also be new mdtests for red-knot,
but this PR does not currently include those.
2025-02-28 17:13:46 -05:00
Douglas Creager ba44e9de13
[red-knot] Don't use separate ID types for each alist (#16415)
Regardless of whether #16408 and #16311 pan out, this part is worth
pulling out as a separate PR.

Before, you had to define a new `IndexVec` index type for each type of
association list you wanted to create. Now there's a single index type
that's internal to the alist implementation, and you use `List<K, V>` to
store a handle to a particular list.

This also adds some property tests for the alist implementation.
2025-02-28 14:55:55 -05:00
Mike Perlov fdf0915283
[red-knot] treat annotated assignments without RHS in stubs as bindings (#16409) 2025-02-28 16:45:21 +00:00
Adam Johnson 5ca6cc2cc8
Exempt unittest context methods for SIM115 rule (#16439) 2025-02-28 16:29:50 +00:00
Alex Waygood 9bb63495dd
[red-knot] Reject HTML comments in mdtest unless they are `snapshot-diagnostics` or are explicitly allowlisted (#16441) 2025-02-28 16:27:28 +00:00
InSync 980faff176
Move rule code from `description` to `check_name` in GitLab output serializer (#16437) 2025-02-28 14:27:01 +00:00
Alex Waygood 0c7c001647
[red-knot] Switch to a handwritten parser for mdtest error assertions (#16422) 2025-02-28 11:33:36 +00:00
Alex Waygood 09d0b227fb
[red-knot] Disallow more invalid type expressions (#16427) 2025-02-28 10:04:30 +00:00
Micha Reiser 091d0af2ab
Bump version to Ruff 0.9.9 (#16434) 2025-02-28 10:17:38 +01:00
Brent Westbrook 3d72138740
Check `LinterSettings::preview` for version-related syntax errors (#16429) 2025-02-28 09:58:22 +01:00
Brent Westbrook 4a23756024
Avoid caching files with unsupported syntax errors (#16425) 2025-02-28 09:58:11 +01:00
InSync 0ced8d053c
[`flake8-copyright`] Add links to applicable options (`CPY001`) (#16421) 2025-02-28 09:11:14 +01:00
Micha Reiser a8e171f82c
Fix string-length limit in documentation for PYI054 (#16432) 2025-02-28 08:32:08 +01:00
Brent Westbrook cf83584abb
Show version-related syntax errors in the playground (#16419)
## Summary

Fixes part of https://github.com/astral-sh/ruff/issues/16417 by
converting `unsupported_syntax_errors` into playground diagnostics.

## Test Plan

A new `ruff_wasm` test, plus trying out the playground locally:

Default settings:

![image](https://github.com/user-attachments/assets/94377ab5-4d4c-44d3-ae63-fe328a53e083)

`target-version = "py310"`:

![image](https://github.com/user-attachments/assets/51c312ce-70e7-43d3-b6ba-098f2750cb28)
2025-02-27 13:28:37 -05:00
Brent Westbrook 764aa0e6a1
Allow passing `ParseOptions` to inline tests (#16357)
## Summary

This PR adds support for a pragma-style header for inline parser tests
containing JSON-serialized `ParseOptions`. For example,

```python
# parse_options: { "target-version": "3.9" }
match 2:
    case 1:
        pass
```

The line must start with `# parse_options: ` and then the rest of the
(trimmed) line is deserialized into `ParseOptions` used for parsing the
the test.

## Test Plan

Existing inline tests, plus two new inline tests for
`match-before-py310`.

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-02-27 10:23:15 -05:00
Brent Westbrook 568cf88c6c
Bump version to 0.9.8 (#16414) 2025-02-27 08:56:11 -05:00
Alex Waygood 040071bbc5
[red-knot] Ignore surrounding whitespace when looking for `<!-- snapshot-diagnostics -->` directives in mdtests (#16380) 2025-02-27 13:25:31 +00:00
Dhruv Manilawala d56d241317
Notify users for invalid client settings (#16361)
## Summary

As mentioned in
https://github.com/astral-sh/ruff/pull/16296#discussion_r1967047387

This PR updates the client settings resolver to notify the user if there
are any errors in the config using a very basic approach. In addition,
each error related to specific settings are logged.

This isn't the best approach because it can log the same message
multiple times when both workspace and global settings are provided and
they both are the same. This is the case for a single workspace VS Code
instance.

I do have some ideas on how to improve this and will explore them during
my free time (low priority):
* Avoid resolving the global settings multiple times as they're static
* Include the source of the setting (workspace or global?)
* Maybe use a struct (`ResolvedClientSettings` +
`Vec<ClientSettingsResolverError>`) instead to make unit testing easier

## Test Plan

Using:
```jsonc
{
  "ruff.logLevel": "debug",
	
  // Invalid settings
  "ruff.configuration": "$RANDOM",
  "ruff.lint.select": ["RUF000", "I001"],
  "ruff.lint.extendSelect": ["B001", "B002"],
  "ruff.lint.ignore": ["I999", "F401"]
}
```

The error logs:
```
2025-02-27 12:30:04.318736000 ERROR Failed to load settings from `configuration`: error looking key 'RANDOM' up: environment variable not found
2025-02-27 12:30:04.319196000 ERROR Failed to load settings from `configuration`: error looking key 'RANDOM' up: environment variable not found
2025-02-27 12:30:04.320549000 ERROR Unknown rule selectors found in `lint.select`: ["RUF000"]
2025-02-27 12:30:04.320669000 ERROR Unknown rule selectors found in `lint.extendSelect`: ["B001"]
2025-02-27 12:30:04.320764000 ERROR Unknown rule selectors found in `lint.ignore`: ["I999"]
```

Notification preview:

<img width="470" alt="Screenshot 2025-02-27 at 12 29 06 PM"
src="https://github.com/user-attachments/assets/61f41d5c-2558-46b3-a1ed-82114fd8ec22"
/>
2025-02-27 08:28:29 +00:00
Darius Carrier 7dad0c471d
Avoid indexing the project if `configurationPreference` is `editorOnly` (#16381)
## Summary

Closes: https://github.com/astral-sh/ruff/issues/16267

This change skips building the `index` in RuffSettingsIndex when the
configuration preference, in the editor settings, is set to
`editorOnly`. This is appropriate due to the fact that the indexes will
go unused as long as the configuration preference persists.

## Test Plan

I have tested this in VSCode and can confirm that we skip indexing when
`editorOnly` is set. Upon switching back to `editorFirst` or
`filesystemFirst` we index the settings as normal.

I don't seen any unit tests for setting indexing at the moment, but I am
happy to give it a shot if that is something we want.
2025-02-27 07:46:14 +05:30
Carl Meyer fb778ee38d
[red-knot] unify LoopState and saved_break_states (#16406)
We currently keep two separate pieces of state regarding the current
loop on `SemanticIndexBuilder`. One is an enum simply reflecting whether
we are currently inside a loop, and the other is the saved flow states
for `break` statements found in the current loop.

For adding loopy control flow, I'll need to add some additional loop
state (`continue` states, for example). Prepare for this by
consolidating our existing loop state into a single struct and
simplifying the API for pushing and popping a loop.

This is purely a refactor, so tests are not changed.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-02-26 22:31:13 +00:00
InSync 671494a620
[`pylint`] Also reports `case np.nan`/`case math.nan` (`PLW0177`) (#16378)
## Summary

Resolves #16374.

`PLW0177` now also reports the pattern of a case branch if it is an
attribute access whose qualified name is that of either `np.nan` or
`math.nan`.

As the rule is in preview, the changes are not preview-gated.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-02-26 13:50:21 -05:00
Vasco Schiavo b89d61bd05
[FURB156] Do not consider docstring(s) (#16391)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-26 16:30:13 +00:00
Brent Westbrook 8c0eac21ab
Use `is_none_or` in `stdlib-module-shadowing` (#16402)
Summary
--
This resolves a TODO I left behind in #16006 now that our MSRV is 1.83.

Test Plan
--
Existing tests
2025-02-26 11:29:00 -05:00
Micha Reiser ea3245b8c4
[red-knot] Fix file watching for new non-project files (#16395) 2025-02-26 16:10:13 +01:00
Carl Meyer 87d011e1bd
[red-knot] fix non-callable reporting for unions (#16387)
Minor follow-up to https://github.com/astral-sh/ruff/pull/16161

This `not_callable` flag wasn't functional, because it could never be
`false`. It was initialized to `true` and then only ever updated with
`|=`, which can never make it `false`.

Add a test that exercises the case where it _should_ be `false` (all of
the union elements are callable) but `bindings` is also empty (all union
elements have binding errors). Before this PR, the added test wrongly
emits a diagnostic that the union `Literal[f1] | Literal[f2]` is not
callable.

And add a test where a union call results in one binding error and one
not-callable error, where we currently give the wrong result (we show
only the binding error), with a TODO.

Also add TODO comments in a couple other tests where ideally we'd report
more than just one error out of a union call.

Also update the flag name to `all_errors_not_callable` to more clearly
indicate the semantics of the flag.
2025-02-26 07:06:04 -08:00
Carl Meyer dd6f6233bd
bump MSRV to 1.83 (#16294)
According to our new MSRV policy (see
https://github.com/astral-sh/ruff/issues/16370 ), bump our MSRV to 1.83
(N - 2), and autofix some new clippy lints.
2025-02-26 06:12:43 -08:00
Dhruv Manilawala bf2c9a41cd
Avoid unnecessary info at non-trace server log level (#16389)
## Summary

Currently, the log messages emitted by the server includes multiple
information which isn't really required most of the time.

Here's the current format:
```
   0.000755625s DEBUG main ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/playground/ruff
   0.016334666s DEBUG ThreadId(10) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/playground/ruff/.vscode
   0.019954541s  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/playground/ruff
   0.020160416s TRACE ruff:main notification{method="textDocument/didOpen"}: ruff_server::server::api: enter
   0.020209625s TRACE ruff:worker:0 request{id=1 method="textDocument/diagnostic"}: ruff_server::server::api: enter
   0.020228166s DEBUG ruff:worker:0 request{id=1 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/test.py
   0.020359833s  INFO     ruff:main ruff_server::server: Configuration file watcher successfully registered
```

This PR updates the following:
* Uses current timestamp (same as red-knot) for all log levels instead
of the uptime value
* Includes the target and thread names only at the trace level

What this means is that the message is reduced to only important
information at DEBUG level:

```
2025-02-26 11:35:02.198375000 DEBUG Indexing settings for workspace: /Users/dhruv/playground/ruff
2025-02-26 11:35:02.209933000 DEBUG Ignored path via `exclude`: /Users/dhruv/playground/ruff/.vscode
2025-02-26 11:35:02.217165000  INFO Registering workspace: /Users/dhruv/playground/ruff
2025-02-26 11:35:02.217631000 DEBUG Included path via `include`: /Users/dhruv/playground/ruff/lsp/test.py
2025-02-26 11:35:02.217684000  INFO Configuration file watcher successfully registered
```

while still showing the other information (thread names and target) at
trace level:
```
2025-02-26 11:35:27.819617000 DEBUG main ruff_server::session::index::ruff_settings: Indexing settings for workspace: /Users/dhruv/playground/ruff
2025-02-26 11:35:27.830500000 DEBUG ThreadId(11) ruff_server::session::index::ruff_settings: Ignored path via `exclude`: /Users/dhruv/playground/ruff/.vscode
2025-02-26 11:35:27.837212000  INFO main ruff_server::session::index: Registering workspace: /Users/dhruv/playground/ruff
2025-02-26 11:35:27.837714000 TRACE ruff:main notification{method="textDocument/didOpen"}: ruff_server::server::api: enter
2025-02-26 11:35:27.838019000  INFO ruff:main ruff_server::server: Configuration file watcher successfully registered
2025-02-26 11:35:27.838084000 TRACE ruff:worker:1 request{id=1 method="textDocument/diagnostic"}: ruff_server::server::api: enter
2025-02-26 11:35:27.838205000 DEBUG ruff:worker:1 request{id=1 method="textDocument/diagnostic"}: ruff_server::resolve: Included path via `include`: /Users/dhruv/playground/ruff/lsp/test.py
```
2025-02-26 13:31:17 +05:30
Dhruv Manilawala be03cb04c1
Expand `ruff.configuration` to allow inline config (#16296)
## Summary

[Internal design
document](https://www.notion.so/astral-sh/In-editor-settings-19e48797e1ca807fa8c2c91b689d9070?pvs=4)

This PR expands `ruff.configuration` to allow inline configuration
directly in the editor. For example:

```json
{
	"ruff.configuration": {
		"line-length": 100,
		"lint": {
			"unfixable": ["F401"],
			"flake8-tidy-imports": {
				"banned-api": {
					"typing.TypedDict": {
						"msg": "Use `typing_extensions.TypedDict` instead"
					}
				}
			}
		},
		"format": {
			"quote-style": "single"
		}
	}
}
```

This means that now `ruff.configuration` accepts either a path to
configuration file or the raw config itself. It's _mostly_ similar to
`--config` with one difference that's highlighted in the following
section. So, it can be said that the format of `ruff.configuration` when
provided the config map is same as the one on the [playground] [^1].

## Limitations

<details><summary><b>Casing (<code>kebab-case</code> v/s/
<code>camelCase</code>)</b></summary>
<p>


The config keys needs to be in `kebab-case` instead of `camelCase` which
is being used for other settings in the editor.

This could be a bit confusing. For example, the `line-length` option can
be set directly via an editor setting or can be configured via
`ruff.configuration`:

```json
{
	"ruff.configuration": {
        "line-length": 100
    },
    "ruff.lineLength": 120
}
```

#### Possible solution

We could use feature flag with [conditional
compilation](https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute)
to indicate that when used in `ruff_server`, we need the `Options`
fields to be renamed as `camelCase` while for other crates it needs to
be renamed as `kebab-case`. But, this might not work very easily because
it will require wrapping the `Options` struct and create two structs in
which we'll have to add `#[cfg_attr(...)]` because otherwise `serde`
will complain:

```
error: duplicate serde attribute `rename_all`
  --> crates/ruff_workspace/src/options.rs:43:38
   |
43 | #[cfg_attr(feature = "editor", serde(rename_all = "camelCase"))]
   |                                      ^^^^^^^^^^
```

</p>
</details> 

<details><summary><b>Nesting (flat v/s nested keys)</b></summary>
<p>

This is the major difference between `--config` flag on the command-line
v/s `ruff.configuration` and it makes it such that `ruff.configuration`
has same value format as [playground] [^1].

The config keys needs to be split up into keys which can result in
nested structure instead of flat structure:

So, the following **won't work**:

```json
{
	"ruff.configuration": {
		"format.quote-style": "single",
		"lint.flake8-tidy-imports.banned-api.\"typing.TypedDict\".msg": "Use `typing_extensions.TypedDict` instead"
	}
}
```

But, instead it would need to be split up like the following:
```json
{
	"ruff.configuration": {
		"format": {
			"quote-style": "single"
		},
		"lint": {
			"flake8-tidy-imports": {
				"banned-api": {
					"typing.TypedDict": {
						"msg": "Use `typing_extensions.TypedDict` instead"
					}
				}
			}
		}
	}
}
```

#### Possible solution (1)

The way we could solve this and make it same as `--config` would be to
add a manual logic of converting the JSON map into an equivalent TOML
string which would be then parsed into `Options`.

So, the following JSON map:
```json
{ "lint.flake8-tidy-imports": { "banned-api": {"\"typing.TypedDict\".msg": "Use typing_extensions.TypedDict instead"}}}
```

would need to be converted into the following TOML string:
```toml
lint.flake8-tidy-imports = { banned-api = { "typing.TypedDict".msg = "Use typing_extensions.TypedDict instead" } }
```

by recursively convering `"key": value` into `key = value` which is to
remove the quotes from key and replacing `:` with `=`.

#### Possible solution (2)

Another would be to just accept `Map<String, String>` strictly and
convert it into `key = value` and then parse it as a TOML string. This
would also match `--config` but quotes might become a nuisance because
JSON only allows double quotes and so it'll require escaping any inner
quotes or use single quotes.

</p>
</details> 

## Test Plan

### VS Code

**Requires https://github.com/astral-sh/ruff-vscode/pull/702**

**`settings.json`**:
```json
{
  "ruff.lint.extendSelect": ["TID"],
  "ruff.configuration": {
    "line-length": 50,
    "format": {
      "quote-style": "single"
    },
    "lint": {
      "unfixable": ["F401"],
      "flake8-tidy-imports": {
        "banned-api": {
          "typing.TypedDict": {
            "msg": "Use `typing_extensions.TypedDict` instead"
          }
        }
      }
    }
  }
}
```

Following video showcases me doing the following:
1. Check diagnostics that it includes `TID`
2. Run `Ruff: Fix all auto-fixable problems` to test `unfixable`
3. Run `Format: Document` to test `line-length` and `quote-style`


https://github.com/user-attachments/assets/0a38176f-3fb0-4960-a213-73b2ea5b1180

### Neovim

**`init.lua`**:
```lua
require('lspconfig').ruff.setup {
  init_options = {
    settings = {
      lint = {
        extendSelect = { 'TID' },
      },
      configuration = {
        ['line-length'] = 50,
        format = {
          ['quote-style'] = 'single',
        },
        lint = {
          unfixable = { 'F401' },
          ['flake8-tidy-imports'] = {
            ['banned-api'] = {
              ['typing.TypedDict'] = {
                msg = 'Use typing_extensions.TypedDict instead',
              },
            },
          },
        },
      },
    },
  },
}
```

Same steps as in the VS Code test:



https://github.com/user-attachments/assets/cfe49a9b-9a89-43d7-94f2-7f565d6e3c9d

## Documentation Preview



https://github.com/user-attachments/assets/e0062f58-6ec8-4e01-889d-fac76fd8b3c7



[playground]: https://play.ruff.rs

[^1]: This has one advantage that the value can be copy-pasted directly
into the playground
2025-02-26 10:17:11 +05:30
Brent Westbrook 78806361fd
Start detecting version-related syntax errors in the parser (#16090)
## Summary

This PR builds on the changes in #16220 to pass a target Python version
to the parser. It also adds the `Parser::unsupported_syntax_errors` field, which
collects version-related syntax errors while parsing. These syntax
errors are then turned into `Message`s in ruff (in preview mode).

This PR only detects one syntax error (`match` statement before Python
3.10), but it has been pretty quick to extend to several other simple
errors (see #16308 for example).

## Test Plan

The current tests are CLI tests in the linter crate, but these could be
supplemented with inline parser tests after #16357.

I also tested the display of these syntax errors in VS Code:


![image](https://github.com/user-attachments/assets/062b4441-740e-46c3-887c-a954049ef26e)

![image](https://github.com/user-attachments/assets/101f55b8-146c-4d59-b6b0-922f19bcd0fa)

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-02-25 23:03:48 -05:00
Douglas Creager b39a4ad01d
[red-knot] Rename constraint to predicate (#16382)
In https://github.com/astral-sh/ruff/pull/16306#discussion_r1966290700,
@carljm pointed out that #16306 introduced a terminology problem, with
too many things called a "constraint". This is a follow-up PR that
renames `Constraint` to `Predicate` to hopefully clear things up a bit.
So now we have that:

- a _predicate_ is a Python expression that might influence type
inference
- a _narrowing constraint_ is a list of predicates that constraint the
type of a binding that is visible at a use
- a _visibility constraint_ is a ternary formula of predicates that
define whether a binding is visible or a statement is reachable

This is a pure renaming, with no behavioral changes.
2025-02-25 14:52:40 -05:00
David Peter 86b01d2d3c
[red-knot] Correct modeling of dunder calls (#16368)
## Summary

Model dunder-calls correctly (and in one single place), by implementing
this behavior (using `__getitem__` as an example).

```py
def getitem_desugared(obj: object, key: object) -> object:
    getitem_callable = find_in_mro(type(obj), "__getitem__")
    if hasattr(getitem_callable, "__get__"):
        getitem_callable = getitem_callable.__get__(obj, type(obj))

    return getitem_callable(key)
```

See the new `calls/dunder.md` test suite for more information. The new
behavior also needs much fewer lines of code (the diff is positive due
to new tests).

## Test Plan

New tests; fix TODOs in existing tests.
2025-02-25 20:38:15 +01:00
David Peter f88328eedd
[red-knot] Handle possibly-unbound instance members (#16363)
## Summary

Adds support for possibly-unbound/undeclared instance members.

## Test Plan

New MD tests.
2025-02-25 20:00:38 +01:00
Douglas Creager fa76f6cbb2
[red-knot] Use arena-allocated association lists for narrowing constraints (#16306)
This PR adds an implementation of [association
lists](https://en.wikipedia.org/wiki/Association_list), and uses them to
replace the previous `BitSet`/`SmallVec` representation for narrowing
constraints.

An association list is a linked list of key/value pairs. We additionally
guarantee that the elements of an association list are sorted (by their
keys), and that they do not contain any entries with duplicate keys.

Association lists have fallen out of favor in recent decades, since you
often need operations that are inefficient on them. In particular,
looking up a random element by index is O(n), just like a linked list;
and looking up an element by key is also O(n), since you must do a
linear scan of the list to find the matching element. Luckily we don't
need either of those operations for narrowing constraints!

The typical implementation also suffers from poor cache locality and
high memory allocation overhead, since individual list cells are
typically allocated separately from the heap. We solve that last problem
by storing the cells of an association list in an `IndexVec` arena.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-02-25 10:58:56 -05:00
Alex Waygood 5c007db7e2
[red-knot] Rewrite `Type::try_iterate()` to improve type inference and diagnostic messages (#16321) 2025-02-25 14:02:03 +00:00
Muspi Merol a1a536b2c5
Normalize inconsistent markdown headings in docstrings (#16364)
I am working on a project that uses ruff linters' docs to generate a
fine-tuning dataset for LLMs.

To achieve this, I first ran the command `ruff rule --all
--output-format json` to retrieve all the rules. Then, I parsed the
explanation field to get these 3 consistent sections:

- `Why is this bad?`
- `What it does`
- `Example`

However, during the initial processing, I noticed that the markdown
headings are not that consistent. For instance:

- In most cases, `Use instead` appears as a normal paragraph within the
`Example` section, but in the file
`crates/ruff_linter/src/rules/flake8_bandit/rules/django_extra.rs` it is
a level-2 heading
- The heading "What it does**?**" is used in some places, while others
consistently use "What it does"
- There are 831 `Example` headings and 65 `Examples`. But all of them
only have one example case

This PR normalized these across all rules.

## Test Plan

CI are passed.
2025-02-25 15:42:55 +05:30
David Peter aac79e453a
[red-knot] Better diagnostics for method calls (#16362)
## Summary

Add better error messages and additional spans for method calls. Can be
reviewed commit-by-commit.

before:

```
error: lint:invalid-argument-type
 --> /home/shark/playground/test.py:6:10
  |
5 | c = C()
6 | c.square("hello")  # error: [invalid-argument-type]
  |          ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 2 (`x`); expected type `int`
7 |
8 | # import inspect
  |
```

after:

```
error: lint:invalid-argument-type
 --> /home/shark/playground/test.py:6:10
  |
5 | c = C()
6 | c.square("hello")  # error: [invalid-argument-type]
  |          ^^^^^^^ Object of type `Literal["hello"]` cannot be assigned to parameter 2 (`x`) of bound method `square`; expected type `int`
7 |
8 | # import inspect
  |
 ::: /home/shark/playground/test.py:2:22
  |
1 | class C:
2 |     def square(self, x: int) -> int:
  |                      ------ info: parameter declared in function definition here
3 |         return x * x
  |
```

## Test Plan

New snapshot test
2025-02-25 09:58:08 +01:00
Micha Reiser fd7b3c83ad
[red-knot] Add argfile and windows glob path support (#16353) 2025-02-25 08:43:13 +01:00
Micha Reiser d895ee0014
[red-knot] Handle pipe-errors gracefully (#16354) 2025-02-25 08:42:52 +01:00
Micha Reiser 4732c58829
Rename `venv-path` to `python` (#16347) 2025-02-24 19:41:06 +01:00
Alex Waygood 45bae29a4b
[red-knot] Fixup some formatting in `infer.rs` (#16348) 2025-02-24 14:44:49 +00:00
Alex Waygood 7059f4249b
[red-knot] Restrict visibility of more things in `class.rs` (#16346) 2025-02-24 14:30:56 +00:00
Mike Perlov 68991d09a8
[red-knot] Add diagnostic for class-object access to pure instance variables (#16036)
## Summary

Add a diagnostic if a pure instance variable is accessed on a class object. For example

```py
class C:
    instance_only: str

    def __init__(self):
        self.instance_only = "a"

# error: Attribute `instance_only` can only be accessed on instances, not on the class object `Literal[C]` itself.
C.instance_only
```


---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-02-24 15:17:16 +01:00
Brent Westbrook e7a6c19e3a
Add `per-file-target-version` option (#16257)
## Summary

This PR is another step in preparing to detect syntax errors in the
parser. It introduces the new `per-file-target-version` top-level
configuration option, which holds a mapping of compiled glob patterns to
Python versions. I intend to use the
`LinterSettings::resolve_target_version` method here to pass to the
parser:


f50849aeef/crates/ruff_linter/src/linter.rs (L491-L493)

## Test Plan

I added two new CLI tests to show that the `per-file-target-version` is
respected in both the formatter and the linter.
2025-02-24 08:47:13 -05:00
Vasco Schiavo 42a5f5ef6a
[PLW1507] Mark fix unsafe (#16343) 2025-02-24 13:42:44 +01:00
Alex Waygood 5bac4f6bd4
[red-knot] Add a test to ensure that `KnownClass::try_from_file_and_name()` is kept up to date (#16326) 2025-02-24 12:14:20 +00:00
Micha Reiser 320a3c68ae
Extract class and instance types (#16337) 2025-02-24 11:36:20 +00:00
David Peter 141ba253da
[red-knot] Add support for `@classmethod`s (#16305)
## Summary

Add support for `@classmethod`s.

```py
class C:
    @classmethod
    def f(cls, x: int) -> str:
        return "a"

reveal_type(C.f(1))  # revealed: str
```

## Test Plan

New Markdown tests
2025-02-24 09:55:34 +01:00
Micha Reiser 5eaf225fc3
Update Salsa part 1 (#16340) 2025-02-24 09:35:21 +01:00
Vasco Schiavo b312b53c2e
[`flake8-pyi`] Mark `PYI030` fix unsafe when comments are deleted (#16322) 2025-02-23 21:22:14 +00:00
InSync c814745643
[`flake8-self`] Ignore attribute accesses on instance-like variables (`SLF001`) (#16149) 2025-02-23 10:00:49 +00:00
Ari Pollak aa88f2dbe5
Fix example for S611 (#16316)
## Summary

* Existing example did not include RawSQL() call like it should
* Also clarify the example a bit to make it clearer that the code is not
secure
## Test Plan

N/A, only documentation updated
2025-02-22 14:15:29 -05:00
Alex Waygood 64effa4aea
[red-knot] Add a regression test for recent improvement to `TypeInferenceBuilder::infer_name_load()` (#16310) 2025-02-21 22:28:42 +00:00
Alex Waygood 224a36f5f3
Teach red-knot that `type(x)` is the same as `x.__class__` (#16301) 2025-02-21 21:05:48 +00:00
Alex Waygood 5347abc766
[red-knot] Generalise special-casing for `KnownClass`es in `Type::bool` (#16300) 2025-02-21 20:46:36 +00:00
Micha Reiser 5fab97f1ef
[red-knot] Diagnostics for incorrect `bool` usages (#16238) 2025-02-21 19:26:05 +01:00
David Peter 3aa7ba31b1
[red-knot] Fix descriptor `__get__` call on class objects (#16304)
## Summary

I spotted a minor mistake in my descriptor protocol implementation where
`C.descriptor` would pass the meta type (`type`) of the type of `C`
(`Literal[C]`) as the owner argument to `__get__`, instead of passing
`Literal[C]` directly.

## Test Plan

New test.
2025-02-21 15:35:41 +01:00
Douglas Creager 4dae09ecff
[red-knot] Better handling of visibility constraint copies (#16276)
Two related changes.  For context:

1. We were maintaining two separate arenas of `Constraint`s in each
use-def map. One was used for narrowing constraints, and the other for
visibility constraints. The visibility constraint arena was interned,
ensuring that we always used the same ID for any particular
`Constraint`. The narrowing constraint arena was not interned.

2. The TDD code relies on _all_ TDD nodes being interned and reduced.
This is an important requirement for TDDs to be a canonical form, which
allows us to use a single int comparison to test for "always true/false"
and to compare two TDDs for equivalence. But we also need to support an
individual `Constraint` having multiple values in a TDD evaluation (e.g.
to handle a `while` condition having different values the first time
it's evaluated vs later times). Previously, we handled that by
introducing a "copy" number, which was only there as a disambiguator, to
allow an interned, deduplicated constraint ID to appear in the TDD
formula multiple times.

A better way to handle (2) is to not intern the constraints in the
visibility constraint arena! The caller now gets to decide: if they add
a `Constraint` to the arena more than once, they get distinct
`ScopedConstraintId`s — which the TDD code will treat as distinct
variables, allowing them to take on different values in the ternary
function.

With that in place, we can then consolidate on a single (non-interned)
arena, which is shared for both narrowing and visibility constraints.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-02-21 09:16:25 -05:00
Darius Carrier b9b094869a
[`pylint`] Fix false positives, add missing methods, and support positional-only parameters (`PLE0302`) (#16263)
## Summary

Resolves 3/4 requests in #16217:

-  Remove not special methods: `__cmp__`, `__div__`, `__nonzero__`, and
`__unicode__`.
-  Add special methods: `__next__`, `__buffer__`, `__class_getitem__`,
`__mro_entries__`, `__release_buffer__`, and `__subclasshook__`.
-  Support positional-only arguments.
-  Add support for module functions `__dir__` and `__getattr__`. As
mentioned in the issue the check is scoped for methods rather than
module functions. I am hesitant to expand the scope of this check
without a discussion.

## Test Plan

- Manually confirmed each example file from the issue functioned as
expected.
- Ran cargo nextest to ensure `unexpected_special_method_signature` test
still passed.

Fixes #16217.
2025-02-21 08:38:51 -05:00
Alex Waygood b3c5932fda
[red-knot] Restrict visibility of the `module_type_symbols` function (#16290) 2025-02-21 10:55:22 +00:00
Alex Waygood fe3ae587ea
[red-knot] Fix subtle detail in where the `types.ModuleType` attribute lookup should happen in `TypeInferenceBuilder::infer_name_load()` (#16284) 2025-02-21 10:48:52 +00:00
Dhruv Manilawala c2b9fa84f7
Refactor workspace logic into `workspace.rs` (#16295)
## Summary

This is just a small refactor to move workspace related structs and impl
out from `server.rs` where `Server` is defined and into a new
`workspace.rs`.
2025-02-21 08:37:29 +00:00
Victorien 793264db13
[`ruff`] Add more Pydantic models variants to the list of default copy semantics (`RUF012`) (#16291) 2025-02-21 08:28:13 +01:00
David Peter d2e034adcd
[red-knot] Method calls and the descriptor protocol (#16121)
## Summary

This PR achieves the following:

* Add support for checking method calls, and inferring return types from
method calls. For example:
  ```py
  reveal_type("abcde".find("abc"))  # revealed: int
  reveal_type("foo".encode(encoding="utf-8"))  # revealed: bytes
  
  "abcde".find(123)  # error: [invalid-argument-type]
  
  class C:
      def f(self) -> int:
          pass
  
  reveal_type(C.f)  # revealed: <function `f`>
  reveal_type(C().f)  # revealed: <bound method: `f` of `C`>
  
  C.f()  # error: [missing-argument]
  reveal_type(C().f())  # revealed: int
  ```
* Implement the descriptor protocol, i.e. properly call the `__get__`
method when a descriptor object is accessed through a class object or an
instance of a class. For example:
  ```py
  from typing import Literal
  
  class Ten:
def __get__(self, instance: object, owner: type | None = None) ->
Literal[10]:
          return 10
  
  class C:
      ten: Ten = Ten()
  
  reveal_type(C.ten)  # revealed: Literal[10]
  reveal_type(C().ten)  # revealed: Literal[10]
  ```
* Add support for member lookup on intersection types.
* Support type inference for `inspect.getattr_static(obj, attr)` calls.
This was mostly used as a debugging tool during development, but seems
more generally useful. It can be used to bypass the descriptor protocol.
For the example above:
  ```py
  from inspect import getattr_static
  
  reveal_type(getattr_static(C, "ten"))  # revealed: Ten
  ```
* Add a new `Type::Callable(…)` variant with the following sub-variants:
* `Type::Callable(CallableType::BoundMethod(…))` — represents bound
method objects, e.g. `C().f` above
* `Type::Callable(CallableType::MethodWrapperDunderGet(…))` — represents
`f.__get__` where `f` is a function
* `Type::Callable(WrapperDescriptorDunderGet)` — represents
`FunctionType.__get__`
* Add new known classes:
  * `types.MethodType`
  * `types.MethodWrapperType`
  * `types.WrapperDescriptorType`
  * `builtins.range`

## Performance analysis

On this branch, we do more work. We need to do more call checking, since
we now check all method calls. We also need to do ~twice as many member
lookups, because we need to check if a `__get__` attribute exists on
accessed members.

A brief analysis on `tomllib` shows that we now call `Type::call` 1780
times, compared to 612 calls before.

## Limitations

* Data descriptors are not yet supported, i.e. we do not infer correct
types for descriptor attribute accesses in `Store` context and do not
check writes to descriptor attributes. I felt like this was something
that could be split out as a follow-up without risking a major
architectural change.
* We currently distinguish between `Type::member` (with descriptor
protocol) and `Type::static_member` (without descriptor protocol). The
former corresponds to `obj.attr`, the latter corresponds to
`getattr_static(obj, "attr")`. However, to model some details correctly,
we would also need to distinguish between a static member lookup *with*
and *without* instance variables. The lookup without instance variables
corresponds to `find_name_in_mro`
[here](https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance).
We currently approximate both using `member_static`, which leads to two
open TODOs. Changing this would be a larger refactoring of
`Type::own_instance_member`, so I chose to leave it out of this PR.

## Test Plan

* New `call/methods.md` test suite for method calls
* New tests in `descriptor_protocol.md`
* New `call/getattr_static.md` test suite for `inspect.getattr_static`
* Various updated tests
2025-02-20 23:22:26 +01:00
David Peter f62e5406f2
[red-knot] Short-circuit bool calls on bool (#16292)
## Summary

This avoids looking up `__bool__` on class `bool` for every
`Type::Instance(bool).bool()` call. 1% performance win on cold cache, 4%
win on incremental performance.
2025-02-20 23:06:11 +01:00
Douglas Creager 1be4394155
[red-knot] Consolidate `SymbolBindings`/`SymbolDeclarations` state (#16286)
This updates the `SymbolBindings` and `SymbolDeclarations` types to use
a single smallvec of live bindings/declarations, instead of splitting
that out into separate containers for each field.

I'm seeing an 11-13% `cargo bench` performance improvement with this
locally (for both cold and incremental). I'm interested to see if
Codspeed agrees!

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-02-20 16:20:23 -05:00
Micha Reiser 470f852f04
[red-knot] Prevent cross-module query dependencies in `own_instance_member` (#16268) 2025-02-20 18:46:45 +01:00
Douglas Creager 529950fba1
[red-knot] Separate `definitions_by_definition` into separate fields (#16277)
A minor cleanup that breaks up a `HashMap` of an enum into separate
`HashMap`s for each variant. (These separate fields were already how
this cache was being described in the big comment at the top of the
file!)
2025-02-20 09:47:01 -05:00
Andrew Gallant 205222ca6b red_knot_python_semantic: avoid adding `callable_ty` to `CallBindingError`
This is a small tweak to avoid adding the callable `Type` on the error
value itself. Namely, it's always available regardless of the error, and
it's easy to pass it down explicitly to the diagnostic generating code.

It's likely that the other `CallBindingError` variants will also want
the callable `Type` to improve diagnostics too. This way, we don't have
to duplicate the `Type` on each variant. It's just available to all of
them.

Ref https://github.com/astral-sh/ruff/pull/16239#discussion_r1962352646
2025-02-20 08:18:59 -05:00
Brent Westbrook 54fccb3ee2
Bump version to 0.9.7 (#16271) 2025-02-20 08:12:11 -05:00
David Peter 8198668fc3
[red-knot] MDTest: Use custom class names instead of builtins (#16269)
## Summary

Follow up on the discussion
[here](https://github.com/astral-sh/ruff/pull/16121#discussion_r1962973298).
Replace builtin classes with custom placeholder names, which should
hopefully make the tests a bit easier to understand.

I carefully renamed things one after the other, to make sure that there
is no functional change in the tests.
2025-02-20 12:25:55 +00:00
Dhruv Manilawala fc6b03c8da
Handle requests received after shutdown message (#16262)
## Summary

This PR should help in
https://github.com/astral-sh/ruff-vscode/issues/676.

There are two issues that this is trying to fix all related to the way
shutdown should happen as per the protocol:
1. After the server handled the [shutdown
request](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#shutdown)
and while waiting for the exit notification:
	
> If a server receives requests after a shutdown request those requests
should error with `InvalidRequest`.
    
But, we raised an error and exited. This PR fixes it by entering a loop
which responds to any request during this period with `InvalidRequest`

2. If the server received an [exit
notification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#exit)
but the shutdown request was never received, the server handled that by
logging and exiting with success but as per the spec:

> The server should exit with success code 0 if the shutdown request has
been received before; otherwise with error code 1.

    So, this PR fixes that as well by raising an error in this case.

## Test Plan

I'm not sure how to go about testing this without using a mock server.
2025-02-20 11:10:42 +00:00
Micha Reiser fb09d63e55
[red-knot] Prefix `Type::call` and `dunder_call` with `try` (#16261) 2025-02-20 09:05:04 +00:00
Alex Waygood 16d0625dfb
Improve internal docs for various string-node APIs (#16256) 2025-02-19 16:13:45 +00:00
Alex Waygood 25920fe489
Rename `ExprStringLiteral::as_unconcatenated_string()` to `ExprStringLiteral::as_single_part_string()` (#16253) 2025-02-19 16:06:57 +00:00
Brent Westbrook 97d0659ce3
Pass `ParserOptions` to the parser (#16220)
## Summary

This is part of the preparation for detecting syntax errors in the
parser from https://github.com/astral-sh/ruff/pull/16090/. As suggested
in [this
comment](https://github.com/astral-sh/ruff/pull/16090/#discussion_r1953084509),
I started working on a `ParseOptions` struct that could be stored in the
parser. For this initial refactor, I only made it hold the existing
`Mode` option, but for syntax errors, we will also need it to have a
`PythonVersion`. For that use case, I'm picturing something like a
`ParseOptions::with_python_version` method, so you can extend the
current calls to something like

```rust
ParseOptions::from(mode).with_python_version(settings.target_version)
```

But I thought it was worth adding `ParseOptions` alone without changing
any other behavior first.

Most of the diff is just updating call sites taking `Mode` to take
`ParseOptions::from(Mode)` or those taking `PySourceType`s to take
`ParseOptions::from(PySourceType)`. The interesting changes are in the
new `parser/options.rs` file and smaller parts of `parser/mod.rs` and
`ruff_python_parser/src/lib.rs`.

## Test Plan

Existing tests, this should not change any behavior.
2025-02-19 10:50:50 -05:00
Douglas Creager cfc6941d5c
[red-knot] Resolve references in eager nested scopes eagerly (#16079)
We now resolve references in "eager" scopes correctly — using the
bindings and declarations that are visible at the point where the eager
scope is created, not the "public" type of the symbol (typically the
bindings visible at the end of the scope).

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-02-19 10:22:30 -05:00
Alex Waygood f50849aeef
Add `text_len()` methods to more `*Prefix` enums in `ruff_python_ast` (#16254) 2025-02-19 14:47:07 +00:00
Micha Reiser 55ea09401a
[red-knot] Allow any `Ranged` argument for `report_lint` and `report_diagnostic` (#16252) 2025-02-19 14:34:56 +01:00
Vasco Schiavo 3032867603
[pycodestyle] Exempt `site.addsitedir(...)` calls (E402) (#16251) 2025-02-19 14:31:47 +01:00
Andrew Gallant 3ea32e2cdd red_knot_python_semantic: improve diagnostic message for "invalid argument type"
This uses the refactoring and support for secondary diagnostic messages
to improve the diagnostic for "invalid argument type." The main
improvement here is that we show where the function being called is
defined, and annotate the span corresponding to the invalid parameter.
2025-02-19 08:24:19 -05:00
Andrew Gallant 87668e24b1 ruff_db: add "secondary" messages to `Diagnostic` trait
This is a small little hack to make the `Diagnostic` trait
capable of supporting attaching multiple spans.

This design should be considered transient. This was just the
quickest way that I could see to pass multiple spans through from
the type checker to the diagnostic renderer.
2025-02-19 08:24:19 -05:00
Andrew Gallant 18a9eddf60 ruff_db: refactor snippet rendering
This commit has no behavioral changes.

This refactor moves the logic for turning a `D: Diagnostic` into
an `annotate_snippets::Message` into its own types. This would
ideally just be a function or something, but the `annotate-snippets`
types want borrowed data, and sometimes we need to produce owned
data. So we gather everything we need into our own types and then
spit it back out in the format that `annotate-snippets` wants.

This factor was motivated by wanting to render multiple snippets.
The logic for generating a code frame is complicated enough that
it's worth splitting out so that we can reuse it for other spans.

(Note that one should consider this prototype-level code. It is
unlikely to survive for long.)
2025-02-19 08:24:19 -05:00
Andrew Gallant 222660170c red_knot_python_semantic: remove `Ranged` impl for `TypeCheckDiagnostic`
It seems nothing is using it, and I'm not sure if it makes semantic
sense. Particularly if we want to support multiple ranges. One could
make an argument that this ought to correspond to the "primary"
range (which we should have), but I think such a concept is better
expressed as an explicit routine if possible.
2025-02-19 08:24:19 -05:00
Micha Reiser e84985e9b3
[red-knot] Refactor `infer_chained_boolean_types` to have access to `TypeInferenceBuilder` (#16222) 2025-02-19 11:13:35 +01:00
InSync 01c3e6b94f
Add `red_knot/README.md` (#16230)
## Summary

Resolves #15979.

The file explains what Red Knot is (a type checker), what state it is in
(not yet ready for user testing), what its goals ("extremely fast") and
non-goals (not a drop-in replacement for other type checkers) are as
well as what the crates contain.

## Test Plan

None.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-02-18 23:31:02 -08:00
Wei Lee e92d43dfcd
[airflow] move class attributed related cases to AIR302_class_attribute (AIR302) (#16226)
## Summary


Move class attribute (property, methods, variables) related cases in
AIR302_names to AIR302_class_attribute

## Test Plan


No functionality change. Test fixture is reogranized
2025-02-19 11:13:17 +05:30
David Peter 877c1066d3
[red-knot] Update tests for attributes inferred from parameters (#16208)
## Summary

Update description and remove TODOs from out `attributes.md` test suite
to reflect our current intentions.

closes https://github.com/astral-sh/ruff/issues/15960
2025-02-18 22:43:11 +01:00
Carl Meyer 00b022d472
[red-knot] update TODO comment in mdtest (#16242)
This comment gave wrong/misleading info about the reason for the wrong
output, just updating it to be correct to avoid confusing our future
selves.
2025-02-18 20:52:17 +00:00
Dylan a23e489c79
[`refurb`] Correctly handle lengths of literal strings in `slice-to-remove-prefix-or-suffix` (`FURB188`) (#16237)
Fixes false negative when slice bound uses length of string literal.

We were meant to check the following, for example. Given:

```python
  text[:bound] if text.endswith(suffix) else text
```
We want to know whether:
   - `suffix` is a string literal and `bound` is a number literal
   - `suffix` is an expression and `bound` is
       exactly `-len(suffix)` (as AST nodes, prior to evaluation.)
       
The issue is that negative number literals like `-10` are stored as
unary operators applied to a number literal in the AST. So when `suffix`
was a string literal but `bound` was `-len(suffix)` we were getting
caught in the match arm where `bound` needed to be a number. This is now
fixed with a guard.


Closes #16231
2025-02-18 12:52:26 -06:00
Brent Westbrook 1907e60fab
Pass `ast::PythonVersion` to `type_hint_resolves_to_any` (#16236)
This is a follow-up to #16170 to use `ast::PythonVersion` in the
`type_hint_resolves_to_any` call chain, as suggested (and implemented!)
by Alex
[here](https://github.com/astral-sh/ruff/pull/16170#discussion_r1960015181).

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-02-18 13:22:50 -05:00
Brent Westbrook a9efdea113
Use `ast::PythonVersion` internally in the formatter and linter (#16170)
## Summary

This PR updates the formatter and linter to use the `PythonVersion`
struct from the `ruff_python_ast` crate internally. While this doesn't
remove the need for the `linter::PythonVersion` enum, it does remove the
`formatter::PythonVersion` enum and limits the use in the linter to
deserializing from CLI arguments and config files and moves most of the
remaining methods to the `ast::PythonVersion` struct.

## Test Plan

Existing tests, with some inputs and outputs updated to reflect the new
(de)serialization format. I think these are test-specific and shouldn't
affect any external (de)serialization.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-02-18 12:03:13 -05:00
InSync 711af0d929
[`refurb`] Manual timezone monkeypatching (`FURB162`) (#16113)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-18 14:35:33 +01:00
sobolevn d8e3fcca97
[`pyupgrade`] Do not upgrade functional TypedDicts with private field names to the class-based syntax (`UP013`) (#16219) 2025-02-18 13:03:27 +00:00
Alex Waygood 66a0467305
Improve docs for PYI019 (#16229) 2025-02-18 12:52:46 +00:00
Micha Reiser 4ed5db0d42
Refactor `CallOutcome` to `Result` (#16161) 2025-02-18 13:34:39 +01:00
Dhruv Manilawala ed9c18d9b4
Include document specific debug info (#16215)
## Summary

Related https://github.com/astral-sh/ruff-vscode/pull/692.

## Test Plan

**When there's no active text document:**

```
[Info  - 10:57:03 PM] Global:
executable = /Users/dhruv/work/astral/ruff/target/debug/ruff
version = 0.9.6
position_encoding = UTF16
workspace_root_folders = [
    "/Users/dhruv/playground/ruff",
]
indexed_configuration_files = [
    "/Users/dhruv/playground/ruff/pyproject.toml",
    "/Users/dhruv/playground/ruff/formatter/ruff.toml",
]
open_documents = 0
client_capabilities = ResolvedClientCapabilities {
    code_action_deferred_edit_resolution: true,
    apply_edit: true,
    document_changes: true,
    workspace_refresh: true,
    pull_diagnostics: true,
}

global_client_settings = ResolvedClientSettings {
    fix_all: true,
    organize_imports: true,
    lint_enable: true,
    disable_rule_comment_enable: true,
    fix_violation_enable: true,
    show_syntax_errors: true,
    editor_settings: ResolvedEditorSettings {
        configuration: None,
        lint_preview: None,
        format_preview: None,
        select: None,
        extend_select: None,
        ignore: None,
        exclude: None,
        line_length: None,
        configuration_preference: EditorFirst,
    },
}
```

**When there's an active text document that's been passed as param:**

```
[Info  - 10:53:33 PM] Global:
executable = /Users/dhruv/work/astral/ruff/target/debug/ruff
version = 0.9.6
position_encoding = UTF16
workspace_root_folders = [
    "/Users/dhruv/playground/ruff",
]
indexed_configuration_files = [
    "/Users/dhruv/playground/ruff/pyproject.toml",
    "/Users/dhruv/playground/ruff/formatter/ruff.toml",
]
open_documents = 1
client_capabilities = ResolvedClientCapabilities {
    code_action_deferred_edit_resolution: true,
    apply_edit: true,
    document_changes: true,
    workspace_refresh: true,
    pull_diagnostics: true,
}

Document:
uri = file:///Users/dhruv/playground/ruff/lsp/play.py
kind = Text
version = 1
client_settings = ResolvedClientSettings {
    fix_all: true,
    organize_imports: true,
    lint_enable: true,
    disable_rule_comment_enable: true,
    fix_violation_enable: true,
    show_syntax_errors: true,
    editor_settings: ResolvedEditorSettings {
        configuration: None,
        lint_preview: None,
        format_preview: None,
        select: None,
        extend_select: None,
        ignore: None,
        exclude: None,
        line_length: None,
        configuration_preference: EditorFirst,
    },
}
config_path = Some("/Users/dhruv/playground/ruff/pyproject.toml")

...
```

Replace `...` at the end with the output of `ruff check --show-settings
path.py`
2025-02-18 15:38:30 +05:30
Dhruv Manilawala bb2a712f6a
Update server to return the debug info as string (#16214)
## Summary

This PR updates the `ruff.printDebugInformation` command to return the
info as string in the response. Currently, we send a `window/logMessage`
request with the info but that has the disadvantage that it's not
visible to the user directly.

What `rust-analyzer` does with it's `rust-analyzer/status` request which
returns it as a string which then the client can just display it in a
separate window. This is what I'm thinking of doing as well.

Other editors can also benefit from it by directly opening a temporary
file with this information that the user can see directly.

There are couple of options here:
1. Keep using the command, keep the log request and return the string
2. Keep using the command, remove the log request and return the string
3. Create a new request similar to `rust-analyzer/status` which returns
a string

This PR implements (1) but I'd want to move towards (2) and remove the
log request completely. We haven't advertised it as such so this would
only require updating the VS Code extension to handle it by opening a
new document with the debug content.

## Test plan

For VS Code, refer to https://github.com/astral-sh/ruff-vscode/pull/694.

For Neovim, one could do:
```lua
local function execute_ruff_command(command)
  local client = vim.lsp.get_clients({ 
    bufnr = vim.api.nvim_get_current_buf(), 
    name = name,
    method = 'workspace/executeCommand',
  })[1]
  if not client then
    return
  end
  client.request('workspace/executeCommand', {
    command = command,
    arguments = {
      { uri = vim.uri_from_bufnr(0) }
    },
    function(err, result)
      if err then
        -- log error
        return
      end
      vim.print(result)
      -- Or, open a new window with the `result` content
    end
  }
```
2025-02-18 15:16:41 +05:30
Wei Lee 2d8ccfe6f2
[`airflow`] Group `ImportPathMoved` and `ProviderName` to avoid misusing (`AIR303`) (#16157)
## Summary

Separate ImportPathMoved and ProviderName to avoid misusing (AIR303)

## Test Plan

only code arrangement is updated. existing test fixture should be not be
changed
2025-02-18 15:11:58 +05:30
Micha Reiser 31180a84e4
Fix unstable formatting of trailing end-of-line comments of parenthesized attribute values (#16187) 2025-02-18 08:43:51 +01:00
Dhruv Manilawala 82eae511ca
Ignore source code actions for a notebook cell (#16154)
## Summary

Related to https://github.com/astral-sh/ruff-vscode/pull/686, this PR
ignores handling source code actions for notebooks which are not
prefixed with `notebook`.

The main motivation is that the native server does not actually handle
it well which results in gibberish code. There's some context about this
in
https://github.com/astral-sh/ruff-vscode/issues/680#issuecomment-2647490812
and the following comments.

closes: https://github.com/astral-sh/ruff-vscode/issues/680

## Test Plan

Running a notebook with the following does nothing except log the
message:
```json
  "notebook.codeActionsOnSave": {
    "source.organizeImports.ruff": "explicit",
  },
```

while, including the `notebook` code actions does make the edit (as
usual):
```json
  "notebook.codeActionsOnSave": {
    "notebook.source.organizeImports.ruff": "explicit"
  },
```
2025-02-18 10:28:03 +05:30
Dhruv Manilawala 9f111eaebf
red-knot: move symbol lookups in `symbol.rs` (#16152)
## Summary

This PR does the following:
* Moves the following from `types.rs` in `symbol.rs`:
	* `symbol`
	* `global_symbol`
	* `imported_symbol`
	* `symbol_from_bindings`
	* `symbol_from_declarations`
	* `SymbolAndQualifiers`
	* `SymbolFromDeclarationsResult`
* Moves the following from `stdlib.rs` in `symbol.rs` and removes
`stdlib.rs`:
	* `known_module_symbol`
	* `builtins_symbol`
	* `typing_symbol` (only for tests)
	* `typing_extensions_symbol`
	* `builtins_module_scope`
	* `core_module_scope`
* Add `symbol_from_bindings_impl` and `symbol_from_declarations_impl` to
keep `RequiresExplicitReExport` an implementation detail
* Make `declaration_type` a `pub(crate)` as it's required in
`symbol_from_declarations` (`binding_type` is already `pub(crate)`

The main motivation is to keep the implementation details private and
only expose an ergonomic API which uses sane defaults for various
scenario to avoid any mistakes from the caller. Refer to
https://github.com/astral-sh/ruff/pull/16133#discussion_r1955262772,
https://github.com/astral-sh/ruff/pull/16133#issue-2850146612 for
details.
2025-02-17 17:45:38 +05:30
purajit 9304fdf4ec
better error messages while loading configuration `extend`s (#15658)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-17 10:35:30 +01:00
Alex Waygood b6b1947010
Improve API exposed on `ExprStringLiteral` nodes (#16192)
## Summary

This PR makes the following changes:
- It adjusts various callsites to use the new
`ast::StringLiteral::contents_range()` method that was introduced in
https://github.com/astral-sh/ruff/pull/16183. This is less verbose and
more type-safe than using the `ast::str::raw_contents()` helper
function.
- It adds a new `ast::ExprStringLiteral::as_unconcatenated_literal()`
helper method, and adjusts various callsites to use it. This addresses
@MichaReiser's review comment at
https://github.com/astral-sh/ruff/pull/16183#discussion_r1957334365.
There is no functional change here, but it helps readability to make it
clearer that we're differentiating between implicitly concatenated
strings and unconcatenated strings at various points.
- It renames the `StringLiteralValue::flags()` method to
`StringLiteralFlags::first_literal_flags()`. If you're dealing with an
implicitly concatenated string `string_node`,
`string_node.value.flags().closer_len()` could give an incorrect result;
this renaming makes it clearer that the `StringLiteralFlags` instance
returned by the method is only guaranteed to give accurate information
for the first `StringLiteral` contained in the `ExprStringLiteral` node.
- It deletes the unused `BytesLiteralValue::flags()` method. This seems
prone to misuse in the same way as `StringLiteralValue::flags()`: if
it's an implicitly concatenated bytestring, the `BytesLiteralFlags`
instance returned by the method would only give accurate information for
the first `BytesLiteral` in the bytestring.

## Test Plan

`cargo test`
2025-02-17 07:58:54 +00:00
Alex Waygood 4941975e74
[red-knot] Recognize `...` as a singleton (#16184) 2025-02-16 22:01:02 +00:00
Dylan d4b4f65e20
[`pep8-naming`] Clarify preview behavior in `invalid-first-argument-name-for-class-method` (`N804`) (#16193)
Adds clarification in the documentation for
[invalid-first-argument-name-for-class-method
(N804)](https://docs.astral.sh/ruff/rules/invalid-first-argument-name-for-class-method/#invalid-first-argument-name-for-class-method-n804)

(Also fixes an unrelated typo).
2025-02-16 15:02:50 -06:00
cake-monotone 96dd1b1587
Consider `__new__` methods as special function type for enforcing class method or static method rules (#13305)
## Summary

`__new__` methods are technically static methods, with `cls` as their
first argument. However, Ruff currently classifies them as classmethod,
which causes two issues:

- It conveys incorrect information, leading to confusion. For example,
in cases like ARG003, `__new__` is explicitly treated as a classmethod.
- Future rules that should apply to staticmethod may not be applied
correctly due to this misclassification.

Motivated by this, the current PR makes the following adjustments:

1. Introduces `FunctionType::NewMethod` as an enum variant, since, for
the purposes of lint rules, `__new__` sometimes behaves like a static
method and other times like a class method. This is an internal change.

2. The following rule behaviors and messages are totally unchanged:
- [too-many-arguments
(PLR0913)](https://docs.astral.sh/ruff/rules/too-many-arguments/#too-many-arguments-plr0913)
- [too-many-positional-arguments
(PLR0917)](https://docs.astral.sh/ruff/rules/too-many-positional-arguments/#too-many-positional-arguments-plr0917)
3. The following rule behaviors are unchanged, but the messages have
been changed for correctness to use "`__new__` method" instead of "class
method":
- [self-or-cls-assignment
(PLW0642)](https://docs.astral.sh/ruff/rules/self-or-cls-assignment/#self-or-cls-assignment-plw0642)
4. The following rules are changed _unconditionally_ (not gated behind
preview) because their current behavior is an honest bug: it just isn't
true that `__new__` is a class method, and it _is_ true that `__new__`
is a static method:
- [unused-class-method-argument
(ARG003)](https://docs.astral.sh/ruff/rules/unused-class-method-argument/#unused-class-method-argument-arg003)
no longer applies to `__new__`
- [unused-static-method-argument
(ARG004)](https://docs.astral.sh/ruff/rules/unused-static-method-argument/#unused-static-method-argument-arg004)
now applies to `__new__`
5. The only changes which differ based on `preview` are the following:
- [invalid-first-argument-name-for-class-method
(N804)](https://docs.astral.sh/ruff/rules/invalid-first-argument-name-for-class-method/#invalid-first-argument-name-for-class-method-n804):
This is _skipped_ when `preview` is _enabled_. When `preview` is
_disabled_, the rule is the same but the _message_ has been modified to
say "`__new__` method" instead of "class method".
- [bad-staticmethod-argument
(PLW0211)](https://docs.astral.sh/ruff/rules/bad-staticmethod-argument/#bad-staticmethod-argument-plw0211):
When `preview` is enabled, this now applies to `__new__`.

Closes #13154

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-02-16 14:12:25 -06:00
Dylan f29c7b03ec
Warn on invalid noqa even when there are no diagnostics (#16178)
On `main` we warn the user if there is an invalid noqa comment[^1] and
at least one of the following holds:

- There is at least one diagnostic
- A lint rule related to `noqa`s is enabled (e.g. `RUF100`)

This is probably strange behavior from the point of view of the user, so
we now show invalid `noqa`s even when there are no diagnostics.

Closes #12831

[^1]: For the current definition of "invalid noqa comment", which may be
expanded in #12811 . This PR is independent of loc. cit. in the sense
that the CLI warnings should be consistent, regardless of which `noqa`
comments are considered invalid.
2025-02-16 13:58:18 -06:00
Brent Westbrook 3a0d45c85b
[`flake8-debugger`] Also flag `sys.breakpointhook` and `sys.__breakpointhook__` (`T100`) (#16191)
## Summary

Fixes #16189.

Only `sys.breakpointhook` is flagged by the upstream linter:

007a745c86/pylint/checkers/stdlib.py (L38)

but I think it makes sense to flag
[`__breakpointhook__`](https://docs.python.org/3/library/sys.html#sys.__breakpointhook__)
too, as suggested in the issue because it
> contain[s] the original value of breakpointhook [...] in case [it
happens] to get replaced with broken or alternative objects.

## Test Plan

New T100 test cases
2025-02-16 14:50:16 -05:00
Aaron Gokaslan 1f17916224
Add doc about usedforsecurity flag for S324 (#16190)
## Summary

Provides documentation about the FIPS compliant flag for Python hashlib
`usedforsecurity`
Fixes #16188 

## Test Plan

* pre-commit hooks

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-02-16 13:06:55 -05:00
Alex Waygood 61fef0a64a
Reduce memory usage of `Docstring` struct (#16183) 2025-02-16 15:23:52 +00:00
Alex Waygood 93aff36147
[red-knot] Improve handling of inherited class attributes (#16160) 2025-02-15 18:22:35 +00:00
Ayush Baweja df45a9db64
[flake8-comprehensions]: Handle trailing comma in C403 fix (#16110)
## Summary

Resolves [#16099 ](https://github.com/astral-sh/ruff/issues/16099) based
on [#15929 ](https://github.com/astral-sh/ruff/pull/15929)

## Test Plan

Added test case `s = set([x for x in range(3)],)` and updated snapshot.

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-02-15 11:45:41 -06:00
InSync 3c69b685ee
[`ruff`] Implicit class variable in dataclass (`RUF045`) (#14349)
## Summary

Implement lint rule to flag un-annotated variable assignments in dataclass definitions.

Resolves #12877.

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-02-15 09:08:13 -06:00
github-actions[bot] 171facd960
Sync vendored typeshed stubs (#16173)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2025-02-15 10:01:34 +00:00
InSync 977447f9b8
Sort linters alphabetically (#16168)
## Summary

Resolves #16164.

Linters are now sorted by their names case-insensitively.

## Test Plan


![](https://github.com/user-attachments/assets/87ffd4d8-1ba5-4a4b-8fed-dd21a020bd27)

Also unit tests.
2025-02-14 22:05:08 +01:00
Micha Reiser b3e99b25bf
Fix missing serde feature for red_knot_python_semantic (#16169)
## Summary

Running `cargo test -p red_knot_python_semantic` failed because of a
missing serde feature. This PR enables the `ruff_python_ast`'`s `serde`
if the crate's `serde` feature is enabled

## Test Plan

`cargo test -p red_knot_python_semantic` compiles again
2025-02-14 20:31:55 +00:00
Carl Meyer dcabb948f3
[red-knot] add special case for float/complex (#16166)
When adjusting the existing tests, I aimed to avoid dealing with the
special case in other tests if it's not necessary to do so (that is,
avoid using `float` and `complex` as examples where we just need "some
type"), and keep the tests for the special case mostly collected in the
mdtest dedicated to that purpose.

Fixes https://github.com/astral-sh/ruff/issues/14932
2025-02-14 12:24:10 -08:00
Vlad Nedelcu 219712860c
[refurb] Check for subclasses includes subscript expressions (FURB189) (#16155)
## Summary

Added checks for subscript expressions on builtin classes as in FURB189.
The object is changed to use the collections objects and the types from
the subscript are kept.

Resolves #16130 

> Note: Added some comments in the code explaining why
## Test Plan


- Added a subscript dict and list class to the test file.
- Tested locally to check that the symbols are changed and the types are
kept.
- No modifications changed on optional `str` values.
2025-02-14 20:21:26 +01:00
Brent Westbrook f58a54f043
Move `red_knot_python_semantic::PythonVersion` to the `ruff_python_ast` crate (#16147)
## Summary

This PR moves the `PythonVersion` struct from the
`red_knot_python_semantic` crate to the `ruff_python_ast` crate so that
it can be used more easily in the syntax error detection work. Compared
to that [prototype](https://github.com/astral-sh/ruff/pull/16090/) these
changes reduce us from 2 `PythonVersion` structs to 1.

This does not unify any of the `PythonVersion` *enums*, but I hope to
make some progress on that in a follow-up.

## Test Plan

Existing tests, this should not change any external behavior.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-02-14 12:48:08 -05:00
Junhson Jean-Baptiste fa28dc5ccf
[internal] Move Linter `OperatorPrecedence` into `ruff_python_ast` crate (#16162)
## Summary

This change begins to resolve #16071 by moving the `OperatorPrecedence`
structs from the `ruff_python_linter` crate into `ruff_python_ast`. This
PR also implements `precedence()` methods on the `Expr` and `ExprRef`
enums.

## Test Plan

Since this change mainly shifts existing logic, I didn't add any
additional tests. Existing tests do pass.
2025-02-14 15:55:07 +01:00
Dhruv Manilawala 63dd68e0ed
Refactor symbol lookup APIs to hide re-export implementation details (#16133)
## Summary

This PR refactors the symbol lookup APIs to better facilitate the
re-export implementation. Specifically,
* Add `module_type_symbol` which returns the `Symbol` that's a member of
`types.ModuleType`
* Rename `symbol` -> `symbol_impl`; add `symbol` which delegates to
`symbol_impl` with `RequireExplicitReExport::No`
* Update `global_symbol` to do `symbol_impl` -> fall back to
`module_type_symbol` and default to `RequireExplicitReExport::No`
* Add `imported_symbol` to do `symbol_impl` with
`RequireExplicitReExport` as `Yes` if the module is in a stub file else
`No`
* Update `known_module_symbol` to use `imported_symbol` with a fallback
to `module_type_symbol`
* Update `ModuleLiteralType::member` to use `imported_symbol` with a
custom fallback

We could potentially also update `symbol_from_declarations` and
`symbol_from_bindings` to avoid passing in the `RequireExplicitReExport`
as it would be always `No` if called directly. We could add
`symbol_from_declarations_impl` and `symbol_from_bindings_impl`.

Looking at the `_impl` functions, I think we should move all of these
symbol related logic into `symbol.rs` where `Symbol` is defined and the
`_impl` could be private while we expose the public APIs at the crate
level. This would also make the `RequireExplicitReExport` an
implementation detail and the caller doesn't need to worry about it.
2025-02-14 15:25:48 +05:30
Dhruv Manilawala 60b3ef2c98
[red-knot] Support re-export conventions for stub files (#16073)
This is an alternative implementation to #15848.

## Summary

This PR adds support for re-export conventions for imports for stub
files.

**How does this work?**
* Add a new flag on the `Import` and `ImportFrom` definitions to
indicate whether they're being exported or not
* Add a new enum to indicate whether the symbol lookup is happening
within the same file or is being queried from another file (e.g., an
import statement)
* When a `Symbol` is being queried, we'll skip the definitions that are
(a) coming from a stub file (b) external lookup and (c) check the
re-export flag on the definition

This implementation does not yet support `__all__` and `*` imports as
both are features that needs to be implemented independently.

closes: #14099
closes: #15476 

## Test Plan

Add test cases, update existing ones if required.
2025-02-14 15:17:51 +05:30
InSync 3d0a58eb60
[`pyupgrade`] Unwrap unary expressions correctly (`UP018`) (#15919)
## Summary

Resolves #15859.

The rule now adds parentheses if the original call wraps an unary
expression and is:

* The left-hand side of a binary expression where the operator is `**`.
* The caller of a call expression.
* The subscripted of a subscript expression.
* The object of an attribute access.

The fix will also be marked as unsafe if there are any comments in its
range.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-02-14 08:42:00 +01:00
Micha Reiser 81e202ed52
Make `CallBinding::callable_ty` required (#16135)
## Summary

The `callable_ty` is always known except in some TODO code where we can
use a `TODO` type instead.

## Test Plan

`cargo test`
2025-02-14 08:15:24 +01:00
Shaygan Hooshyari 0a75a1d56b
Replace is-macro with implementation in enums (#16144) 2025-02-13 22:49:00 +00:00
Vlad Nedelcu cb8b23d609
[flake8-pyi] Avoid flagging `custom-typevar-for-self` on metaclass methods (PYI019) (#16141) 2025-02-13 18:44:11 +00:00
Alex Waygood be49151a3d
[red-knot] Remove a parameter from the `symbol_by_id()` query (#16138) 2025-02-13 13:33:40 +00:00
InSync 7d2e40be2d
[`pylint`] Do not offer fix for raw strings (`PLE251`) (#16132)
## Summary

Resolves #13294, follow-up to #13882.

At #13882, it was concluded that a fix should not be offered for raw
strings. This change implements that. The five rules in question are now
no longer always fixable.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-13 08:36:11 +00:00
Brent Westbrook f8093b65ea
[`flake8-builtins`] Update documentation (`A005`) (#16097)
Follow-up to https://github.com/astral-sh/ruff/pull/15951 to update
* the options links in A005 to reference
`lint.flake8-builtins.builtins-strict-checking`
* the description of the rule to explain strict vs non-strict checking
* the option documentation to point back to the rule
2025-02-12 12:50:13 -05:00
Alex Waygood c31352f52b
[`ruff`] Skip RUF001 diagnostics when visiting string type definitions (#16122) 2025-02-12 16:27:38 +00:00
Andrew Gallant a9671e7008
ruff_db: add a vector for configuring diagnostic output (#16118)
For now, the only thing one can configure is whether color is enabled or
not. This avoids needing to ask the `colored` crate whether colors have
been globally enabled or disabled. And, more crucially, avoids the need
to _set_ this global flag for testing diagnostic output. Doing so can
have unintended consequences, as outlined in #16115.

Fixes #16115
2025-02-12 14:38:05 +00:00
Micha Reiser 03f08283ad
[red-knot] Fallback to `requires-python` if no `python-version` is specified (#16028)
## Summary

Add support for the `project.requires-python` field in `pyproject.toml`
files.

Fall back to the resolved lower bound of `project.requires-python` if
the `environment.python-version` field is `None` (or more accurately,
initialize `environment.python-version with `requires-python`'s lower
bound if left unspecified).

## UX design

There are two options on how we can handle the fallback to
`requires-python`'s lower bound:

1. Store the resolved lower bound in `environment.python-version` if
that field is `None` (Implemented in this PR)
2. Store the `requires-python` constraint separately. 

There's no observed difference unless a user-level configuration (or any
other inherited configuration is used). Let's discuss it on the given
example


**User configuration**

```toml
[environment]
python-version = "3.10"
```

**Project configuration (`pyproject.toml`)**

```toml
[project]
name = "test"
requires-python = ">= 3.12"

[tool.knot]
# No environment table
```

The resolved version for 1. is 3.12 because the `requires-python`
constraint precedence takes precedence over the `python-version` in the
user configuration. 2. resolves to 3.10 because all `python-version`
constraints take precedence before falling back to `requires-python`.

Ruff implements 1. It's also the easier to implement and it does seem
intuitive to me that the more local `requires-python` constraint takes
precedence.


## Test plan

Added CLI and unit tests.
2025-02-12 11:47:59 +00:00
Vasco Schiavo ae1b381c06
[`pylint`] Correct ordering of arguments in fix for `if-stmt-min-max` (`PLR1730`) (#16080)
The PR addresses the issue #16040 .

---

The logic used into the rule is the following:

Suppose to have an expression of the form 

```python
if a cmp b:
    c = d
```
where `a`,` b`, `c` and `d` are Python obj and `cmp` one of `<`, `>`,
`<=`, `>=`.

Then:

- `if a=c and b=d`
    
    - if `<=` fix with `a = max(b, a)`
    - if `>=`  fix with `a = min(b, a)`
    - if `>` fix with `a = min(a, b)`
    - if `<` fix with `a = max(a, b)`

- `if a=d and b=c`

    - if `<=` fix with `b = min(a, b)`
    - if `>=`  fix with `b = max(a, b)`
    - if `>` fix with `b = max(b, a)`
    - if `<` fix with `b = min(b, a)`
 
- do nothing, i.e., we cannot fix this case.

---

In total we have 8 different and possible cases.

```

| Case  | Expression       | Fix           |
|-------|------------------|---------------|
| 1     | if a >= b: a = b | a = min(b, a) |
| 2     | if a <= b: a = b | a = max(b, a) |
| 3     | if a <= b: b = a | b = min(a, b) |
| 4     | if a >= b: b = a | b = max(a, b) |
| 5     | if a > b: a = b  | a = min(a, b) |
| 6     | if a < b: a = b  | a = max(a, b) |
| 7     | if a < b: b = a  | b = min(b, a) |
| 8     | if a > b: b = a  | b = max(b, a) |
```

I added them in the tests. 

Please double-check that I didn't make any mistakes. It's quite easy to
mix up > and <.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-12 10:27:46 +01:00
David Peter 366ae1feaa
[red-knot] Document 'public type of undeclared symbols' behavior (#16096)
## Summary

After I was asked twice within the same day, I thought it would be a
good idea to write some *user facing* documentation that explains our
reasoning behind inferring `Unknown | T_inferred` for public uses of
undeclared symbols. This is a major deviation from the behavior of other
type checkers and it seems like a good practice to defend our choice
like this.
2025-02-12 08:52:11 +01:00
Wei Lee 86c5cba472
[`airflow`] Fix `ImportPathMoved` / `ProviderName` misuse (`AIR303`) (#16013)
## Summary


* fix ImportPathMoved / ProviderName misuse
* oncrete names, such as `["airflow", "config_templates",
"default_celery", "DEFAULT_CELERY_CONFIG"]`, should use `ProviderName`.
In contrast, module paths like `"airflow", "operators", "weekday", ...`
should use `ImportPathMoved`. Misuse may lead to incorrect detection.

## Test Plan

update test fixture
2025-02-12 12:34:16 +05:30
Andrew Gallant 6e34f74c16
add diagnostic `Span` (couples `File` and `TextRange`) (#16101)
This essentially makes it impossible to construct a `Diagnostic`
that has a `TextRange` but no `File`.

This is meant to be a precursor to multi-span support.

(Note that I consider this more of a prototyping-change and not
necessarily what this is going to look like longer term.)

Reviewers can probably review this PR as one big diff instead of
commit-by-commit.
2025-02-11 14:55:12 -05:00
Micha Reiser 9c179314ed
Remove `Hash` and `Eq` from `AstNodeRef` for types not implementing `Eq` or `Hash` (#16100)
## Summary

This is a follow up to
https://github.com/astral-sh/ruff/pull/15763#discussion_r1949681336

It reverts the change to using ptr equality for `AstNodeRef`s, which in
turn removes the `Eq`, `PartialEq`, and `Hash` implementations for
`AstNodeRef`s parametrized with AST nodes.
Cheap comparisons shouldn't be needed because the node field is
generally marked as `[#tracked]` and `#[no_eq]` and removing the
implementations even enforces that those
attributes are set on all `AstNodeRef` fields (which is good).

The only downside this has is that we technically wouldn't have to mark
the `Unpack::target` as `#[tracked]` because
the `target` field is accessed in every query accepting `Unpack` as an
argument.

Overall, enforcing the use of `#[tracked]` seems like a good trade off,
espacially considering that it's very likely that
we'd probably forget to mark the `Unpack::target` field as tracked if we
add a new `Unpack` query that doesn't access the target.

## Test Plan

`cargo test`
2025-02-11 19:55:50 +01:00
Micha Reiser ce31c2693b
Fix release build warning about unused todo type message (#16102) 2025-02-11 18:38:41 +00:00
Brent Westbrook 7b487d853a
[`pydocstyle`] Handle arguments with the same names as sections (`D417`) (#16011)
## Summary

Fixes #16007. The logic from the last fix for this (#9427) was
sufficient, it just wasn't being applied because `Attributes` sections
aren't expected to have nested sections. I just deleted the outer
conditional, which should hopefully fix this for all section types.

## Test Plan

New regression test, plus the existing D417 tests.
2025-02-11 12:05:29 -05:00
Alex Waygood df1d430294
[red-knot] Reduce usage of `From<Type>` implementations when working with `Symbol`s (#16076) 2025-02-11 11:09:37 +00:00
Ibraheem Ahmed 69d86d1d69
Transition to salsa coarse-grained tracked structs (#15763)
## Summary

Transition to using coarse-grained tracked structs (depends on
https://github.com/salsa-rs/salsa/pull/657). For now, this PR doesn't
add any `#[tracked]` fields, meaning that any changes cause the entire
struct to be invalidated. It also changes `AstNodeRef` to be
compared/hashed by pointer address, instead of performing a deep AST
comparison.

## Test Plan

This yields a 10-15% improvement on my machine (though weirdly some runs
were 5-10% without being flagged as inconsistent by criterion, is there
some non-determinism involved?). It's possible that some of this is
unrelated, I'll try applying the patch to the current salsa version to
make sure.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-11 11:38:50 +01:00
InSync 7fbd89cb39
[`pyupgrade`] Handle micro version numbers correctly (`UP036`) (#16091)
## Summary

Resolves #16082.

`UP036` will now also take into consideration whether or not a micro
version number is set:

* If a third element doesn't exist, the existing logic is preserved.
* If it exists but is not an integer literal, the check will not be
reported.
* If it is an integer literal but doesn't fit into a `u8`, the check
will be reported as invalid.
* Otherwise, the compared version is determined to always be less than
the target version when:
	* The target's minor version is smaller than that of the comparator, or
* The operator is `<`, the micro version is 0, and the two minor
versions compare equal.

As this is considered a bugfix, it is not preview-gated.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-11 07:40:56 +00:00
David Peter 0019d39f6e
[red-knot] `T | object == object` (#16088)
## Summary

- Simplify unions with `object` to `object`.
- Add a new `Type::object(db)` constructor to abbreviate
`KnownClass::Object.to_instance(db)` in some places.
- Add a `Type::is_object` and `Class::is_object` function to make some
tests for a bit easier to read.

closes #16084

## Test Plan

New Markdown tests.
2025-02-10 23:07:06 +01:00
Dylan f30fac6326
[`ruff`] Skip singleton starred expressions for `incorrectly-parenthesized-tuple-in-subscript` (`RUF031`) (#16083)
The index in subscript access like `d[*y]` will not be linted or
autofixed with parentheses, even when
`lint.ruff.parenthesize-tuple-in-subscript = true`.

Closes #16077
2025-02-10 11:30:07 -06:00
Micha Reiser a4c8c49ac2
Delete left-over `verbosity.rs (#16081) 2025-02-10 16:06:05 +00:00
Micha Reiser af832560fc
[red-knot] User-level configuration (#16021)
## Summary

This PR adds support for user-level configurations
(`~/.config/knot/knot.toml`) to Red Knot.

Red Knot will watch the user-level configuration file for changes but
only if it exists
when the process start. It doesn't watch for new configurations, 
mainly to simplify things for now (it would require watching the entire
`.config` directory because the `knot` subfolder might not exist
either).

The new `ConfigurationFile` struct seems a bit overkill for now but I
plan to use it for
hierarchical configurations as well. 


Red Knot uses the same strategy as uv and Ruff by using the etcetera
crate.

## Test Plan

Added CLI and file watching test
2025-02-10 16:44:23 +01:00
Micha Reiser f7819e553f
Add `user_configuration_directory` to `System` (#16020)
## Summary

This PR adds a new `user_configuration_directory` method to `System`. We
need it to resolve where to lookup a user-level `knot.toml`
configuration file.
The method belongs to `System` because not all platforms have a
convention of where to store such configuration files (e.g. wasm).


I refactored `TestSystem` to be a simple wrapper around an `Arc<dyn
System...>` and use the `System.as_any` method instead to cast it down
to an `InMemory` system. I also removed some `System` specific methods
from `InMemoryFileSystem`, they don't belong there.

This PR removes the `os` feature as a default feature from `ruff_db`.
Most crates depending on `ruff_db` don't need it because they only
depend on `System` or only depend on `os` for testing. This was
necessary to fix a compile error with `red_knot_wasm`

## Test Plan

I'll make use of the method in my next PR. So I guess we won't know if
it works before then but I copied the code from Ruff/uv, so I have high
confidence that it is correct.

`cargo test`
2025-02-10 15:50:55 +01:00
Micha Reiser 678b0c2d39
[red-knot] Resolve `Options` to `Settings` (#16000)
## Summary

This PR generalize the idea that we may want to emit diagnostics for 
invalid or incompatible configuration values similar to how we already 
do it for `rules`. 

This PR introduces a new `Settings` struct that is similar to `Options`
but, unlike
`Options`, are fields have their default values filled in and they use a
representation optimized for reads.

The diagnostics created during loading the `Settings` are stored on the
`Project` so that we can emit them when calling `check`.

The motivation for this work is that it simplifies adding new settings.
That's also why I went ahead and added the `terminal.error-on-warning`
setting to demonstrate how new settings are added.

## Test Plan

Existing tests, new CLI test.
2025-02-10 15:28:45 +01:00
Dhruv Manilawala 524cf6e515
Bump version to 0.9.6 (#16074) 2025-02-10 18:14:04 +05:30
InSync b69eb9099a
Fix reference definition labels for backtick-quoted shortcut links (#16035)
## Summary

Resolves #16010.

The changes boil down to something like this:

```diff
-/// The [FastAPI documentation] recommends the use of [`typing.Annotated`]
+/// The [FastAPI documentation] recommends the use of [`typing.Annotated`][typing-annotated]

-/// [typing.Annotated]: https://docs.python.org/3/library/typing.html#typing.Annotated
+/// [typing-annotated]: https://docs.python.org/3/library/typing.html#typing.Annotated
```

## Test Plan

Mkdocs:


![](https://github.com/user-attachments/assets/a2e6bf22-56fa-4b2c-9500-1c1256c5a218)

GitHub:

> ## Why is this bad?
> The [FastAPI documentation] recommends the use of
[`typing.Annotated`][typing-annotated]
> 
> ...
>
> [FastAPI documentation]:
https://fastapi.tiangolo.com/tutorial/query-params-str-validations/?h=annotated#advantages-of-annotated
> [typing-annotated]:
https://docs.python.org/3/library/typing.html#typing.Annotated

[CommonMark
dingus](https://spec.commonmark.org/dingus/?text=%23%23%20Why%20is%20this%20bad%3F%0AThe%20%5BFastAPI%20documentation%5D%20recommends%20the%20use%20of%20%5B%60typing.Annotated%60%5D%5Btyping-annotated%5D%0A%0A...%0A%0A%5BFastAPI%20documentation%5D%3A%20https%3A%2F%2Ffastapi.tiangolo.com%2Ftutorial%2Fquery-params-str-validations%2F%3Fh%3Dannotated%23advantages-of-annotated%0A%5Btyping-annotated%5D%3A%20https%3A%2F%2Fdocs.python.org%2F3%2Flibrary%2Ftyping.html%23typing.Annotated):

```html
<h2>Why is this bad?</h2>
<p>The <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/?h=annotated#advantages-of-annotated">FastAPI documentation</a> recommends the use of <a href="https://docs.python.org/3/library/typing.html#typing.Annotated"><code>typing.Annotated</code></a></p>
<p>...</p>
```
2025-02-10 09:54:22 +01:00
ABDULRAHMAN ALRAHMA d2f661f795
RUF009 should behave similar to B008 and ignore attributes with immutable types (#16048)
This PR resolved #15772

Before PR:
```
def _(
    this_is_fine: int = f(),           # No error
    this_is_not: list[int] = f()       # B008: Do not perform function call `f` in argument defaults
): ...


@dataclass
class _:
    this_is_not_fine: list[int] = f()  # RUF009: Do not perform function call `f` in dataclass defaults
    this_is_also_not: int = f()        # RUF009: Do not perform function call `f` in dataclass defaults
```

After PR:
```
def _(
    this_is_fine: int = f(),           # No error
    this_is_not: list[int] = f()       # B008: Do not perform function call `f` in argument defaults
): ...


@dataclass
class _:
    this_is_not_fine: list[int] = f()  # RUF009: Do not perform function call `f` in dataclass defaults
    this_is_fine: int = f()
```
2025-02-10 09:46:23 +01:00
InSync 07cf8852a3
[`pylint`] Also report when the object isn't a literal (`PLE1310`) (#15985)
## Summary

Follow-up to #15984.

Previously, `PLE1310` would only report when the object is a literal:

```python
'a'.strip('//')  # error

foo = ''
foo.strip('//')  # no error
```

After this change, objects whose type can be inferred to be either `str`
or `bytes` will also be reported in preview.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-02-10 09:31:27 +01:00
Dhruv Manilawala 869a9543e4
Root exclusions in the server to project root (#16043)
## Summary

fixes: #16041 

## Test Plan

Using the [project](https://github.com/bwcc-clan/polebot) in the linked
issue:

Notice how the project "polebot" is in the "play" directory which is
included in the `exclude` setting as:

```toml
exclude = ["play"]
```

**Before this fix**

```
DEBUG ruff:worker:0 ruff_server::resolve: Ignored path via `exclude`: /private/tmp/ruff-test/play/polebot/src/utils/log_tools.py
```

**After this fix**

```
DEBUG ruff:worker:2 ruff_server::resolve: Included path via `include`: /private/tmp/ruff-test/play/polebot/src/utils/log_tools.py
```

I also updated the same project to remove the "play" directory from the
`exclude` setting and made sure that anything under the `polebot/play`
directory is included:

```
DEBUG  ruff:worker:4 ruff_server::resolve: Included path via `include`: /private/tmp/ruff-test/play/polebot/play/test.py
```

And, excluded when I add the directory back:

```
DEBUG  ruff:worker:2 ruff_server::resolve: Ignored path via `exclude`: /private/tmp/ruff-test/play/polebot/play/test.py
```
2025-02-10 04:57:14 +00:00
Dhruv Manilawala cc0a5dd14a
Directly include `Settings` struct for the server (#16042)
## Summary

This PR refactors the `RuffSettings` struct to directly include the
resolved `Settings` instead of including the specific fields from it.
The server utilizes a lot of it already, so it makes sense to just
include the entire struct for simplicity.

### `Deref`

I implemented `Deref` on `RuffSettings` to return the `Settings` because
`RuffSettings` is now basically a wrapper around it with the config path
as the other field. This path field is only used for debugging
("printDebugInformation" command).
2025-02-10 10:20:01 +05:30
Brent Westbrook 88b543d73a
[`flake8-builtins`] Make strict module name comparison optional (`A005`) (#15951)
## Summary

This PR adds the configuration option
`lint.flake8-builtins.builtins-strict-checking`, which is used in A005
to determine whether the fully-qualified module name (relative to the
project root or source directories) should be checked instead of just
the final component as is currently the case.

As discussed in
https://github.com/astral-sh/ruff/issues/15399#issuecomment-2587017147,
the default value of the new option is `false` on preview, so modules
like `utils.logging` from the initial report are no longer flagged by
default. For non-preview the default is still strict checking.

## Test Plan

New A005 test module with the structure reported in #15399.

Fixes #15399
2025-02-09 19:33:03 -05:00
InSync f367aa8367
[`ruff`] Indented form feeds (`RUF054`) (#16049)
## Summary

Resolves #12321.

The physical-line-based `RUF054` checks for form feed characters that
are preceded by only tabs and spaces, but not any other characters,
including form feeds.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-02-09 19:23:48 -05:00
David Salvisberg 9ae98d4a09
[`flake8-type-checking`] Avoid `TC004` false positive with `__getattr__` (#16052) 2025-02-09 16:27:06 +00:00
Dylan 0af4b23d9f
[`ruff`] Skip type definitions for `missing-f-string-syntax` (`RUF027`) (#16054)
As an f-string is never correct in a type definition context, we skip
[missing-f-string-syntax
(RUF027)](https://docs.astral.sh/ruff/rules/missing-f-string-syntax/#missing-f-string-syntax-ruf027)
in this case.

Closes #16037
2025-02-09 10:16:28 -06:00
Dylan f178ecc2d7
[`flake8-pyi`] Extend fix to Python <= 3.9 for `redundant-none-literal` (`PYI061`) (#16044)
This PR extends the fix offered for [redundant-none-literal
(PYI061)](https://docs.astral.sh/ruff/rules/redundant-none-literal/#redundant-none-literal-pyi061)
to include Python versions <= 3.9 by using `typing.Optional` instead of
the operator `|`. We also offer the fix with `|` for any target version
on stub files.

Closes #15795
2025-02-09 09:58:53 -06:00
InSync a46fbda948
[`flake8-datetime`] Ignore `.replace()` calls while looking for `.astimezone` (#16050)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-02-09 15:48:59 +00:00
Alex Waygood fc59e1b17f
[red-knot] Merge `TypeInferenceBuilder::infer_name_load` and `TypeInferenceBuilder::lookup_name` (#16019)
## Summary

No functional change here; this is another simplification split out from
my outcome-refactor branch to reduce the diff there. This merges
`TypeInferenceBuilder::infer_name_load` and
`TypeInferenceBuilder::lookup_name`. This removes the need to have
extensive doc-comments about the purpose of
`TypeInferenceBuilder::lookup_name`, since the method only makes sense
when called from the specific context of
`TypeInferenceBuilder::infer_name_load`.

## Test Plan

`cargo test -p red_knot_python_semantic`
2025-02-08 19:42:14 +00:00
Alex Waygood 22728808aa
[`pyupgrade`] Ensure we do not rename two type parameters to the same name (`UP049`) (#16038)
Fixes #16024

## Summary

This PR adds proper isolation for `UP049` fixes so that two type
parameters are not renamed to the same name, which would introduce
invalid syntax. E.g. for this:

```py
class Foo[_T, __T]: ...
```

we cannot apply two autofixes to the class, as that would produce
invalid syntax -- this:

```py
class Foo[T, T]: ...
```

The "isolation" here means that Ruff won't apply more than one fix to
the same type-parameter list in a single iteration of the loop it does
to apply all autofixes. This means that after the first autofix has been
done, the semantic model will have recalculated which variables are
available in the scope, meaning that the diagnostic for the second
parameter will be deemed unfixable since it collides with an existing
name in the same scope (the name we autofixed the first parameter to in
an earlier iteration of the autofix loop).

Cc. @ntBre, for interest!

## Test Plan

I added an integration test that reproduces the bug on `main`.
2025-02-08 15:44:04 +00:00
InSync a04ddf2a55
[`pyupgrade`] [`ruff`] Don't apply renamings if the new name is shadowed in a scope of one of the references to the binding (`UP049`, `RUF052`) (#16032)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-02-08 11:25:23 +00:00
Dylan 3a806ecaa1
[`flake8-annotations`] Correct syntax for `typing.Union` in suggested return type fixes for `ANN20x` rules (#16025)
When suggesting a return type as a union in Python <=3.9, we now avoid a
`TypeError` by correctly suggesting syntax like `Union[int,str,None]`
instead of `Union[int | str | None]`.
2025-02-07 17:17:20 -06:00
InSync a29009e4ed
[`pyupgrade`] Comments within parenthesized value ranges should not affect applicability (`UP040`) (#16027)
## Summary

Follow-up to #16026.

Previously, the fix for this would be marked as unsafe, even though all
comments are preserved:

```python
# .pyi
T: TypeAlias = (  # Comment
	int | str
)
```

Now it is safe: comments within the parenthesized range no longer affect
applicability.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Dylan <53534755+dylwil3@users.noreply.github.com>
2025-02-07 14:44:33 -06:00
InSync 19f3424a1a
[`pylint`] Do not report calls when object type and argument type mismatch, remove custom escape handling logic (`PLE1310`) (#15984)
## Summary

Resolves #15968.

Previously, these would be considered violations:

```python
b''.strip('//')
''.lstrip('//', foo = "bar")
```

...while these are not:

```python
b''.strip(b'//')
''.strip('\\b\\x08')
```

Ruff will now not report when the types of the object and that of the
argument mismatch, or when there are extra arguments.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-02-07 14:31:07 -06:00
Brent Westbrook d4a5772d96
[`flake8-builtins`] Match upstream module name comparison (`A005`) (#16006)
See #15951 for the original discussion and reviews. This is just the
first half of that PR (reaching parity with `flake8-builtins` without
adding any new configuration options) split out for nicer changelog
entries.

For posterity, here's a script for generating the module structure that
was useful for interactive testing and creating the table
[here](https://github.com/astral-sh/ruff/pull/15951#issuecomment-2640662041).
The results for this branch are the same as the `Strict` column there,
as expected.

```shell
mkdir abc collections foobar urlparse

for i in */
do
	touch $i/__init__.py
done	

cp -r abc foobar collections/.
cp -r abc collections foobar/.

touch ruff.toml

touch foobar/logging.py
```

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-07 13:55:56 -05:00
Alex Waygood efa8a3ddcc
[`pyupgrade`] Don't introduce invalid syntax when upgrading old-style type aliases with parenthesized multiline values (`UP040`) (#16026) 2025-02-07 17:05:17 +00:00
Dylan 46fe17767d
Pass `Checker` by immutable reference to lint rules (#16012)
This very large PR changes the field `.diagnostics` in the `Checker`
from a `Vec<Diagnostic>` to a `RefCell<Vec<Diagnostic>>`, adds methods
to push new diagnostics to this cell, and then removes unnecessary
mutability throughout all of our lint rule implementations.

Consequently, the compiler may now enforce what was, till now, the
_convention_ that the only changes to the `Checker` that can happen
during a lint are the addition of diagnostics[^1].

The PR is best reviewed commit-by-commit. I have tried to keep the large
commits limited to "bulk actions that you can easily see are performing
the same find/replace on a large number of files", and separate anything
ad-hoc or with larger diffs. Please let me know if there's anything else
I can do to make this easier to review!

Many thanks to [`ast-grep`](https://github.com/ast-grep/ast-grep),
[`helix`](https://github.com/helix-editor/helix), and good ol'
fashioned`git` magic, without which this PR would have taken the rest of
my natural life.

[^1]: And randomly also the seen variables violating `flake8-bugbear`?
2025-02-07 09:05:50 -06:00
David Peter 1f7a29d347
[red-knot] Unpacker: Make invariant explicit and directly return a Type (#16018)
## Summary

- Do not return `Option<Type<…>>` from `Unpacker::get`, but just `Type`.
Panic otherwise.
- Rename `Unpacker::get` to `Unpacker::expression_type`
2025-02-07 12:00:04 +00:00
Wei Lee 618bfaf884
[`airflow`] Add `external_task.{ExternalTaskMarker, ExternalTaskSensor}` for `AIR302` (#16014)
## Summary

Apply suggestions similar to
https://github.com/astral-sh/ruff/pull/15922#discussion_r1940697704


## Test Plan

a test fixture has been updated
2025-02-07 16:38:34 +05:30
Alex Waygood b1c61cb2ee
[`ruff`] Fix invalid annotation in docs example (#16016) 2025-02-07 10:45:51 +00:00
David Peter 97e6fc3793
[red-knot] Unpacking and for loop assignments to attributes (#16004)
## Summary

* Support assignments to attributes in more cases:
    - assignments in `for` loops
    - in unpacking assignments
* Add test for multi-target assignments
* Add tests for all other possible assignments to attributes that could
   possibly occur (in decreasing order of likeliness):
    - augmented attribute assignments
    - attribute assignments in `with` statements
    - attribute assignments in comprehensions
- Note: assignments to attributes in named expressions are not
   syntactically allowed

closes #15962

## Test Plan

New Markdown tests
2025-02-07 11:30:51 +01:00
Micha Reiser 38351e00ee
[red-knot] Partial revert of relative import handling for files in the root of a search path (#16001)
## Summary

This PR reverts the behavior changes from
https://github.com/astral-sh/ruff/pull/15990

But it isn't just a revert, it also:

* Adds a test covering this specific behavior
* Preserves the improvement to use `saturating_sub` in the package case
to avoid overflows in the case of invalid syntax
* Use `ancestors` instead of a `for` loop

## Test Plan

Added test
2025-02-07 11:04:09 +01:00
Micha Reiser 26c37b1e0e
Add knot.toml schema (#15735)
## Summary

Adds a JSON schema generation step for Red Knot. This PR doesn't yet add
a publishing step because it's still a bit early for that


## Test plan

I tested the schema in Zed, VS Code and PyCharm:

* PyCharm: You have to manually add a schema mapping (settings JSON
Schema Mappings)
* Zed and VS code support the inline schema specification

```toml
#:schema /Users/micha/astral/ruff/knot.schema.json


[environment]
extra-paths = []


[rules]
call-possibly-unbound-method = "error"
unknown-rule = "error"

# duplicate-base = "error"
```

```json
{
    "$schema": "file:///Users/micha/astral/ruff/knot.schema.json",

    "environment": {
        "python-version": "3.13",
        "python-platform": "linux2"
    },

    "rules": {
        "unknown-rule": "error"
    }
}
```


https://github.com/user-attachments/assets/a18fcd96-7cbe-4110-985b-9f1935584411


The Schema overall works but all editors have their own quirks:

* PyCharm: Hovering a name always shows the section description instead
of the description of the specific setting. But it's the same for other
settings in `pyproject.toml` files 🤷
* VS Code (JSON): Using the generated schema in a JSON file gives
exactly the experience I want
* VS Code (TOML): 
* Properties with multiple possible values are repeated during
auto-completion without giving any hint how they're different. ![Screen
Shot 2025-02-06 at 14 05 35
PM](https://github.com/user-attachments/assets/d7f3c2a9-2351-4226-9fc1-b91aa192a237)
* The property description mushes together the description of the
property and the value, which looks sort of ridiculous. ![Screen Shot
2025-02-06 at 14 04 40
PM](https://github.com/user-attachments/assets/8b72f04a-c62a-49b5-810f-7ddd472884d0)
* Autocompletion and documentation hovering works (except the
limitations mentioned above)
* Zed:
* Very similar to VS Code with the exception that it uses the
description attribute to distinguish settings with multiple possible
values ![Screen Shot 2025-02-06 at 14 08 19
PM](https://github.com/user-attachments/assets/78a7f849-ff4e-44ff-8317-708eaf02dc1f)


I don't think there's much we can do here other than hope (or help)
editors improve their auto completion. The same short comings also apply
to ruff, so this isn't something new. For now, I think this is good
enough
2025-02-07 10:59:40 +01:00
InSync 7db5a924af
[`flake8-comprehensions`] Detect overshadowed `list`/`set`/`dict`, ignore variadics and named expressions (`C417`) (#15955)
## Summary

Part of #15809 and #15876.

This change brings several bugfixes:

* The nested `map()` call in `list(map(lambda x: x, []))` where `list`
is overshadowed is now correctly reported.
* The call will no longer reported if:
	* Any arguments given to `map()` are variadic.
	* Any of the iterables contain a named expression.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-07 08:58:05 +00:00
Junhson Jean-Baptiste 349f93389e
[flake8-simplify] Only trigger SIM401 on known dictionaries (SIM401) (#15995)
## Summary

This change resolves #15814 to ensure that `SIM401` is only triggered on
known dictionary types. Before, the rule was getting triggered even on
types that _resemble_ a dictionary but are not actually a dictionary.

I did this using the `is_known_to_be_of_type_dict(...)` functionality.
The logic for this function was duplicated in a few spots, so I moved
the code to a central location, removed redundant definitions, and
updated existing calls to use the single definition of the function!

## Test Plan

Since this PR only modifies an existing rule, I made changes to the
existing test instead of adding new ones. I made sure that `SIM401` is
triggered on types that are clearly dictionaries and that it's not
triggered on a simple custom dictionary-like type (using a modified
version of [the code in the issue](#15814))

The additional changes to de-duplicate `is_known_to_be_of_type_dict`
don't break any existing tests -- I think this should be fine since the
logic remains the same (please let me know if you think otherwise, I'm
excited to get feedback and work towards a good fix 🙂).

---------

Co-authored-by: Junhson Jean-Baptiste <junhsonjb@naan.mynetworksettings.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-07 08:25:20 +00:00
InSync bb979e05ac
[`flake8-pie`] Remove following comma correctly when the unpacked dictionary is empty (`PIE800`) (#16008)
## Summary

Resolves #15997.

Ruff used to introduce syntax errors while fixing these cases, but no
longer will:

```python
{"a": [], **{},}
#         ^^^^ Removed, leaving two contiguous commas

{"a": [], **({})}
#         ^^^^^ Removed, leaving a stray closing parentheses
```

Previously, the function would take a shortcut if the unpacked
dictionary is empty; now, both cases are handled using the same logic
introduced in #15394. This change slightly modifies that logic to also
remove the first comma following the dictionary, if and only if it is
empty.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-02-07 08:52:10 +01:00
Dylan 10d3e64ccd
Bump version to 0.9.5 (#16002) 2025-02-06 13:24:45 -06:00
InSync 84ceddcbd9
[`ruff`] Classes with mixed type variable style (`RUF053`) (#15841) 2025-02-06 18:35:51 +00:00
Ayush Baweja ba2f0e998d
[flake8-pyi] Add autofix for unused-private-type-var (PYI018) (#15999)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-02-06 18:08:36 +00:00
Alex Waygood 18b497a913
[red-knot] Fixup a couple of nits in the `red_knot_test` README (#15996) 2025-02-06 15:04:27 +00:00
Micha Reiser 7cac0da44d
Workaround Even Better TOML crash related to `allOf` (#15992)
## Summary

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

Even Better TOML doesn't support `allOf` well. In fact, it just crashes.

This PR works around this limitation by avoid using `allOf` in the
automatically
derived schema for the docstring formatting setting. 

### Alternatives

schemars introduces `allOf` whenver it sees a `$ref` alongside other
object properties
because this is no longer valid according to Draft 7. We could replace
the
visitor performing the rewrite but I prefer not to because replacing
`allOf` with `oneOf`
is only valid for objects that don't have any other `oneOf` or `anyOf`
schema.

## Test Plan


https://github.com/user-attachments/assets/25d73b2a-fee1-4ba6-9ffe-869b2c3bc64e
2025-02-06 16:00:50 +01:00
David Peter e345307260
[red-knot] Fix diagnostic range for non-iterable unpacking assignments (#15994)
## Summary

I noticed that the diagnostic range in specific unpacking assignments is
wrong. For this example

```py
a, b = 1
```

we previously got (see first commit):

```
error: lint:not-iterable
 --> /src/mdtest_snippet.py:1:1
  |
1 | a, b = 1
  | ^^^^ Object of type `Literal[1]` is not iterable
  |
```

and with this change, we get:

```
error: lint:not-iterable
 --> /src/mdtest_snippet.py:1:8
  |
1 | a, b = 1
  |        ^ Object of type `Literal[1]` is not iterable
  |
```

## Test Plan

New snapshot tests.
2025-02-06 15:36:22 +01:00
Micha Reiser 5588c75d65
[red-knot] Fix relative imports in `src.root` (#15990)
## Summary

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

Red Knot failed to resolve relative imports if the importing module is
located at a search path root.

The issue was that the module resolver returned an `Err(TooManyDots)` as
soon as the parent of the current module is `None` (which is the case
for a module at the search path root).
However, this is incorrect if a `tail` (a module name) exists.
2025-02-06 14:08:20 +00:00
Raymond Berger 9d2105b863
add instance variable examples to RUF012 (#15982)
## Summary

Closes #15804 

Add more examples to the documentation.

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-06 14:01:09 +00:00
David Salvisberg 8fcac0ff36
Recognize all symbols named `TYPE_CHECKING` for `in_type_checking_block` (#15719)
Closes #15681

## Summary

This changes `analyze::typing::is_type_checking_block` to recognize all
symbols named "TYPE_CHECKING".
This matches the current behavior of mypy and pyright as well as
`flake8-type-checking`.

It also drops support for detecting `if False:` and `if 0:` as type
checking blocks. This used to be an option for
providing backwards compatibility with Python versions that did not have
a `typing` module, but has since
been removed from the typing spec and is no longer supported by any of
the mainstream type checkers.

## Test Plan

`cargo nextest run`

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-06 14:45:12 +01:00
Vasco Schiavo 81059d05fc
[`pep8-naming`] Consider any number of leading underscore for `N801` (#15988)
## Summary

The PR addresses the issue #15939 

Let me know if you think there are other test cases I should add ;-)
2025-02-06 14:08:27 +05:30
Vasco Schiavo 24bab7e82e
[pycodestyle] Exempt `sys.path += ...` calls (E402) (#15980)
## Summary

The PR addresses issue #15886 .
2025-02-06 08:51:51 +01:00
David Peter d0555f7b5c
[red-knot] Litate tests: minor follow-up (#15987)
## Summary

- Minor wording update
- Code improvement (thanks Alex)
- Removed all unnecessary filenames throughout our Markdown tests (two
new ones were added in the meantime)
- Minor rewording of the statically-known-branches introduction
2025-02-06 07:15:26 +00:00
Douglas Creager 0906554357
[red-knot] Combine terminal statement support with statically known branches (#15817)
This example from @sharkdp shows how terminal statements can appear in
statically known branches:
https://github.com/astral-sh/ruff/pull/15676#issuecomment-2618809716

```py
def _(cond: bool):
    x = "a"
    if cond:
        x = "b"
        if True:
            return

    reveal_type(x)  # revealed: "a", "b"; should be "a"
```

We now use visibility constraints to track reachability, which allows us
to model this correctly. There are two related changes as a result:

- New bindings are not assumed to be visible; they inherit the current
"scope start" visibility, which effectively means that new bindings are
visible if/when the current flow is reachable

- When simplifying visibility constraints after branching control flow,
we only simplify if none of the intervening branches included a terminal
statement. That is, earlier unaffected bindings are only _actually_
unaffected if all branches make it to the merge point.
2025-02-05 17:47:49 -05:00
David Peter d296f602e7
[red-knot] Merge Markdown code blocks inside a single section (#15950)
## Summary

Allow for literate style in Markdown tests and merge multiple (unnamed)
code blocks into a single embedded file.

closes #15941

## Test Plan

- Interactively made sure that error-lines were reported correctly in
  multi-snippet sections.
2025-02-05 22:26:15 +01:00
Andrew Gallant d47088c8f8
[red-knot] fix unresolvable import range (#15976)
This causes the diagnostic to highlight the actual unresovable import
instead of the entire `from ... import ...` statement.

While we're here, we expand the test coverage to cover all of the
possible ways that an `import` or a `from ... import` can fail.

Some considerations:

* The first commit in this PR adds a regression test for the current
behavior.
* This creates a new `mdtest/diagnostics` directory. Are folks cool
with this? I guess the idea is to put tests more devoted to diagnostics
than semantics in this directory. (Although I'm guessing there will
be some overlap.)

Fixes #15866
2025-02-05 14:01:58 -05:00
David Peter 1f0ad675d3
[red-knot] Initial set of descriptor protocol tests (#15972)
## Summary

This is a first step towards creating a test suite for
[descriptors](https://docs.python.org/3/howto/descriptor.html). It does
not (yet) aim to be exhaustive.

relevant ticket: #15966 

## Test Plan

Compared desired behavior with the runtime behavior and the behavior of
existing type checkers.

---------

Co-authored-by: Mike Perlov <mishamsk@gmail.com>
2025-02-05 19:47:43 +01:00
Andrew Gallant a84b27e679 red_knot_test: add support for diagnostic snapshotting
This ties together everything from the previous commits.
Some interesting bits here are how the snapshot is generated
(where we include relevant info to make it easier to review
the snapshots) and also a tweak to how inline assertions are
processed.

This commit also includes some example snapshots just to get
a sense of what they look like. Follow-up work should add
more of these I think.
2025-02-05 13:02:54 -05:00
Andrew Gallant 8d4679b3ae red_knot_test: update README with section on diagnostic snapshotting
I split this out into a separate commit and put it here
so that reviewers can get a conceptual model of what the
code is doing before seeing the code. (Hopefully that helps.)
2025-02-05 13:02:54 -05:00
Andrew Gallant b40a7cce15 red_knot_test: add snapshot path
This makes it possible for callers to set where snapshots
should be stored. In general, I think we expect this to
always be set, since otherwise snapshots will end up in
`red_knot_test`, which is where the tests are actually run.
But that's overall counter-intuitive. This permits us to
store snapshots from mdtests alongside the mdtests themselves.
2025-02-05 13:02:54 -05:00
Andrew Gallant 54b3849dfb ruff_db: add more `dyn Diagnostic` impls
I found it useful to have the `&dyn Diagnostic` trait impl
specifically. I added `Arc<dyn Diagnostic>` for completeness.

(I do kind of wonder if we should be preferring `Arc<dyn ...>`
over something like `Box<dyn ...>` more generally, especially
for things with immutable APIs. It would make cloning cheap.)
2025-02-05 13:02:54 -05:00
Andrew Gallant ffd94e9ace red_knot_test: generate names for unnamed files using more local reasoning
This change was done to reduce snapshot churn. Previously,
if one added a new section to an Markdown test suite, then
the snapshots of all sections with unnamed files below it would
necessarily change because of the unnamed file count being
global to the test suite.

Instead, we track counts based on section. While adding new
unnamed files within a section will still change unnamed
files below it, I believe this will be less "churn" because
the snapshot will need to change anyway. Some churn is still
possible, e.g., if code blocks are re-ordered. But I think this
is an acceptable trade-off.
2025-02-05 13:02:54 -05:00
Alex Waygood c816542704
[red-knot] Fix some instance-attribute TODOs around `ModuleType` (#15974) 2025-02-05 15:33:37 +00:00
Alex Waygood 2ebb5e8d4b
[red-knot] Make `Symbol::or_fall_back_to()` lazy (#15943) 2025-02-05 14:51:02 +00:00
Dylan c69b19fe1d
[`flake8-comprehensions`] Handle trailing comma in fixes for `unnecessary-generator-list/set` (`C400`,`C401`) (#15929)
The unsafe fixes for the rules [unnecessary-generator-list
(C400)](https://docs.astral.sh/ruff/rules/unnecessary-generator-list/#unnecessary-generator-list-c400)
and [unnecessary-generator-set
(C401)](https://docs.astral.sh/ruff/rules/unnecessary-generator-set/#unnecessary-generator-set-c401)
used to introduce syntax errors if the argument to `list` or `set` had a
trailing comma, because the fix would retain the comma after
transforming the function call to a comprehension.

This PR accounts for the trailing comma when replacing the end of the
call with a `]` or `}`.

Closes #15852
2025-02-05 07:38:03 -06:00
Brent Westbrook 076d35fb93
[minor] Mention UP049 in UP046 and UP047, add `See also` section to UP040 (#15956)
## Summary

Minor docs follow-up to #15862 to mention UP049 in the UP046 and UP047
`See also` sections. I wanted to mention it in UP040 too but realized it
didn't have a `See also` section, so I also added that, adapted from the
other two rules.

## Test Plan

cargo test
2025-02-05 08:34:47 -05:00
Dylan 16f2a93fca
[`ruff`] Analyze deferred annotations before enforcing `mutable-(data)class-default` and `function-call-in-dataclass-default-argument` (`RUF008`,`RUF009`,`RUF012`) (#15921) 2025-02-05 06:44:19 -06:00