Commit Graph

248 Commits

Author SHA1 Message Date
Alex Waygood 0079cc6817
[ty] Minor cleanup for `site-packages` discovery logic (#18446) 2025-06-03 18:49:14 +00:00
Matthew Mckee e8ea40012a
[ty] Add generic inference for dataclasses (#18443)
## Summary

An issue seen here https://github.com/astral-sh/ty/issues/500

The `__init__` method of dataclasses had no inherited generic context,
so we could not infer the type of an instance from a constructor call
with generics

## Test Plan

Add tests to classes.md` in generics folder
2025-06-03 09:59:43 -07:00
Abhijeet Prasad Bodas 71d8a5da2a
[ty] dataclasses: Allow using dataclasses.dataclass as a function. (#18440)
## Summary

Part of https://github.com/astral-sh/ty/issues/111

Using `dataclass` as a function, instead of as a decorator did not work
as expected prior to this.
Fix that by modifying the dataclass overload's return type.

## Test Plan

New mdtests, fixing the existing TODO.
2025-06-03 09:50:29 -07:00
Douglas Creager 2c3b3d3230
[ty] Create separate `FunctionLiteral` and `FunctionType` types (#18360)
This updates our representation of functions to more closely match our
representation of classes.

The new `OverloadLiteral` and `FunctionLiteral` classes represent a
function definition in the AST. If a function is generic, this is
unspecialized. `FunctionType` has been updated to represent a function
type, which is specialized if the function is generic. (These names are
chosen to match `ClassLiteral` and `ClassType` on the class side.)

This PR does not add a separate `Type` variant for `FunctionLiteral`.
Maybe we should? Possibly as a follow-on PR?

Part of https://github.com/astral-sh/ty/issues/462

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-03 10:59:31 -04:00
Dhruv Manilawala 8d98c601d8
[ty] Infer `list[T]` when unpacking non-tuple type (#18438)
## Summary

Follow-up from #18401, I was looking at whether that would fix the issue
at https://github.com/astral-sh/ty/issues/247#issuecomment-2917656676
and it didn't, which made me realize that the PR only inferred `list[T]`
when the value type was tuple but it could be other types as well.

This PR fixes the actual issue by inferring `list[T]` for the non-tuple
type case.

## Test Plan

Add test cases for starred expression involved with non-tuple type. I
also added a few test cases for list type and list literal.

I also verified that the example in the linked issue comment works:
```py
def _(line: str):
    a, b, *c = line.split(maxsplit=2)
    c.pop()
```
2025-06-03 19:17:47 +05:30
David Peter 0986edf427
[ty] Meta-type of type variables should be type[..] (#18439)
## Summary

Came across this while debugging some ecosystem changes in
https://github.com/astral-sh/ruff/pull/18347. I think the meta-type of a
typevar-annotated variable should be equal to `type`, not `<class
'object'>`.

## Test Plan

New Markdown tests.
2025-06-03 15:22:00 +02:00
lipefree f23d2c9b9e
[ty] Support using legacy typing aliases for generic classes in type annotations (#18404)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-06-03 12:09:51 +01:00
Dhruv Manilawala 2289187b74
Infer `list[T]` for starred target in unpacking (#18401)
## Summary

Closes: astral-sh/ty#191

## Test Plan

Update existing tests.
2025-06-03 07:25:07 +05:30
lipefree f379eb6e62
[ty] Treat lambda functions as instances of types.FunctionType (#18431) 2025-06-02 16:46:26 +01:00
Alex Waygood 47698883ae
[ty] Fix false positives for legacy `ParamSpec`s inside `Callable` type expressions (#18426) 2025-06-02 14:10:00 +01:00
Alex Waygood e2d96df501
[ty] Improve diagnostics if the user attempts to import a stdlib module that does not exist on their configured Python version (#18403) 2025-06-02 10:52:26 +00:00
Matthew Mckee 97b824db3e
[ty] Ensure `Literal` types are considered assignable to anything their `Instance` supertypes are assignable to (#18351) 2025-06-01 16:39:56 +01:00
Micha Reiser 220ab88779
[ty] Promote projects to good that now no longer hang (#18370) 2025-06-01 17:25:46 +02:00
Micha Reiser 54f597658c
[ty] Fix multithreading related hangs and panics (#18238) 2025-06-01 11:07:55 +02:00
Zanie Blue 88866f0048
[ty] Infer the Python version from the environment if feasible (#18057)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-05-30 21:22:51 +00:00
Dylan 9bbf4987e8
Implement template strings (#17851)
This PR implements template strings (t-strings) in the parser and
formatter for Ruff.

Minimal changes necessary to compile were made in other parts of the code (e.g. ty, the linter, etc.). These will be covered properly in follow-up PRs.
2025-05-30 15:00:56 -05:00
Carl Meyer ad024f9a09
[ty] support callability of bound/constrained typevars (#18389)
## Summary

Allow a typevar to be callable if it is bound to a callable type, or
constrained to callable types.

I spent some time digging into why this support didn't fall out
naturally, and ultimately the reason is that we look up `__call__` on
the meta type (since its a dunder), and our implementation of
`Type::to_meta_type` for `Type::Callable` does not return a type with
`__call__`.

A more general solution here would be to have `Type::to_meta_type` for
`Type::Callable` synthesize a protocol with `__call__` and return an
intersection with that protocol (since for a type to be callable, we
know its meta-type must have `__call__`). That solution could in
principle also replace the special-case handling of `Type::Callable`
itself, here in `Type::bindings`. But that more general approach would
also be slower, and our protocol support isn't quite ready for that yet,
and handling this directly in `Type::bindings` is really not bad.

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

## Test Plan

Added mdtests.
2025-05-30 12:01:51 -07:00
Andrew Gallant fc549bda94
[ty] Minor tweaks to "list all members" docs and tests (#18388)
Ref https://github.com/astral-sh/ruff/pull/18251#pullrequestreview-2881810681
2025-05-30 13:36:57 -04:00
Alex Waygood 77c8ddf101
[ty] Fix broken property tests for disjointness (#18384) 2025-05-30 16:49:20 +01:00
David Peter e730f27f80
[ty] List available members for a given type (#18251)
This PR adds initial support for listing all attributes of
an object. It is exposed through a new `all_members`
routine in `ty_extensions`, which is in turn used to test
the functionality.

The purpose of listing all members is for code
completion. That is, given a `object.<CURSOR>`, we
would like to list all available attributes on
`object`.
2025-05-30 11:24:20 -04:00
Micha Reiser 8005ebb405
Update salsa past generational id change (#18362) 2025-05-30 15:31:33 +02:00
Alex Waygood ad2f667ee4
[ty] Improve tests for `site-packages` discovery (#18374)
## Summary

- Convert tests demonstrating our resilience to malformed/absent
`version` fields in `pyvenf.cfg` files to mdtests. Also make them more
expansive.
- Convert the regression test I added in
https://github.com/astral-sh/ruff/pull/18157 to an mdtest
- Add comments next to unit tests that cannot be converted to mdtests
(but where it's not obvious why they can't) so I don't have to do this
exercise again 😄
- In `site_packages.rs`, factor out the logic for figuring out where we
expect the system-installation `site-packages` to be. Currently we have
the same logic twice.

## Test Plan

`cargo test -p ty_python_semantic`
2025-05-30 07:32:21 +01:00
Carl Meyer 363f061f09
[ty] _typeshed.Self is not a special form (#18377)
## Summary

This change was based on a mis-reading of a comment in typeshed, and a
wrong assumption about what was causing a test failure in a prior PR.
Reverting it doesn't cause any tests to fail.

## Test Plan

Existing tests.
2025-05-29 17:11:13 -07:00
InSync 9b0dfc505f
[ty] Callable types are disjoint from non-callable `@final` nominal instance types (#18368)
## Summary

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

Callable types are now considered to be disjoint from nominal instance
types where:

* The class is `@final`, and
* Its `__call__` either does not exist or is not assignable to `(...) ->
Unknown`.

## Test Plan

Markdown tests.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-05-29 23:27:27 +00:00
lipefree 695de4f27f
[ty] Add diagnosis for function with no return statement but with return type annotation (#18359)
## Summary

Partially implement https://github.com/astral-sh/ty/issues/538, 
```py
from pathlib import Path

def setup_test_project(registry_name: str, registry_url: str, project_dir: str) -> Path:
    pyproject_file = Path(project_dir) / "pyproject.toml"
    pyproject_file.write_text("...", encoding="utf-8")
```
As no return statement is defined in the function `setup_test_project`
with annotated return type `Path`, we provide the following diagnosis :

- error[invalid-return-type]: Function **always** implicitly returns
`None`, which is not assignable to return type `Path`

with a subdiagnostic : 
- note: Consider changing your return annotation to `-> None` or adding a `return` statement
 
## Test Plan

mdtests with snapshots to capture the subdiagnostic. I have to mention
that existing snapshots were modified since they now fall in this
category.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-05-29 23:17:18 +00:00
Andrew Gallant 33ed502edb ty_ide: improve completions by using scopes
Previously, completions were based on just returning every identifier
parsed in the current Python file. In this commit, we change it to
identify an expression under the cursor and then return all symbols
available to the scope containing that expression.

This is still returning too much, and also, in some cases, not enough.
Namely, it doesn't really take the specific context into account other
than scope. But this does improve on the status quo. For example:

    def foo(): ...
    def bar():
        def fast(): ...
    def foofoo(): ...

    f<CURSOR>

When asking for completions here, the LSP will no longer include `fast`
as a possible completion in this context.

Ref https://github.com/astral-sh/ty/issues/86
2025-05-29 10:31:30 -04:00
Alex Waygood 47a2ec002e
[ty] Split `Type::KnownInstance` into two type variants (#18350) 2025-05-29 14:47:55 +01:00
Matthew Mckee c60b4d7f30
[ty] Add subtyping between Callable types and class literals with `__init__` (#17638)
## Summary

Allow classes with `__init__` to be subtypes of `Callable`

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

## Test Plan

Update is_subtype_of.md

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-05-28 13:43:07 -07:00
Douglas Creager 452f992fbc
[ty] Simplify signature types, use them in `CallableType` (#18344)
There were many fields in `Signature` and friends that really had more
to do with how a signature was being _used_ — how it was looked up,
details about an individual call site, etc. Those fields more properly
belong in `Bindings` and friends.

This is a pure refactoring, and should not affect any tests or ecosystem
projects.

I started on this journey in support of
https://github.com/astral-sh/ty/issues/462. It seemed worth pulling out
as a separate PR.

One major concrete benefit of this refactoring is that we can now use
`CallableSignature` directly in `CallableType`. (We can't use
`CallableSignature` directly in that `Type` variant because signatures
are not currently interned.)
2025-05-28 13:11:45 -04:00
Alex Waygood a5ebb3f3a2
[ty] Support ephemeral uv virtual environments (#18335) 2025-05-28 14:54:59 +00:00
David Peter bbcd7e0196
[ty] Synthetic function-like callables (#18242)
## Summary

We create `Callable` types for synthesized functions like the `__init__`
method of a dataclass. These generated functions are real functions
though, with descriptor-like behavior. That is, they can bind `self`
when accessed on an instance. This was modeled incorrectly so far.

## Test Plan

Updated tests
2025-05-28 10:00:56 +02:00
Carl Meyer 3eada01153
put similar dunder-call tests next to each other (#18343)
Follow-up from post-land review on
https://github.com/astral-sh/ruff/pull/18260
2025-05-27 12:16:41 -07:00
Alex Waygood 3e811fc369
[ty] Derive `PartialOrd, Ord` for `KnownInstanceType` (#18340) 2025-05-27 19:37:01 +01:00
Alex Waygood 743764d384
[ty] Simplify `Type::try_bool()` (#18342)
## Summary

I don't think we're ever going to add any `KnownInstanceType` variants
that evaluate to `False` in a boolean context; the
`KnownInstanceType::bool()` method just seems like unnecessary
complexity.

## Test Plan

`cargo test -p ty_python_semantic`
2025-05-27 19:32:17 +01:00
Alex Waygood e03e05d2b3
[ty] Simplify `Type::normalized` slightly (#18339) 2025-05-27 18:08:59 +00:00
Alex Waygood 9ec4a178a4
[ty] Move arviz off the list of selected primer projects (#18336) 2025-05-27 17:51:19 +01:00
Alex Waygood 6453ac9ea1
[ty] Tell the user why we inferred a certain Python version when reporting version-specific syntax errors (#18295) 2025-05-26 20:44:43 +00:00
Alex Waygood 0a11baf29c
[ty] Implement implicit inheritance from `Generic[]` for PEP-695 generic classes (#18283) 2025-05-26 20:40:16 +01:00
lipefree 1d20cf9570
[ty] Add hint if async context manager is used in non-async with statement (#18299)
# Summary

Adds a subdiagnostic hint in the following scenario where a
synchronous `with` is used with an async context manager:
```py
class Manager:
    async def __aenter__(self): ...
    async def __aexit__(self, *args): ...

# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not implement `__enter__` and `__exit__`"
# note: Objects of type `Manager` *can* be used as async context managers
# note: Consider using `async with` here
with Manager():
    ...
```

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

## Test Plan

New MD snapshot tests

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-05-26 21:34:47 +02:00
David Peter 4e68dd96a6
[ty] Infer types for ty_extensions.Intersection[A, B] tuple expressions (#18321)
## Summary

fixes astral-sh/ty#366

## Test Plan

* Added panic corpus regression tests
* I also wrote a hover regression test (see below), but decided not to
include it. The corpus tests are much more "effective" at finding these
types of errors, since they exhaustively check all expressions for
types.

<details>

```rs
#[test]
fn hover_regression_test_366() {
    let test = cursor_test(
        r#"
    from ty_extensions import Intersection

    class A: ...
    class B: ...

    def _(x: Intersection[A,<CURSOR> B]):
        pass
    "#,
    );

    assert_snapshot!(test.hover(), @r"
    A & B
    ---------------------------------------------
    ```text
    A & B
    ```
    ---------------------------------------------
    info[hover]: Hovered content is
     --> main.py:7:31
      |
    5 |         class B: ...
    6 |
    7 |         def _(x: Intersection[A, B]):
      |                               ^^-^
      |                               | |
      |                               | Cursor offset
      |                               source
    8 |             pass
      |
    ");
}
```

</details>
2025-05-26 17:08:52 +02:00
David Peter e1b662bf5d
[ty] Always pass `NO_INSTANCE_FALLBACK` in `try_call_dunder_with_policy` (#18315)
## Summary

The previous `try_call_dunder_with_policy` API was a bit of a footgun
since you needed to pass `NO_INSTANCE_FALLBACK` in *addition* to other
policies that you wanted for the member lookup. Implicit calls to dunder
methods never access instance members though, so we can do this
implicitly in `try_call_dunder_with_policy`.

No functional changes.
2025-05-26 13:20:27 +02:00
Felix Scherz f885cb8a2f
[ty] use `__getattribute__` to lookup unknown members on a type (#18280)
## Summary

`Type::member_lookup_with_policy` now falls back to calling
`__getattribute__` when a member cannot be found as a second fallback
after `__getattr__`.


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

## Test Plan

Added markdown tests.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: David Peter <mail@david-peter.de>
2025-05-26 12:59:45 +02:00
David Peter 4ef2c223c9
[ty] Respect `MRO_NO_OBJECT_FALLBACK` policy when looking up symbols on `type` instances (#18312)
## Summary

This should address a problem that came up while working on
https://github.com/astral-sh/ruff/pull/18280. When looking up an
attribute (typically a dunder method) with the `MRO_NO_OBJECT_FALLBACK`
policy, the attribute is first looked up on the meta type. If the meta
type happens to be `type`, we go through the following branch in
`find_name_in_mro_with_policy`:


97ff015c88/crates/ty_python_semantic/src/types.rs (L2565-L2573)

The problem is that we now look up the attribute on `object` *directly*
(instead of just having `object` in the MRO). In this case,
`MRO_NO_OBJECT_FALLBACK` has no effect in `class_member_from_mro`:


c3feb8ce27/crates/ty_python_semantic/src/types/class.rs (L1081-L1082)

So instead, we need to explicitly respect the `MRO_NO_OBJECT_FALLBACK`
policy here by returning `Symbol::Unbound`.

## Test Plan

Added new Markdown tests that explain the ecosystem changes that we
observe.
2025-05-26 12:03:29 +02:00
David Peter 7eca6f96e3
[ty] Fix attribute writes to unions/intersections including modules (#18313)
## Summary

Fix a bug that involved writes to attributes on union/intersection types
that included modules as elements.

This is a prerequisite to avoid some ecosystem false positives in
https://github.com/astral-sh/ruff/pull/18312

## Test Plan

Added regression test
2025-05-26 11:41:03 +02:00
Alex Waygood e293411679
[ty] `get_protocol_members` returns a frozenset, not a tuple (#18284) 2025-05-23 23:20:34 +00:00
lipefree 53d19f8368
[ty] Resolving Python path using `CONDA_PREFIX` variable to support Conda and Pixi (#18267) 2025-05-23 20:00:42 +02:00
InSync a1399656c9
[ty] Fix binary intersection comparison inference logic (#18266)
## Summary

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

`infer_binary_intersection_type_comparison()` now checks for all
positive members before concluding that an operation is unsupported for
a given intersection type.

## Test Plan

Markdown tests.

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-05-23 12:55:17 +02:00
David Peter 93ac0934dd
[ty] Type compendium (#18263)
## Summary

This is something I wrote a few months ago, and continued to update from
time to time. It was mostly written for my own education. I found a few
bugs while writing it at the time (there are still one or two TODOs in
the test assertions that are probably bugs). Our other tests are fairly
comprehensive, but they are usually structured around a certain
functionality or operation (subtyping, assignability, narrowing). The
idea here was to focus on individual *types and their properties*.

closes #197 (added `JustFloat` and `JustComplex` to `ty_extensions`).
2025-05-23 11:41:31 +02:00
Alex Waygood d02c9ada5d
[ty] Do not carry the generic context of `Protocol` or `Generic` in the `ClassBase` enum (#17989)
## Summary

It doesn't seem to be necessary for our generics implementation to carry
the `GenericContext` in the `ClassBase` variants. Removing it simplifies
the code, fixes many TODOs about `Generic` or `Protocol` appearing
multiple times in MROs when each should only appear at most once, and
allows us to more accurately detect runtime errors that occur due to
`Generic` or `Protocol` appearing multiple times in a class's bases.

In order to remove the `GenericContext` from the `ClassBase` variant, it
turns out to be necessary to emulate
`typing._GenericAlias.__mro_entries__`, or we end up with a large number
of false-positive `inconsistent-mro` errors. This PR therefore also does
that.

Lastly, this PR fixes the inferred MROs of PEP-695 generic classes,
which implicitly inherit from `Generic` even if they have no explicit
bases.

## Test Plan

mdtests
2025-05-22 21:37:03 -04:00
Carl Meyer 0b181bc2ad
Fix instance vs callable subtyping/assignability (#18260)
## Summary

Fix some issues with subtying/assignability for instances vs callables.
We need to look up dunders on the class, not the instance, and we should
limit our logic here to delegating to the type of `__call__`, so it
doesn't get out of sync with the calls we allow.

Also, we were just entirely missing assignability handling for
`__call__` implemented as anything other than a normal bound method
(though we had it for subtyping.)

A first step towards considering what else we want to change in
https://github.com/astral-sh/ty/issues/491

## Test Plan

mdtests

---------

Co-authored-by: med <medioqrity@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-05-22 19:47:05 +00:00
InSync bcefa459f4
[ty] Rename `call-possibly-unbound-method` to `possibly-unbound-implicit-call` (#18017) 2025-05-22 15:25:51 +00:00
Brandt Bucher 91b7a570c2
[ty] Implement Python's floor division semantics for `Literal` `int`s (#18249)
Division works differently in Python than in Rust. If the result is
negative and there is a remainder, the division rounds down (instead of
towards zero). The remainder needs to be adjusted to compensate so that
`(lhs // rhs) * rhs + (lhs % rhs) == lhs`.

Fixes astral-sh/ty#481.
2025-05-22 10:42:29 -04:00
justin 01eeb2f0d6
[ty] Support frozen dataclasses (#17974)
## Summary
https://github.com/astral-sh/ty/issues/111

This PR adds support for `frozen` dataclasses. It will emit a diagnostic
with a similar message to mypy

Note: This does not include emitting a diagnostic if `__setattr__` or
`__delattr__` are defined on the object as per the
[spec](https://docs.python.org/3/library/dataclasses.html#module-contents)

## Test Plan
mdtest

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-05-22 00:20:34 -04:00
Alex Waygood cb04343b3b
[ty] Split `invalid-base` error code into two error codes (#18245) 2025-05-21 18:02:39 -04:00
Alex Waygood 02394b8049
[ty] Improve `invalid-type-form` diagnostic where a module-literal type is used in a type expression and the module has a member which would be valid in a type expression (#18244) 2025-05-21 15:38:56 -04:00
Alex Waygood 41463396cf
[ty] Add a subdiagnostic if `invalid-return-type` is emitted on a method with an empty body on a non-protocol subclass of a protocol class (#18243) 2025-05-21 17:38:07 +00:00
David Peter da4be789ef
[ty] Ignore `ClassVar` declarations when resolving instance members (#18241)
## Summary

Make sure that the following definitions all lead to the same outcome
(bug originally noticed by @AlexWaygood)

```py
from typing import ClassVar

class Descriptor:
    def __get__(self, instance, owner) -> int:
        return 42

class C:
    a: ClassVar[Descriptor]
    b: Descriptor = Descriptor()
    c: ClassVar[Descriptor] = Descriptor()

reveal_type(C().a)  # revealed: int  (previously: int | Descriptor)
reveal_type(C().b)  # revealed: int
reveal_type(C().c)  # revealed: int
```

## Test Plan

New Markdown tests
2025-05-21 19:23:35 +02:00
Max Mynter 02fd48132c
[ty] Don't warn `yield` not in function when `yield` is in function (#18008) 2025-05-21 18:16:25 +02:00
Alex Waygood d37592175f
[ty] Tell the user why we inferred the Python version we inferred (#18082) 2025-05-21 11:06:27 -04:00
Micha Reiser 76ab77fe01
[ty] Support `import <namespace>` and `from <namespace> import module` (#18137) 2025-05-21 07:28:33 +00:00
Carl Meyer d098118e37
[ty] disable division-by-zero by default (#18220)
## Summary

I think `division-by-zero` is a low-value diagnostic in general; most
real division-by-zero errors (especially those that are less obvious to
the human eye) will occur on values typed as `int`, in which case we
don't issue the diagnostic anyway. Mypy and pyright do not emit this
diagnostic.

Currently the diagnostic is prone to false positives because a) we do
not silence it in unreachable code, and b) we do not implement narrowing
of literals from inequality checks. We will probably fix (a) regardless,
but (b) is low priority apart from division-by-zero.

I think we have many more important things to do and should not allow
false positives on a low-value diagnostic to be a distraction. Not
opposed to re-enabling this diagnostic in future when we can prioritize
reducing its false positives.

References https://github.com/astral-sh/ty/issues/443

## Test Plan

Existing tests.
2025-05-20 14:47:56 -04:00
Ramil Aleskerov 7917269d9a
[ty] Add support for PyPy virtual environments (#18203)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-05-20 14:46:50 -04:00
Alex Waygood e8d4f6d891
[ty] Ensure that a function-literal type is always equivalent to itself (#18227) 2025-05-20 14:11:03 -04:00
Alex Waygood 60b486abce
[ty] Deeply normalize many types (#18222) 2025-05-20 11:41:26 -04:00
InSync 76ab3425d3
[ty] Integer indexing into `bytes` returns `int` (#18218)
## Summary

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

ty was hardcoded to infer `BytesLiteral` types for integer indexing into
`BytesLiteral`. It will now infer `IntLiteral` types instead.

## Test Plan

Markdown tests.
2025-05-20 16:44:12 +02:00
Micha Reiser 3b56c7ca3d
Update salsa (#18212) 2025-05-20 09:19:34 +02:00
Adam Aaronson 8729cb208f
[ty] Raise `invalid-exception-caught` even when exception is not captured (#18202)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-05-19 18:13:34 -04:00
Emily B. Zhang a2c87c2bc1
[ty] Add note to `unresolved-import` hinting to users to configure their Python environment (#18207)
Closes https://github.com/astral-sh/ty/issues/453.

## Summary

Add an additional info diagnostic to `unresolved-import` check to hint
to users that they should make sure their Python environment is properly
configured for ty, linking them to the corresponding doc. This
diagnostic is only shown when an import is not relative, e.g., `import
maturin` not `import .maturin`.

## Test Plan

Updated snapshots with new info message and reran tests.
2025-05-19 17:24:25 -04:00
Douglas Creager ce43dbab58
[ty] Promote literals when inferring class specializations from constructors (#18102)
This implements the stopgap approach described in
https://github.com/astral-sh/ty/issues/336#issuecomment-2880532213 for
handling literal types in generic class specializations.

With this approach, we will promote any literal to its instance type,
but _only_ when inferring a generic class specialization from a
constructor call:

```py
class C[T]:
    def __init__(self, x: T) -> None: ...

reveal_type(C("string"))  # revealed: C[str]
```

If you specialize the class explicitly, we still use whatever type you
provide, even if it's a literal:

```py
from typing import Literal

reveal_type(C[Literal[5]](5))  # revealed: C[Literal[5]]
```

And this doesn't apply at all to generic functions:

```py
def f[T](x: T) -> T:
    return x

reveal_type(f(5))  # revealed: Literal[5]
```

---

As part of making this happen, we also generalize the `TypeMapping`
machinery. This provides a way to apply a function to type, returning a
new type. Complicating matters is that for function literals, we have to
apply the mapping lazily, since the function's signature is not created
until (and if) someone calls its `signature` method. That means we have
to stash away the mappings that we want to apply to the signatures
parameter/return annotations once we do create it. This requires some
minor `Cow` shenanigans to continue working for partial specializations.
2025-05-19 15:42:54 -04:00
Felix Scherz fb589730ef
[ty]: Consider a class with a dynamic element in its MRO assignable to any subtype of `type` (#18205) 2025-05-19 19:30:30 +00:00
Douglas Creager 4fad15805b
[ty] Use first matching constructor overload when inferring specializations (#18204)
This is a follow-on to #18155. For the example raised in
https://github.com/astral-sh/ty/issues/370:

```py
import tempfile

with tempfile.TemporaryDirectory() as tmp: ...
```

the new logic would notice that both overloads of `TemporaryDirectory`
match, and combine their specializations, resulting in an inferred type
of `str | bytes`.

This PR updates the logic to match our other handling of other calls,
where we only keep the _first_ matching overload. The result for this
example then becomes `str`, matching the runtime behavior. (We still do
not implement the full [overload resolution
algorithm](https://typing.python.org/en/latest/spec/overload.html#overload-call-evaluation)
from the spec.)
2025-05-19 15:12:28 -04:00
David Peter 0ede831a3f
[ty] Add hint that PEP 604 union syntax is only available in 3.10+ (#18192)
## Summary

Add a new diagnostic hint if you try to use PEP 604 `X | Y` union syntax
in a non-type-expression before 3.10.

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

## Test Plan

New snapshot test
2025-05-19 19:47:31 +02:00
Douglas Creager 97058e8093
[ty] Infer function call typevars in both directions (#18155)
This primarily comes up with annotated `self` parameters in
constructors:

```py
class C[T]:
    def __init__(self: C[int]): ...
```

Here, we want infer a specialization of `{T = int}` for a call that hits
this overload.

Normally when inferring a specialization of a function call, typevars
appear in the parameter annotations, and not in the argument types. In
this case, this is reversed: we need to verify that the `self` argument
(`C[T]`, as we have not yet completed specialization inference) is
assignable to the parameter type `C[int]`.

To do this, we simply look for a typevar/type in both directions when
performing inference, and apply the inferred specialization to argument
types as well as parameter types before verifying assignability.

As a wrinkle, this exposed that we were not checking
subtyping/assignability for function literals correctly. Our function
literal representation includes an optional specialization that should
be applied to the signature. Before, function literals were considered
subtypes of (assignable to) each other only if they were identical Salsa
objects. Two function literals with different specializations should
still be considered subtypes of (assignable to) each other if those
specializations result in the same function signature (typically because
the function doesn't use the typevars in the specialization).

Closes https://github.com/astral-sh/ty/issues/370
Closes https://github.com/astral-sh/ty/issues/100
Closes https://github.com/astral-sh/ty/issues/258

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-05-19 11:45:40 -04:00
David Peter 4c889d5251
[ty] Support `typing.TypeAliasType` (#18156)
## Summary

Support direct uses of `typing.TypeAliasType`, as in:

```py
from typing import TypeAliasType

IntOrStr = TypeAliasType("IntOrStr", int | str)

def f(x: IntOrStr) -> None:
    reveal_type(x)  # revealed: int | str
```

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

## Ecosystem

The new false positive here:
```diff
+ error[invalid-type-form] altair/utils/core.py:49:53: The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`
```
comes from the fact that we infer the second argument as a type
expression now. We silence false positives for PEP695 `ParamSpec`s, but
not for `P = ParamSpec("P")` inside `Callable[P, ...]`.

## Test Plan

New Markdown tests
2025-05-19 16:36:49 +02:00
Alex Waygood dd04ca7f58
[ty] Add regression test for fixed `pyvenv.cfg` parsing bug (#18157) 2025-05-17 21:10:15 +00:00
Carl Meyer 2abcd86c57
Revert "[ty] Better control flow for boolean expressions that are inside if (#18010)" (#18150)
This reverts commit 9910ec700c.

## Summary

This change introduced a serious performance regression. Revert it while
we investigate.

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

## Test Plan

Timing on the snippet in https://github.com/astral-sh/ty/issues/431
again shows times similar to before the regression.
2025-05-17 08:27:32 -04:00
Matthew Mckee c6e55f673c
Remove pyvenv.cfg validation check for lines with multiple `=` (#18144) 2025-05-17 08:42:39 +02:00
Micha Reiser 3d55a16c91
[ty] Migrate the namespace package module resolver tests to mdtests (#18133)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-05-16 19:56:33 +02:00
Alex Waygood 0adbb3d600
[ty] Fix assignability checks for invariant generics parameterized by gradual types (#18138) 2025-05-16 13:37:07 -04:00
Alex Waygood 28fb802467
[ty] Merge `SemanticIndexBuilder` impl blocks (#18135)
## Summary

just a minor nit followup to
https://github.com/astral-sh/ruff/pull/18010 -- put all the
non-`Visitor` methods of `SemanticIndexBuilder` in the same impl block
rather than having multiple impl blocks

## Test Plan

`cargo build`
2025-05-16 11:05:02 -04:00
Micha Reiser 1ba56b4bc6
[ty] Fix relative imports in stub packages (#18132) 2025-05-16 15:30:10 +02:00
TomerBin 9910ec700c
[ty] Better control flow for boolean expressions that are inside if (#18010)
## Summary
With this PR we now detect that x is always defined in `use`:
```py
if flag and (x := number):
    use(x)
```

When outside if, it's still detected as possibly not defined
```py
flag and (x := number)
# error: [possibly-unresolved-reference]
use(x)
```
In order to achieve that, I had to find a way to get access to the
flow-snapshots of the boolean expression when analyzing the flow of the
if statement. I did it by special casing the visitor of boolean
expression to return flow control information, exporting two snapshots -
`maybe_short_circuit` and `no_short_circuit`. When indexing
boolean expression itself we must assume all possible flows, but when
it's inside if statement, we can be smarter than that.

## Test Plan
Fixed existing and added new mdtests.
I went through some of mypy primer results and they look fine

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-05-16 11:59:21 +00:00
Micha Reiser 9ae698fe30
Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00
David Peter e67b35743a
[ty] NamedTuple 'fallback' attributes (#18127)
## Summary

Add various attributes to `NamedTuple` classes/instances that are
available at runtime.

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

## Test Plan

New Markdown tests
2025-05-16 12:56:43 +02:00
David Peter 8644c9da43
[ty] Regression test for relative import in stubs package (#18123)
## Summary

Regression test for https://github.com/astral-sh/ty/issues/408
2025-05-16 12:49:35 +02:00
Micha Reiser 196e4befba
Update MSRV to 1.85 and toolchain to 1.87 (#18126) 2025-05-16 09:19:55 +02:00
David Peter 6e39250015
[ty] Allow unions including `Any`/`Unknown` as bases (#18094)
## Summary

Alternative fix for https://github.com/astral-sh/ty/issues/312

## Test Plan

New Markdown test
2025-05-16 06:57:26 +02:00
Felix Scherz d3a7cb3fe4
[ty] support accessing `__builtins__` global (#18118)
## Summary

The PR adds an explicit check for `"__builtins__"` during name lookup,
similar to how `"__file__"` is implemented. The inferred type is
`Any`.

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

## Test Plan

Added a markdown test for `__builtins__`.

---------

Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2025-05-15 22:01:38 +02:00
Andrew Gallant 69393b2e6e
[ty] Improve invalid method calls for unmatched overloads (#18122)
This makes an easy tweak to allow our diagnostics for unmatched
overloads to apply to method calls. Previously, they only worked for
function calls.

There is at least one other case worth addressing too, namely, class
literals. e.g., `type()`. We had a diagnostic snapshot test case to
track it.

Closes astral-sh/ty#274
2025-05-15 11:39:14 -04:00
David Peter c066bf0127
[ty] `type[…]` is always assignable to `type` (#18121)
## Summary

Model that `type[C]` is always assignable to `type`, even if `C` is not
fully static.

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

## Test Plan

* New Markdown tests
* Property tests
2025-05-15 17:13:47 +02:00
David Peter 279dac1c0e
[ty] Make dataclass instances adhere to DataclassInstance (#18115)
## Summary

Make dataclass instances adhere to the `DataclassInstance` protocol.

fixes astral-sh/ty#400

## Test Plan

New Markdown tests
2025-05-15 14:27:23 +02:00
InSync 46be305ad2
[ty] Include synthesized arguments in displayed counts for `too-many-positional-arguments` (#18098)
## Summary

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

All arguments, synthesized or not, are now accounted for in
`too-many-positional-arguments`'s error message.

For example, consider this example:

```python
class C:
	def foo(self): ...

C().foo(1)  # !!!
```

Previously, ty would say:

> Too many positional arguments to bound method foo: expected 0, got 1

After this change, it will say:

> Too many positional arguments to bound method foo: expected 1, got 2

This is what Python itself does too:

```text
Traceback (most recent call last):
  File "<python-input-0>", line 3, in <module>
    C().foo()
    ~~~~~~~^^
TypeError: C.foo() takes 0 positional arguments but 1 was given
```

## Test Plan

Markdown tests.
2025-05-14 22:51:23 -04:00
Alex Waygood c3a4992ae9
[ty] Fix normalization of unions containing instances parameterized with unions (#18112) 2025-05-14 22:48:33 -04:00
Alex Waygood 9aa6330bb1
[ty] Fix `redundant-cast` false positives when casting to `Unknown` (#18111) 2025-05-14 22:38:53 -04:00
github-actions[bot] b600ff106a
Sync vendored typeshed stubs (#18110)
Close and reopen this PR to trigger CI

---------

Co-authored-by: typeshedbot <>
Co-authored-by: Carl Meyer <carl@astral.sh>
2025-05-14 22:14:52 -04:00
David Peter 6800a9f6f3
[ty] Add type-expression syntax link to invalid-type-expression (#18104)
## Summary

Add a link to [this
page](https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions)
when emitting `invalid-type-expression` diagnostics.
2025-05-14 18:56:44 +00:00
Alex Waygood 0590b38214
[ty] Fix more generics-related TODOs (#18062) 2025-05-14 12:26:52 -04:00
Andrew Gallant faf54c0181 ty_python_semantic: improve failed overloaded function call
The diagnostic now includes a pointer to the implementation definition
along with each possible overload.

This doesn't include information about *why* each overload failed. But
given the emphasis on concise output (since there can be *many*
unmatched overloads), it's not totally clear how to include that
additional information.

Fixes #274
2025-05-14 11:13:41 -04:00
Andrew Gallant 451c5db7a3 ty_python_semantic: move some routines to `FunctionType`
These are, after all, specific to function types. The methods on `Type`
are more like conveniences that return something when the type *happens*
to be a function. But defining them on `FunctionType` itself makes it
easy to call them when you have a `FunctionType` instead of a `Type`.
2025-05-14 11:13:41 -04:00
Andrew Gallant bd5b7f415f ty_python_semantic: rejigger handling of overload error conditions
I found the previous code somewhat harder to read. Namely, a `for`
loop was being used to encode "execute zero or one times, but not
more." Which is sometimes okay, but it seemed clearer to me to use
more explicit case analysis here.

This should have no behavioral changes.
2025-05-14 11:13:41 -04:00