Commit Graph

8537 Commits

Author SHA1 Message Date
Micha Reiser 7532155c9b
[ty] Add suggestion to unknown rule diagnostics, rename `unknown-rule` lint to `ignore-comment-unknown-rule` (#20948) 2025-10-18 12:44:21 +02:00
Bhuminjay Soni 7198e53182
[syntax-errors] Alternative `match` patterns bind different names (#20682)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
This PR implements semantic syntax error where alternative patterns bind
different names

## Test Plan

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

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-17 21:35:48 +00:00
Shunsuke Shibayama e4384fc212
[ty] impl `VarianceInferable` for `KnownInstanceType` (#20924)
## Summary

Derived from #20900

Implement `VarianceInferable` for `KnownInstanceType` (especially for
`KnownInstanceType::TypeAliasType`).

The variance of a type alias matches its value type. In normal usage,
type aliases are expanded to value types, so the variance of a type
alias can be obtained without implementing this. However, for example,
if we want to display the variance when hovering over a type alias, we
need to be able to obtain the variance of the type alias itself (cf.
#20900).

## Test Plan

I couldn't come up with a way to test this in mdtest, so I'm testing it
in a test submodule at the end of `types.rs`.
I also added a test to `mdtest/generics/pep695/variance.md`, but it
passes without the changes in this PR.
2025-10-17 21:12:19 +02:00
David Peter 6e7ff07065
[ty] Provide completions on `TypeVar`s (#20943)
## Summary

closes https://github.com/astral-sh/ty/issues/1370

## Test Plan

New snapshot tests
2025-10-17 20:05:20 +02:00
Alex Waygood c7e2bfd759
[ty] `continue` and `break` statements outside loops are syntax errors (#20944)
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-17 17:13:40 +00:00
Alex Waygood c424007645
Update usage instructions and lockfile for py-fuzzer script (#20940) 2025-10-17 15:57:17 +01:00
Brent Westbrook 0115fd3757
Avoid reusing nested, interpolated quotes before Python 3.12 (#20930)
## Summary

Fixes #20774 by tracking whether an `InterpolatedStringState` element is
nested inside of another interpolated element. This feels like kind of a
naive fix, so I'm welcome to other ideas. But it resolves the problem in
the issue and clears up the syntax error in the black compatibility
test, without affecting many other cases.

The other affected case is actually interesting too because the
[input](96b156303b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/fstring.py (L707))
is invalid, but the previous quote selection fixed the invalid syntax:

```pycon
Python 3.11.13 (main, Sep  2 2025, 14:20:25) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f'{1: abcd "{'aa'}" }'  # input
  File "<stdin>", line 1
    f'{1: abcd "{'aa'}" }'
                  ^^
SyntaxError: f-string: expecting '}'
>>> f'{1: abcd "{"aa"}" }'  # old output
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid format specifier ' abcd "aa" ' for object of type 'int'
>>> f'{1: abcd "{'aa'}" }'  # new output
  File "<stdin>", line 1
    f'{1: abcd "{'aa'}" }'
                  ^^
SyntaxError: f-string: expecting '}'
```

We now preserve the invalid syntax in the input.

Unfortunately, this also seems to be another edge case I didn't consider
in https://github.com/astral-sh/ruff/pull/20867 because we don't flag
this as a syntax error after 0.14.1:

<details><summary>Shell output</summary>
<p>

```
> uvx ruff@0.14.0 check --ignore ALL --target-version py311 - <<EOF
f'{1: abcd "{'aa'}" }'
EOF
invalid-syntax: Cannot reuse outer quote character in f-strings on Python 3.11 (syntax was added in Python 3.12)
 --> -:1:14
  |
1 | f'{1: abcd "{'aa'}" }'
  |              ^
  |

Found 1 error.
> uvx ruff@0.14.1 check --ignore ALL --target-version py311 - <<EOF
f'{1: abcd "{'aa'}" }'
EOF
All checks passed!
> uvx python@3.11 -m ast <<EOF
f'{1: abcd "{'aa'}" }'
EOF
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/home/brent/.local/share/uv/python/cpython-3.11.13-linux-x86_64-gnu/lib/python3.11/ast.py", line 1752, in <module>
    main()
  File "/home/brent/.local/share/uv/python/cpython-3.11.13-linux-x86_64-gnu/lib/python3.11/ast.py", line 1748, in main
    tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/brent/.local/share/uv/python/cpython-3.11.13-linux-x86_64-gnu/lib/python3.11/ast.py", line 50, in parse
    return compile(source, filename, mode, flags,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<stdin>", line 1
    f'{1: abcd "{'aa'}" }'
                  ^^
SyntaxError: f-string: expecting '}'
```

</p>
</details> 


I assumed that was the same `ParseError` as the one caused by
`f"{1:""}"`, but this is a nested interpolation inside of the format
spec.

## Test Plan

New test copied from the black compatibility test. I guess this is a
duplicate now, I started working on this branch before the new black
tests were imported, so I could delete the separate test in our fixtures
if that's preferable.
2025-10-17 08:49:16 -04:00
David Peter cfbd42c22a
[ty] Support `dataclass_transform` for base class models (#20783)
## Summary

Support `dataclass_transform` when used on a (base) class.

## Typing conformance

* The changes in `dataclasses_transform_class.py` look good, just a few
mistakes due to missing `alias` support.
* I didn't look closely at the changes in
`dataclasses_transform_converter.py` since we don't support `converter`
yet.

## Ecosystem impact

The impact looks huge, but it's concentrated on a single project (ibis).
Their setup looks more or less like this:

* the real `Annotatable`:
d7083c2c96/ibis/common/grounds.py (L100-L101)
* the real `DataType`:
d7083c2c96/ibis/expr/datatypes/core.py (L161-L179)
* the real `Array`:
d7083c2c96/ibis/expr/datatypes/core.py (L1003-L1006)


```py
from typing import dataclass_transform

@dataclass_transform()
class Annotatable:
    pass

class DataType(Annotatable):
    nullable: bool = True

class Array[T](DataType):
    value_type: T
```

They expect something like `Array([1, 2])` to work, but ty, pyright,
mypy, and pyrefly would all expect there to be a first argument for the
`nullable` field on `DataType`. I don't really understand on what
grounds they expect the `nullable` field to be excluded from the
signature, but this seems to be the main reason for the new diagnostics
here. Not sure if related, but it looks like their typing setup is not
really complete
(https://github.com/ibis-project/ibis/issues/6844#issuecomment-1868274770,
this thread also mentions `dataclass_transform`).

## Test Plan

Update pre-existing tests.
2025-10-17 14:04:31 +02:00
Mark Z. Ding fc3b341529
[ty] Truncate Literal type display in some situations (#20928) 2025-10-17 11:50:58 +00:00
Aria Desires 64edfb6ef6
[ty] add legacy namespace package support (#20897)
Detect legacy namespace packages and treat them like namespace packages
when looking them up as the *parent* of the module we're interested in.
In all other cases treat them like a regular package.

(This PR is coauthored by @MichaReiser in a shared coding session)

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

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-17 03:16:37 +00:00
Ibraheem Ahmed 96b156303b
[ty] Prefer declared type for invariant collection literals (#20927)
## Summary

Prefer the declared type for collection literals, e.g.,
```py
x: list[Any] = [1, "2", (3,)]
reveal_type(x)  # list[Any]
```

This solves a large part of https://github.com/astral-sh/ty/issues/136
for invariant generics, where respecting the declared type is a lot more
important. It also means that annotated dict literals with `dict[_,
Any]` is a way out of https://github.com/astral-sh/ty/issues/1248.
2025-10-16 16:11:28 -04:00
Douglas Creager b0e10a9777
[ty] Don't track inferability via different `Type` variants (#20677)
We have to track whether a typevar appears in a position where it's
inferable or not. In a non-inferable position (in the body of the
generic class or function that binds it), assignability must hold for
every possible specialization of the typevar. In an inferable position,
it only needs to hold for _some_ specialization.
https://github.com/astral-sh/ruff/pull/20093 is working on using
constraint sets to model assignability of typevars, and the constraint
sets that we produce will be the same for inferable vs non-inferable
typevars; what changes is what we _compare_ that constraint set to. (For
a non-inferable typevar, the constraint set must equal the set of valid
specializations; for an inferable typevar, it must not be `never`.)

When I first added support for tracking inferable vs non-inferable
typevars, it seemed like it would be easiest to have separate `Type`
variants for each. The alternative (which lines up with the Δ set in
[POPL15](https://doi.org/10.1145/2676726.2676991)) would be to
explicitly plumb through a list of inferable typevars through our type
property methods. That seemed cumbersome.

In retrospect, that was the wrong decision. We've had to jump through
hoops to translate types between the inferable and non-inferable
variants, which has been quite brittle. Combined with the original point
above, that much of the assignability logic will become more identical
between inferable and non-inferable, there is less justification for the
two `Type` variants. And plumbing an extra `inferable` parameter through
all of these methods turns out to not be as bad as I anticipated.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-16 15:59:46 -04:00
Ibraheem Ahmed 25023cc0ea
[ty] Use declared variable types as bidirectional type context (#20796)
## Summary

Use the declared type of variables as type context for the RHS of assignment expressions, e.g.,
```py
x: list[int | str]
x = [1]
reveal_type(x)  # revealed: list[int | str]
```
2025-10-16 15:40:39 -04:00
Ibraheem Ahmed 1ade4f2081
[ty] Avoid unnecessarily widening generic specializations (#20875)
## Summary

Ignore the type context when specializing a generic call if it leads to
an unnecessarily wide return type. For example, [the example mentioned
here](https://github.com/astral-sh/ruff/pull/20796#issuecomment-3403319536)
works as expected after this change:
```py
def id[T](x: T) -> T:
    return x

def _(i: int):
    x: int | None = id(i)
    y: int | None = i
    reveal_type(x)  # revealed: int
    reveal_type(y)  # revealed: int
```

I also added extended our usage of `filter_disjoint_elements` to tuple
and typed-dict inference, which resolves
https://github.com/astral-sh/ty/issues/1266.
2025-10-16 19:17:37 +00:00
David Peter 8dad58de37
[ty] Support dataclass-transform `field_specifiers` (#20888)
## Summary

Add support for the `field_specifiers` parameter on
`dataclass_transform` decorator calls.

closes https://github.com/astral-sh/ty/issues/1068

## Conformance test results

All true positives ✔️ 

## Ecosystem analysis

* `trio`: this is the kind of change that I would expect from this PR.
The code makes use of a dataclass `Outcome` with a `_unwrapped: bool =
attr.ib(default=False, eq=False, init=False)` field that is excluded
from the `__init__` signature, so we now see a bunch of
constructor-call-related errors going away.
* `home-assistant/core`: They have a `domain: str = attr.ib(init=False,
repr=False)` field and then use
  ```py
    @domain.default
    def _domain_default(self) -> str:
        # …
  ```
This accesses the `default` attribute on `dataclasses.Field[…]` with a
type of `default: _T | Literal[_MISSING_TYPE.MISSING]`, so we get those
"Object of type `_MISSING_TYPE` is not callable" errors. I don't really
understand how that is supposed to work. Even if `_MISSING_TYPE` would
be absent from that union, what does this try to call? pyright also
issues an error and it doesn't seem to work at runtime? So this looks
like a true positive?
* `attrs`: Similar here. There are some new diagnostics on code that
tries to access `.validator` on a field. This *does* work at runtime,
but I'm not sure how that is supposed to type-check (without a [custom
plugin](2c6c395935/mypy/plugins/attrs.py (L575-L602))).
pyright errors on this as well.
* A handful of new false positives because we don't support `alias` yet

## Test Plan

Updated tests.
2025-10-16 20:49:11 +02:00
Dylan 2bffef5966
Bump 0.14.1 (#20925) 2025-10-16 12:44:13 -05:00
Brent Westbrook e64d772788
Standardize syntax error construction (#20903)
Summary
--

This PR unifies the two different ways Ruff and ty construct syntax
errors. Ruff has been storing the primary message in the diagnostic
itself, while ty attached the message to the primary annotation:

```
> ruff check try.py
invalid-syntax: name capture `x` makes remaining patterns unreachable
 --> try.py:2:10
  |
1 | match 42:
2 |     case x: ...
  |          ^
3 |     case y: ...
  |

Found 1 error.
> uvx ty check try.py
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
Checking ------------------------------------------------------------ 1/1 files                                                                                                 
error[invalid-syntax]
 --> try.py:2:10
  |
1 | match 42:
2 |     case x: ...
  |          ^ name capture `x` makes remaining patterns unreachable
3 |     case y: ...
  |

Found 1 diagnostic
```

I think there are benefits to both approaches, and I do like ty's
version, but I feel like we should pick one (and it might help with
#20901 eventually). I slightly prefer Ruff's version, so I went with
that. Hopefully this isn't too controversial, but I'm happy to close
this if it is.

Note that this shouldn't change any other diagnostic formats in ty
because
[`Diagnostic::primary_message`](98d27c4128/crates/ruff_db/src/diagnostic/mod.rs (L177))
was already falling back to the primary annotation message if the
diagnostic message was empty. As a result, I think this change will
partially resolve the FIXME therein.

Test Plan
--

Existing tests with updated snapshots
2025-10-16 11:56:32 -04:00
Auguste Lalande 03696687ea
[`pydoclint`] Implement `docstring-extraneous-parameter` (`DOC102`) (#20376)
## Summary

Implement `docstring-extraneous-parameter` (`DOC102`). This rule checks
that all parameters present in a functions docstring are also present in
its signature.

Split from #13280, per this
[comment](https://github.com/astral-sh/ruff/pull/13280#issuecomment-3280575506).

Part of #12434.

## Test Plan

Test cases added.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-16 11:26:51 -04:00
Micha Reiser 058fc37542
[ty] Fix panic 'missing root' when handling completion request (#20917) 2025-10-16 16:23:02 +02:00
Aria Desires 7155a62e5c
[ty] Add version hint for failed stdlib attribute accesses (#20909)
This is the ultra-minimal implementation of

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

that was previously discussed as a good starting point. In particular we
don't actually bother trying to figure out the exact python versions,
but we still mention "hey btw for No Reason At All... you're on python
3.10" when you try to access something that has a definition rooted in
the stdlib that we believe exists sometimes.
2025-10-16 14:07:33 +00:00
Aria Desires 6a1e91ce97
[ty] Check typeshed VERSIONS for parent modules when reporting failed stdlib imports (#20908)
This is a drive-by improvement that I stumbled backwards into while
looking into

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

I was writing some simple tests for "thing not in old version of stdlib"
diagnostics and checked what was added in 3.14, and saw
`compression.zstd` and to my surprise discovered that `import
compression.zstd` and `from compression import zstd` had completely
different quality diagnostics.

This is because `compression` and `compression.zstd` were *both*
introduced in 3.14, and so per VERSIONS policy only an entry for
`compression` was added, and so we don't actually have any definite info
on `compression.zstd` and give up on producing a diagnostic. However the
`from compression import zstd` form fails on looking up `compression`
and we *do* have an exact match for that, so it gets a better
diagnostic!

(aside: I have now learned about the VERSIONS format and I *really* wish
they would just enumerate all the submodules but, oh well!)

The fix is, when handling an import failure, if we fail to find an exact
match *we requery with the parent module*. In cases like
`compression.zstd` this lets us at least identify that, hey, not even
`compression` exists, and luckily that fixes the whole issue. In cases
where the parent module and submodule were introduced at different times
then we may discover that the parent module is in-range and that's fine,
we don't produce the richer stdlib diagnostic.
2025-10-16 13:25:08 +00:00
Carl Meyer d23826ce46
[ty] cache Type::is_redundant_with (#20477)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-10-16 13:46:56 +02:00
Micha Reiser 5fb142374d
Fix run-away for mutually referential instance attributes (#20645) 2025-10-16 13:24:41 +02:00
Micha Reiser 9393279f65
[ty] Limit shown import paths to at most 5 unless ty runs with `-v` (#20912) 2025-10-16 13:18:09 +02:00
David Peter c8133104e8
[ty] Use field-specifier return type as the default type for the field (#20915)
## Summary

`dataclasses.field` and field-specifier functions of commonly used
libraries like `pydantic`, `attrs`, and `SQLAlchemy` all return the
default type for the field (or `Any`) instead of an actual `Field`
instance, even if this is not what happens at runtime. Let's make use of
this fact and assume that *all* field specifiers return the type of the
default value of the field.

For standard dataclasses, this leads to more or less the same outcome
(see test diff for details), but this change is important for 3rd party
dataclass-transformers.

## Test Plan

Tested the consequences of this change on the field-specifiers branch as
well.
2025-10-16 13:13:45 +02:00
David Peter 0cc663efcd
[ty] Do not assume that `field`s have a default value (#20914)
## Summary

fixes https://github.com/astral-sh/ty/issues/1366

## Test Plan

Added regression test
2025-10-16 12:49:24 +02:00
Eric Mark Martin c9dfb51f49
[ty] Fix match pattern value narrowing to use equality semantics (#20882)
## Summary

Resolves https://github.com/astral-sh/ty/issues/1349.

Fix match statement value patterns to use equality comparison semantics
instead of incorrectly narrowing to literal types directly. Value
patterns use equality for matching, and equality can be overridden, so
we can't always narrow to the matched literal.

## Test Plan

Updated match.md with corrected expected types and an additional example
with explanation

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-10-16 07:50:32 +00:00
Bhuminjay Soni 73520e4acd
[syntax-errors]: implement F702 as semantic syntax error (#20869)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

This PR implements `F702`
https://docs.astral.sh/ruff/rules/continue-outside-loop/ as semantic
syntax error.

## Test Plan

<!-- How was it tested? -->
Tests are already previously written in F702

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
2025-10-15 19:27:15 +00:00
Alex Waygood fd568f0221
[ty] Heterogeneous unpacking support for unions (#20377) 2025-10-15 19:30:03 +01:00
Shunsuke Shibayama 9de34e7ac1
[ty] refactor `Place` (#20871)
## Summary

Part of astral-sh/ty#1341

The following changes will be made to `Place`.

* Introduce `TypeOrigin`
* `Place::Type` -> `Place::Defined`
* `Place::Unbound` -> `Place::Undefined`
* `Boundness` -> `Definedness`

`TypeOrigin::Declared`+`Definedness::PossiblyUndefined` are patterns
that weren't considered before, but this PR doesn't address them yet,
only refactors.

## Test Plan

Refactoring
2025-10-15 20:19:19 +02:00
Wei Lee d2a6ef7491
[`airflow`] Add warning to `airflow.datasets.DatasetEvent` usage (`AIR301`) (#20551)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

`airflow.datasets.DatasetEvent` has been removed in 3 but `AssetEvent`
might be added in the future

## Test Plan

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

update the test fixture and reorg in the second commit
2025-10-15 12:19:55 -04:00
Dan Parizher 98d27c4128
[`flake8-pyi`] Fix operator precedence by adding parentheses when needed (`PYI061`) (#20508)
## Summary

Fixes #20265

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-15 15:06:03 +00:00
Dan Parizher c06c3f9505
[`pyupgrade`] Fix false negative for `TypeVar` with default argument in `non-pep695-generic-class` (`UP046`) (#20660)
## Summary

Fixes #20656

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-15 14:51:55 +00:00
Alex Waygood 9e404a30c3
Update parser snapshots (#20893) 2025-10-15 14:21:24 +00:00
Brent Westbrook 8b9ab48ac6
Fix syntax error false positives for escapes and quotes in f-strings (#20867)
Summary
--

Fixes #20844 by refining the unsupported syntax error check for [PEP
701]
f-strings before Python 3.12 to allow backslash escapes and escaped
outer quotes
in the format spec part of f-strings. These are only disallowed within
the
f-string expression part on earlier versions. Using the examples from
the PR:

```pycon
>>> f"{1:\x64}"
'1'
>>> f"{1:\"d\"}"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid format specifier '"d"' for object of type 'int'
```

Note that the second case is a runtime error, but this is actually
avoidable if
you override `__format__`, so despite being pretty weird, this could
actually be
a valid use case.

```pycon
>>> class C:
...     def __format__(*args, **kwargs): return "<C>"
...
>>> f"{C():\"d\"}"
'<C>'
```

At first I thought narrowing the range we check to exclude the format
spec would
only work for escapes, but it turns out that cases like `f"{1:""}"` are
already
covered by an existing `ParseError`, so we can just narrow the range of
both our
escape and quote checks.

Our comment check also seems to be working correctly because it's based
on the
actual tokens. A case like
[this](https://play.ruff.rs/9f1c2ff2-cd8e-4ad7-9f40-56c0a524209f):

```python
f"""{1:# }"""
```

doesn't include a comment token, instead the `#` is part of an
`InterpolatedStringLiteralElement`.

Test Plan
--

New inline parser tests

[PEP 701]: https://peps.python.org/pep-0701/
2025-10-15 09:23:16 -04:00
Douglas Creager 8817ea5c84
[ty] Add (unused) `inferable` parameter to type property methods (#20865)
A large part of the diff on #20677 just involves threading a new
`inferable` parameter through all of the type property methods. In the
interests of making that PR easier to review, I've pulled that bit out
into here, so that it can be reviewed in isolation. This should be a
pure refactoring, with no logic changes or behavioral changes.
2025-10-15 09:05:15 -04:00
github-actions[bot] cafb96aa7a
[ty] Sync vendored typeshed stubs (#20876)
Close and reopen this PR to trigger CI

---------

Co-authored-by: typeshedbot <>
Co-authored-by: David Peter <mail@david-peter.de>
2025-10-15 11:13:32 +02:00
Andrew Gallant 651f7963a7
[ty] Add some completion ranking improvements (#20807)
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-15 08:59:33 +00:00
Micha Reiser 4fc7dd300c
Improved error recovery for unclosed strings (including f- and t-strings) (#20848) 2025-10-15 09:50:56 +02:00
Dan Parizher 9e1aafd0ce
[`pyupgrade`] Extend `UP019` to detect `typing_extensions.Text` (`UP019`) (#20825)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-15 06:52:14 +00:00
Dylan abf685b030
[`flake8-bugbear`] Omit annotation in preview fix for `B006` (#20877)
Closes #20864
2025-10-15 01:14:01 +00:00
Paillat e1e3eb7209
fix(docs): Fix typo in `RUF015` description (#20873)
## Summary
Fixed a typo. It should be "or", not "of". Both `.pop()` and `next()` on
an empty collection will raise `IndexError`, not "`[0]` of the `pop()`
function"

## Test Plan

n/a
2025-10-14 21:38:31 +00:00
Alex Waygood 43eddc566f
[ty] Improve and extend tests for instance attributes redeclared in subclasses (#20866)
Part of https://github.com/astral-sh/ty/issues/1345
2025-10-14 19:31:34 +01:00
Brent Westbrook 591e9bbccb
Remove parentheses around multiple exception types on Python 3.14+ (#20768)
Summary
--

This PR implements the black preview style from
https://github.com/psf/black/pull/4720. As of Python 3.14, you're
allowed to omit the parentheses around groups of exceptions, as long as
there's no `as` binding:

**3.13**

```pycon
Python 3.13.4 (main, Jun  4 2025, 17:37:06) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try: ...
... except (Exception, BaseException): ...
...
Ellipsis
>>> try: ...
... except Exception, BaseException: ...
...
  File "<python-input-1>", line 2
    except Exception, BaseException: ...
           ^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized
```

**3.14**

```pycon
Python 3.14.0rc2 (main, Sep  2 2025, 14:20:56) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try: ...
... except Exception, BaseException: ...
...
Ellipsis
>>> try: ...
... except (Exception, BaseException): ...
...
Ellipsis
>>> try: ...
... except Exception, BaseException as e: ...
...
  File "<python-input-2>", line 2
    except Exception, BaseException as e: ...
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized when using 'as'
```

I think this ended up being pretty straightforward, at least once Micha
showed me where to start :)

Test Plan
--

New tests

At first I thought we were deviating from black in how we handle
comments within the exception type tuple, but I think this applies to
how we format all tuples, not specifically with the new preview style.
2025-10-14 11:17:45 -04:00
Brent Westbrook 1ed9b215b9
Update Black tests (#20794)
Summary
--

```shell
git clone git@github.com:psf/black.git ../other/black
crates/ruff_python_formatter/resources/test/fixtures/import_black_tests.py ../other/black
```

Then ran our tests and accepted the snapshots

I had to make a small fix to our tuple normalization logic for `del`
statements
in the second commit, otherwise the tests were panicking at a changed
AST. I
think the new implementation is closer to the intention described in the
nearby
comment anyway, though.

The first commit adds the new Python, settings, and `.expect` files, the
next three commits make some small
fixes to help get the tests running, and then the fifth commit accepts
all but one of the new snapshots. The last commit includes the new
unsupported syntax error for one f-string example, tracked in #20774.

Test Plan
--

Newly imported tests. I went through all of the new snapshots and added
review comments below. I think they're all expected, except a few cases
I wasn't 100% sure about.
2025-10-14 10:14:59 -04:00
Alex Waygood 9090aead0f
[ty] Fix further issues in `super()` inference logic (#20843) 2025-10-14 12:48:47 +00:00
Micha Reiser 441ba20876
[ty] Document when a rule was added (#20859) 2025-10-14 14:33:48 +02:00
David Peter 6341bb7403
[ty] Treat `Callable` dunder members as bound method descriptors (#20860)
## Summary

Dunder methods (at least the ones defined in the standard library)
always take an instance of the class as the first parameter. So it seems
reasonable to generally treat them as bound method descriptors if they
are defined via a `Callable` type.

This removes just a few false positives from the ecosystem, but solves
three user-reported issues:

closes https://github.com/astral-sh/ty/issues/908
closes https://github.com/astral-sh/ty/issues/1143
closes https://github.com/astral-sh/ty/issues/1209

In addition to the change here, I also considered [making `ClassVar`s
bound method descriptors](https://github.com/astral-sh/ruff/pull/20861).
However, there was zero ecosystem impact. So I think we can also close
https://github.com/astral-sh/ty/issues/491 with this PR.

closes https://github.com/astral-sh/ty/issues/491

## Test Plan

Added regression test
2025-10-14 14:27:52 +02:00
David Peter ac2c530377
[ty] Handle decorators which return unions of `Callable`s (#20858)
## Summary

If a function is decorated with a decorator that returns a union of
`Callable`s, also treat it as a union of function-like `Callable`s.

Labeling as `internal`, since the previous change has not been released
yet.

## Test Plan

New regression test.
2025-10-14 09:47:50 +00:00
Dan Parizher c69fa75cd5
Fix false negatives in `Truthiness::from_expr` for lambdas, generators, and f-strings (#20704) 2025-10-14 03:06:17 -05:00
David Peter f73bb45be6
[ty] Rename Type unwrapping methods (#20857)
## Summary

Rename "unwrapping" methods on `Type` from e.g.
`Type::into_class_literal` to `Type::as_class_literal`. I personally
find that name more intuitive, since no transformation of any kind is
happening. We are just unwrapping from certain enum variants. An
alternative would be `try_as_class_literal`, which would follow the
[`strum` naming
scheme](https://docs.rs/strum/latest/strum/derive.EnumTryAs.html), but
is slightly longer.

Also rename `Type::into_callable` to `Type::try_upcast_to_callable`.
Note that I intentionally kept names like
`FunctionType::into_callable_type`, because those return `CallableType`,
not `Option<Type<…>>`.

## Test Plan

Pure refactoring
2025-10-14 09:53:29 +02:00
Matt Norton e338d2095e
Update `lint.flake8-type-checking.quoted-annotations` docs (#20765)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-14 06:43:24 +00:00
Douglas Creager 5e08e5451d
[ty] Add separate type for typevar "identity" (#20813)
As part of #20598, we added `is_identical_to` methods to
`TypeVarInstance` and `BoundTypeVarInstance`, which compare when two
typevar instances refer to "the same" underlying typevar, even if we
have forced their lazy bounds/constraints as part of marking typevars as
inferable. (Doing so results in a different salsa interned struct ID,
since we've changed the contents of the `bounds_or_constraints` field.)

It turns out that marking typevars as inferable is not the only way that
we might force lazy bounds/constraints; it also happens when we
materialize a type containing a typevar. This surfaced as ecosystem
report failures on #20677.

That means that we need a more long-term fix to this problem.
(`is_identical_to`, and its underlying `original` field, were meant to
be a temporary fix until we removed the `MarkTypeVarsInferable` type
mapping.)

This PR extracts out a separate type (`TypeVarIdentity`) that only
includes the fields that actually inform whether two typevars are "the
same". All other properties of the typevar (default, bounds/constraints,
etc) still live in `TypeVarInstance`. Call sites that care about typevar
identity can now either store just `TypeVarIdentity` (if they never need
access to those other properties), or continue to store
`TypeVarInstance` but pull out its `identity` when performing those "are
they the same typevar" comparisons. (All of this also applies
respectively to `BoundTypeVar{Identity,Instance}`.) In particular,
constraint sets now work on `BoundTypeVarIdentity`, and generic contexts
still _store_ a `BoundTypeVarInstance` (since we might need access to
defaults when specializing), but are keyed on `BoundTypeVarIdentity`.
2025-10-13 20:09:27 -04:00
Douglas Creager aba0bd568e
[ty] Diagnostic for generic classes that reference typevars in enclosing scope (#20822)
Generic classes are not allowed to bind or reference a typevar from an
enclosing scope:

```py
def f[T](x: T, y: T) -> None:
    class Ok[S]: ...
    # error: [invalid-generic-class]
    class Bad1[T]: ...
    # error: [invalid-generic-class]
    class Bad2(Iterable[T]): ...

class C[T]:
    class Ok1[S]: ...
    # error: [invalid-generic-class]
    class Bad1[T]: ...
    # error: [invalid-generic-class]
    class Bad2(Iterable[T]): ...
```

It does not matter if the class uses PEP 695 or legacy syntax. It does
not matter if the enclosing scope is a generic class or function. The
generic class cannot even _reference_ an enclosing typevar in its base
class list.

This PR adds diagnostics for these cases.

In addition, the PR adds better fallback behavior for generic classes
that violate this rule: any enclosing typevars are not included in the
class's generic context. (That ensures that we don't inadvertently try
to infer specializations for those typevars in places where we
shouldn't.) The `dulwich` ecosystem project has [examples of
this](d912eaaffd/dulwich/config.py (L251))
that were causing new false positives on #20677.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-13 19:30:49 -04:00
Bhuminjay Soni 2b729b4d52
[syntax-errors]: break outside loop F701 (#20556)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

This PR implements https://docs.astral.sh/ruff/rules/break-outside-loop/
(F701) as a semantic syntax error.

## Test Plan

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

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-13 20:00:59 +00:00
David Peter 4b8e278a88
[ty] Treat `Callable`s as bound-method descriptors in special cases (#20802)
## Summary

Treat `Callable`s as bound-method descriptors if `Callable` is the
return type of a decorator that is applied to a function definition. See
the [rendered version of the new test
file](https://github.com/astral-sh/ruff/blob/david/callables-as-descriptors/crates/ty_python_semantic/resources/mdtest/call/callables_as_descriptors.md)
for the full description of this new heuristic.

I could imagine that we want to treat `Callable`s as bound-method
descriptors in other cases as well, but this seems like a step in the
right direction. I am planning to add other "use cases" from
https://github.com/astral-sh/ty/issues/491 to this test suite.

partially addresses https://github.com/astral-sh/ty/issues/491
closes https://github.com/astral-sh/ty/issues/1333

## Ecosystem impact

All positive

* 2961 removed `unsupported-operator` diagnostics on `sympy`, which was
one of the main motivations for implementing this change
* 37 removed `missing-argument` diagnostics, and no added call-error
diagnostics, which is an indicator that this heuristic shouldn't cause
many false positives
* A few removed `possibly-missing-attribute` diagnostics when accessing
attributes like `__name__` on decorated functions. The two added
`unused-ignore-comment` diagnostics are also cases of this.
* One new `invalid-assignment` diagnostic on `dd-trace-py`, which looks
suspicious, but only because our `invalid-assignment` diagnostics are
not great. This is actually a "Implicit shadowing of function"
diagnostic that hides behind the `invalid-assignment` diagnostic,
because a module-global function is being patched through a
`module.func` attribute assignment.

## Test Plan

New Markdown tests.
2025-10-13 21:17:47 +02:00
David Peter d912f13661
[ty] Do not bind self to non-positional parameters (#20850)
## Summary

closes https://github.com/astral-sh/ty/issues/1333

## Test Plan

Regression test
2025-10-13 20:44:27 +02:00
Brent Westbrook 71f8389f61
Fix syntax error false positives on parenthesized context managers (#20846)
This PR resolves the issue noticed in
https://github.com/astral-sh/ruff/pull/20777#discussion_r2417233227.
Namely, cases like this were being flagged as syntax errors despite
being perfectly valid on Python 3.8:

```pycon
Python 3.8.20 (default, Oct  2 2024, 16:34:12)
[Clang 18.1.8 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> with (open("foo.txt", "w")): ...
...
Ellipsis
>>> with (open("foo.txt", "w")) as f: print(f)
...
<_io.TextIOWrapper name='foo.txt' mode='w' encoding='UTF-8'>
```

The second of these was already allowed but not the first:

```shell
> ruff check --target-version py38 --ignore ALL - <<EOF
with (open("foo.txt", "w")): ...
with (open("foo.txt", "w")) as f: print(f)
EOF
invalid-syntax: Cannot use parentheses within a `with` statement on Python 3.8 (syntax was added in Python 3.9)
 --> -:1:6
  |
1 | with (open("foo.txt", "w")): ...
  |      ^
2 | with (open("foo.txt", "w")) as f: print(f)
  |

Found 1 error.
```

There was some discussion of related cases in
https://github.com/astral-sh/ruff/pull/16523#discussion_r1984657793, but
it seems I overlooked the single-element case when flagging tuples. As
suggested in the other thread, we can just check if there's more than
one element or a trailing comma, which will cause the tuple parsing on
<=3.8 and avoid the false positives.
2025-10-13 14:13:27 -04:00
Micha Reiser 373fe8a39c
[ty] Remove 'pre-release software' warning (#20817) 2025-10-13 19:50:19 +02:00
Brent Westbrook 975891fc90
Render unsupported syntax errors in formatter tests (#20777)
## Summary

Based on the suggestion in
https://github.com/astral-sh/ruff/issues/20774#issuecomment-3383153511,
I added rendering of unsupported syntax errors in our `format` test.

In support of this, I added a `DummyFileResolver` type to `ruff_db` to
pass to `DisplayDiagnostics::new` (first commit). Another option would
obviously be implementing this directly in the fixtures, but we'd have
to import a `NotebookIndex` somehow; either by depending directly on
`ruff_notebook` or re-exporting it from `ruff_db`. I thought it might be
convenient elsewhere to have a dummy resolver, for example in the
parser, where we currently have a separate rendering pipeline
[copied](https://github.com/astral-sh/ruff/blob/main/crates/ruff_python_parser/tests/fixtures.rs#L321)
from our old rendering code in `ruff_linter`. I also briefly tried
implementing a `TestDb` in the formatter since I noticed the
`ruff_python_formatter::db` module, but that was turning into a lot more
code than the dummy resolver.

We could also push this a bit further if we wanted. I didn't add the new
snapshots to the black compatibility tests or to the preview snapshots,
for example. I thought it was kind of noisy enough (and helpful enough)
already, though. We could also use a shorter diagnostic format, but the
full output seems most useful once we accept this initial large batch of
changes.

## Test Plan

I went through the baseline snapshots pretty quickly, but they all
looked reasonable to me, with one exception I noted below. I also tested
that the case from #20774 produces a new unsupported syntax error.
2025-10-13 10:00:37 -04:00
David Peter 195e8f0684
[ty] Treat functions, methods, and dynamic types as function-like `Callable`s (#20842)
## Summary

Treat functions, methods, and dynamic types as function-like `Callable`s

closes https://github.com/astral-sh/ty/issues/1342
closes https://github.com/astral-sh/ty/issues/1344

## Ecosystem analysis

All removed diagnostics look like cases of
https://github.com/astral-sh/ty/issues/1344

## Test Plan

Added regression test
2025-10-13 15:21:55 +02:00
Alex Waygood 513d2996ec
[ty] Move logic for `super()` inference to a new `types::bound_super` submodule (#20840) 2025-10-13 11:18:13 +00:00
Alex Waygood d83d7a0dcd
[ty] Fix false-positive diagnostics on `super()` calls (#20814) 2025-10-13 10:57:46 +00:00
David Peter 565dbf3c9d
[ty] Move `class_member` to `member` module (#20837)
## Summary

Move the `class_member` function to the `member` module. This allows us
to move the `member` module into the `types` module and to reduce the
visibility of its contents to `pub(super)`. The drawback is that we need
to make `place::place_by_id` public.

## Test Plan

Pure refactoring.
2025-10-13 10:58:37 +02:00
Takayuki Maeda f715d70be1
[`ruff`] Use DiagnosticTag for more flake8 and numpy rules (#20758) 2025-10-13 10:29:15 +02:00
David Peter 9b9c9ae092
[ty] Prefer declared base class attribute over inferred attribute on subclass (#20764)
## Summary

When accessing an (instance) attribute on a given class, we were
previously traversing its MRO, and building a union of types (if the
attribute was available on multiple classes in the MRO) until we found a
*definitely bound* symbol. The idea was that possibly unbound symbols in
a subclass might only partially shadow the underlying base class
attribute.

This behavior was problematic for two reasons:
* if the attribute was definitely bound on a class (e.g. `self.x =
None`), we would have stopped iterating, even if there might be a `x:
str | None` declaration in a base class (the bug reported in
https://github.com/astral-sh/ty/issues/1067).
* if the attribute originated from an implicit instance attribute
assignment (e.g. `self.x = 1` in method `Sub.foo`), we might stop
looking and miss another implicit instance attribute assignment in a
base class method (e.g. `self.x = 2` in method `Base.bar`).

With this fix, we still iterate the MRO of the class, but we only stop
iterating if we find a *definitely declared* symbol. In this case, we
only return the declared attribute type. Otherwise, we keep building a
union of inferred attribute types.

The implementation here seemed to be the easiest fix for
https://github.com/astral-sh/ty/issues/1067 that also kept the ecosystem
impact low (the changes that I see all look correct). However, as the
Markdown tests show, there are other things to fix in this area. For
example, we should do a similar thing for *class attributes*. This is
more involved, though (affects many different areas and probably
involves a change to our descriptor protocol implementation), so I'd
like to postpone this to a follow-up.

closes https://github.com/astral-sh/ty/issues/1067

## Test Plan

Updated Markdown tests, including a regression test for
https://github.com/astral-sh/ty/issues/1067.
2025-10-13 09:28:57 +02:00
Micha Reiser c80ee1a50b
[ty] Log files that are slow to type check (#20836) 2025-10-13 09:15:54 +02:00
Alex Waygood 7064c38e53
[ty] Filter out `revealed-type` and `undefined-reveal` diagnostics from mdtest snapshots (#20820) 2025-10-12 18:39:32 +00:00
Shunsuke Shibayama dc64c08633
[ty] bidirectional type inference using function return type annotations (#20528)
## Summary

Implements bidirectional type inference using function return type
annotations.

This PR was originally proposed to solve astral-sh/ty#1167, but this
does not fully resolve it on its own.
Additionally, I believe we need to allow dataclasses to generate their
own `__new__` methods, [use constructor return types ​​for
inference](5844c0103d/crates/ty_python_semantic/src/types.rs (L5326-L5328)),
and a mechanism to discard type narrowing like `& ~AlwaysFalsy` if
necessary (at a more general level than this PR).

## Test Plan

`mdtest/bidirectional.md` is added.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Ibraheem Ahmed <ibraheem@ibraheem.ca>
2025-10-11 00:38:35 +00:00
Shunsuke Shibayama 11a9e7ee44
[ty] use type context more aggressively to infer values ​​when constructing a `TypedDict` (#20806)
## Summary

Based on @ibraheemdev's comment on #20792:

> I think we can also update our bidirectional inference code, [which
makes the same
assumption](https://github.com/astral-sh/ruff/blob/main/crates/ty_python_semantic/src/types/infer/builder.rs?rgh-link-date=2025-10-09T21%3A30%3A31Z#L5860).

This PR also adds more test cases for how `TypedDict` annotations affect
generic call inference.

## Test Plan

New tests in `typed_dict.md`
2025-10-10 16:51:16 -07:00
ageorgou bbd3856de8
[`flake8-datetimez`] Clarify docs for several rules (#20778)
## Summary

Resolves #19384.

- Distinguishes more clearly between `date` and `datetime` objects.
- Uniformly links to the relevant Python docs from rules in this
category.

I've tried to be clearer, but there's still a contradiction in the rules
as written: we say "use timezone-aware objects", but `date`s are
inherently timezone-naive.

Also, the full docs don't always match the error message: for instance,
in [DTZ012](https://docs.astral.sh/ruff/rules/call-date-fromtimestamp/),
the example says to use:
```python
datetime.datetime.fromtimestamp(946684800, tz=datetime.UTC)
```
while `fix_title` returns "Use `datetime.datetime.fromtimestamp(ts,
tz=...)**.date()**` instead".
I have left this as it was for now.

## Test Plan
Ran `mkdocs` locally and inspected result.
2025-10-10 13:02:24 +00:00
David Peter ae83a1fd2d
[ty] Additional tests for `dataclass_transform` (class-level overwrites, `field_specifiers`) (#20788)
## Summary

Adds a set of basic new tests corresponding to open points in
https://github.com/astral-sh/ty/issues/1327, to document the state of
support for `dataclass_transform`.
2025-10-10 11:22:06 +00:00
Alex Waygood 44807c4a05
[ty] Better implementation of assignability for intersections with negated gradual elements (#20773) 2025-10-10 11:10:17 +00:00
David Peter 69f9182033
[ty] Annotations are deferred by default for 3.14+ (#20799)
## Summary

Type annotations are deferred by default starting with Python 3.14. No
`from __future__ import annotations` import is necessary.

## Test Plan

New Markdown test
2025-10-10 12:05:03 +02:00
David Peter 949a4f1c42
[ty] Simplify and fix `CallableTypeOf[..]` implementation (#20797)
## Summary

Simplify and fix the implementation of
`ty_extensions.CallableTypeOf[..]`.

closes https://github.com/astral-sh/ty/issues/1331

## Test Plan

Added regression test.
2025-10-10 12:04:37 +02:00
David Peter a82833a998
[ty] Update mypy_primer and project lists (#20798)
## Summary

Pulls in two updates to `mypy_primer` projects:

* https://github.com/hauntsaninja/mypy_primer/pull/201 (add
`django-test-migrations`)
* https://github.com/hauntsaninja/mypy_primer/pull/122 (remove
`SinbadCogs`)

## Test Plan

CI on this PR
2025-10-10 11:08:39 +02:00
Micha Reiser 4bd454f9b5
Shard ty walltime benchmarks (#20791) 2025-10-10 07:55:50 +02:00
pieterh-oai 66885e4bce
[`flake8-logging-format`] Avoid dropping implicitly concatenated pieces in the `G004` fix (#20793)
## Summary

The original autofix for G004 was quietly dropping everything but the
f-string components of any implicit concatenation sequence; this
addresses that.

Side note: It looks like `f_strings` is a bit risky to use (since it
implicitly skips non-f-string parts); use iter and include implicitly
concatenated pieces. We should consider if it's worth having
(convenience vs. bit risky).

## Test Plan

```
cargo test -p ruff_linter
```

Backtest (run new testcases against previous implementation):
```
git checkout HEAD^ crates/ruff_linter/src/rules/flake8_logging_format/rules/logging_call.rs
cargot test -p ruff_linter

```
2025-10-09 18:14:38 -04:00
Carl Meyer 8248193ed9
[ty] defer inference of legacy TypeVar bound/constraints/defaults (#20598)
## Summary

This allows us to handle self-referential bounds/constraints/defaults
without panicking.

Handles more cases from https://github.com/astral-sh/ty/issues/256

This also changes the way we infer the types of legacy TypeVars. Rather
than understanding a constructor call to `typing[_extension].TypeVar`
inside of any (arbitrarily nested) expression, and having to use a
special `assigned_to` field of the semantic index to try to best-effort
figure out what name the typevar was assigned to, we instead understand
the creation of a legacy `TypeVar` only in the supported syntactic
position (RHS of a simple un-annotated assignment with one target). In
any other position, we just infer it as creating an opaque instance of
`typing.TypeVar`. (This behavior matches all other type checkers.)

So we now special-case TypeVar creation in `TypeInferenceBuilder`, as a
special case of an assignment definition, rather than deeper inside call
binding. This does mean we re-implement slightly more of
argument-parsing, but in practice this is minimal and easy to handle
correctly.

This is easier to implement if we also make the RHS of a simple (no
unpacking) one-target assignment statement no longer a standalone
expression. Which is fine to do, because simple one-target assignments
don't need to infer the RHS more than once. This is a bonus performance
(0-3% across various projects) and significant memory-usage win, since
most assignment statements are simple one-target assignment statements,
meaning we now create many fewer standalone-expression salsa
ingredients.

This change does mean that inference of manually-constructed
`TypeAliasType` instances can no longer find its Definition in
`assigned_to`, which regresses go-to-definition for these aliases. In a
future PR, `TypeAliasType` will receive the same treatment that
`TypeVar` did in this PR (moving its special-case inference into
`TypeInferenceBuilder` and supporting it only in the correct syntactic
position, and lazily inferring its value type to support recursion),
which will also fix the go-to-definition regression. (I decided a
temporary edge-case regression is better in this case than doubling the
size of this PR.)

This PR also tightens up and fixes various aspects of the validation of
`TypeVar` creation, as seen in the tests.

We still (for now) treat all typevars as instances of `typing.TypeVar`,
even if they were created using `typing_extensions.TypeVar`. This means
we'll wrongly error on e.g. `T.__default__` on Python 3.11, even if `T`
is a `typing_extensions.TypeVar` instance at runtime. We share this
wrong behavior with both mypy and pyrefly. It will be easier to fix
after we pull in https://github.com/python/typeshed/pull/14840.

There are some issues that showed up here with typevar identity and
`MarkTypeVarsInferable`; the fix here (using the new `original` field
and `is_identical_to` methods on `BoundTypeVarInstance` and
`TypeVarInstance`) is a bit kludgy, but it can go away when we eliminate
`MarkTypeVarsInferable`.

## Test Plan

Added and updated mdtests.

### Conformance suite impact

The impact here is all positive:

* We now correctly error on a legacy TypeVar with exactly one constraint
type given.
* We now correctly error on a legacy TypeVar with both an upper bound
and constraints specified.

### Ecosystem impact

Basically none; in the setuptools case we just issue slightly different
errors on an invalid TypeVar definition, due to the modified validation
code.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-09 21:08:37 +00:00
Ibraheem Ahmed b086ffe921
[ty] Type-context aware literal promotion (#20776)
## Summary

Avoid literal promotion when a literal type annotation is provided, e.g.,
```py
x: list[Literal[1]] = [1]
```

Resolves https://github.com/astral-sh/ty/issues/1198. This does not fix
issue https://github.com/astral-sh/ty/issues/1284, but it does make it
more relevant because after this change, it is possible to directly
instantiate a generic type with a literal specialization.
2025-10-09 16:53:53 -04:00
Dan Parizher 537ec5f012
[`fastapi`] Fix false positives for path parameters that FastAPI doesn't recognize (`FAST003`) (#20687)
## Summary

Fixes #20680

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-09 16:10:21 -04:00
Shunsuke Shibayama db91ac7dce
[ty] allow any string `Literal` type expression as a key when constructing a `TypedDict` (#20792) 2025-10-09 18:24:11 +00:00
David Peter 75f3c0e8e6
[ty] Respect `dataclass_transform` parameters for metaclass-based models (#20780)
## Summary

Respect parameters such as `frozen_default` for metaclass-based
`@dataclass_transformer` models.

Related to: https://github.com/astral-sh/ty/issues/1260

## Typing conformance changes

Those are all correct (new true positives)

## Test Plan

New Markdown tests
2025-10-09 13:24:20 +00:00
wangxiaolei f0d0b57900
[ty] `dataclass_transform`: Support `frozen_default` and `kw_only_default` (#20761)
## Summary

- Add support for eq, kw_only, and frozen parameter overrides in
@dataclass_transform
- Previously only order parameter override was supported
- Update test documentation to reflect fixed behavior
- Resolves issue where kw_only_default and frozen_default could not be
overridden

closes https://github.com/astral-sh/ty/issues/1260

## Test Plan

New Markdown tests

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-10-09 09:34:49 +02:00
Alex Waygood b0c6217e0b
[ty] Fix broken property tests for disjointness of intersections (#20775)
## Summary

Two stable property tests are currently failing on `main`, following
f054b8a55e
(of course, I only thought to run the property tests again around 30
minutes _after_ landing that PR...). The issue is quite subtle, and took
me an annoying amount of time to pin down: we're matching over `(self,
other)` in `Type::is_disjoint_from_impl`, but `other` here is shadowed
by the binding in the `match` branch, which means that the wrong key is
inserted into the cache of the `IsDisjointFrom` cycle detector:


f054b8a55e/crates/ty_python_semantic/src/types.rs (L2408-L2435)

This PR fixes that issue, and also adds a few `Debug` implementations to
our cycle detectors, so that issues like this are easier to debug in the
future.

I'm adding the `internal` label, as this fixes a bug that hasn't yet
appeared in any released version of ty, so it doesn't deserve its own
changelog entry.

## Test Plan

`QUICKCHECK_TESTS=1000000 cargo test --release -p ty_python_semantic --
--ignored types::property_tests::stable` now once again passes on `main`

I considered adding new mdtests as well, but the examples that the
property tests were throwing at me all seemed _quite_ obscure and
somewhat unlikely to occur in the real world. I don't think it's worth
it.
2025-10-08 22:28:56 +01:00
Alex Waygood f054b8a55e
[ty] Improve assignability/subtyping between two protocol types (#20368) 2025-10-08 18:37:30 +00:00
Alex Waygood b9c84add07
[ty] Disambiguate classes that live in different modules but have the same fully qualified names (#20756)
## Summary

Even disambiguating classes using their fully qualified names is not
enough for some diagnostics. We've seen real-world examples in the
ecosystem (and https://github.com/astral-sh/ruff/pull/20368 introduces
some more!) where two types can be different, but can still have the
same fully qualified name. In these cases, our disambiguation machinery
needs to print the file path and line number of the class in order to
disambiguate classes with similar names in our diagnostics.

Helps with https://github.com/astral-sh/ty/issues/1306

## Test Plan

Mdtests
2025-10-08 18:27:40 +01:00
David Peter 150ea92d03
[ty] Add tests for instance attributes in class hierarchies (#20767)
## Summary

This adds a couple of new test cases related to
https://github.com/astral-sh/ty/issues/1067 and beyond that. For now,
they are just documenting the current (problematic) behavior. Since the
topic has some subtleties, I'd like to merge this prior to the actual
bugfix(es) in order to evaluate the changes in an easier way.
2025-10-08 17:46:47 +02:00
David Peter 697998f836
[ty] Do not re-export `ide_support` attributes from `types` (#20769)
## Summary

The `types` module currently re-exports a lot of functions and data
types from `types::ide_support`. One of these is called `Member`, a name
that is overloaded several times already. And I'd like to add one more
`Member` struct soon. Making the whole `ide_support` module public seems
cleaner to me, anyway.

## Test Plan

Pure refactoring.
2025-10-08 17:45:28 +02:00
Andrew Gallant 3771f1567c [ty] Add an evaluation for completions
This is still early days, but I hope the framework introduced here makes
it very easy to add new truth data. Truth data should be seen as a form
of regression test for non-ideal ranking of completion suggestions.

I think it would help to read `crates/ty_completion_eval/README.md`
first to get an idea of what you're reviewing.
2025-10-08 08:44:21 -04:00
David Peter 6b94e620fe
[ty] Fix accidental Liskov violation in protocol tests (#20763)
## Summary

We have the following test in `protocols.md`:
```py
class HasX(Protocol):
    x: int

# […]

class Foo:
    x: int

# […]

class FooBool(Foo):
    x: bool

static_assert(not is_subtype_of(FooBool, HasX))
static_assert(not is_assignable_to(FooBool, HasX))
```

If `Foo` was indeed intended to be a base class of `FooBool`, then `x:
bool` should be reported as a Liskov violation. And then it's a matter
of definition whether or not these assertions should hold true or not
(should the incorrect override take precedence or not?). So it looks to
me like this is just an oversight, probably a copy-paste error from
another test right before it, where `FooSub` is indeed intended to be a
subclass of `Foo`.

I am fixing this because this test started to fail on a branch of mine
that changes how attribute lookup in inheritance chains works.
2025-10-08 14:04:37 +02:00
Mark Z. Ding f95eb90951
[ty] Truncate type display for long unions in some situations (#20730)
## Summary

Fixes [astral-sh/ty#1307](https://github.com/astral-sh/ty/issues/1307)

Unions with length <= 5 are unaffected to minimize test churn
Unions with length > 5 will only display the first 3 elements + "...
omitted x union elements"
Here "length" is defined as the number of elements after condensation to
literals

Edit: we no longer truncate in revel case. 
Before:

> info: Attempted to call union type `(def f1() -> int) | (def f2(name:
str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) ->
int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) |
(Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`

After:

> info: Attempted to call union type `(def f1() -> int) | (def f2(name:
str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union
elements`

The below comparisons are outdated, but left here as a reference.

Before:
```reveal_type(x)  # revealed: Literal[1, 2] | A | B | C | D | E | F | G```
```reveal_type(x) # revealed: Result1A | Result1B | Result2A | Result2B
| Result3 | Result4```
After:
```reveal_type(x)  # revealed: Literal[1, 2] | A | B | ... omitted 5 union elements```
```reveal_type(x) # revealed: Result1A | Result1B | Result2A | ...
omitted 3 union elements```

This formatting is consistent with
`crates/ty_python_semantic/src/types/call/bind.rs` line 2992

## Test Plan

Cosmetic only, covered and verified by changes in mdtest
2025-10-08 11:21:26 +01:00
David Peter 1f1542db51
[ty] Use 3.14 as the default version (#20759)
## Summary

Bump the latest supported Python version of ty to 3.14 and updates some
references from 3.13 to 3.14.

This also fixes a bug with `dataclasses.field` on 3.14 (which adds a new
keyword-only parameter to that function, breaking our previously naive
matching on the parameter structure of that function).

## Test Plan

A `ty check` on a file with template strings (without any further
configuration) doesn't raise errors anymore.
2025-10-08 11:38:47 +02:00
Takayuki Maeda abbbe8f3af
[`ruff`] Use `DiagnosticTag` for more pyupgrade rules (#20734) 2025-10-08 06:52:43 +02:00
Carl Meyer 5d3a35e071
[ty] fix implicit Self on generic class with typevar default (#20754)
## Summary

Typevar attributes (bound/constraints/default) can be either lazily
evaluated or eagerly evaluated. Currently they are lazily evaluated for
PEP 695 typevars, and eager for legacy and synthetic typevars.
https://github.com/astral-sh/ruff/pull/20598 will make them lazy also
for legacy typevars, and the ecosystem report on that PR surfaced the
issue fixed here (because legacy typevars are much more common in the
ecosystem than PEP 695 typevars.)

Applying a transform to a typevar (normalization, materialization, or
mark-inferable) will reify all lazy attributes and create a new typevar
with eager attributes. In terms of Salsa identity, this transformed
typevar will be considered different from the original typevar, whether
or not the attributes were actually transformed.

In general, this is not a problem, since all typevars in a given generic
context will be transformed, or not, together.

The exception to this was implicit-self vs explicit Self annotations.
The typevar we created for implicit self was created initially using
inferable typevars, whereas an explicit Self annotation is initially
non-inferable, then transformed via mark-inferable when accessed as part
of a function signature. If the containing class (which becomes the
upper bound of `Self`) is generic, and has e.g. a lazily-evaluated
default, then the explicit-Self annotation will reify that default in
the upper bound, and the implicit-self would not, leading them to be
treated as different typevars, and causing us to fail to solve a call to
a method such as `def method(self) -> Self` correctly.

The fix here is to treat implicit-self more like explicit-Self,
initially creating it as non-inferable and then using the mark-inferable
transform on it. This is less efficient, but restores the invariant that
all typevars in a given generic context are transformed together, or
not, fixing the bug.

In the improved-constraint-solver work, the separation of typevars into
"inferable" and "non-inferable" is expected to disappear, along with the
mark-inferable transform, which would render both this bug and the fix
moot. So this fix is really just temporary until that lands.

There is a performance regression, but not a huge one: 1-2% on most
projects, 5% on one outlier. This seems acceptable, given that it should
be fully recovered by removing the mark-inferable transform.

## Test Plan

Added mdtests that failed before this change.
2025-10-08 01:38:24 +00:00
Alex Waygood ff386b4797
[ty] Improve diagnostics for bad `@overload` definitions (#20745) 2025-10-07 21:52:57 +00:00
Dan Parizher 1bf4969c96
[`ruff`] Suppress diagnostic for f-string interpolations with debug text (`RUF010`) (#20525)
## Summary

Fixes #20519
2025-10-07 16:57:59 -04:00
liam 2be73e9afb
[`flake8-bugbear`] Mark `B905` and `B912` fixes as unsafe (#20695)
Resolves https://github.com/astral-sh/ruff/issues/20694

This PR updates the `zip_without_explicit_strict` and
`map_without_explicit_strict` rules so their fixes are always marked
unsafe, following Brent's guidance that adding `strict=False` can
silently preserve buggy behaviour when inputs differ. The fix safety
docs now spell out that reasoning, the applicability drops to `Unsafe`,
and the snapshots were refreshed so Ruff clearly warns users before
applying the edit.
2025-10-07 16:55:56 -04:00
Amethyst Reese beea8cdfec
Bump 0.14.0 (#20751) 2025-10-07 11:05:47 -07:00
Douglas Creager 416e956fe0
[ty] Infer better specializations of unions with `None` (etc) (#20749)
This PR adds a specialization inference special case that lets us handle
the following examples better:

```py
def f[T](t: T | None) -> T: ...
def g[T](t: T | int | None) -> T | int: ...

def _(x: str | None):
    reveal_type(f(x))  # revealed: str (previously str | None)

def _(y: str | int | None):
    reveal_type(g(x))  # revealed: str | int (previously str | int | None)
```

We already have a special case for when the formal is a union where one
element is a typevar, but it maps the entire actual type to the typevar
(as you can see in the "previously" results above).

The new special case kicks in when the actual is also a union. Now, we
filter out any actual union elements that are already subtypes of the
formal, and only bind whatever types remain to the typevar. (The `|
None` pattern appears quite often in the ecosystem results, but it's
more general and works with any number of non-typevar union elements.)

The new constraint solver should handle this case as well, but it's
worth adding this heuristic now with the old solver because it
eliminates some false positives from the ecosystem report, and makes the
ecosystem report less noisy on the other constraint solver PRs.
2025-10-07 13:33:42 -04:00
Brent Westbrook 88c0ce3e38
Update default and latest Python versions for 3.14 (#20725)
Summary
--

Closes #19467 and also removes the warning about using Python 3.14
without
preview enabled.

I also bumped `PythonVersion::default` to 3.9 because it reaches EOL
this month,
but we could also defer that for now if we wanted.

The first three commits are related to the `latest` bump to 3.14; the
fourth commit
bumps the default to 3.10.

Note that this PR also bumps the default Python version for ty to 3.10
because
there was a test asserting that it stays in sync with
`ast::PythonVersion`.

Test Plan
--

Existing tests

I spot-checked the ecosystem report, and I believe these are all
expected. Inbits doesn't specify a target Python version, so I guess
we're applying the default. UP007, UP035, and UP045 all use the new
default value to emit new diagnostics.
2025-10-07 12:23:11 -04:00
Amethyst Reese 8fb29eafb8
[ruff] improve handling of intermixed comments inside from-imports (#20561)
Resolves a crash when attempting to format code like:

```
from x import (a as # whatever
b)
```

Reworks the way comments are associated with nodes when parsing modules,
so that all possible comment positions can be retained and reproduced during
formatting.

Overall follows Black's formatting style for multi-line import statements.

Fixes issue #19138
2025-10-07 08:14:09 -07:00
David Peter 23ebfe7777
[ty] Fix tiny mistake in protocol tests (#20743) 2025-10-07 11:58:35 +00:00
David Peter f90d6466e0
[ty] Make `infer_method_information` less confusing (#20740)
## Summary

`infer_method_information` was previously calling
`ClassLiteral::to_class_type`, which uses the default-specialization of
a generic class. This specialized `ClassType` was later only used if the
class was non-generic, making the specialization irrelevant. The
implementation was still a bit confusing, so this PR proposes a way to
avoid turning the class literal into a `ClassType`.
2025-10-07 10:12:23 +00:00
Micha Reiser 15af4c0a34
Move --show-settings snapshots to separate files (#20741) 2025-10-07 11:42:38 +02:00
Renkai Ge 76f8e5b755
Refactor Rust lint test structure to use RuffTestFixture (#20689)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-07 11:28:00 +02:00
chiri b66a3e7451
[`refurb`] Add fixes for `FURB101`, `FURB103` (#20520)
## Summary

Part of `PTH-*` fixes:
https://github.com/astral-sh/ruff/pull/19404#issuecomment-3089639686

## Test Plan
`cargo nextest run furb`
2025-10-06 18:09:07 -04:00
Alex Waygood 70f51e9648
[ty] Print `display` of types when a property test fails (#20720) 2025-10-06 14:44:24 +01:00
Dan Parizher 9a29f7a339
[`isort`] Fix inserting required imports before future imports (`I002`) (#20676)
## Summary

Fixes #20674

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-10-06 13:40:36 +00:00
Nikolas Hearp 1c5666ce5d
[`RUF051`] Ignore if `else`/`elif` block is present (#20705)
## Summary

Fixes #20700

`else` and `elif` blocks could previously be deleted when applying a fix
for this rule. If an `else` or `elif` branch is detected the rule will
not trigger. So now the rule will only flag if it is safe.
2025-10-06 08:02:27 -05:00
Alex Waygood 42b297bf44
[ty] Improve documentation for `extra-paths` and `python` config settings (#20717)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-10-06 12:20:00 +00:00
Alex Waygood 80b337669f
[ty] Add `--venv` as an alias to `--python` (#20718) 2025-10-06 13:03:05 +01:00
Alex Waygood 1ce57edf33
[ty] Enforce that `typing_extensions` must come from a stdlib search path (#20715) 2025-10-06 11:43:34 +00:00
Ibraheem Ahmed 2ce3aba458
[ty] Use annotated parameters as type context (#20635)
## Summary

Use the type annotation of function parameters as bidirectional type
context when inferring the argument expression. For example, the
following example now type-checks:

```py
class TD(TypedDict):
    x: int

def f(_: TD): ...

f({ "x": 1 })
```

Part of https://github.com/astral-sh/ty/issues/168.
2025-10-03 17:14:51 -04:00
Douglas Creager b83ac5e234
[ty] Clean up inherited generic contexts (#20647)
We add an `inherited_generic_context` to the constructors of a generic
class. That lets us infer specializations of the class when invoking the
constructor. The constructor might itself be generic, in which case we
have to merge the list of typevars that we are willing to infer in the
constructor call.

Before we did that by tracking the two (and their specializations)
separately, with distinct `Option` fields/parameters. This PR updates
our call binding logic such that any given function call has _one_
optional generic context that we're willing to infer a specialization
for. If needed, we use the existing `GenericContext::merge` method to
create a new combined generic context for when the class and constructor
are both generic. This simplifies the call binding code considerably,
and is no more complex in the constructor call logic.

We also have a heuristic that we will promote any literals in the
specialized types of a generic class, but we don't promote literals in
the specialized types of the function itself. To handle this, we now
track this `should_promote_literals` property within `GenericContext`.
And moreover, we track this separately for each typevar, instead of a
single property for the generic context as a whole, so that we can
correctly merge the generic context of a constructor method (where the
option should be `false`) with the inherited generic context of its
containing class (where the option should be `true`).

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-10-03 13:55:43 -04:00
Alex Waygood c91b457044
[ty] Introduce `TypeRelation::Redundancy` (#20602)
## Summary

The union `T | U` can be validly simplified to `U` iff:
1. `T` is a subtype of `U` OR
2. `T` is equivalent to `U` OR
3. `U` is a union and contains a type that is equivalent to `T` OR
4. `T` is an intersection and contains a type that is equivalent to `U`

(In practice, the only situation in which 2, 3 or 4 would be true when
(1) was not true would be if `T` or `U` is a dynamic type.)

Currently we achieve these simplifications in the union builder by doing
something along the lines of `t.is_subtype_of(db, u) ||
t.is_equivalent_to_(db, u) ||
t.into_intersection().is_some_and(|intersection|
intersection.positive(db).contains(&u)) ||
u.into_union().is_some_and(|union| union.elements(db).contains(&t))`.
But this is both slow and misses some cases (it doesn't simplify the
union `Any | (Unknown & ~None)` to `Any`, for example). We can improve
the consistency and performance of our union simplifications by adding a
third type relation that sits in between `TypeRelation::Subtyping` and
`TypeRelation::Assignability`: `TypeRelation::UnionSimplification`.

This change leads to simpler, more user-friendly types due to the more
consistent simplification. It also lead to a pretty huge performance
improvement!

## Test Plan

Existing tests, plus some new ones.
2025-10-03 18:35:30 +01:00
Igor Drokin 673167a565
[`flake8-bugbear`] Include certain guaranteed-mutable expressions: tuples, generators, and assignment expressions (`B006`) (#20024)
## Summary
Resolves #20004

The implementation now supports guaranteed-mutable expressions in the
following cases:
- Tuple literals with mutable elements (supporting deep nesting)
- Generator expressions
- Named expressions (walrus operator) containing mutable components

Preserves original formatting for assignment value:

```python
# Test case
def f5(x=([1, ])):
    print(x)
```
```python
# Fix before
def f5(x=(None)):
    if x is None:
        x = [1]
    print(x)
```
```python
# Fix after 
def f5(x=None):
    if x is None:
        x = ([1, ])
    print(x)
```
The expansion of detected expressions and the new fixes gated behind
previews.

## Test Plan
- Added B006_9.py with a bunch of test cases
- Generated snapshots

---------

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-10-03 09:29:36 -05:00
Dan Parizher 805d179dc0
[`flake8-comprehensions`] Clarify fix safety documentation (`C413`) (#20640)
## Summary

Fixes #20632
2025-10-03 09:23:57 -05:00
Daniel Kongsgaard f73ead11cb
[ty] improve base conda distinction from child conda (#20675)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

#19990 didn't completely fix the base vs. child conda environment
distinction, since it detected slightly different behavior than what I
usually see in conda. E.g., I see something like the following:
```
(didn't yet activate conda, but base is active)
➜ printenv | grep CONDA
CONDA_PYTHON_EXE=/opt/anaconda3/bin/python
CONDA_PREFIX=/opt/anaconda3
CONDA_DEFAULT_ENV=base
CONDA_EXE=/opt/anaconda3/bin/conda
CONDA_SHLVL=1
CONDA_PROMPT_MODIFIER=(base)

(activating conda)
➜ conda activate test

(test is an active conda environment)
❯ printenv | grep CONDA
CONDA_PREFIX=/opt/anaconda3/envs/test
CONDA_PYTHON_EXE=/opt/anaconda3/bin/python
CONDA_SHLVL=2
CONDA_PREFIX_1=/opt/anaconda3
CONDA_DEFAULT_ENV=test
CONDA_PROMPT_MODIFIER=(test)
CONDA_EXE=/opt/anaconda3/bin/conda
```

But the current behavior looks for `CONDA_DEFAULT_ENV =
basename(CONDA_PREFIX)` for the base environment instead of the child
environment, where we actually see this equality.

This pull request fixes that and updates the tests correspondingly.

## Test Plan

I updated the existing tests with the new behavior. Let me know if you
want more tests. Note: It shouldn't be necessary to test for the case
where we have `conda/envs/base`, since one should not be able to create
such an environment (one with the name of `CONDA_DEFAULT_ENV`).

---------

Co-authored-by: Aria Desires <aria.desires@gmail.com>
2025-10-03 13:56:06 +00:00
liam ebfb33c30b
[`ruff`] Extend FA102 with listed PEP 585-compatible APIs (#20659)
Resolves https://github.com/astral-sh/ruff/issues/20512

This PR expands FA102’s preview coverage to flag every
PEP 585-compatible API that breaks without from `from __future__ import
annotations`, including `collections.abc`. The rule now treats asyncio
futures, pathlib-style queues, weakref containers, shelve proxies, and
the full `collections.abc` family as generics once preview mode is
enabled.

Stable behavior is unchanged; the broader matching runs behind
`is_future_required_preview_generics_enabled`, letting us vet the new
diagnostics before marking them as stable.

I've also added a snapshot test that covers all of the newly supported
types.

Check out
https://docs.python.org/3/library/stdtypes.html#standard-generic-classes
for a list of commonly used PEP 585-compatible APIs.
2025-10-03 09:45:32 -04:00
Takayuki Maeda 7d7237c660
[`ruff`] Handle argfile expansion errors gracefully (#20691)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

Fixes #20655

- Guard `argfile::expand_args_from` with contextual error handling so
missing @file arguments surface a friendly failure instead of panicking.
- Extract existing stderr reporting into `report_error` for reuse on
both CLI parsing failures and runtime errors.

## Test Plan

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

Add a regression test to integration_test.rs.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-10-03 13:36:07 +00:00
Takayuki Maeda 542f080035
[`flynt`] Fix f-string quoting for mixed quote joiners (`FLY002`) (#20662)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

Fixes #19837

Track quote usage across the joiner and parts to choose a safe f-string
quote or skip the fix when both appear.

## Test Plan

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

Add regression coverage to FLY002.py
2025-10-03 09:15:57 -04:00
Andrew Gallant 92eee816ed [ty] Fix file root matching for `/`
Previously, we would always add `/{*filepath}` as our wildcard to match
descendant paths. But when the root is just `/` (as it can be in tests,
weird environments or in the ty playground), this causes a double `/`
and inhibits most descendant matches.

The regression test added in this commit fails without this fix.
Specifically, it panics because it can't find a file root for
`/project`.

Fixes #1277
2025-10-03 08:18:03 -04:00
Andrew Gallant 00c0c567dc [ruff,ty] Enable tracing's `log` feature
This has the effect of emitting tracing events via `log`
whenever there isn't an active tracing subscriber present.

This makes it so `ty_wasm` logs tracing messages to the
JavaScript console automatically (via our use of `console_log`).
2025-10-03 08:18:03 -04:00
Dan Parizher f9688bd05c
[`flake8-annotations`] Fix return type annotations to handle shadowed builtin symbols (`ANN201`, `ANN202`, `ANN204`, `ANN205`, `ANN206`) (#20612)
## Summary

Fixes #20610

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-10-02 22:44:06 +00:00
Dylan 188c0dce29
Bump 0.13.3 (#20685) 2025-10-02 14:14:05 -05:00
Dhruv Manilawala 4e94b22815
[ty] Support single-starred argument for overload call (#20223)
## Summary

closes: https://github.com/astral-sh/ty/issues/247

This PR adds support for variadic arguments to overload call evaluation.

This basically boils down to making sure that the overloads are not
filtered out incorrectly during the step 5 in the overload call
evaluation algorithm. For context, the step 5 tries to filter out the
remaining overloads after finding an overload where the materialization
of argument types are assignable to the parameter types.

The issue with the previous implementation was that it wouldn't unpack
the variadic argument and wouldn't consider the many-to-one (multiple
arguments mapping to a single variadic parameter) correctly. This PR
fixes that.

## Test Plan

Update existing test cases and resolve the TODOs.
2025-10-02 10:41:56 -04:00
Alex Waygood 0639da2552
[ty] `~T` should never be assignable to `T` (#20606)
## Summary

Currently we do not emit an error on this code:

```py
from ty_extensions import Not

def f[T](x: T, y: Not[T]) -> T:
    x = y
    return x
```

But we should do! `~T` should never be assignable to `T`.

This fixes a small regression introduced in
14fe1228e7 (diff-8049ab5af787dba29daa389bbe2b691560c15461ef536f122b1beab112a4b48aR1443-R1446),
where a branch that previously returned `false` was replaced with a
branch that returns `C::always_satisfiable` -- the opposite of what it
used to be! The regression occurred because we didn't have any tests for
this -- so I added some tests in this PR that fail on `main`. I only
spotted the problem because I was going through the code of
`has_relation_to_impl` with a fine toothcomb for
https://github.com/astral-sh/ruff/pull/20602 😄
2025-10-02 07:52:47 +01:00
Dan Parizher caf48f4bfc
[`pylint`] Clarify fix safety to include left-hand hashability (`PLR6201`) (#20518)
## Summary

Fixes #20510
2025-10-01 13:58:24 -04:00
David Peter 71d711257a
[ty] No union with `Unknown` for module-global symbols (#20664)
## Summary

Quoting from the newly added comment:

Module-level globals can be mutated externally. A `MY_CONSTANT = 1`
global might be changed to `"some string"` from code outside of the
module that we're looking at, and so from a gradual-guarantee
perspective, it makes sense to infer a type of `Literal[1] | Unknown`
for global symbols. This allows the code that does the mutation to type
check correctly, and for code that uses the global, it accurately
reflects the lack of knowledge about the type.

External modifications (or modifications through `global` statements)
that would require a wider type are relatively rare. From a practical
perspective, we can therefore achieve a better user experience by
trusting the inferred type. Users who need the external mutation to work
can always annotate the global with the wider type. And everyone else
benefits from more precise type inference.

I initially implemented this by applying literal promotion to the type
of the unannotated module globals (as suggested in
https://github.com/astral-sh/ty/issues/1069), but the ecosystem impact
showed a lot of problems (https://github.com/astral-sh/ruff/pull/20643).
I fixed/patched some of these problems, but this PR seems like a good
first step, and it seems sensible to apply the literal promotion change
in a second step that can be evaluated separately.

closes https://github.com/astral-sh/ty/issues/1069

## Ecosystem impact

This seems like an (unexpectedly large) net positive with 650 fewer
diagnostics overall.. even though this change will certainly catch more
true positives.

* There are 666 removed `type-assertion-failure` diagnostics, where we
were previously used the correct type already, but removing the
`Unknown` now leads to an "exact" match.
* 1464 of the 1805 total new diagnostics are `unresolved-attribute`
errors, most (1365) of which were previously
`possibly-missing-attribute` errors. So they could also be counted as
"changed" diagnostics.
* For code that uses constants like
  ```py
  IS_PYTHON_AT_LEAST_3_10 = sys.version_info >= (3, 10)
  ```
where we would have previously inferred a type of `Literal[True/False] |
Unknown`, removing the `Unknown` now allows us to do reachability
analysis on branches that use these constants, and so we get a lot of
favorable ecosystem changes because of that.
* There is code like the following, where we previously emitted
`conflicting-argument-forms` diagnostics on calls to the aliased
`assert_type`, because its type was `Unknown | def …` (and the call to
`Unknown` "used" the type form argument in a non type-form way):
  ```py
  if sys.version_info >= (3, 11):
      import typing
  
      assert_type = typing.assert_type
  else:
      import typing_extensions
  
      assert_type = typing_extensions.assert_type
  ```
* ~100 new `invalid-argument-type` false positives, due to missing
`**kwargs` support (https://github.com/astral-sh/ty/issues/247)

## Typing conformance

```diff
+protocols_modules.py:25:1: error[invalid-assignment] Object of type `<module '_protocols_modules1'>` is not assignable to `Options1`
```

This diagnostic should apparently not be there, but it looks like we
also fail other tests in that file, so it seems to be a limitation that
was previously hidden by `Unknown` somehow.

## Test Plan

Updated tests and relatively thorough ecosystem analysis.
2025-10-01 16:40:30 +02:00
David Peter 56d630e303
[ty] Enums: allow multiple aliases to point to the same member (#20669)
## Summary

closes https://github.com/astral-sh/ty/issues/1293

## Test Plan

Regression test
2025-10-01 15:51:53 +02:00
David Peter 963bc8c228
[ty] Reformulation of public symbol inference test suite (#20667)
## Summary

Reformulation of the public symbol type inference test suite to use
class scopes instead of module scopes. This is in preparation for an
upcoming change to module-global scopes (#20664).

## Test Plan

Updated tests
2025-10-01 14:26:17 +02:00
Alex Waygood 20eb5b5b35
[ty] Fix subtyping of invariant generics specialized with `Any` (#20650) 2025-10-01 10:05:54 +00:00
github-actions[bot] d9473a2fcf
[ty] Sync vendored typeshed stubs (#20658)
---------

Co-authored-by: typeshedbot <>
Co-authored-by: David Peter <mail@david-peter.de>
2025-10-01 10:11:48 +02:00
Douglas Creager a422716267
[ty] Fix flaky constraint set rendering (#20653)
This doesn't seem to be flaky in the sense of tests failing
non-deterministically, but they are flaky in the sense of unrelated
changes causing testing failures from the clauses of a constraint set
being rendered in different orders. This flakiness is because we're
using Salsa IDs to determine the order in which typevars appear in a
constraint set BDD, and those IDs are assigned non-deterministically.

The fix is ham-fisted but effective: sort the constraints in each
clause, and the clauses in each set, as part of the rendering process.
Constraint sets are only rendered in our test cases, so we don't need to
over-optimize this.
2025-10-01 09:14:35 +02:00
Igor Drokin 11dae2cf1b
[`pyupgrade`] Prevent infinite loop with `I002` and `UP026` (#20634)
## Summary
Closes #20601

Do not treat imports as unused for the rule [unnecessary-builtin-import
(UP029)](https://docs.astral.sh/ruff/rules/unnecessary-builtin-import/)
if they are required by
`isort`([missing-required-import](https://docs.astral.sh/ruff/rules/missing-required-import/))

## Test Plan
- Added test case `i002_up029_conflict` to ensure there is no conflict

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
2025-09-30 17:11:34 -04:00
Wei Lee 7fee877c50
[`airflow`]: rename `AutoImport` as `Rename` (internal) (#20563)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

Since we are trying to import both `AutoImport` and `SourceModuleMoved`,
the previous naming was not as descriptive. Renaming it to `Rename`
better reflects the intention.

## Test Plan

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

no functionality change
2025-09-30 15:56:26 -04:00
Dan Parizher 7c87b31533
[`ruff`] Do not flag `%r` + `repr()` combinations (`RUF065`) (#20600)
## Summary

Fixes the first part of #20583
2025-09-30 15:49:50 -04:00
Brent Westbrook 2b1d3c60fa
Display diffs for `ruff format --check` and add support for different output formats (#20443)
## Summary

This PR uses the new `Diagnostic` type for rendering formatter
diagnostics. This allows the formatter to inherit all of the output
formats already implemented in the linter and ty. For example, here's
the new `full` output format, with the formatting diff displayed using
the same infrastructure as the linter:

<img width="592" height="364" alt="image"
src="https://github.com/user-attachments/assets/6d09817d-3f27-4960-aa8b-41ba47fb4dc0"
/>


<details><summary>Resolved TODOs</summary>
<p>

~~There are several limitiations/todos here still, especially around the
`OutputFormat` type~~:
- [x] A few literal `todo!`s for the remaining `OutputFormat`s without
matching `DiagnosticFormat`s
- [x] The default output format is `full` instead of something more
concise like the current output
- [x] Some of the output formats (namely JSON) have information that
doesn't make much sense for these diagnostics

The first of these is definitely resolved, and I think the other two are
as well, based on discussion on the design document. In brief, we're
okay inheriting the default `OutputFormat` and can separate the global
option into `lint.output-format` and `format.output-format` in the
future, if needed; and we're okay including redundant information in the
non-human-readable output formats.

My last major concern is with the performance of the new code, as
discussed in the `Benchmarks` section below.

A smaller question is whether we should use `Diagnostic`s for formatting
errors too. I think the answer to this is yes, in line with changes
we're making in the linter too. I still need to implement that here.

</p>
</details> 

<details><summary>Benchmarks</summary>
<p>


The values in the table are from a large benchmark on the CPython 3.10
code
base, which involves checking 2011 files, 1872 of which need to be
reformatted.
`stable` corresponds to the same code used on `main`, while
`preview-full` and
`preview-concise` use the new `Diagnostic` code gated behind `--preview`
for the
`full` and `concise` output formats, respectively. `stable-diff` uses
the
`--diff` to compare the two diff rendering approaches. See the full
hyperfine
command below for more details. For a sense of scale, the `stable`
output format
produces 1873 lines on stdout, compared to 855,278 for `preview-full`
and
857,798 for `stable-diff`.

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |

|:------------------|--------------:|---------:|---------:|-------------:|
| `stable` | 201.2 ± 6.8 | 192.9 | 220.6 | 1.00 |
| `preview-full` | 9113.2 ± 31.2 | 9076.1 | 9152.0 | 45.29 ± 1.54 |
| `preview-concise` | 214.2 ± 1.4 | 212.0 | 217.6 | 1.06 ± 0.04 |
| `stable-diff` | 3308.6 ± 20.2 | 3278.6 | 3341.8 | 16.44 ± 0.56 |

In summary, the `preview-concise` diagnostics are ~6% slower than the
stable
output format, increasing the average runtime from 201.2 ms to 214.2 ms.
The
`full` preview diagnostics are much more expensive, taking over 9113.2
ms to
complete, which is ~3x more expensive even than the stable diffs
produced by the
`--diff` flag.

My main takeaways here are:
1. Rendering `Edit`s is much more expensive than rendering the diffs
from `--diff`
2. Constructing `Edit`s actually isn't too bad

### Constructing `Edit`s

I also took a closer look at `Edit` construction by modifying the code
and
repeating the `preview-concise` benchmark and found that the main issue
is
constructing a `SourceFile` for use in the `Edit` rendering. Commenting
out the
`Edit` construction itself has basically no effect:

| Command   |   Mean [ms] | Min [ms] | Max [ms] |    Relative |
|:----------|------------:|---------:|---------:|------------:|
| `stable`  | 197.5 ± 1.6 |    195.0 |    200.3 |        1.00 |
| `no-edit` | 208.9 ± 2.2 |    204.8 |    212.2 | 1.06 ± 0.01 |

However, also omitting the source text from the `SourceFile`
construction
resolves the slowdown compared to `stable`. So it seems that copying the
full
source text into a `SourceFile` is the main cause of the slowdown for
non-`full`
diagnostics.

| Command          |   Mean [ms] | Min [ms] | Max [ms] |    Relative |
|:-----------------|------------:|---------:|---------:|------------:|
| `stable`         | 202.4 ± 2.9 |    197.6 |    207.9 |        1.00 |
| `no-source-text` | 202.7 ± 3.3 |    196.3 |    209.1 | 1.00 ± 0.02 |

### Rendering diffs

The main difference between `stable-diff` and `preview-full` seems to be
the diffing strategy we use from `similar`. Both versions use the same
algorithm, but in the existing
[`CodeDiff`](https://github.com/astral-sh/ruff/blob/main/crates/ruff_linter/src/source_kind.rs#L259)
rendering for the `--diff` flag, we only do line-level diffing, whereas
for `Diagnostic`s we use `TextDiff::iter_inline_changes` to highlight
word-level changes too. Skipping the word diff for `Diagnostic`s closes
most of the gap:

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `stable-diff` | 3.323 ± 0.015 | 3.297 | 3.341 | 1.00 |
| `preview-full` | 3.654 ± 0.019 | 3.618 | 3.682 | 1.10 ± 0.01 |

(In some repeated runs, I've seen as small as a ~5% difference, down
from 10% in the table)

This doesn't actually change any of our snapshots, but it would
obviously change the rendered result in a terminal since we wouldn't
highlight the specific words that changed within a line.

Another much smaller change that we can try is removing the deadline
from the `iter_inline_changes` call. It looks like there's a fair amount
of overhead from the default 500 ms deadline for computing these, and
using `iter_inline_changes(op, None)` (`None` for the optional deadline
argument) improves the runtime quite a bit:

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `stable-diff` | 3.322 ± 0.013 | 3.298 | 3.341 | 1.00 |
| `preview-full` | 5.296 ± 0.030 | 5.251 | 5.366 | 1.59 ± 0.01 |

<hr>

<details><summary>hyperfine command</summary>

```shell
cargo build --release --bin ruff && hyperfine --ignore-failure --warmup 10 --export-markdown /tmp/table.md \
  -n stable -n preview-full -n preview-concise -n stable-diff \
  "./target/release/ruff format --check ./crates/ruff_linter/resources/test/cpython/ --no-cache" \
  "./target/release/ruff format --check ./crates/ruff_linter/resources/test/cpython/ --no-cache --preview --output-format=full" \
  "./target/release/ruff format --check ./crates/ruff_linter/resources/test/cpython/ --no-cache --preview --output-format=concise" \
  "./target/release/ruff format --check ./crates/ruff_linter/resources/test/cpython/ --no-cache --diff"
```

</details>

</p>
</details> 

## Test Plan

Some new CLI tests and manual testing
2025-09-30 12:00:51 -04:00
David Peter b483d3b0b9
[ty] Literal promotion refactor (#20646)
## Summary

Not sure if this was the original intention, but it looks to me like the
previous `Type::literal_promotion_type` was more of an implementation
detail for the actual operation of promoting all literals in a
possibly-nested position of a type.

This is not a pure refactor, as I'm technically changing the behavior
for that protocols diagnostic message suggestion.

## Test Plan

New Markdown test
2025-09-30 14:22:36 +02:00
David Peter 130a794c2b
[ty] Add tests for nested generic functions (#20631)
## Summary

Add two simple tests that we recently discussed with @dcreager. They
demonstrate that the `TypeMapping::MarkTypeVarsInferable` operation
really does need to keep track of the binding context.

## Test Plan

Made sure that those tests fail if we create
`TypeMapping::MarkTypeVarsInferable(None)`s everywhere.
2025-09-30 08:44:18 +02:00
Dan Parizher 1c08f71a00
[`cli`] Add conflict between `--add-noqa` and `--diff` options (#20642) 2025-09-30 08:34:18 +02:00
Alex Waygood 8664842d00
[ty] Ensure first-party search paths always appear in a sensible order (#20629)
This PR ensures that we always put `./src` before `.` in our list of
first-party search paths. This better emulates the fact that at runtime,
the module name of a file `src/foo.py` would almost certainly be `foo`
rather than `src.foo`.

I wondered if fixing this might fix
https://github.com/astral-sh/ruff/pull/20603#issuecomment-3345317444. It
seems like that's not the case, but it also seems like it leads to
better diagnostics because we report much more intuitive module names to
the user in our error messages -- so, it's probably a good change
anyway.
2025-09-29 21:19:13 +01:00
David Peter 0092794302
[ty] Use `typing.Self` for the first parameter of instance methods (#20517)
## Summary

Modify the (external) signature of instance methods such that the first
parameter uses `Self` unless it is explicitly annotated. This allows us
to correctly type-check more code, and allows us to infer correct return
types for many functions that return `Self`. For example:

```py
from pathlib import Path
from datetime import datetime, timedelta

reveal_type(Path(".config") / ".ty")  # now Path, previously Unknown

def _(dt: datetime, delta: timedelta):
    reveal_type(dt - delta)  # now datetime, previously Unknown
```

part of https://github.com/astral-sh/ty/issues/159

## Performance

I ran benchmarks locally on `attrs`, `freqtrade` and `colour`, the
projects with the largest regressions on CodSpeed. I see much smaller
effects locally, but can definitely reproduce the regression on `attrs`.
From looking at the profiling results (on Codspeed), it seems that we
simply do more type inference work, which seems plausible, given that we
now understand much more return types (of many stdlib functions). In
particular, whenever a function uses an implicit `self` and returns
`Self` (without mentioning `Self` anywhere else in its signature), we
will now infer the correct type, whereas we would previously return
`Unknown`. This also means that we need to invoke the generics solver in
more cases. Comparing half a million lines of log output on attrs, I can
see that we do 5% more "work" (number of lines in the log), and have a
lot more `apply_specialization` events (7108 vs 4304). On freqtrade, I
see similar numbers for `apply_specialization` (11360 vs 5138 calls).
Given these results, I'm not sure if it's generally worth doing more
performance work, especially since none of the code modifications
themselves seem to be likely candidates for regressions.

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/attrs` | 92.6 ± 3.6 | 85.9 |
102.6 | 1.00 |
| `./ty_self check /home/shark/ecosystem/attrs` | 101.7 ± 3.5 | 96.9 |
113.8 | 1.10 ± 0.06 |

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/freqtrade` | 599.0 ± 20.2 |
568.2 | 627.5 | 1.00 |
| `./ty_self check /home/shark/ecosystem/freqtrade` | 607.9 ± 11.5 |
594.9 | 626.4 | 1.01 ± 0.04 |

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./ty_main check /home/shark/ecosystem/colour` | 423.9 ± 17.9 | 394.6
| 447.4 | 1.00 |
| `./ty_self check /home/shark/ecosystem/colour` | 426.9 ± 24.9 | 373.8
| 456.6 | 1.01 ± 0.07 |

## Test Plan

New Markdown tests

## Ecosystem report

* apprise: ~300 new diagnostics related to problematic stubs in apprise
😩
* attrs: a new true positive, since [this
function](4e2c89c823/tests/test_make.py (L2135))
is missing a `@staticmethod`?
* Some legitimate true positives
* sympy: lots of new `invalid-operator` false positives in [matrix
multiplication](cf9f4b6805/sympy/matrices/matrixbase.py (L3267-L3269))
due to our limited understanding of [generic `Callable[[Callable[[T1,
T2], T3]], Callable[[T1, T2], T3]]` "identity"
types](cf9f4b6805/sympy/core/decorators.py (L83-L84))
of decorators. This is not related to type-of-self.

## Typing conformance results

The changes are all correct, except for
```diff
+generics_self_usage.py:50:5: error[invalid-assignment] Object of type `def foo(self) -> int` is not assignable to `(typing.Self, /) -> int`
```
which is related to an assignability problem involving type variables on
both sides:
```py
class CallableAttribute:
    def foo(self) -> int:
        return 0

    bar: Callable[[Self], int] = foo  # <- we currently error on this assignment
```

---------

Co-authored-by: Shaygan Hooshyari <sh.hooshyari@gmail.com>
2025-09-29 21:08:08 +02:00
Alex Waygood 1d3e4a9153
[ty] Remove unnecessary `parsed_module()` calls (#20630) 2025-09-29 16:05:12 +01:00
Brent Westbrook 00c8851ef8
Remove `TextEmitter` (#20595)
## Summary

Addresses
https://github.com/astral-sh/ruff/pull/20443#discussion_r2381237640 by
factoring out the `match` on the ruff output format in a way that should
be reusable by the formatter.

I didn't think this was going to work at first, but the fact that the
config holds options that apply only to certain output formats works in
our favor here. We can set up a single config for all of the output
formats and then use `try_from` to convert the `OutputFormat` to a
`DiagnosticFormat` later.

## Test Plan

Existing tests, plus a few new ones to make sure relocating the
`SHOW_FIX_SUMMARY` rendering worked, that was untested before. I deleted
a bunch of test code along with the `text` module, but I believe all of
it is now well-covered by the `full` and `concise` tests in `ruff_db`.

I also merged this branch into
https://github.com/astral-sh/ruff/pull/20443 locally and made sure that
the API actually helps. `render_diagnostics` dropped in perfectly and
passed the tests there too.
2025-09-29 08:46:25 -04:00
Alex Waygood 1cf19732b9
[ty] Use fully qualified names to distinguish ambiguous protocols in diagnostics (#20627) 2025-09-29 12:02:07 +00:00
Douglas Creager cf2b083668
[ty] Apply type mappings to functions eagerly (#20596)
`TypeMapping` is no longer cow-shaped.

Before, `TypeMapping` defined a `to_owned` method, which would make an
owned copy of the type mapping. This let us apply type mappings to
function literals lazily. The primary part of a function that you have
to apply the type mapping to is its signature. The hypothesis was that
doing this lazily would prevent us from constructing the signature of a
function just to apply a type mapping; if you never ended up needed the
updated function signature, that would be extraneous work.

But looking at the CI for this PR, it looks like that hypothesis is
wrong! And this definitely cleans up the code quite a bit. It also means
that over time we can consider replacing all of these `TypeMapping` enum
variants with separate `TypeTransformer` impls.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-09-29 13:24:40 +02:00
Alex Waygood 3f640dacd4
[ty] Improve disambiguation of class names in diagnostics (#20603) 2025-09-29 11:43:11 +01:00
Takayuki Maeda 666f53331f
[`ruff`] Fix minor typos in doc comments (#20623) 2025-09-29 08:56:23 +02:00
Rahul Sahoo 4e33501115
Fixed documentation for try_consider_else (#20587)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

This PR addresses #20570 . In the example, the correct usage had a
bug/issue where in the except block after logging exception, None was
getting returned, which made the linters flag out the code. So adding an
empty raise solves the issue.

## Test Plan

Tested it by building the doc locally.
2025-09-27 13:50:51 +00:00
Alex Waygood 6b3c493cff
[ty] Use `Top` materializations for `TypeIs` special form (#20591) 2025-09-26 17:24:43 +00:00
Alex Waygood e4de179cdd
[ty] Simplify `Any | (Any & T)` to `Any` (#20593) 2025-09-26 17:00:10 +01:00
Dylan 57e1ff8294
[`pyflakes`] Handle some common submodule import situations for `unused-import` (`F401`) (#20200)
# Summary

The PR under review attempts to make progress towards the age-old
problem of submodule imports, specifically with regards to their
treatment by the rule [`unused-import`
(`F401`)](https://docs.astral.sh/ruff/rules/unused-import/).

Some related issues:
- https://github.com/astral-sh/ruff/issues/60
- https://github.com/astral-sh/ruff/issues/4656

Prior art:
- https://github.com/astral-sh/ruff/pull/13965
- https://github.com/astral-sh/ruff/pull/5010
- https://github.com/astral-sh/ruff/pull/5011
- https://github.com/astral-sh/ruff/pull/666

See the PR summary for a detailed description.
2025-09-26 08:22:26 -05:00
David Peter 3932f7c849
[ty] Fix subtyping for dynamic specializations (#20592)
## Summary

Fixes a bug observed by @AlexWaygood where `C[Any] <: C[object]` should
hold for a class that is covariant in its type parameter (and similar
subtyping relations involving dynamic types for other variance
configurations).

## Test Plan

New and updated Markdown tests
2025-09-26 15:05:03 +02:00
Alex Waygood 2af8c53110
[ty] Add more tests for subtyping/assignability between two protocol types (#20573) 2025-09-26 12:07:57 +01:00
Dan Parizher 0bae7e613d
Use `Annotation::tags` instead of hardcoded rule matching in ruff server (#20565)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-26 09:06:26 +02:00
Douglas Creager 02ebb2ee61
[ty] Change to BDD representation for constraint sets (#20533)
While working on #20093, I kept running into test failures due to
constraint sets not simplifying as much as they could, and therefore not
being easily testable against "always true" and "always false".

This PR updates our constraint set representation to use BDDs. Because
BDDs are reduced and ordered, they are canonical — equivalent boolean
formulas are represented by the same interned BDD node.

That said, there is a wrinkle, in that the "variables" that we use in
these BDDs — the individual constraints like `Lower ≤ T ≤ Upper` are not
always independent of each other.

As an example, given types `A ≤ B ≤ C ≤ D` and a typevar `T`, the
constraints `A ≤ T ≤ C` and `B ≤ T ≤ D` "overlap" — their intersection
is non-empty. So we should be able to simplify

```
(A ≤ T ≤ C) ∧ (B ≤ T ≤ D) == (B ≤ T ≤ C)
```

That's not a simplification that the BDD structure can perform itself,
since those three constraints are modeled as separate BDD variables, and
are therefore "opaque" to the BDD algorithms.

That means we need to perform this kind of simplification ourselves. We
look at pairs of constraints that appear in a BDD and see if they can be
simplified relative to each other, and if so, replace the pair with the
simplification. A large part of the toil of getting this PR to work was
identifying all of those patterns and getting that substitution logic
correct.

With this new representation, all existing tests pass, as well as some
new ones that represent test failures that were occuring on #20093.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-25 21:55:35 -04:00
Francesco Giacometti e66a872c14
[ty] Coalesce allocations for parameter info in ArgumentMatcher (#20586)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

Follow up on #20495. The improvement suggested by @AlexWaygood cannot be
applied as-is since the `argument_matches` vector is indexed by argument
number, while the two boolean vectors are indexed by parameter number.
Still coalescing the latter two saves one allocation.
2025-09-25 20:56:59 -04:00
Dan Parizher 589a674a8d
[`isort`] Fix infinite loop when checking equivalent imports (`I002`, `PLR0402`) (#20381)
## Summary

Fixes #20380

The fix exempts required imports from `PLR0402`
2025-09-25 16:08:15 -05:00
Brent Westbrook e4ac9e9041
Replace two more uses of unsafe with const `Option::unwrap` (#20584)
I guess I missed these in #20007, but I found them today while grepping
for something else. `Option::unwrap` has been const since 1.83, so we
can use it here and avoid some unsafe code.
2025-09-25 15:35:13 -04:00
Dylan f2b7c82534
Handle t-string prefixes in `SimpleTokenizer` (#20578)
The simple tokenizer is meant to skip strings, but it was recording a
`Name` token for t-strings (from the `t`). This PR fixes that.
2025-09-25 14:33:37 -05:00
Bhuminjay Soni cfc64d1707
[syntax-errors]: future-feature-not-defined (F407) (#20554)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

This PR implements
https://docs.astral.sh/ruff/rules/future-feature-not-defined/ (F407) as
a semantic syntax error.

## Test Plan

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

I have written inline tests as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
2025-09-25 13:52:24 -04:00
Brent Westbrook 6b7a9dc2f2
[`isort`] Clarify dependency between `order-by-type` and `case-sensitive` settings (#20559)
Summary
--

Fixes #20536 by linking between the isort options `case-sensitive` and
`order-by-type`. The latter takes precedence over the former, so it
seems good to clarify this somewhere.

I tweaked the wording slightly, but this is otherwise based on the patch
from @SkylerWittman in
https://github.com/astral-sh/ruff/issues/20536#issuecomment-3326097324
(thank you!)

Test Plan
--

N/a

---------

Co-authored-by: Skyler Wittman <skyler.wittman@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-25 16:25:12 +00:00
Brent Westbrook 9903104328
[`pylint`] Fix missing `max-nested-blocks` in settings display (#20574)
Summary
--

This fixes a bug pointed out in #20560 where one of the `pylint`
settings wasn't used in its `Display` implementation.

Test Plan
--

Existing tests with updated snapshots
2025-09-25 12:14:28 -04:00
Giovani Moutinho beec2f2dbb
[`flake8-simplify`] Improve help message clarity (`SIM105`) (#20548)
## Summary

Improve the SIM105 rule message to prevent user confusion about how to
properly use `contextlib.suppress`.

The previous message "Replace with `contextlib.suppress(ValueError)`"
was ambiguous and led users to incorrectly use
`contextlib.suppress(ValueError)` as a statement inside except blocks
instead of replacing the entire try-except-pass block with `with
contextlib.suppress(ValueError):`.

This change makes the message more explicit:
- **Before**: `"Use \`contextlib.suppress({exception})\` instead of
\`try\`-\`except\`-\`pass\`"`
- **After**: `"Replace \`try\`-\`except\`-\`pass\` block with \`with
contextlib.suppress({exception})\`"`

The fix title is also updated to be more specific:
- **Before**: `"Replace with \`contextlib.suppress({exception})\`"`  
- **After**: `"Replace \`try\`-\`except\`-\`pass\` with \`with
contextlib.suppress({exception})\`"`

Fixes #20462

## Test Plan

-  All existing SIM105 tests pass with updated snapshots
-  Cargo clippy passes without warnings  
-  Full test suite passes
-  The new messages clearly indicate that the entire try-except-pass
block should be replaced with a `with` statement, preventing the misuse
described in the issue

---------

Co-authored-by: Giovani Moutinho <e@mgiovani.dev>
2025-09-25 11:19:26 -04:00
Micha Reiser c256c7943c
[ty] Update salsa to fix hang when cycle head panics (#20577) 2025-09-25 17:13:07 +02:00
Dhruv Manilawala 35ed55ec8c
[ty] Filter overloads using variadic parameters (#20547)
## Summary

Closes: https://github.com/astral-sh/ty/issues/551

This PR adds support for step 4 of the overload call evaluation
algorithm which states that:

> If the argument list is compatible with two or more overloads,
determine whether one or more of the overloads has a variadic parameter
(either `*args` or `**kwargs`) that maps to a corresponding argument
that supplies an indeterminate number of positional or keyword
arguments. If so, eliminate overloads that do not have a variadic
parameter.

And, with that, the overload call evaluation algorithm has been
implemented completely end to end as stated in the typing spec.

## Test Plan

Expand the overload call test suite.
2025-09-25 14:58:00 +00:00
Brent Westbrook b0bdf0334e
Bump 0.13.2 (#20576) 2025-09-25 10:37:46 -04:00
David Peter efbb80f747
[ty] Remove hack in protocol satisfiability check (#20568)
## Summary

This removes a hack in the protocol satisfiability check that was
previously needed to work around missing assignability-modeling of
inferable type variables. Assignability of type variables is not
implemented fully, but some recent changes allow us to remove that hack
with limited impact on the ecosystem (and the test suite). The change in
the typing conformance test is favorable.

## Test Plan

* Adapted Markdown tests
* Made sure that this change works in combination with
https://github.com/astral-sh/ruff/pull/20517
2025-09-25 13:35:47 +02:00
Alex Waygood 21be94ac33
[ty] Explicitly test assignability/subtyping between unions of nominal types and protocols with method members (#20557) 2025-09-25 09:21:29 +00:00
Alex Waygood b7d5dc98c1
[ty] Add tests for interactions of `@classmethod`, `@staticmethod`, and protocol method members (#20555) 2025-09-25 10:14:53 +01:00
Dhruv Manilawala e1bb74b25a
[ty] Match variadic argument to variadic parameter (#20511)
## Summary

Closes: https://github.com/astral-sh/ty/issues/1236

This PR fixes a bug where the variadic argument wouldn't match against
the variadic parameter in certain scenarios.

This was happening because I didn't realize that the `all_elements`
iterator wouldn't keep on returning the variable element (which is
correct, I just didn't realize it back then).

I don't think we can use the `resize` method here because we don't know
how many parameters this variadic argument is matching against as this
is where the actual parameter matching occurs.

## Test Plan

Expand test cases to consider a few more combinations of arguments and
parameters which are variadic.
2025-09-25 07:51:56 +00:00
Aria Desires edeb45804e
[ty] fallback to resolve_real_module in file_to_module (#20461)
This is a naive(?) implementation of the approach @MichaReiser
originally suggested to me in https://github.com/astral-sh/ty/issues/869

Fixes https://github.com/astral-sh/ty/issues/869
Fixes https://github.com/astral-sh/ty/issues/1195
2025-09-24 21:15:35 -04:00
Ibraheem Ahmed bea92c8229
[ty] More precise type inference for dictionary literals (#20523)
## Summary

Extends https://github.com/astral-sh/ruff/pull/20360 to dictionary
literals. This also improves our `TypeDict` support by passing through
nested type context.
2025-09-24 18:12:00 -04:00
Ed Cuss f2cc2f604f
[`flake8-pyi`] Avoid syntax error from conflict with `PIE790` (`PYI021`) (#20010)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
First contribution so please let me know if I've made a mistake
anywhere. This was aimed to fix #19982, it adds the isolation level to
PYI021 to in the same style as the PIE790 rule.

fixes: #19982

## Test Plan

<!-- How was it tested? -->
I added a case to the PYI021.pyi file where the two rules are present as
there wasn't a case with them both interacting, using the minimal
reproducible example that @ntBre created on the issue (I think I got the
`# ERROR` markings wrong, so please let me know how to fix that if I
did).

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-24 21:26:59 +00:00
Dan Parizher c361e2f759
[`flake8-bandit`] Clarify the supported hashing functions (`S324`) (#20534)
## Summary

Fixes #16572

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-24 20:10:23 +00:00
Bhuminjay Soni e6073d0cca
[syntax-errors]: multiple-starred-expressions (F622) (#20243)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

This PR implements
https://docs.astral.sh/ruff/rules/multiple-starred-expressions/ as a
semantic syntax error

## Test Plan

 I have added inline tests as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-24 19:32:55 +00:00
Brent Westbrook 73b4b1ed17
[ty] Make `FileResolver::path` return a full path (#20550)
## Summary

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

From finding references with the LSP, `FileResolver::path` is only
called once, in `UnifiedFile::path`, so I went through those references,
and it looked safe to make this change in every case. Most of the
references are in the various output formats, where we inherited the
absolute vs relative path decision from Ruff. Two other uses are as
fallbacks if converting a relativized path to a string fails. Finally,
we use the path for sorting and in `UnifiedFile::relative_path`.

## Test Plan

Existing tests, with snapshots updated to show absolute paths (in the
`TestDb` this just added a `/` in front of the file names). I also
updated the GitLab CLI test to set the `CI_PROJECT_DIR` environment
variable and ran a test in GitLab CI:

<img width="613" height="114" alt="image"
src="https://github.com/user-attachments/assets/8ab81dba-54fd-4a24-9110-77ef89293cff"
/>
2025-09-24 13:16:51 -04:00
Amethyst Reese 83f80effec
include `.pyw` files by default when linting and formatting (#20458)
- Adds test cases exercising file selection by extension with
`--preview` enabled and disabled.
- Adds `INCLUDE_PREVIEW` with file patterns including `*.pyw`.
- In global preview mode, default configuration selects patterns from
`INCLUDE_PREVIEW`.
- Manually tested ruff server with local vscode for both formatting and
linting of a `.pyw` file.

Closes https://github.com/astral-sh/ruff/issues/13246
2025-09-24 08:39:30 -07:00
David Peter fcc76bb7b2
[ty] Todo-types for `os.fdopen`, `NamedTemporaryFile`, and `Path.open` (#20549)
## Summary

This applies the trick that we use for `builtins.open` to similar
functions that have the same problem. The reason is that the problem
would otherwise become even more pronounced once we add understanding of
the implicit type of `self` parameters, because then something like
`(base_path / "test.bin").open("rb")` also leads to a wrong return type
and can result in false positives.

## Test Plan

New Markdown tests
2025-09-24 15:43:58 +02:00
Dan Parizher 3e1e02e9b6
Fix non‑BMP code point handling in quick‑fixes and markers (#20526)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-24 10:08:00 +02:00
Shunsuke Shibayama 722f1a7d7a
[ty] fix stack overflow when comparing recursive `NamedTuple` types with `is_disjoint_from` (#20538)
## Summary

I found this bug while working on #20528.
The minimum reproducible code is:

```python
from __future__ import annotations

from typing import NamedTuple
from ty_extensions import is_disjoint_from, static_assert

class Path(NamedTuple):
    prev: Path | None
    key: str

static_assert(not is_disjoint_from(Path, Path))
```

A stack overflow occurs when a nominal instance type inherits from
`NamedTuple` and is defined recursively.
This PR fixes this bug.

## Test Plan

mdtest updated
2025-09-23 19:29:03 +02:00
ShikChen dbc5983503
Update import path to ruff-wasm-web (#20539) 2025-09-23 16:57:26 +00:00
Dan Parizher 46decd4feb
[`pyupgrade`] Fix `UP008` to not apply when `__class__` is a local variable (`UP008`) (#20497)
## Summary

Fixes #20491
2025-09-23 10:56:39 -04:00
Renkai Ge bf38e69870
[ty] Rename "possibly unbound" diagnostics to "possibly missing" (#20492)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-09-23 14:26:55 +00:00
fgiacome 4ed8c65d29
[ty] Add positional-only-parameter-as-kwarg error (#20495) 2025-09-23 15:10:45 +01:00
Pieter Cardillo Kwok edb920b4d5
[`flake8-async`] Implement `blocking-path-method` (`ASYNC240`) (#20264)
## Summary
Adds a new rule to find and report use of `os.path` or `pathlib.Path` in
async functions.

Issue: #8451

## Test Plan

Using `cargo insta test`
2025-09-23 08:30:47 -04:00
Dan Parizher 346842f003
[`pyflakes`] Fix false positives for `__annotate__` (Py3.14+) and `__warningregistry__` (`F821`) (#20154)
## Summary

Fixes #19970

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-09-23 08:16:00 -04:00
David Peter 742f8a4ee6
[ty] Use `C[T]` instead of `C[Unknown]` for the upper bound of `Self` (#20479)
### Summary

This PR includes two changes, both of which are necessary to resolve
https://github.com/astral-sh/ty/issues/1196:

* For a generic class `C[T]`, we previously used `C[Unknown]` as the
upper bound of the `Self` type variable. There were two problems with
this. For one, when `Self` appeared in contravariant position, we would
materialize its upper bound to `Bottom[C[Unknown]]` (which might
simplify to `C[Never]` if `C` is covariant in `T`) when accessing
methods on `Top[C[Unknown]]`. This would result in `invalid-argument`
errors on the `self` parameter. Also, using an upper bound of
`C[Unknown]` would mean that inside methods, references to `T` would be
treated as `Unknown`. This could lead to false negatives. To fix this,
we now use `C[T]` (with a "nested" typevar) as the upper bound for
`Self` on `C[T]`.
* In order to make this work, we needed to allow assignability/subtyping
of inferable typevars to other types, since we now check assignability
of e.g. `C[int]` to `C[T]` (when checking assignability to the upper
bound of `Self`) when calling an instance-method on `C[int]` whose
`self` parameter is annotated as `self: Self` (or implicitly `Self`,
following https://github.com/astral-sh/ruff/pull/18007).

closes https://github.com/astral-sh/ty/issues/1196
closes https://github.com/astral-sh/ty/issues/1208


### Test Plan

Regression tests for both issues.
2025-09-23 14:02:25 +02:00
Matthew Mckee fd5c48c539
[ty] Add support for inlay hints on attribute assignment (#20485) 2025-09-23 13:14:46 +02:00
justin ef4df34652
[ty] implement `auto()` for `StrEnum` (#20524)
## Summary
see discussion here:
https://github.com/astral-sh/ty/issues/876#issuecomment-3310130167

https://docs.python.org/3/library/enum.html#enum.StrEnum

> Note Using
[auto](https://docs.python.org/3/library/enum.html#enum.auto) with
[StrEnum](https://docs.python.org/3/library/enum.html#enum.StrEnum)
results in the lower-cased member name as the value.

## Test Plan
- new mdtest
- also, added a test to assert the (already correct) behavior for
`IntEnum`

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-09-23 12:22:59 +02:00
Manuel Mendez 036f3616a1
[ty] Add PYTHONPATH to EnvVars and fix on Windows (#20490)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-23 08:27:05 +00:00
Matthew Mckee 68ae9c8a15
[ty] Fix class literal subtyping with object fallback (#20521)
## Summary

@ibraheemdev notes this example failed

```py
from typing import Callable

class X:
    ...

def f(callable: Callable[[], X]) -> X:
    return callable()

x = f(X)
```

Resolves https://github.com/astral-sh/ty/issues/1210

The issue was that we set the `Self` to the class type instead of the
instance type of the class.

## Test Plan

Fix tests in `is_subtype_of.md`
2025-09-22 17:26:25 -07:00
Dan Parizher 094bf70a60
[`flake8-bultins`] Detect class-scope builtin shadowing in decorators, default args, and attribute initializers (`A003`) (#20178)
## Summary
Fix #20171

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-22 18:12:45 -04:00
Ibraheem Ahmed 32d00cd569 update `get-size2` to 0.7.0 2025-09-22 17:37:46 -04:00
Micha Reiser 0c7cfd2a8d
Update transitive dependencies (#20513) 2025-09-22 12:50:53 +02:00
renovate[bot] 61bb2a8245
Update Rust crate anyhow to v1.0.100 (#20499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-22 09:51:52 +02:00
Alex Waygood f1aacd0f2c
[ty] The runtime object `typing.Protocol` is an instance of `_ProtocolMeta` (#20488)
## Summary

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

This bug doesn't currently cause us any real-world issues, because we
don't yet understand the signatures typeshed gives us for `isinstance()`
and `issubclass()` (typeshed's annotations there use PEP-613 type
aliases). #20107 demonstrates that this will start causing us issues as
soon as we add support for PEP-613 aliases, however, so it makes sense
to fix it now.

## Test Plan

Added mdtests
2025-09-22 08:29:03 +01:00
Manuel Mendez 2c6c3e78f6
[ty] Search PYTHONPATH to find modules (#20441)
Co-authored-by: Nate Lust <natelust@linux.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-20 13:40:10 +02:00
Micha Reiser 3ffe56d19d
[ty] Remove unnecessary `FileScopeId` to `ScopeId` conversion (#20481) 2025-09-20 11:20:10 +00:00
GF eb354608d2
[ty] Add LSP debug information command (#20379)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-20 11:15:13 +00:00
Ibraheem Ahmed 12086dfa69 re-infer RHS of annotated assignments in isolation for assignability diagnostics 2025-09-19 17:00:37 -04:00
Ibraheem Ahmed 5f294f9f2e use type context for inference of generic function calls 2025-09-19 17:00:37 -04:00
Gary Yendell 44fc87f491
[`ruff`] Add `logging-eager-conversion` (`RUF065`) (#19942)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

Fixes #12734

I have started with simply checking if any arguments that are providing
extra values to the log message are calls to `str` or `repr`, as
suggested in the linked issue. There was a concern that this could cause
false positives and the check should be more explicit. I am happy to
look into that if I have some further examples to work with.

If this is the accepted solution then there are more cases to add to the
test and it should possibly also do test for the same behavior via the
`extra` keyword.

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

## Test Plan

I have added a new test case and python file to flake8_logging_format
with examples of this anti-pattern.

<!-- How was it tested? -->
2025-09-19 16:43:44 -04:00
Takayuki Maeda 43cda2dfe9
[`ruff`] Fix B004 to skip invalid hasattr/getattr calls (#20486)
## Summary

Fixes #20440

Fix B004 to skip invalid hasattr/getattr calls

- Add argument validation for `hasattr` and `getattr`
- Skip B004 rule when function calls have invalid argument patterns
2025-09-19 13:44:42 -05:00
Takayuki Maeda bd5b3e4f6e
Deduplicate input paths (#20105)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

Fixes #20035, fixes #19395

This is for deduplicating input paths to avoid processing the same file
multiple times.

This is my first contribution, so I'm sorry if I miss something. Please
tell me if this is needed for this feature.

## Test Plan

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

I just added a test `find_python_files_deduplicated` in
eee1020e32/crates/ruff_workspace/src/resolver.rs (L1017)
. This pull request adds changes to `WalkPythonFilesState::finish`,
which is used in `python_files_in_path`, so they affect some commands
such as `analyze`, `format`, `check` and so on. I will add snapshot
tests for them if necessary.

I’ve already confirmed that the same thing happens with ruff check as
well.

```
$ echo "x   = 1" > example/foo.py
$ uvx ruff check example example/foo.py
I002 [*] Missing required import: `from __future__ import annotations`
--> /path/to/example/foo.py:1:1
help: Insert required import: `from __future__ import annotations`

I002 [*] Missing required import: `from __future__ import annotations`
--> /path/to/example/foo.py:1:1
help: Insert required import: `from __future__ import annotations`

Found 2 errors.
[*] 2 fixable with the `--fix` option.
```
2025-09-19 14:40:23 -04:00
Andrew Gallant 3bf4dae452 [ty] Make auto-import work in the playground
It turned out that we weren't quite funneling the new completion data
all the way through.

I followed the docs for [`CompletionItem`] for the Monaco editor. It's
similar, but not identical, to the LSP protocol specification.

[`CompletionItem`]: https://microsoft.github.io/monaco-editor/typedoc/interfaces/languages.CompletionItem.html
2025-09-19 14:35:51 -04:00
Takayuki Maeda 8eeca023d6
[`ruff`] `FURB164` Replace -nan with nan when using the value to construct `Decimal` (#20391)
## Summary

Fixes #19699

Normalize `Decimal(float("-nan"))` to `Decimal("nan")`.

The same handling is implemented in:

c3e873dd82/crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs (L165)
2025-09-19 13:04:29 -05:00
Dan Parizher c94ddb590f
[`flake8-bugbear`] Add `B912`: `map()` without an explicit `strict=` parameter (#20429)
## Summary

Implements new rule `B912` that requires the `strict=` argument for
`map(...)` calls with two or more iterables on Python 3.14+, following
the same pattern as `B905` for `zip()`.

Closes #20057

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-09-19 12:54:44 -05:00
renovate[bot] bae8ddfb8a
Update Rust crate hashbrown to 0.16.0 (#20399)
Coming soon: The Renovate bot (GitHub App) will be renamed to Mend. PRs
from Renovate will soon appear from 'Mend'. Learn more
[here](https://redirect.github.com/renovatebot/renovate/discussions/37842).

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [hashbrown](https://redirect.github.com/rust-lang/hashbrown) |
workspace.dependencies | minor | `0.15.0` -> `0.16.0` |

---

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

---

### Release Notes

<details>
<summary>rust-lang/hashbrown (hashbrown)</summary>

###
[`v0.16.0`](https://redirect.github.com/rust-lang/hashbrown/blob/HEAD/CHANGELOG.md#0160---2025-08-28)

[Compare
Source](https://redirect.github.com/rust-lang/hashbrown/compare/v0.15.5...v0.16.0)

##### Changed

- Bump foldhash, the default hasher, to 0.2.0.
- Replaced `DefaultHashBuilder` with a newtype wrapper around `foldhash`
instead
  of re-exporting it directly.

</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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: David Peter <mail@david-peter.de>
Co-authored-by: Ibraheem Ahmed <ibraheem@ibraheem.ca>
2025-09-19 13:24:45 -04:00
Dan Parizher c0fb235a70
[`flake8-comprehensions`] Preserve trailing commas for single-element lists (`C409`) (#19571)
## Summary

Fixes #19568
2025-09-19 09:27:14 -04:00
Andrew Gallant b5a3503a58 [ty] Enable auto-import for completions in WASM builds by default
Now that imports are actually inserted, this should give us some
valuable dog-fooding experience.

Note that we don't currently do any ranking on completions, so until
that is improved, even in-scope completions could suffer. With that
said, this shouldn't have any impact at all in several scenarios (like
completions for attributes on objects).
2025-09-19 08:11:59 -04:00
Andrew Gallant d45209f425 [ty] Add some tricky test cases for the auto-import importer
We don't attempt to fix these yet. I think there are bigger fish to fry.

I came up with these based on this discussion:
https://github.com/astral-sh/ruff/pull/20439#discussion_r2357769518

Here's one example:

```
if ...:
    from foo import MAGIC
else:
    from bar import MAGIC

MAG<CURSOR>
```

Now in this example, completions will include `MAGIC` from the local
scope. That is, auto-import is involved with that completion. But at
present, auto-import will suggest importing `foo` and `bar` because we
haven't de-duplicated completions yet. Which is fine.

Here's another example:

```
if ...:
    import foo as fubar
else:
    import bar as fubar

MAG<CURSOR>
```

Now here, there is no `MAGIC` symbol in scope. So auto-import is in
play. Let's assume that the user selects `MAGIC` from `foo` in this
example. (`bar` also has `MAGIC`.)

Since we currently ignore the declaration site for symbols with
multiple possible bindings, the importer today doesn't know that
`fubar` _could_ contain `MAGIC`. But even if it did, what would we do
with that information? Should we do this?

```
if ...:
    import foo as fubar
    from foo import MAGIC
else:
    import bar as fubar

MAGIC
```

Or could we reason that `bar` also has `MAGIC`?

```
if ...:
    import foo as fubar
else:
    import bar as fubar

fubar.MAGIC
```

But if we did that, we're making an assumption of user intent, since
they *selected* `foo.MAGIC` but not `bar.MAGIC`.

Anyway, I don't think we need to settle on an answer today, but I
wanted to capture some of these tricky cases in tests at the very
least.
2025-09-19 07:54:07 -04:00
Dhruv Manilawala 902b0b4ce9
[ty] Add support for `**kwargs` (#20430)
## Summary

This PR adds support for unpacking `**kwargs` argument.

This can be matched against any standard (positional or keyword),
keyword-only, or keyword variadic parameter that haven't been matched
yet.

This PR also takes care of special casing `TypedDict` because the key
names and the corresponding value type is known, so we can be more
precise in our matching and type checking step. In the future, this
special casing would be extended to include `ParamSpec` as well.

Part of astral-sh/ty#247

## Test Plan

Add test cases for various scenarios.
2025-09-19 05:00:30 +00:00
Frazer McLean bc89d0394c
[`flake8-simplify`] Fix incorrect fix for positive `maxsplit` without separator (`SIM905`) (#20056)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

Resolves #20033

## Test Plan

unit tests added to the new split function, existing snapshot test
updated.

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-18 20:56:34 +00:00
Dylan 7b40428b6a
Bump 0.13.1 (#20473) 2025-09-18 19:25:17 +00:00
Takayuki Maeda b4b5d67a4a
[`flynt`] Use triple quotes for joined raw strings with newlines (`FLY002`) (#20197)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

Fixes #19887

- flynt(FLY002): When joining only string constants, upgrade raw
single-quoted strings to raw triple-quoted if the resulting
content contains a newline.
- Choose a safe triple-quote delimiter by switching to the opposite
quote style if the preferred triple appears inside the
content.
- Update FLY002 snapshot to include the `\n'.join([r'line1','line2'])`
case.

## Test Plan

I've added one test case to FLY002.py.

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

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-18 13:18:29 -04:00
Micha Reiser 0b60584b7e
Bump MSRV to Rust 1.88 (#20470) 2025-09-18 17:52:37 +02:00
Takayuki Maeda 821b2f8b2e
[`refurb`] Mark `single-item-membership-test` fix as always unsafe (`FURB171`) (#20279)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

Fixes #20255

Mark single-item-membership-test fixes as always unsafe

- Always set `Applicability::Unsafe` for FURB171 fixes
- Update “Fix safety” docs to reflect always-unsafe behavior
- Expand tests (not in, nested set/frozenset, commented args)

## Test Plan

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

I have added new test cases to
`crates/ruff_linter/resources/test/fixtures/refurb/FURB171_0.py` and
`crates/ruff_linter/resources/test/fixtures/refurb/FURB171_1.py`.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-18 15:24:15 +00:00
Micha Reiser 1758f26d94
Update rust toolchain to 1.90 (#20469) 2025-09-18 16:54:49 +02:00
Eric Mark Martin 2502ff7638
[ty] Make TypeIs invariant in its type argument (#20428)
## Summary

What it says on the tin. See the [typing
spec](https://docs.python.org/3/library/typing.html#typing.TypeIs) for
justification.

## Test Plan

Add more tests to PEP 695 `variance.md` suite.
2025-09-18 07:53:13 -07:00
chiri 144373fb3c
[`flake8-use-pathlib`] Fix `PTH101`, `PTH104`, `PTH105`, `PTH121` fixes (#20143)
## Summary
Fixes https://github.com/astral-sh/ruff/issues/20134

## Test Plan
`cargo nextest run flake8_use_pathlib`

---------

Co-authored-by: Dan Parizher <danparizher@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-18 14:17:54 +00:00
Dan Parizher 91995aa516
[`pyupgrade`] Fix false positive when class name is shadowed by local variable (`UP008`) (#20427)
## Summary

Fixes #20422

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-18 14:05:05 +00:00
Andrew Gallant 1ebbe73a1d [ty] Swap `detail` and `description` fields for `CompletionItemLabelDetails`
This seems to be more consistent with how other LSPs work (like
`rust-analyzer`), and also I think is more consistent with how
`CompletionItem.detail` is itself rendered. Namely, in VS Code, it
is right-aligned. And it's also where we put the type signature.
But `CompletionItemLabelDetails.detail` is left-aligned where as
`CompletionItemLabelDetails.description` is right-aligned. So let's
swap them such that type signatures go in the latter and not the
former.

This also adds a space before the module name and contextualizes
it with `(import <name>)` to help aide the end user in figuring out
selecting the completion will do.

Fixes #1200
2025-09-18 09:11:17 -04:00
Shahar Naveh 48ada2d359
Generator preferred quote style (#20434) 2025-09-18 12:57:21 +02:00
David Peter 50bd3943da
[ty] Faster iteration on mdtests (#20465)
## Summary

This change reduces MD test compilation time from 6s to 3s on my laptop.
We don't need to build the unit tests and the corpus tests when we're
only interested in Markdown-based tests.

## Test Plan

local benchmarks
2025-09-18 10:48:52 +00:00
Dan Parizher 5707958dad
[`flake8-simplify`] Fix diagnostic to show correct method name for rsplit calls (`SIM905`) (#20459)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-18 07:52:08 +00:00
Nikolas Hearp c4d359306b
Add fixes to `output-format=sarif` (#20300)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-09-18 09:37:04 +02:00
Ibraheem Ahmed e84d523bcf
[ty] Infer more precise types for collection literals (#20360)
## Summary

Part of https://github.com/astral-sh/ty/issues/168. Infer more precise types for collection literals (currently, only `list` and `set`). For example,

```py
x = [1, 2, 3] # revealed: list[Unknown | int]
y: list[int] = [1, 2, 3] # revealed: list[int]
```

This could easily be extended to `dict` literals, but I am intentionally limiting scope for now.
2025-09-17 18:51:50 -04:00
chiri bfb0902446
[`flake8-use-pathlib`] Add fix for `PTH123` (#20169)
## Summary
Part of https://github.com/astral-sh/ruff/issues/2331

## Test Plan
`cargo nextest run flake8_use_pathlib`

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-17 15:47:29 -04:00
Shaygan Hooshyari 05622ae757
[ty] Bind Self typevar to method context (#20366)
Fixes: https://github.com/astral-sh/ty/issues/1173

<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

This PR will change the logic of binding Self type variables to bind
self to the immediate function that it's used on.
Since we are binding `self` to methods and not the class itself we need
to ensure that we bind self consistently.

The fix is to traverse scopes containing the self and find the first
function inside a class and use that function to bind the typevar for
self.

If no such scope is found we fallback to the normal behavior. Using Self
outside of a class scope is not legal anyway.

## Test Plan

Added a new mdtest.

Checked the diagnostics that are not emitted anymore in [primer
results](https://github.com/astral-sh/ruff/pull/20366#issuecomment-3289411424).
It looks good altough I don't completely understand what was wrong
before.

---------

Co-authored-by: Douglas Creager <dcreager@dcreager.net>
2025-09-17 14:58:54 -04:00
Andrew Gallant 3fcbe8bde6 [ty] Remove TODO about using a non-panicking lookup method
Ref https://github.com/astral-sh/ruff/pull/20439#discussion_r2355082049

Ref https://github.com/astral-sh/ruff/pull/18455#discussion_r2126833137
2025-09-17 13:59:28 -04:00
Andrew Gallant 64a4e2889e [ty] Add new completion data to wasm bridge 2025-09-17 13:59:28 -04:00
Andrew Gallant 5816985ecd [ruff] Remove `Locator` from `Importer`
It seems like we'd like to remove `Locator` since it's a bit
awkward in how it works:
https://github.com/astral-sh/ruff/pull/20439#discussion_r2354683797

It looked pretty easy to rip it out of the `Importer`, so that's
one less thing using it.
2025-09-17 13:59:28 -04:00
Andrew Gallant b6a29592e7 [ty] Add a new abstraction for adding imports to a module
This is somewhat inspired by a similar abstraction in
`ruff_linter`. The main idea is to create an importer once
for a module that you want to add imports to. And then call
`import` to generate an edit for each symbol you want to
add.

I haven't done any performance profiling here yet. I don't
know if it will be a bottleneck. In particular, I do expect
`Importer::import` (but not `Importer::new`) to get called
many times for a single completion request when auto-import
is enabled. Particularly in projects with a lot of unimported
symbols. Because I don't know the perf impact, I didn't do
any premature optimization here. But there are surely some
low hanging fruit if this does prove to be a problem.

New tests make up a big portion of the diff here. I tried to
think of a bunch of different cases, although I'm sure there
are more.
2025-09-17 13:59:28 -04:00
Andrew Gallant bcc8d6910b [ty] Refactor to handle unimported completions
This rejiggers some stuff in the main completions entrypoint
in `ty_ide`. A more refined `Completion` type is defined
with more information. In particular, to support auto-import,
we now include a module name and an "edit" for inserting an
import.

This also rolls the old "detailed completion" into the new
completion type. Previously, we were relying on the completion
type for `ty_python_semantic`. But `ty_ide` is really the code
that owns completions.

Note that this code doesn't build as-is. The next commit will
add the importer used here in `add_unimported_completions`.
2025-09-17 13:59:28 -04:00
Andrew Gallant 02ee22db78 [ty] Add module to result returned by "all symbols" API
Based on how this API is currently implemented, this doesn't
really cost us anything. But it gives us access to more
information about where the symbol is defined.
2025-09-17 13:59:28 -04:00
Andrew Gallant 0a2325c5fe [ty] Move `CompletionKind` to `ty_ide`
I think this is a better home for it. This way, `ty_ide`
more clearly owns how the "kind" of a completion is computed.
In particular, it is computed differently for things where
we know its type versus unimported symbols.
2025-09-17 13:59:28 -04:00
Andrew Gallant 6c3c963f8a [ty] Include definition site for "members of" API
In the course of writing the "add an import" implementation,
I realized that we needed to know which symbols were in scope
and how they were defined. This was necessary to be able to
determine how to add a new import in a way that (minimally)
does not conflict with existing symbols.

I'm not sure that this is fully correct (especially for
symbol bindings) and it's unclear to me in which cases a
definition site will be missing. But this seems to work for
some of the basic cases that I tried.
2025-09-17 13:59:28 -04:00
Andrew Gallant 6ec52991cb [ty] Fix a bug with "all_submodule_names_for_package" API
The names of the submodules returned should be *complete*. This
is the contract of `Module::name`. However, we were previously
only returning the basename of the submodule.
2025-09-17 13:59:28 -04:00
Andrew Gallant cf16fc4aa4 [ty] Export some stuff from `ty_python_semantic`
We're going to want to use this outside of `ty_python_semantic`.
Specifically, in `ty_ide`.
2025-09-17 13:59:28 -04:00
Andrew Gallant 61a49c89eb [ruff] Add `TextRange::to_std_range`
This can already be accomplished via a `From` impl (and indeed,
that's how this is implemented). But in a generic context, the
turbo-fishing that needs to be applied is quite annoying.
2025-09-17 13:59:28 -04:00
Andrew Gallant da5eb85087 [ruff] Add API for splicing into an existing import statement
Basically, given a `from module import name1, name2, ...` statement,
we'd like to be able to insert another name in that list.

This new `Insertion::existing_import` API provides such
functionality. There isn't much to it, although we are careful
to try and avoid inserting nonsense for import statements
that are already invalid.
2025-09-17 13:59:28 -04:00
Andrew Gallant a47a50e6e2 [ruff] Provide a way to get an owned `Stylist`
This makes it easier to test with in some cases and generally shouldn't
cost anything.
2025-09-17 13:59:28 -04:00
Andrew Gallant 880a867696 [ruff] Move `Insertion` abstraction out of `ruff_linter`
This refactors the importer abstraction to use a shared
`Insertion`. This is mostly just moving some code around
with some slight tweaks.

The plan here is to keep the rest of the importing code
in `ruff_linter` and then write something ty-specific on
top of `Insertion`. This ends up sharing some code, but
not as much as would be ideal. In particular, the
`ruff_linter` imported is pretty tightly coupled with
ruff's semantic model. So to share the code, we'd need to
abstract over that.
2025-09-17 13:59:28 -04:00
Andrew Gallant ec2720c814 [ruff] Small tweak to import
As I was playing around in this file, it was much nicer
to just use `cst::` everywhere, similar to what we do with
`ruff_python_ast`.
2025-09-17 13:59:28 -04:00
chiri cb3c3ba94d
[`flake8-use-pathlib`] A bit clean up `PTH100` (#20452)
## Summary

Part of https://github.com/astral-sh/ruff/pull/20215

## Test Plan
2025-09-17 12:11:30 -04:00
chiri c585c9f6d4
[`flake8-use-pathlib`] Make `PTH111` fix unsafe because it can change behavior (#20215)
## Summary

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

## Test Plan
2025-09-17 11:23:55 -04:00
Brent Westbrook ac5488086f
[ty] Add GitHub output format (#20358)
## Summary

This PR wires up the GitHub output format moved to `ruff_db` in #20320
to the ty CLI.

It's a bit smaller than the GitLab version (#20155) because some of the
helpers were already in place, but I did factor out a few
`DisplayDiagnosticConfig` constructor calls in Ruff. I also exposed the
`GithubRenderer` and a wrapper `DisplayGithubDiagnostics` type because
we needed a way to configure the program name displayed in the GitHub
diagnostics. This was previously hard-coded to `Ruff`:

<img width="675" height="247" alt="image"
src="https://github.com/user-attachments/assets/592da860-d2f5-4abd-bc5a-66071d742509"
/>

Another option would be to drop the program name in the output format,
but I think it can be helpful in workflows with multiple programs
emitting annotations (such as Ruff and ty!)

## Test Plan

New CLI test, and a manual test with `--config 'terminal.output-format =
"github"'`
2025-09-17 09:50:25 -04:00
Carl Meyer 7e464b8150
[ty] move graphql-core to good.txt (#20447)
## Summary

With https://github.com/astral-sh/ruff/pull/20446, graphql-core now
checks without error; we can move it to `good.txt`.

## Test Plan

CI
2025-09-17 10:09:32 +02:00
David Peter ffd650e5fd
[ty] Update mypy_primer (#20433)
## Summary

Revert the materialize-changes, see
https://github.com/hauntsaninja/mypy_primer/pull/208

## Test Plan

CI
2025-09-17 09:51:48 +02:00
Carl Meyer 99ec4d2c69
[ty] detect cycles in binary comparison inference (#20446)
## Summary

Catch infinite recursion in binary-compare inference.

Fixes the stack overflow in `graphql-core` in mypy-primer.

## Test Plan

Added two tests that stack-overflowed before this PR.
2025-09-17 09:45:25 +02:00
justin 9f0b942b9e
[ty] infer `name` and `value` for enum members (#20311)
## summary
- this pr implements the following attributes for `Enum` members:
  - `name`
  - `_name_`
  - `value`
  - `_value_`
- adds a TODO test for `my_enum_class_instance.name`
- only implements if the instance is a subclass of `Enum` re: this
[comment](https://github.com/astral-sh/ruff/pull/19481#issuecomment-3103460307)
and existing
[test](c34449ed7c/crates/ty_python_semantic/resources/mdtest/enums.md?plain=1#L625)

### pointers
- https://github.com/astral-sh/ty/issues/876
- https://typing.python.org/en/latest/spec/enums.html#enum-definition
- https://github.com/astral-sh/ruff/pull/19481#issuecomment-3103460307

## test plan
- mdtests
- triaged conformance diffs here:
https://diffswarm.dev/d-01k531ag4nee3xmdeq4f3j66pb
- triaged mypy primer diffs here for django-stubs:
https://diffswarm.dev/d-01k5331n13k9yx8tvnxnkeawp3
  - added a TODO test for overriding `.value`
- discord diff seems reasonable

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-09-17 09:36:27 +02:00
Carl Meyer c2fa449954
[ty] support type aliases in binary compares (#20445)
## Summary

Add missing `Type::TypeAlias` clauses to `infer_binary_type_comparison`.

## Test Plan

Added mdtests that failed before.
2025-09-17 09:33:26 +02:00
Carl Meyer 681ad2fd92
[ty] move primer projects to good.txt (#20444)
## Summary

After https://github.com/astral-sh/ruff/pull/20359 we can move all but
three remaining projects over to `good.txt`.

## Test Plan

CI
2025-09-17 09:31:27 +02:00
Takayuki Maeda 98071b49c2
[`playground`] Enable inline noqa for multiline strings in playground (#20442) 2025-09-17 09:29:40 +02:00
Carl Meyer d121a76aef
[ty] no more diverging query cycles in type expressions (#20359)
## Summary

Use `Type::Divergent` to short-circuit diverging types in type
expressions. This avoids panicking in a wide variety of cases of
recursive type expressions.

Avoids many panics (but not yet all -- I'll be tracking down the rest)
from https://github.com/astral-sh/ty/issues/256 by falling back to
Divergent. For many of these recursive type aliases, we'd like to
support them properly (i.e. really understand the recursive nature of
the type, not just fall back to Divergent) but that will be future work.

This switches `Type::has_divergent_type` from using `any_over_type` to a
custom set of visit methods, because `any_over_type` visits more than we
need to visit, and exercises some lazy attributes of type, causing
significantly more work. This change means this diff doesn't regress
perf; it even reclaims some of the perf regression from
https://github.com/astral-sh/ruff/pull/20333.

## Test Plan

Added mdtest for recursive type alias that panics on main.

Verified that we can now type-check `packaging` (and projects depending
on it) without panic; this will allow moving a number of mypy-primer
projects from `bad.txt` to `good.txt` in a subsequent PR.
2025-09-16 16:44:11 -07:00
Bhuminjay Soni c3f2187fda
[syntax-errors]: import from * only allowed at module scope (F406) (#20166)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

This PR implements F406
https://docs.astral.sh/ruff/rules/undefined-local-with-nested-import-star-usage/
as a semantic syntax error

## Test Plan

I have written inline tests as directed in #17412

---------

Signed-off-by: 11happy <soni5happy@gmail.com>
2025-09-16 15:53:28 -04:00
Amethyst Reese 8a027b0d74
[ruff] Treat panics as fatal diagnostics, sort panics last (#20258)
- Convert panics to diagnostics with id `Panic`, severity `Fatal`, and
the error as the diagnostic message, annotated with a `Span` with empty
code block and no range.
- Updates the post-linting message diagnostic handling to track the
maximum severity seen, and then prints the "report a bug in ruff"
message only if the max severity was `Fatal`

This depends on the sorting changes since it creates diagnostics with no
range specified.
2025-09-16 11:33:37 -07:00
Dan Parizher aa63c24b8f
[`pycodestyle`] Fix `E301` to only trigger for functions immediately within a class (#19768)
## Summary

Fixes #19752

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-16 11:00:07 -04:00
Douglas Creager 1f46c18921
[ty] More constraint set simplifications via simpler constraint representation (#20423)
Previously, we used a very fine-grained representation for individual
constraints: each constraint was _either_ a range constraint, a
not-equivalent constraint, or an incomparable constraint. These three
pieces are enough to represent all of the "real" constraints we need to
create — range constraints and their negation.

However, it meant that we weren't picking up as many chances to simplify
constraint sets as we could. Our simplification logic depends on being
able to look at _pairs_ of constraints or clauses to see if they
simplify relative to each other. With our fine-grained representation,
we could easily encounter situations that we should have been able to
simplify, but that would require looking at three or more individual
constraints.

For instance, negating a range constraint would produce:

```
¬(Base ≤ T ≤ Super) = ((T ≤ Base) ∧ (T ≠ Base)) ∨ (T ≁ Base) ∨
                      ((Super ≤ T) ∧ (T ≠ Super)) ∨ (T ≁ Super)
```

That is, `T` must be (strictly) less than `Base`, (strictly) greater
than `Super`, or incomparable to either.

If we tried to union those back together, we should get `always`, since
`x ∨ ¬x` should always be true, no matter what `x` is. But instead we
would get:

```
(Base ≤ T ≤ Super) ∨ ((T ≤ Base) ∧ (T ≠ Base)) ∨ (T ≁ Base) ∨ ((Super ≤ T) ∧ (T ≠
 Super)) ∨ (T ≁ Super)
```

Nothing would simplify relative to each other, because we'd have to look
at all five union elements to see that together they do in fact combine
to `always`.

The fine-grained representation was nice, because it made it easier to
[work out the math](https://dcreager.net/theory/constraints/) for
intersections and unions of each kind of constraint. But being able to
simplify is more important, since the example above comes up immediately
in #20093 when trying to handle constrained typevars.

The fix in this PR is to go back to a more coarse-grained
representation, where each individual constraint consists of a positive
range (which might be `always` / `Never ≤ T ≤ object`), and zero or more
negative ranges. The intuition is to think of a constraint as a region
of the type space (representable as a range) with zero or more "holes"
removed from it.

With this representation, negating a range constraint produces:

```
¬(Base ≤ T ≤ Super) = (always ∧ ¬(Base ≤ T ≤ Super))
```

(That looks trivial, because it is! We just move the positive range to
the negative side.)

The math is not that much harder than before, because there are only
three combinations to consider (each for intersection and union) —
though the fact that there can be multiple holes in a constraint does
require some nested loops. But the mdtest suite gives me confidence that
this is not introducing any new issues, and it definitely removes a
troublesome TODO.

(As an aside, this change also means that we are back to having each
clause contain no more than one individual constraint for any typevar.
This turned out to be important, because part of our simplification
logic was also depending on that!)

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-16 10:05:01 -04:00
Takayuki Maeda 0d424d8e78
[`ruff`] Add `analyze.string-imports-min-dots` to settings documentation (#20375) 2025-09-16 13:19:34 +02:00
David Peter 2a6dde4acb
[ty] Remove `Self` from generic context when binding `Self` (#20364)
## Summary

This mainly removes an internal inconsistency, where we didn't remove
the `Self` type variable when eagerly binding `Self` to an instance
type. It has no observable effect, apparently.

builds on top of https://github.com/astral-sh/ruff/pull/20328

## Test Plan

None
2025-09-15 17:40:10 +02:00
David Peter 25cbf38a47
[ty] Patch `Self` for fallback-methods on `NamedTuple`s and `TypedDict`s (#20328)
## Summary

We use classes like
[`_typeshed._type_checker_internals.NamedTupleFallback`](d9c76e1d9f/stdlib/_typeshed/_type_checker_internals.pyi (L54-L75))
to tack on additional attributes/methods to instances of user-defined
`NamedTuple`s (or `TypedDict`s), even though these classes are not
present in the MRO of those types.

The problem is that those classes use implicit and explicit `Self`
annotations which refer to `NamedTupleFallback` itself, instead of to
the actual type that we're adding those methods to:
```py
class NamedTupleFallback(tuple[Any, ...]):
    # […]
    def _replace(self, **kwargs: Any) -> typing_extensions.Self: ...
```

In effect, when we access `_replace` on an instance of a custom
`NamedTuple` instance, its `self` parameter and return type refer to the
wrong `Self`. This leads to incorrect *"Argument to bound method
`_replace` is incorrect: Argument type `Person` does not satisfy upper
bound `NamedTupleFallback` of type variable `Self`"* errors on #18007.
It would also lead to similar errors on `TypedDict`s, if they would
already implement assignability properly.


## Test Plan

I applied the following patch to typeshed and verified that no errors
appear anymore.

<details>

```diff
diff --git a/crates/ty_vendored/vendor/typeshed/stdlib/_typeshed/_type_checker_internals.pyi b/crates/ty_vendored/vendor/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
index feb22aae00..8e41034f19 100644
--- a/crates/ty_vendored/vendor/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
+++ b/crates/ty_vendored/vendor/typeshed/stdlib/_typeshed/_type_checker_internals.pyi
@@ -29,27 +29,27 @@ class TypedDictFallback(Mapping[str, object], metaclass=ABCMeta):
         __readonly_keys__: ClassVar[frozenset[str]]
         __mutable_keys__: ClassVar[frozenset[str]]
 
-    def copy(self) -> typing_extensions.Self: ...
+    def copy(self: typing_extensions.Self) -> typing_extensions.Self: ...
     # Using Never so that only calls using mypy plugin hook that specialize the signature
     # can go through.
-    def setdefault(self, k: Never, default: object) -> object: ...
+    def setdefault(self: typing_extensions.Self, k: Never, default: object) -> object: ...
     # Mypy plugin hook for 'pop' expects that 'default' has a type variable type.
-    def pop(self, k: Never, default: _T = ...) -> object: ...  # pyright: ignore[reportInvalidTypeVarUse]
-    def update(self, m: typing_extensions.Self, /) -> None: ...
-    def __delitem__(self, k: Never) -> None: ...
-    def items(self) -> dict_items[str, object]: ...
-    def keys(self) -> dict_keys[str, object]: ...
-    def values(self) -> dict_values[str, object]: ...
+    def pop(self: typing_extensions.Self, k: Never, default: _T = ...) -> object: ...  # pyright: ignore[reportInvalidTypeVarUse]
+    def update(self: typing_extensions.Self, m: typing_extensions.Self, /) -> None: ...
+    def __delitem__(self: typing_extensions.Self, k: Never) -> None: ...
+    def items(self: typing_extensions.Self) -> dict_items[str, object]: ...
+    def keys(self: typing_extensions.Self) -> dict_keys[str, object]: ...
+    def values(self: typing_extensions.Self) -> dict_values[str, object]: ...
     @overload
-    def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
+    def __or__(self: typing_extensions.Self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
     @overload
-    def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    def __or__(self: typing_extensions.Self, value: dict[str, Any], /) -> dict[str, object]: ...
     @overload
-    def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
+    def __ror__(self: typing_extensions.Self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...
     @overload
-    def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ...
+    def __ror__(self: typing_extensions.Self, value: dict[str, Any], /) -> dict[str, object]: ...
     # supposedly incompatible definitions of __or__ and __ior__
-    def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...  # type: ignore[misc]
+    def __ior__(self: typing_extensions.Self, value: typing_extensions.Self, /) -> typing_extensions.Self: ...  # type: ignore[misc]
 
 # Fallback type providing methods and attributes that appear on all `NamedTuple` types.
 class NamedTupleFallback(tuple[Any, ...]):
@@ -61,18 +61,18 @@ class NamedTupleFallback(tuple[Any, ...]):
         __orig_bases__: ClassVar[tuple[Any, ...]]
 
     @overload
-    def __init__(self, typename: str, fields: Iterable[tuple[str, Any]], /) -> None: ...
+    def __init__(self: typing_extensions.Self, typename: str, fields: Iterable[tuple[str, Any]], /) -> None: ...
     @overload
     @typing_extensions.deprecated(
         "Creating a typing.NamedTuple using keyword arguments is deprecated and support will be removed in Python 3.15"
     )
-    def __init__(self, typename: str, fields: None = None, /, **kwargs: Any) -> None: ...
+    def __init__(self: typing_extensions.Self, typename: str, fields: None = None, /, **kwargs: Any) -> None: ...
     @classmethod
     def _make(cls, iterable: Iterable[Any]) -> typing_extensions.Self: ...
-    def _asdict(self) -> dict[str, Any]: ...
-    def _replace(self, **kwargs: Any) -> typing_extensions.Self: ...
+    def _asdict(self: typing_extensions.Self) -> dict[str, Any]: ...
+    def _replace(self: typing_extensions.Self, **kwargs: Any) -> typing_extensions.Self: ...
     if sys.version_info >= (3, 13):
-        def __replace__(self, **kwargs: Any) -> typing_extensions.Self: ...
+        def __replace__(self: typing_extensions.Self, **kwargs: Any) -> typing_extensions.Self: ...
 
 # Non-default variations to accommodate couroutines, and `AwaitableGenerator` having a 4th type parameter.
 _S = TypeVar("_S")
```

</details>
2025-09-15 16:21:53 +02:00
Alex Waygood e8b4450125
[ty] Remove unneeded disjoint-base special casing for `builtins.tuple` (#20414) 2025-09-15 13:12:20 +01:00
Alex Waygood 8341da7f63
[ty] Allow annotation expressions to be `ast::Attribute` nodes (#20413)
Fixes https://github.com/astral-sh/ty/issues/1187
2025-09-15 12:06:48 +01:00
Takayuki Maeda 093fa72656
[`ty`] Include `NamedTupleFallback` members in `NamedTuple` instance completions (#20356)
## Summary

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

Include `NamedTupleFallback` members in `NamedTuple` instance
completions.

- Augment instance attribute completions when completing on NamedTuple
instances by merging members from
`_typeshed._type_checker_internals.NamedTupleFallback`

## Test Plan

Adds a minimal completion test `namedtuple_fallback_instance_methods`

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-09-15 11:00:03 +02:00
David Peter 02c58f1beb
[ty] Remove 'materialize' from the ecosystem projects (#20412)
## Summary

This project was [recently removed from
mypy_primer](https://github.com/astral-sh/ruff/pull/20378), so we need
to remove it from `good.txt` in order for ecosystem-analyzer to work
correctly.

## Test Plan

Run mypy_primer and ecosystem-analyzer on this branch.
2025-09-15 10:42:35 +02:00
github-actions[bot] 276ee1bb1e
[ty] Sync vendored typeshed stubs (#20394)
Close and reopen this PR to trigger CI

---------

Co-authored-by: typeshedbot <>
Co-authored-by: David Peter <mail@david-peter.de>
2025-09-15 09:30:28 +02:00
William Woodruff 1fa64a24b8
Revert "[ruff]: Build loongarch64 binaries in CI (#20361)" (#20372) 2025-09-12 17:21:04 -04:00
Alex Waygood 1745554809
[ty] Temporary hack to reduce false positives around `builtins.open()` (#20367)
## Summary

https://github.com/astral-sh/ruff/pull/20165 added a lot of false
positives around calls to `builtins.open()`, because our missing support
for PEP-613 type aliases means that we don't understand typeshed's
overloads for `builtins.open()` at all yet, and therefore always select
the first overload. This didn't use to matter very much, but now that we
have a much stricter implementation of protocol assignability/subtyping
it matters a lot, because most of the stdlib functions dealing with I/O
(`pickle`, `marshal`, `io`, `json`, etc.) are annotated in typeshed as
taking in protocols of some kind.

In lieu of full PEP-613 support, which is blocked on various things and
might not land in time for our next alpha release, this PR adds some
temporary special-casing for `builtins.open()` to avoid the false
positives. We just infer `Todo` for anything that isn't meant to match
typeshed's first `open()` overload. This should be easy to rip out again
once we have proper support for PEP-613 type aliases, which hopefully
should be pretty soon!

## Test Plan

Added an mdtest
2025-09-12 22:20:38 +01:00
Alex Waygood 98708976e4
[ty] Fix subtyping/assignability of function- and class-literal types to callback protocols (#20363)
## Summary

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

We were treating any function as being assignable to any callback
protocol, because we were trying to figure out a type's `Callable`
supertype by looking up the `__call__` attribute on the type's
meta-type. But a function-literal's meta-type is `types.FunctionType`,
and `types.FunctionType.__call__` is `(...) -> Any`, which is not very
helpful!

While working on this PR, I also realised that assignability between
class-literals and callback protocols was somewhat broken too, so I
fixed that at the same time.

## Test Plan

Added mdtests
2025-09-12 22:20:09 +01:00
Igor Drokin c7f6b85fb3
[`ruff`] Allow dataclass attribute value instantiation from nested frozen dataclass (`RUF009`) (#20352)
## Summary
Resolves #20266

Definition of the frozen dataclass attribute can be instantiation of a
nested frozen dataclass as well as a non-nested one.

### Problem explanation
The `function_call_in_dataclass_default` function is invoked during the
"defined scope" stage, after all scopes have been processed. At this
point, the semantic references the top-level scope. When
`SemanticModel::lookup_attribute` executes, it searches for bindings in
the top-level module scope rather than the class scope, resulting in an
error.

To solve this issue, the lookup should be evaluated through the class
scope.

## Test Plan
- Added test case from issue

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
2025-09-12 16:46:49 -04:00
Takayuki Maeda 151ba49b36
[`pyupgrade`] Prevent infinite loop with `I002` and `UP026` (#20327)
## Summary

Fixes #19842

Prevent infinite loop with I002 and UP026

- Implement isort-aware handling for UP026 (deprecated mock import):
- Add CLI integration tests in crates/ruff/tests/lint.rs:

## Test Plan

I have added two integration tests
`pyupgrade_up026_respects_isort_required_import_fix` and
`pyupgrade_up026_respects_isort_required_import_from_fix` in
`crates/ruff/tests/lint.rs`.
2025-09-12 15:45:26 -05:00
Carl Meyer 82796df9b5
[ty] add cycle handling to BoundMethodType::into_callable_type() (#20369)
## Summary

This looks like it should fix the errors that we've been seeing in sympy
in recent mypy-primer runs.

## Test Plan

I wasn't able to reproduce the sympy failures locally; it looks like
there is probably a dependency on the order in which files are checked.
So I don't have a minimal reproducible example, and wasn't able to add a
test :/ Obviously I would be happier if we could commit a regression
test here, but since the change is straightforward and clearly
desirable, I'm not sure how many hours it's worth trying to track it
down.

Mypy-primer is still failing in CI on this PR, because it fails on the
"old" ty commit already (i.e. on main). But it passes [on a no-op PR
stacked on top of this](https://github.com/astral-sh/ruff/pull/20370),
which strongly suggests this PR fixes the problem.
2025-09-12 13:39:38 -07:00
Igor Drokin dfec94608c
[`flake8-bugbear`] Mark the fix for `unreliable-callable-check` as always unsafe (`B004`) (#20318)
## Summary
Resolves #20282

Makes the rule fix always unsafe, because the replacement may not be
semantically equivalent to the original expression, potentially changing
the behavior of the code.

Updated docstring with examples.

## Test Plan
- Added two tests from issue and regenerated the snapshot

---------

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-12 19:27:17 +00:00
Takayuki Maeda ff677a96e4
[`ruff`] Recognize t-strings, generators, and lambdas in `invalid-index-type` (`RUF016`) (#20213)
## Summary

Fixes #20204

Recognize t-strings, generators, and lambdas in RUF016

- Accept boolean literals as valid index and slice bounds.
- Add TString, Generator, and Lambda to `CheckableExprType`.
- Expand RUF016.py fixture and update snapshots accordingly.
2025-09-12 13:37:02 -05:00
Dylan b6bd32d9dc
Track t-strings and f-strings for token-based rules and suppression comments (#20357)
Our token-based rules and `noqa` extraction used an `Indexer` that kept
track of f-string ranges but not t-strings. We've updated the `Indexer`
and downstream uses thereof to handle both f-strings and t-strings.

Most of the diff is renaming and adding tests.

Note that much of the "new" logic gets to be naive because the lexer has
already ensured that f and t-string "starts" are paired with their
respective "ends", even amidst nesting and so on.

Finally: one could imagine wanting to know if a given interpolated
string range corresponds to an f-string or a t-string, but I didn't find
a place where we actually needed this.

Closes #20310
2025-09-12 13:00:12 -05:00
SkyBird ec863bcde7
[ruff]: Build loongarch64 binaries in CI (#20361)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

This PR adds support for building loongarch64 binaries in CI. As such
support has been merged in uv (astral-sh/uv#15387) it's time to consider
adding it to ruff.

Please note that as Ubuntu is not yet available for loongarch64, I have
elected to use a Debian Trixie container maintained by community
members. In addition, as Debian's pip does not allow installing modules
system-wide, I have modified the workflow to install additional modules
in a virtual environment.

Since the workflow is shared between all targets, the only way to handle
this difference (between Debian and Ubuntu) is just to install pip in a
venv for all targets. If there is a better (and less intrusive) way to
work around this, please let me know.

## Test Plan

Tests are included in CI and the loongarch64 artifacts built in [this
workflow](https://github.com/SkyBird233/ruff/actions/runs/17640270032/job/50125471548)
has been smoke tested.
2025-09-12 13:49:13 -04:00
Dan Parizher 7be11b496d
[`flake8-simplify`] Detect unnecessary `None` default for additional key expression types (`SIM910`) (#20343)
## Summary

Fixes #20341

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-09-12 10:17:54 -04:00
Alex Waygood 33b3d44ebd
[ty] Proper assignability/subtyping checks for protocols with method members (#20165) 2025-09-12 10:10:31 +00:00
Dhruv Manilawala bb9be263c7
[ty] Retry parameter matching for argument type expansion (#20153)
## Summary

This PR addresses an issue for a variadic argument when involved in
argument type expansion of overload call evaluation.

The issue is that the expansion of the variadic argument could result in
argument list of different arity. For example, in `*args: tuple[int] |
tuple[int, str]`, the expansion would lead to the variadic argument
being unpacked into 1 and 2 element respectively. This means that the
parameter matching that was performed initially isn't sufficient and
each expanded argument list would need to redo the parameter matching
again.

This is currently done by redoing the parameter matching directly,
maintaining the state of argument forms (and the conflicting forms), and
updating the `Bindings` values if it changes.

Closes: astral-sh/ty#735

## Test Plan

Update existing mdtest.
2025-09-12 08:40:07 +00:00
Douglas Creager 1cd8ab3f26
[ty] Remove the `Constraints` trait (#20355)
This PR removes the `Constraints` trait. We removed the `bool`
implementation several weeks back, and are using `ConstraintSet`
everywhere. There have been discussions about trying to include the
reason for an assignability failure as part of the result, but that
there are no concrete plans to do so soon, and it's not clear that we'll
need the `Constraints` trait to do that. (We can ideally just update the
`ConstraintSet` type directly.)

In the meantime, this just complicates the code for no good reason.

This PR is a pure refactoring, and contains no behavioral changes.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-09-11 20:55:28 -04:00
Ibraheem Ahmed 36888198a6
[ty] Integrate type context for bidirectional inference (#20337)
## Summary

Adds the infrastructure necessary to perform bidirectional type
inference (https://github.com/astral-sh/ty/issues/168) without any
typing changes.
2025-09-11 15:19:12 -04:00
Carl Meyer c4cd5c00fd
[ty] guard against recursion in determining completion kind (#20354)
## Summary

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

## Test Plan

Added test.
2025-09-11 11:25:06 -07:00
Brent Westbrook 5bf6977ded
Move GitHub rendering to `ruff_db` (#20320)
## Summary

This is the GitHub analog to #20117. This PR prepares to add a GitHub
output format to ty by moving the implementation from `ruff_linter` to
`ruff_db`. Hopefully this one is a bit easier to review
commit-by-commit. Almost all of the refactoring this time is in the
first commit, then the second commit adds the new `OutputFormat` variant
and moves the file into `ruff_db`. The third commit is just a small
touch up to use a private method that accommodates ty files so that we
can run the tests and update/move the snapshots.

I had to push a fourth commit to fix and test diagnostics without a
span/file.

## Test Plan

Existing tests
2025-09-11 13:11:15 -04:00
Douglas Creager abb705aa4e
[ty] Add dedicated variant for `NominalInstance(object)` (#20340)
Previously, `Type::object` would find the definition of the `object`
class in typeshed, load that in (to produce a `ClassLiteral` and
`ClassType`), and then create a `NominalInstance` of that class.

It's possible that we are using a typeshed that doesn't define `object`.
We will not be able to do much useful work with that kind of typeshed,
but it's still a possibility that we have to support at least without
panicking. Previously, we would handle this situation by falling back on
`Unknown`.

In most cases, that's a perfectly fine fallback! But `object` is also
our top type — the type of all values. `Unknown` is _not_ an acceptable
stand-in for the top type.

This PR adds a new `NominalInstance` variant for "instances of
`object`". Unlike other nominal instances, we do not need to load in
`object`'s `ClassType` to instantiate this variant. We will use this new
variant even when the current typeshed does not define an `object`
class, ensuring that we have a fully static representation of our top
type at all times.

There are several operations that need access to a nominal instance's
class, and for this new `object` variant we load it lazily only when
it's needed. That means this operation is now fallible, since this is
where the "typeshed doesn't define `object`" failure shows up.

This new approach also has the benefit of avoiding some salsa cycles
that were cropping up while I was debugging #20093, since the new
constraint set representation was trying to instantiate `Type::object`
while in the middle of processing its definition in typeshed. Cycle
handling was kicking in correctly and returning the `Unknown` fallback
mentioned above. But the constraint set implementation depends on
`Type::object` being a distinct and fully static type, highlighting that
this is a correctness fix, not just an optimization fix.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-09-11 13:02:58 -04:00
Alex Waygood 0e3697a643
[ty] Minor fixes to `Protocol` tests (#20347) 2025-09-11 14:42:13 +00:00
Carl Meyer ffd4340dce
[ty] use Type::Divergent to avoid panic in infinitely-nested-tuple implicit attribute (#20333)
## Summary

Use `Type::Divergent` to avoid "too many iterations" panic on an
infinitely-nested tuple in an implicit instance attribute.

The regression here is from checking all tuple elements to see if they
contain a Divergent type. It's 5% on one project, 1% on another, and
zero on the rest. I spent some time looking into eliminating this
regression by tracking a flag on inference results to note if they could
possibly contain any Divergent type, but this doesn't really work --
there are too many different ways a type containing a Divergent type
could enter an inference result. Still thinking about whether there are
other ways to reduce this. One option is if we see certain kinds of
non-atomic types that are commonly expensive to check for Divergent, we
could make `has_divergent_type` a Salsa query on those types.

## Test Plan

Added mdtest.

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-09-11 06:51:22 -07:00
Alex Waygood 89f17467ef
[ty] Require that implementors of `Constraints` also implement `Debug` (#20348)
The debug representation isn't as useful as calling `.display(db)`, but
it's still kind-of annoying when `dbg!()` calls don't compile locally
due to the compiler not being able to guarantee that an object of type
`impl Constraints` implements `Debug`
2025-09-11 14:34:40 +01:00
David Peter 59c8fda3f8
[ty] Fix CallableTypeOf[…] for classmethods (#20345)
## Summary

See https://github.com/astral-sh/ruff/pull/20338#discussion_r2337731998

## Test Plan

Regression test.
2025-09-11 10:14:38 +02:00
Carl Meyer c6b92b918e
[ty] split up `types/infer.rs` (#20342) 2025-09-10 19:25:07 -07:00
Amethyst Reese a3ec8ca9df
Remove Diagnostic::expect_range and all consumers (#20322)
Replace usage with `range().unwrap_or_default()` or more appropriate
alternatives based on context.
2025-09-10 17:19:20 -07:00
Takayuki Maeda 12c337c948
[`RUF102`] Respect rule redirects in invalid rule code detection (#20245)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

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

Fixes #20235

• Fix `RUF102` to properly handle rule redirects when validating noqa
codes
• Update `code_is_valid` to check redirect targets before determining
validity
• Add test case for rule redirects (TCH002 in this case)

## Test Plan

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

I have added a test case for rule redirects to
`crates/ruff_linter/resources/test/fixtures/ruff/RUF102.py`.
2025-09-10 14:27:07 -07:00
Dan Parizher 4c64ba4ee1
[`flake8-bandit`] Fix truthiness: dict-only `**` displays not truthy for `shell` (`S602`, `S604`, `S609`) (#20177)
## Summary
Fixes #19927
2025-09-10 17:06:33 -04:00
David Peter cde5e4e343
[ty] Fix `CallableTypeOf[…]` for bound methods (#20338)
## Summary

`CallableTypeOf[bound_method]` would previously bind `self` to the
bound method type itself, instead of binding it to the instance type
stored inside the bound method type.

## Test Plan

Added regression test
2025-09-10 21:13:23 +02:00
Alex Waygood 8a0edf0da8
[ty] Ensure various special-cased builtin functions are understood as assignable to `Callable` (#20331) 2025-09-10 19:03:33 +00:00
Alex Waygood d23cae870e
[ty] Ensure various special-cased bound methods are understood as assignable to `Callable` (#20330) 2025-09-10 19:58:54 +01:00
Douglas Creager 2ac4147435
[ty] Add mdtests that exercise constraint sets (#20319)
This PR adds a new `ty_extensions.ConstraintSet` class, which is used to
expose constraint sets to our mdtest framework. This lets us write a
large collection of unit tests that exercise the invariants and rewrite
rules of our constraint set implementation.

As part of this, `is_assignable_to` and friends are updated to return a
`ConstraintSet` instead of a `bool`, and we implement
`ConstraintSet.__bool__` to return when a constraint set is always
satisfied. That lets us still use
`static_assert(is_assignable_to(...))`, since the assertion will coerce
the constraint set to a bool, and also lets us
`reveal_type(is_assignable_to(...))` to see more detail about
whether/when the two types are assignable. That lets us get rid of
`reveal_when_assignable_to` and friends, since they are now redundant
with the expanded capabilities of `is_assignable_to`.
2025-09-10 13:22:19 -04:00
Alex Waygood ffead90410
[ty] Add more tests for special-cased builtin functions and methods (#20329) 2025-09-10 18:08:32 +01:00
Brent Westbrook a1fdd66f10
Bump 0.13.0 (#20336) 2025-09-10 12:11:22 -04:00
Shunsuke Shibayama 8770b95509
[ty] introduce `DivergentType` (#20312)
## Summary

From #17371

In #17371, `DivergentType` was introduced to prevent type inference for
recursive functions from diverging and causing panics. This turned out
to be useful for other divergent type inferences
(https://github.com/astral-sh/ruff/pull/17371#discussion_r2329337965),
so I extracted the introduction part of `DivergentType` into this PR so
that we can use it without waiting for the merge of #17371.

Note that this PR only introduces `DivergentType` and does not actually
address divergent type inference yet. Please refer to
https://github.com/astral-sh/ruff/pull/17371/files#diff-20b910c6e20faa962bb1642e111db1cbad8e66ace089bdd966ac9d7f9fa99ff2R542-R622
etc. when implementing handling of divergence suppression using this
type.

## Test Plan

---------

Co-authored-by: Carl Meyer <carl@oddbird.net>
2025-09-10 08:32:26 -07:00
David Peter 65982a1e14
[ty] Use 'unknown' specialization for upper bound on Self (#20325)
## Summary

closes https://github.com/astral-sh/ty/issues/1156

## Test Plan

Added a regression test
2025-09-10 17:00:28 +02:00
David Peter 57d1f7132d
[ty] Simplify unions of enum literals and subtypes thereof (#20324)
## Summary

When adding an enum literal `E = Literal[Color.RED]` to a union which
already contained a subtype of that enum literal(!), we were previously
not simplifying the union correctly. My assumption is that our property
tests didn't catch that earlier, because the only possible non-trivial
subytpe of an enum literal that I can think of is `Any & E`. And in
order for that to be detected by the property tests, it would have to
randomly generate `Any & E | E` and then also compare that with `E` on
the other side (in an equivalence test, or the subtyping-antisymmetry
test).

closes https://github.com/astral-sh/ty/issues/1155

## Test Plan

* Added a regression test.
* I also ran the property tests for a while, but probably not for two
months worth of daily CI runs.
2025-09-10 15:54:06 +02:00
Loïc Riegel 7a75702237 Ignore deprecated rules unless selected by exact code (#20167)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

Closes #18349

After this change:
- All deprecated rules are deselected by default
- They are only selected if the user specifically selects them by code,
e.g. `--select UP038`
- Thus, `--select ALL --select UP --select UP0` won't select the
deprecated rule UP038
- Documented the change in version policy. From now on, deprecating a
rule should increase the minor version

## Test Plan

Integration tests in "integration_tests.rs"

Also tested with a temporary test package:
```
~> ../../ruff/target/debug/ruff.exe check --select UP038
warning: Rule `UP038` is deprecated and will be removed in a future release.
warning: Detected debug build without --no-cache.
UP038 Use `X | Y` in `isinstance` call instead of `(X, Y)`
 --> main.py:2:11
  |
1 | def main():
2 |     print(isinstance(25, (str, int)))
  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
help: Convert to `X | Y`

Found 1 error.
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).

~> ../../ruff/target/debug/ruff.exe check --select UP03
warning: Detected debug build without --no-cache.
All checks passed!

~> ../../ruff/target/debug/ruff.exe check --select UP0
warning: Detected debug build without --no-cache.
All checks passed!

~> ../../ruff/target/debug/ruff.exe check --select UP
warning: Detected debug build without --no-cache.
All checks passed!

~> ../../ruff/target/debug/ruff.exe check --select ALL
# warnings and errors, but because of other errors, UP038 was deselected
```
2025-09-10 09:00:27 -04:00
Dylan 9ca632c84f Stabilize adding future import via config option (#20277)
Introduced in #19100. Removed gating, updated tests, removed warning(s),
and updated documentation.
2025-09-10 09:00:27 -04:00
Dylan 64fe7d30a3 [`flake8-errmsg`] Stabilize extending `raw-string-in-exception` (`EM101`) to support byte strings (#20273)
Introduced in #18867. Removed gating, updated tests, updated docs.
2025-09-10 09:00:27 -04:00
Brent Westbrook beeeb8d5c5 Stabilize the remaining Airflow rules (#20250)
- **Stabilize `airflow3-suggested-update` (`AIR311`)**
- **Stabilize `airflow3-suggested-to-move-to-provider` (`AIR312`)**
- **Stabilize `airflow3-removal` (`AIR301`)**
- **Stabilize `airflow3-moved-to-provider` (`AIR302`)**
- **Stabilize `airflow-dag-no-schedule-argument` (`AIR002`)**

I put this all in one PR to make it easier to double check with @Lee-W
before we merge this. I also made a few minor documentation changes and
updated one error message that I want to make sure are okay. But for the
most part this just moves the rules from `RuleGroup::Preview` to
`RuleGroup::Stable`!

Fixes #17749
2025-09-10 09:00:27 -04:00
Dylan b6fca52855 [`flake8-bugbear`] Stabilize support for non-context-manager calls in `assert-raises-exception` (`B017`) (#20274)
Introduced in #19063. Removed gating, updated tests. Not documented so
docs are the same.
2025-09-10 09:00:27 -04:00
Dylan ac7f882c78 [`flake8-commas`] Stabilize support for trailing comma checks in type parameter lists (`COM812`, `COM819`) (#20275)
Introduced in #19390. Removed gating, updated tests. No documentation to
update.
2025-09-10 09:00:27 -04:00
Dylan aef0a107a8 [`pygrep_hooks`] Stabilize using`AsyncMock` methods in `invalid-mock-access` (`PGH005`) (#20272)
Introduced in #18547. Removed gating, updated tests. Not documented so
documentation is the same.
2025-09-10 09:00:27 -04:00
Dylan 512395f4e6 Stabilize new strategy for classifying imports as first party (#20268)
This stabilizes the behavior introduced in #16565 which (roughly) tries
to match an import like `import a.b.c` to an actual directory path
`a/b/c` in order to label it as first-party, rather than simply looking
for a directory `a`.

Mainly this affects the sorting of imports in the presence of namespace
packages, but a few other rules are affected as well.
2025-09-10 09:00:27 -04:00
Dylan 5dec37fbaf [`pylint`] Stabilize ignoring `__init__.py` for `useless-import-alias` (`PLC0414`) (#20271)
Stabilizes change from #18400. Removed gating, updated docs, updated
tests.
2025-09-10 09:00:27 -04:00
Dylan 4bda9dad68 [`pylint`] Stabilize adding U+061C to `bidirectional-unicode` (`PLE2502`) (#20276)
Introduced in #20106. Removed gating. Updated tests. No documentation to
update.
2025-09-10 09:00:27 -04:00
Dylan 9d972d0583 [`flake8-simplify`] Stabilize fix safety of `multiple-with-statements` (`SIM117`) (#20270)
Introduced in #18208. Removed gating, updated tests and docs.
2025-09-10 09:00:27 -04:00
Brent Westbrook 1bbb553d6f Stabilize `pytest-raises-ambiguous-pattern` (`RUF043`) (#20253)
This one has been a bit contentious in the past. It usually uncovers
~700 ecosystem hits. See:

- https://github.com/astral-sh/ruff/pull/16657
- https://github.com/astral-sh/ruff/issues/16690

But I think there's consensus that it's okay to merge as-is. We'd love
an
autofix since it's so common, but we can't reliably tell what a user
meant. The
pattern is ambiguous after all 😆

This is the first rule that actually needed its test case relocated, but
the
docs looked good.
2025-09-10 09:00:27 -04:00
Brent Westbrook bb4c51afb2 Stabilize `f-string-number-format` (`FURB116`) (#20247)
Tests and docs look good
2025-09-10 09:00:27 -04:00
हिमांशु 3dbdd2b883 [`pyupgrade`] Remove `non-pep604-isinstance` (`UP038`) (#19156)
## Summary
This PR Removes deprecated UP038 as per instructed in #18727 
closes #18727 
## Test Plan
I have run tests non of them failing 

One Question i have is do we have to document that UP038 is removed?

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-10 09:00:27 -04:00
हिमांशु d8e43bf9f7 [`pandas-vet`] Remove `pandas-df-variable-name` (`PD901`) (#19223)
## Summary
closes #7710 

## Test Plan

It is is removal so i don't think we have to add tests otherwise i have
followed test plan mentioned in contributing.md

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-10 09:00:27 -04:00
हिमांशु ee448eab2d Remove deprecated macOS config file discovery (#19210)
## Summary
In #11115 we moved from defaulting to $HOME/Library/Application Support
to $XDG_HOME on macOS and added a deprecation warning if we find files
in the old location. So this PR removes the warning
closes #19145 

## Test Plan
Ran `cargo test`
2025-09-10 09:00:27 -04:00
Brent Westbrook 307b7df027 Stabilize `redundant-none-literal` (`PYI061`) (#20236)
Tests and docs look good
2025-09-10 09:00:27 -04:00
Brent Westbrook e139104aba Stabilize `generic-not-last-base-class` (`PYI059`) (#20246)
Tests and docs look good

We nearly stabilized this last time
(https://github.com/astral-sh/ruff/pull/18601), but it needed one more
bug fix and a documentation improvement
(https://github.com/astral-sh/ruff/pull/18611)
2025-09-10 09:00:27 -04:00
Brent Westbrook 9bb9b54168 Stabilize `useless-class-metaclass-type` (`UP050`) (#20230)
Tests and docs look good
2025-09-10 09:00:27 -04:00
Brent Westbrook 262f2767ca Stabilize `os-symlink` (`PTH211`) (#20229)
Summary
--

Rule and test/snapshot updated, the docs look good

My one hesitation here is that we could hold off stabilizing the rule
until its fix is also ready for stabilization, but this is also the only
preview PTH rule, so I think it's okay to stabilize the rule and later
(probably in the next minor release) stabilize the fixes together.
2025-09-10 09:00:27 -04:00
Brent Westbrook 1de9dac9d5 Stabilize `unused-unpacked-variable` (`RUF059`) (#20233)
The tests looked good. For the docs, I added a `## See also` section
pointing to
the closely-related F841 (unused-variable) and the corresponding section
to F841
pointing back to RUF059. It seems like you'd probably want both of these
active
or at least to know about the other when reading the docs.
2025-09-10 09:00:27 -04:00
Brent Westbrook 1cf6c2439f Stabilize `long-sleep-not-forever` (`ASYNC116`) (#20244)
Tests and docs look good
2025-09-10 09:00:27 -04:00
David Peter 2b51ec6531
[ty] Improve specialization-error diagnostics (#20326)
## Summary

Add information about the upper bound or the constraints of the type
variable to the `SpecializationError` diagnostics.
2025-09-10 14:01:23 +02:00
Alex Waygood b85c995927
[ty] `"foo".startswith` is not an instance of `types.MethodWrapperType` (#20317) 2025-09-10 11:14:26 +00:00
Alex Waygood fd7eb1e22f
[ty] Allow protocols to participate in nominal subtyping as well as structural subtyping (#20314) 2025-09-10 11:05:50 +00:00
Alex Waygood 4de7d653bd
[ty] Treat `Hashable`, and similar protocols, equivalently to `object` for subtyping/assignability (#20284) 2025-09-10 11:38:58 +01:00
Brent Westbrook 9cb37db510
Bump LibCST to 1.8.4 (#20321)
This should fix the fuzz build on `main`. They added support for
t-strings, which made one of our matches non-exhaustive.

https://github.com/Instagram/LibCST/releases/tag/v1.8.4
2025-09-09 17:34:33 -04:00
Douglas Creager ed06fb5ce2
[ty] Use partial-order-friendly representation of typevar constraints (#20306)
The constraint representation that we added in #19997 was subtly wrong,
in that it didn't correctly model that type assignability is a _partial_
order — it's possible for two types to be incomparable, with neither a
subtype of the other. That means the negation of a constraint like `T ≤
t` (typevar `T` must be a subtype of `t`) is **_not_** `t < T`, but
rather `t < T ∨ T ≁ t` (using ≁ to mean "not comparable to").

That means we need to update our constraint representation to be an
enum, so that we can track both _range_ constraints (upper/lower bound
on the typevar), and these new _incomparable_ constraints.

Since we need an enum now, that also lets us simplify how we were
modeling range constraints. Before, we let the lower/upper bounds be
either open (<) or closed (≤). Now, range constraints are always closed,
and we add a third kind of constraint for _not equivalent_ (≠). We can
translate an open upper bound `T < t` into `T ≤ t ∧ T ≠ t`.

We already had the logic for doing adding _clauses_ to a _set_ by doing
a pairwise simplification. We copy that over to where we add
_constraints_ to a _clause_. To calculate the intersection or union of
two constraints, the new enum representation makes it easy to break down
all of the possibilities into a small number of cases: intersect range
with range, intersect range with not-equivalent, etc. I've done the math
[here](https://dcreager.net/theory/constraints/) to show that the
simplifications for each of these cases is correct.
2025-09-09 15:54:47 -04:00
Igor Drokin 54df73c9f7
[`pyupgrade`] Apply `UP008` only when the `__class__` cell exists (#19424)
## Summary

Resolves #19357 

Skip UP008 diagnostic for `builtins.super(P, self)` calls when
`__class__` is not referenced locally, preventing incorrect fixes.

**Note:** I haven't found concrete information about which cases
`__class__` will be loaded into the scope. Let me know if anyone has
references, it would be useful to enhance the implementation. I did a
lot of tests to determine when `__class__` is loaded. Considered
sources:
1. [Python doc
super](https://docs.python.org/3/library/functions.html#super)
2. [Python doc classes](https://docs.python.org/3/tutorial/classes.html)
3. [pep-3135](https://peps.python.org/pep-3135/#specification)

As I understand it, Python will inject at runtime into local scope a
`__class__` variable if it detects references to `super` or `__class__`.
This allows calling `super()` and passing appropriate parameters.
However, the compiler doesn't do the same for `builtins.super`, so we
need to somehow introduce `__class__` into the local scope.

I figured out `__class__` will be in scope with valid value when two
conditions are met:
1. `super` or `__class__` names have been loaded within function scope
4. `__class__` is not overridden.

I think my solution isn't elegant, so I would be appreciate a detailed
review.

## Test Plan

Added 19 test cases, updated snapshots.

---------

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
2025-09-09 14:59:23 -04:00
Amethyst Reese d7524ea6d4
Refactor diagnostic start|end location helpers (#20309)
- Renames functions to drop `expect_` from names.
- Make functions return `Option<LineColumn>` to appropriately signal
  when range is not available.
- Update existing consumers to use `unwrap_or_default()`. Uncertain if
  there are better fallback behaviors for individual consumers.
2025-09-09 11:39:31 -07:00
Alex Waygood bf66178959
[ty] Add tests for protocols with generic method members (#20316) 2025-09-09 16:44:00 +00:00
Zanie Blue 9cdac2d6fb
Add support for using uv as an alternative formatter backend (#19665)
This adds a new `backend: internal | uv` option to the LSP
`FormatOptions` allowing users to perform document and range formatting
operations though uv. The idea here is to prototype a solution for users
to transition to a `uv format` command without encountering version
mismatches (and consequently, formatting differences) between the LSP's
version of `ruff` and uv's version of `ruff`.

The primarily alternative to this would be to use uv to discover the
`ruff` version used to start the LSP in the first place. However, this
would increase the scope of a minimal `uv format` command beyond "run a
formatter", and raise larger questions about how uv should be used to
coordinate toolchain discovery. I think those are good things to
explore, but I'm hesitant to let them block a `uv format`
implementation. Another downside of using uv to discover `ruff`, is that
it needs to be implemented _outside_ the LSP; e.g., we'd need to change
the instructions on how to run the LSP and implement it in each editor
integration, like the VS Code plugin.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-09-09 20:39:53 +05:30
Igor Drokin 79706a2e26
[`pyupgrade`] Enable rule triggering for stub files (`UP043`) (#20027)
## Summary
Resolves #20011

Implemented alternative triggering condition for rule
[`UP043`](https://docs.astral.sh/ruff/rules/unnecessary-default-type-args/)
based on requirements outlined in [issue
#20011](https://github.com/astral-sh/ruff/issues/20011)
## Test Plan
Created .pyi file to ensure triggering the rule

---------

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
Co-authored-by: dylwil3 <dylwil3@gmail.com>
2025-09-09 12:57:26 +00:00
Andrew Gallant 25853e2377 Allow the `if_not_else` Clippy lint
Specifically, the [`if_not_else`] lint will sometimes flag
code to change the order of `if` and `else` bodies if this
would allow a `!` to be removed. While perhaps tasteful in
some cases, there are many cases in my experience where this
bows to other competing concerns that impact readability.
(Such as the relative sizes of the `if` and `else` bodies,
or perhaps an ordering that just makes the code flow in a
more natural way.)

[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#/if_not_else
2025-09-09 08:49:25 -04:00
Renkai Ge 61f906d8e7
[ty] equality narrowing on enums that don't override `__eq__` or `__ne__` (#20285)
Add equality narrowing for enums, if they don't override `__eq__` or `__ne__` in an unsafe way.

Follow-up to PR https://github.com/astral-sh/ruff/pull/20164

Fixes https://github.com/astral-sh/ty/issues/939
2025-09-08 16:56:28 -07:00
Shunsuke Shibayama 08a561fc05
[ty] more precise lazy scope place lookup (#19932)
## Summary

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

Now lazy snapshots are updated to take into account new bindings on
every symbol reassignment.

```python
def outer(x: A | None):
    if x is None:
        x = A()

    reveal_type(x)  # revealed: A

    def inner() -> None:
        # lazy snapshot: {x: A}
        reveal_type(x)  # revealed: A
    inner()

def outer() -> None:
    x = None

    x = 1

    def inner() -> None:
        # lazy snapshot: {x: Literal[1]} -> {x: Literal[1, 2]}
        reveal_type(x)  # revealed: Literal[1, 2]
    inner()

    x = 2
```

Closes astral-sh/ty#559.

## Test Plan

Some TODOs in `public_types.md` now work properly.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-08 21:08:35 +00:00
Ibraheem Ahmed aa5d665d52
[ty] Add support for generic PEP695 type aliases (#20219)
## Summary

Adds support for generic PEP695 type aliases, e.g.,
```python
type A[T] = T
reveal_type(A[int]) # A[int]
```

Resolves https://github.com/astral-sh/ty/issues/677.
2025-09-08 13:26:21 -07:00
David Peter d55edb3d74
[ty] Support "legacy" `typing.Self` in combination with PEP 695 generic contexts (#20304)
## Summary

Support cases like the following, where we need the generic context to
include both `Self` and `T` (not just `T`):

```py
from typing import Self

class C:
    def method[T](self: Self, arg: T): ...

C().method(1)
```

closes https://github.com/astral-sh/ty/issues/1131

## Test Plan

Added regression test
2025-09-08 16:57:09 +02:00
arielle ab86ae1760
[`pep8-naming`] Fix formatting of `__all__` (`N816`) (#20301)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

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

## Summary

Noticed this was not escaped when writing a project that parses the
result of `ruff rule --outputformat json`. This is visible here:
<https://docs.astral.sh/ruff/rules/mixed-case-variable-in-global-scope/#why-is-this-bad>

## Test Plan

documentation only

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-09-08 14:40:38 +00:00
David Peter 916968d0ff
[ty] Fix signature of `NamedTupleLike._make` (#20302) 2025-09-08 14:53:17 +02:00
Alex Waygood deb3d3d150
[ty] Fall back to `object` for attribute access on synthesized protocols (#20286) 2025-09-08 13:04:37 +01:00
justin 08fcf7e106
[ty] initial support for `slots=True` in dataclasses (#20278) 2025-09-07 18:25:35 +01:00
Carl Meyer 2467c4352e
[ty] propagate visitors and constraints through has_relation_in_invariant_position (#20259)
## Summary

The sub-checks for assignability and subtyping of materializations
performed in `has_relation_in_invariant_position` and
`is_subtype_in_invariant_position` need to propagate the
`HasRelationToVisitor`, or we can stack overflow.

A side effect of this change is that we also propagate the
`ConstraintSet` through, rather than using `C::from_bool`, which I think
may also become important for correctness in cases involving type
variables (though it isn't testable yet, since we aren't yet actually
creating constraints other than always-true and always-false.)

## Test Plan

Added mdtest (derived from code found in pydantic) which
stack-overflowed before this PR.

With this change incorporated, pydantic now checks successfully on my
draft PR for PEP 613 TypeAlias support.
2025-09-06 00:17:17 +00:00
Amethyst Reese a27c64811e
Add support for sorting diagnostics without a range (#20257)
- Update ruff ordering compare to work with optional ranges (treating
  them as starting from zero)
2025-09-05 15:23:30 -07:00
Alex Waygood 5d52902e18
[ty] Implement the legacy PEP-484 convention for indicating positional-only parameters (#20248)
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-09-05 17:56:06 +01:00