mirror of https://github.com/astral-sh/ruff
Merge remote-tracking branch 'origin/main' into dcreager/gradual-bounds
* origin/main: [ty] disallow explicit specialization of type variables themselves (#21938) [ty] Improve diagnostics for unsupported binary operations and unsupported augmented assignments (#21947) [ty] update implicit root docs (#21955)
This commit is contained in:
commit
431a9d31d7
|
|
@ -158,7 +158,7 @@ If left unspecified, ty will try to detect common project layouts and initialize
|
|||
* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
|
||||
* otherwise, default to `.` (flat layout)
|
||||
|
||||
Besides, if a `./python` or `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file),
|
||||
Additionally, if a `./python` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file),
|
||||
it will also be included in the first party search path.
|
||||
|
||||
**Default value**: `null`
|
||||
|
|
@ -443,7 +443,7 @@ If left unspecified, ty will try to detect common project layouts and initialize
|
|||
* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
|
||||
* otherwise, default to `.` (flat layout)
|
||||
|
||||
Besides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
|
||||
Additionally, if a `./python` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
|
||||
it will also be included in the first party search path.
|
||||
|
||||
**Default value**: `null`
|
||||
|
|
|
|||
|
|
@ -516,7 +516,7 @@ pub struct EnvironmentOptions {
|
|||
/// * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
|
||||
/// * otherwise, default to `.` (flat layout)
|
||||
///
|
||||
/// Besides, if a `./python` or `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file),
|
||||
/// Additionally, if a `./python` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file),
|
||||
/// it will also be included in the first party search path.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
|
|
@ -658,7 +658,7 @@ pub struct SrcOptions {
|
|||
/// * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
|
||||
/// * otherwise, default to `.` (flat layout)
|
||||
///
|
||||
/// Besides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
|
||||
/// Additionally, if a `./python` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
|
||||
/// it will also be included in the first party search path.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
from typing import TypeAlias, TypeVar
|
||||
|
||||
T = TypeVar("T", bound="A[0]")
|
||||
A: TypeAlias = T
|
||||
def _(x: A):
|
||||
if x:
|
||||
pass
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
def _[T: T[0]](x: T):
|
||||
if x:
|
||||
pass
|
||||
|
|
@ -38,6 +38,8 @@ reveal_type(x) # revealed: int
|
|||
|
||||
## Unsupported types
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
```py
|
||||
class C:
|
||||
def __isub__(self, other: str) -> int:
|
||||
|
|
|
|||
|
|
@ -79,31 +79,31 @@ reveal_type(Sub() & Sub()) # revealed: Literal["&"]
|
|||
reveal_type(Sub() // Sub()) # revealed: Literal["//"]
|
||||
|
||||
# No does not implement any of the dunder methods.
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `No`"
|
||||
reveal_type(No() + No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `-` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `-` is not supported between two objects of type `No`"
|
||||
reveal_type(No() - No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `*` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `*` is not supported between two objects of type `No`"
|
||||
reveal_type(No() * No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `@` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `@` is not supported between two objects of type `No`"
|
||||
reveal_type(No() @ No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `/` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `/` is not supported between two objects of type `No`"
|
||||
reveal_type(No() / No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `%` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `%` is not supported between two objects of type `No`"
|
||||
reveal_type(No() % No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `**` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `**` is not supported between two objects of type `No`"
|
||||
reveal_type(No() ** No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `<<` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `<<` is not supported between two objects of type `No`"
|
||||
reveal_type(No() << No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `>>` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `>>` is not supported between two objects of type `No`"
|
||||
reveal_type(No() >> No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `|` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `|` is not supported between two objects of type `No`"
|
||||
reveal_type(No() | No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `^` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `^` is not supported between two objects of type `No`"
|
||||
reveal_type(No() ^ No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `&` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `&` is not supported between two objects of type `No`"
|
||||
reveal_type(No() & No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `//` is not supported between objects of type `No` and `No`"
|
||||
# error: [unsupported-operator] "Operator `//` is not supported between two objects of type `No`"
|
||||
reveal_type(No() // No()) # revealed: Unknown
|
||||
|
||||
# Yes does not implement any of the reflected dunder methods.
|
||||
|
|
@ -293,6 +293,8 @@ reveal_type(Yes() // No()) # revealed: Literal["//"]
|
|||
|
||||
## Classes
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
Dunder methods defined in a class are available to instances of that class, but not to the class
|
||||
itself. (For these operators to work on the class itself, they would have to be defined on the
|
||||
class's type, i.e. `type`.)
|
||||
|
|
@ -307,11 +309,11 @@ class Yes:
|
|||
class Sub(Yes): ...
|
||||
class No: ...
|
||||
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `<class 'Yes'>` and `<class 'Yes'>`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Yes'>`"
|
||||
reveal_type(Yes + Yes) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `<class 'Sub'>` and `<class 'Sub'>`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Sub'>`"
|
||||
reveal_type(Sub + Sub) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `<class 'No'>` and `<class 'No'>`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'No'>`"
|
||||
reveal_type(No + No) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
|
@ -336,11 +338,11 @@ def sub() -> type[Sub]:
|
|||
def no() -> type[No]:
|
||||
return No
|
||||
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `type[Yes]` and `type[Yes]`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `type[Yes]`"
|
||||
reveal_type(yes() + yes()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `type[Sub]` and `type[Sub]`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `type[Sub]`"
|
||||
reveal_type(sub() + sub()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `type[No]` and `type[No]`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `type[No]`"
|
||||
reveal_type(no() + no()) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
|
@ -350,30 +352,54 @@ reveal_type(no() + no()) # revealed: Unknown
|
|||
def f():
|
||||
pass
|
||||
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f + f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `-` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `-` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f - f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `*` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `*` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f * f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `@` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `@` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f @ f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `/` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `/` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f / f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `%` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `%` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f % f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `**` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `**` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f**f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `<<` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `<<` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f << f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `>>` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `>>` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f >> f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `|` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `|` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f | f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `^` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `^` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f ^ f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `&` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `&` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f & f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Operator `//` is not supported between objects of type `def f() -> Unknown` and `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Operator `//` is not supported between two objects of type `def f() -> Unknown`"
|
||||
reveal_type(f // f) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Classes from different modules with the same name
|
||||
|
||||
We use the fully qualified names in diagnostics if the two classes have the same unqualified name,
|
||||
but are nonetheless different.
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
`mod1.py`:
|
||||
|
||||
```py
|
||||
class A: ...
|
||||
```
|
||||
|
||||
`mod2.py`:
|
||||
|
||||
```py
|
||||
import mod1
|
||||
|
||||
class A: ...
|
||||
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `mod2.A` and `mod1.A`"
|
||||
A() + mod1.A()
|
||||
```
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ class A:
|
|||
def __init__(self):
|
||||
self.__add__ = add_impl
|
||||
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `A` and `A`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `A`"
|
||||
# revealed: Unknown
|
||||
reveal_type(A() + A())
|
||||
```
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ cannot be added, because that would require addition of `int` and `str` or vice
|
|||
def f2(i: int, s: str, int_or_str: int | str):
|
||||
i + i
|
||||
s + s
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `int | str` and `int | str`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `int | str`"
|
||||
reveal_type(int_or_str + int_or_str) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ T = TypeVar("T", int, str)
|
|||
|
||||
def same_constrained_types(t1: T, t2: T) -> T:
|
||||
# TODO: no error
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `T@same_constrained_types` and `T@same_constrained_types`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `T@same_constrained_types`"
|
||||
return t1 + t2
|
||||
```
|
||||
|
||||
|
|
@ -287,7 +287,7 @@ and an `int` and a `str` cannot be added together:
|
|||
|
||||
```py
|
||||
def unions_are_different(t1: int | str, t2: int | str) -> int | str:
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `int | str` and `int | str`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `int | str`"
|
||||
return t1 + t2
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,34 @@ S = TypeVar("S", **{"bound": int})
|
|||
reveal_type(S) # revealed: TypeVar
|
||||
```
|
||||
|
||||
### No explicit specialization
|
||||
|
||||
A type variable itself cannot be explicitly specialized; the result of the specialization is
|
||||
`Unknown`. However, generic PEP 613 type aliases that point to type variables can be explicitly
|
||||
specialized.
|
||||
|
||||
```py
|
||||
from typing import TypeVar, TypeAlias
|
||||
|
||||
T = TypeVar("T")
|
||||
ImplicitPositive = T
|
||||
Positive: TypeAlias = T
|
||||
|
||||
def _(
|
||||
# error: [invalid-type-form] "A type variable itself cannot be specialized"
|
||||
a: T[int],
|
||||
# error: [invalid-type-form] "A type variable itself cannot be specialized"
|
||||
b: T[T],
|
||||
# error: [invalid-type-form] "A type variable itself cannot be specialized"
|
||||
c: ImplicitPositive[int],
|
||||
d: Positive[int],
|
||||
):
|
||||
reveal_type(a) # revealed: Unknown
|
||||
reveal_type(b) # revealed: Unknown
|
||||
reveal_type(c) # revealed: Unknown
|
||||
reveal_type(d) # revealed: int
|
||||
```
|
||||
|
||||
### Type variables with a default
|
||||
|
||||
Note that the `__default__` property is only available in Python ≥3.13.
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ methods that are compatible with the return type, so the `return` expression is
|
|||
```py
|
||||
def same_constrained_types[T: (int, str)](t1: T, t2: T) -> T:
|
||||
# TODO: no error
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `T@same_constrained_types` and `T@same_constrained_types`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `T@same_constrained_types`"
|
||||
return t1 + t2
|
||||
```
|
||||
|
||||
|
|
@ -256,7 +256,7 @@ and an `int` and a `str` cannot be added together:
|
|||
|
||||
```py
|
||||
def unions_are_different(t1: int | str, t2: int | str) -> int | str:
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `int | str` and `int | str`"
|
||||
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `int | str`"
|
||||
return t1 + t2
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,26 @@ def f[T: (int,)]():
|
|||
pass
|
||||
```
|
||||
|
||||
### No explicit specialization
|
||||
|
||||
A type variable itself cannot be explicitly specialized; the result of the specialization is
|
||||
`Unknown`. However, generic type aliases that point to type variables can be explicitly specialized.
|
||||
|
||||
```py
|
||||
type Positive[T] = T
|
||||
|
||||
def _[T](
|
||||
# error: [invalid-type-form] "A type variable itself cannot be specialized"
|
||||
a: T[int],
|
||||
# error: [invalid-type-form] "A type variable itself cannot be specialized"
|
||||
b: T[T],
|
||||
c: Positive[int],
|
||||
):
|
||||
reveal_type(a) # revealed: Unknown
|
||||
reveal_type(b) # revealed: Unknown
|
||||
reveal_type(c) # revealed: int
|
||||
```
|
||||
|
||||
## Invalid uses
|
||||
|
||||
Note that many of the invalid uses of legacy typevars do not apply to PEP 695 typevars, since the
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ def _(int_or_int: IntOrInt, list_of_int_or_list_of_int: ListOfIntOrListOfInt):
|
|||
`NoneType` has no special or-operator behavior, so this is an error:
|
||||
|
||||
```py
|
||||
None | None # error: [unsupported-operator] "Operator `|` is not supported between objects of type `None` and `None`"
|
||||
None | None # error: [unsupported-operator] "Operator `|` is not supported between two objects of type `None`"
|
||||
```
|
||||
|
||||
When constructing something nonsensical like `int | 1`, we emit a diagnostic for the expression
|
||||
|
|
@ -414,6 +414,7 @@ def _(
|
|||
list_or_tuple_legacy: ListOrTupleLegacy[int],
|
||||
my_callable: MyCallable[[str, bytes], int],
|
||||
annotated_int: AnnotatedType[int],
|
||||
# error: [invalid-type-form] "A type variable itself cannot be specialized"
|
||||
transparent_alias: TransparentAlias[int],
|
||||
optional_int: MyOptional[int],
|
||||
):
|
||||
|
|
@ -427,7 +428,7 @@ def _(
|
|||
reveal_type(list_or_tuple_legacy) # revealed: list[int] | tuple[int, ...]
|
||||
reveal_type(my_callable) # revealed: (str, bytes, /) -> int
|
||||
reveal_type(annotated_int) # revealed: int
|
||||
reveal_type(transparent_alias) # revealed: int
|
||||
reveal_type(transparent_alias) # revealed: Unknown
|
||||
reveal_type(optional_int) # revealed: int | None
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,15 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/assignment/annotations.m
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Operator `|` is not supported between objects of type `<class 'int'>` and `<class 'str'>`
|
||||
error[unsupported-operator]: Unsupported `|` operation
|
||||
--> src/mdtest_snippet.py:2:12
|
||||
|
|
||||
1 | # error: [unsupported-operator]
|
||||
2 | IntOrStr = int | str
|
||||
| ^^^^^^^^^
|
||||
| ---^^^---
|
||||
| | |
|
||||
| | Has type `<class 'str'>`
|
||||
| Has type `<class 'int'>`
|
||||
|
|
||||
info: Note that `X | Y` PEP 604 union syntax is only available in Python 3.10 and later
|
||||
info: Python 3.9 was assumed when resolving types because it was specified on the command line
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: augmented.md - Augmented assignment - Unsupported types
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/assignment/augmented.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | class C:
|
||||
2 | def __isub__(self, other: str) -> int:
|
||||
3 | return 42
|
||||
4 |
|
||||
5 | x = C()
|
||||
6 | # error: [unsupported-operator] "Operator `-=` is not supported between objects of type `C` and `Literal[1]`"
|
||||
7 | x -= 1
|
||||
8 |
|
||||
9 | reveal_type(x) # revealed: int
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `-=` operation
|
||||
--> src/mdtest_snippet.py:7:1
|
||||
|
|
||||
5 | x = C()
|
||||
6 | # error: [unsupported-operator] "Operator `-=` is not supported between objects of type `C` and `Literal[1]`"
|
||||
7 | x -= 1
|
||||
| -^^^^-
|
||||
| | |
|
||||
| | Has type `Literal[1]`
|
||||
| Has type `C`
|
||||
8 |
|
||||
9 | reveal_type(x) # revealed: int
|
||||
|
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: custom.md - Custom binary operations - Classes
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/binary/custom.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing import Literal
|
||||
2 |
|
||||
3 | class Yes:
|
||||
4 | def __add__(self, other) -> Literal["+"]:
|
||||
5 | return "+"
|
||||
6 |
|
||||
7 | class Sub(Yes): ...
|
||||
8 | class No: ...
|
||||
9 |
|
||||
10 | # error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Yes'>`"
|
||||
11 | reveal_type(Yes + Yes) # revealed: Unknown
|
||||
12 | # error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Sub'>`"
|
||||
13 | reveal_type(Sub + Sub) # revealed: Unknown
|
||||
14 | # error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'No'>`"
|
||||
15 | reveal_type(No + No) # revealed: Unknown
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `+` operation
|
||||
--> src/mdtest_snippet.py:11:13
|
||||
|
|
||||
10 | # error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Yes'>`"
|
||||
11 | reveal_type(Yes + Yes) # revealed: Unknown
|
||||
| ---^^^---
|
||||
| |
|
||||
| Both operands have type `<class 'Yes'>`
|
||||
12 | # error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Sub'>`"
|
||||
13 | reveal_type(Sub + Sub) # revealed: Unknown
|
||||
|
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `+` operation
|
||||
--> src/mdtest_snippet.py:13:13
|
||||
|
|
||||
11 | reveal_type(Yes + Yes) # revealed: Unknown
|
||||
12 | # error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Sub'>`"
|
||||
13 | reveal_type(Sub + Sub) # revealed: Unknown
|
||||
| ---^^^---
|
||||
| |
|
||||
| Both operands have type `<class 'Sub'>`
|
||||
14 | # error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'No'>`"
|
||||
15 | reveal_type(No + No) # revealed: Unknown
|
||||
|
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `+` operation
|
||||
--> src/mdtest_snippet.py:15:13
|
||||
|
|
||||
13 | reveal_type(Sub + Sub) # revealed: Unknown
|
||||
14 | # error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'No'>`"
|
||||
15 | reveal_type(No + No) # revealed: Unknown
|
||||
| --^^^--
|
||||
| |
|
||||
| Both operands have type `<class 'No'>`
|
||||
|
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: custom.md - Custom binary operations - Classes from different modules with the same name
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/binary/custom.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mod1.py
|
||||
|
||||
```
|
||||
1 | class A: ...
|
||||
```
|
||||
|
||||
## mod2.py
|
||||
|
||||
```
|
||||
1 | import mod1
|
||||
2 |
|
||||
3 | class A: ...
|
||||
4 |
|
||||
5 | # error: [unsupported-operator] "Operator `+` is not supported between objects of type `mod2.A` and `mod1.A`"
|
||||
6 | A() + mod1.A()
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `+` operation
|
||||
--> src/mod2.py:6:1
|
||||
|
|
||||
5 | # error: [unsupported-operator] "Operator `+` is not supported between objects of type `mod2.A` and `mod1.A`"
|
||||
6 | A() + mod1.A()
|
||||
| ---^^^--------
|
||||
| | |
|
||||
| | Has type `mod1.A`
|
||||
| Has type `mod2.A`
|
||||
|
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -24,11 +24,11 @@ reveal_type(+Sub()) # revealed: bool
|
|||
reveal_type(-Sub()) # revealed: str
|
||||
reveal_type(~Sub()) # revealed: int
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `No`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `No`"
|
||||
reveal_type(+No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `No`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `No`"
|
||||
reveal_type(-No()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `No`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `No`"
|
||||
reveal_type(~No()) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
|
@ -52,25 +52,25 @@ class Yes:
|
|||
class Sub(Yes): ...
|
||||
class No: ...
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `<class 'Yes'>`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `<class 'Yes'>`"
|
||||
reveal_type(+Yes) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `<class 'Yes'>`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `<class 'Yes'>`"
|
||||
reveal_type(-Yes) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `<class 'Yes'>`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `<class 'Yes'>`"
|
||||
reveal_type(~Yes) # revealed: Unknown
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `<class 'Sub'>`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `<class 'Sub'>`"
|
||||
reveal_type(+Sub) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `<class 'Sub'>`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `<class 'Sub'>`"
|
||||
reveal_type(-Sub) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `<class 'Sub'>`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `<class 'Sub'>`"
|
||||
reveal_type(~Sub) # revealed: Unknown
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `<class 'No'>`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `<class 'No'>`"
|
||||
reveal_type(+No) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `<class 'No'>`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `<class 'No'>`"
|
||||
reveal_type(-No) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `<class 'No'>`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `<class 'No'>`"
|
||||
reveal_type(~No) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
|
@ -80,11 +80,11 @@ reveal_type(~No) # revealed: Unknown
|
|||
def f():
|
||||
pass
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `def f() -> Unknown`"
|
||||
reveal_type(+f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `def f() -> Unknown`"
|
||||
reveal_type(-f) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `def f() -> Unknown`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `def f() -> Unknown`"
|
||||
reveal_type(~f) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
|
@ -113,25 +113,25 @@ def sub() -> type[Sub]:
|
|||
def no() -> type[No]:
|
||||
return No
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `type[Yes]`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `type[Yes]`"
|
||||
reveal_type(+yes()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `type[Yes]`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `type[Yes]`"
|
||||
reveal_type(-yes()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `type[Yes]`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `type[Yes]`"
|
||||
reveal_type(~yes()) # revealed: Unknown
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `type[Sub]`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `type[Sub]`"
|
||||
reveal_type(+sub()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `type[Sub]`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `type[Sub]`"
|
||||
reveal_type(-sub()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `type[Sub]`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `type[Sub]`"
|
||||
reveal_type(~sub()) # revealed: Unknown
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `type[No]`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `type[No]`"
|
||||
reveal_type(+no()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `type[No]`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `type[No]`"
|
||||
reveal_type(-no()) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `type[No]`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `type[No]`"
|
||||
reveal_type(~no()) # revealed: Unknown
|
||||
```
|
||||
|
||||
|
|
@ -160,10 +160,10 @@ reveal_type(+Sub) # revealed: bool
|
|||
reveal_type(-Sub) # revealed: str
|
||||
reveal_type(~Sub) # revealed: int
|
||||
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for type `<class 'No'>`"
|
||||
# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `<class 'No'>`"
|
||||
reveal_type(+No) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for type `<class 'No'>`"
|
||||
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `<class 'No'>`"
|
||||
reveal_type(-No) # revealed: Unknown
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for type `<class 'No'>`"
|
||||
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `<class 'No'>`"
|
||||
reveal_type(~No) # revealed: Unknown
|
||||
```
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ reveal_type(~a) # revealed: Literal[True]
|
|||
class NoDunder: ...
|
||||
|
||||
b = NoDunder()
|
||||
+b # error: [unsupported-operator] "Unary operator `+` is not supported for type `NoDunder`"
|
||||
-b # error: [unsupported-operator] "Unary operator `-` is not supported for type `NoDunder`"
|
||||
~b # error: [unsupported-operator] "Unary operator `~` is not supported for type `NoDunder`"
|
||||
+b # error: [unsupported-operator] "Unary operator `+` is not supported for object of type `NoDunder`"
|
||||
-b # error: [unsupported-operator] "Unary operator `-` is not supported for object of type `NoDunder`"
|
||||
~b # error: [unsupported-operator] "Unary operator `~` is not supported for object of type `NoDunder`"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -7994,7 +7994,7 @@ impl<'db> Type<'db> {
|
|||
) {
|
||||
let matching_typevar = |bound_typevar: &BoundTypeVarInstance<'db>| {
|
||||
match bound_typevar.typevar(db).kind(db) {
|
||||
TypeVarKind::Legacy | TypeVarKind::TypingSelf
|
||||
TypeVarKind::Legacy | TypeVarKind::Pep613Alias | TypeVarKind::TypingSelf
|
||||
if binding_context.is_none_or(|binding_context| {
|
||||
bound_typevar.binding_context(db)
|
||||
== BindingContext::Definition(binding_context)
|
||||
|
|
@ -9472,6 +9472,8 @@ pub enum TypeVarKind {
|
|||
ParamSpec,
|
||||
/// `def foo[**P]() -> None: ...`
|
||||
Pep695ParamSpec,
|
||||
/// `Alias: typing.TypeAlias = T`
|
||||
Pep613Alias,
|
||||
}
|
||||
|
||||
impl TypeVarKind {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ use ruff_db::{
|
|||
use ruff_diagnostics::{Edit, Fix};
|
||||
use ruff_python_ast::name::Name;
|
||||
use ruff_python_ast::token::parentheses_iterator;
|
||||
use ruff_python_ast::{self as ast, AnyNodeRef, StringFlags};
|
||||
use ruff_python_ast::{self as ast, AnyNodeRef, PythonVersion, StringFlags};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::fmt::{self, Formatter};
|
||||
|
|
@ -4155,6 +4155,120 @@ pub(super) fn report_unsupported_comparison<'db>(
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn report_unsupported_augmented_assignment<'db>(
|
||||
context: &InferContext<'db, '_>,
|
||||
stmt: &ast::StmtAugAssign,
|
||||
left_ty: Type<'db>,
|
||||
right_ty: Type<'db>,
|
||||
) {
|
||||
report_unsupported_binary_operation_impl(
|
||||
context,
|
||||
stmt.range(),
|
||||
&stmt.target,
|
||||
&stmt.value,
|
||||
left_ty,
|
||||
right_ty,
|
||||
OperatorDisplay {
|
||||
operator: stmt.op,
|
||||
is_augmented_assignment: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn report_unsupported_binary_operation<'db>(
|
||||
context: &InferContext<'db, '_>,
|
||||
binary_expression: &ast::ExprBinOp,
|
||||
left_ty: Type<'db>,
|
||||
right_ty: Type<'db>,
|
||||
operator: ast::Operator,
|
||||
) {
|
||||
let Some(mut diagnostic) = report_unsupported_binary_operation_impl(
|
||||
context,
|
||||
binary_expression.range(),
|
||||
&binary_expression.left,
|
||||
&binary_expression.right,
|
||||
left_ty,
|
||||
right_ty,
|
||||
OperatorDisplay {
|
||||
operator,
|
||||
is_augmented_assignment: false,
|
||||
},
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
let db = context.db();
|
||||
if operator == ast::Operator::BitOr
|
||||
&& (left_ty.is_subtype_of(db, KnownClass::Type.to_instance(db))
|
||||
|| right_ty.is_subtype_of(db, KnownClass::Type.to_instance(db)))
|
||||
&& Program::get(db).python_version(db) < PythonVersion::PY310
|
||||
{
|
||||
diagnostic.info(
|
||||
"Note that `X | Y` PEP 604 union syntax is only available in Python 3.10 and later",
|
||||
);
|
||||
add_inferred_python_version_hint_to_diagnostic(db, &mut diagnostic, "resolving types");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct OperatorDisplay {
|
||||
operator: ast::Operator,
|
||||
is_augmented_assignment: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for OperatorDisplay {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.is_augmented_assignment {
|
||||
write!(f, "{}=", self.operator)
|
||||
} else {
|
||||
write!(f, "{}", self.operator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report_unsupported_binary_operation_impl<'a>(
|
||||
context: &'a InferContext<'a, 'a>,
|
||||
range: TextRange,
|
||||
left: &ast::Expr,
|
||||
right: &ast::Expr,
|
||||
left_ty: Type<'a>,
|
||||
right_ty: Type<'a>,
|
||||
operator: OperatorDisplay,
|
||||
) -> Option<LintDiagnosticGuard<'a, 'a>> {
|
||||
let db = context.db();
|
||||
let diagnostic_builder = context.report_lint(&UNSUPPORTED_OPERATOR, range)?;
|
||||
let display_settings = DisplaySettings::from_possibly_ambiguous_types(db, [left_ty, right_ty]);
|
||||
|
||||
let mut diagnostic =
|
||||
diagnostic_builder.into_diagnostic(format_args!("Unsupported `{operator}` operation"));
|
||||
|
||||
if left_ty == right_ty {
|
||||
diagnostic.set_primary_message(format_args!(
|
||||
"Both operands have type `{}`",
|
||||
left_ty.display_with(db, display_settings.clone())
|
||||
));
|
||||
diagnostic.annotate(context.secondary(left));
|
||||
diagnostic.annotate(context.secondary(right));
|
||||
diagnostic.set_concise_message(format_args!(
|
||||
"Operator `{operator}` is not supported between two objects of type `{}`",
|
||||
left_ty.display_with(db, display_settings.clone())
|
||||
));
|
||||
} else {
|
||||
for (ty, expr) in [(left_ty, left), (right_ty, right)] {
|
||||
diagnostic.annotate(context.secondary(expr).message(format_args!(
|
||||
"Has type `{}`",
|
||||
ty.display_with(db, display_settings.clone())
|
||||
)));
|
||||
}
|
||||
diagnostic.set_concise_message(format_args!(
|
||||
"Operator `{operator}` is not supported between objects of type `{}` and `{}`",
|
||||
left_ty.display_with(db, display_settings.clone()),
|
||||
right_ty.display_with(db, display_settings.clone())
|
||||
));
|
||||
}
|
||||
|
||||
Some(diagnostic)
|
||||
}
|
||||
|
||||
/// This function receives an unresolved `from foo import bar` import,
|
||||
/// where `foo` can be resolved to a module but that module does not
|
||||
/// have a `bar` member or submodule.
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ use crate::types::diagnostic::{
|
|||
report_invalid_type_checking_constant, report_named_tuple_field_with_leading_underscore,
|
||||
report_namedtuple_field_without_default_after_field_with_default, report_non_subscriptable,
|
||||
report_possibly_missing_attribute, report_possibly_unresolved_reference,
|
||||
report_rebound_typevar, report_slice_step_size_zero, report_unsupported_comparison,
|
||||
report_rebound_typevar, report_slice_step_size_zero, report_unsupported_augmented_assignment,
|
||||
report_unsupported_binary_operation, report_unsupported_comparison,
|
||||
};
|
||||
use crate::types::function::{
|
||||
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
|
||||
|
|
@ -5865,6 +5866,24 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
};
|
||||
|
||||
if is_pep_613_type_alias {
|
||||
let inferred_ty =
|
||||
if let Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) = inferred_ty {
|
||||
let identity = TypeVarIdentity::new(
|
||||
self.db(),
|
||||
typevar.identity(self.db()).name(self.db()),
|
||||
typevar.identity(self.db()).definition(self.db()),
|
||||
TypeVarKind::Pep613Alias,
|
||||
);
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
|
||||
self.db(),
|
||||
identity,
|
||||
typevar._bound_or_constraints(self.db()),
|
||||
typevar.explicit_variance(self.db()),
|
||||
typevar._default(self.db()),
|
||||
)))
|
||||
} else {
|
||||
inferred_ty
|
||||
};
|
||||
self.add_declaration_with_binding(
|
||||
target.into(),
|
||||
definition,
|
||||
|
|
@ -5925,22 +5944,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
let op = assignment.op;
|
||||
let db = self.db();
|
||||
|
||||
let report_unsupported_augmented_op = |ctx: &mut InferContext| {
|
||||
let Some(builder) = ctx.report_lint(&UNSUPPORTED_OPERATOR, assignment) else {
|
||||
return;
|
||||
};
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Operator `{op}=` is not supported between objects of type `{}` and `{}`",
|
||||
target_type.display(db),
|
||||
value_type.display(db)
|
||||
));
|
||||
};
|
||||
|
||||
// Fall back to non-augmented binary operator inference.
|
||||
let mut binary_return_ty = || {
|
||||
self.infer_binary_expression_type(assignment.into(), false, target_type, value_type, op)
|
||||
.unwrap_or_else(|| {
|
||||
report_unsupported_augmented_op(&mut self.context);
|
||||
report_unsupported_augmented_assignment(
|
||||
&self.context,
|
||||
assignment,
|
||||
target_type,
|
||||
value_type,
|
||||
);
|
||||
Type::unknown()
|
||||
})
|
||||
};
|
||||
|
|
@ -5964,7 +5977,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
UnionType::from_elements(db, [outcome.return_type(db), binary_return_ty()])
|
||||
}
|
||||
Err(CallDunderError::CallError(_, bindings)) => {
|
||||
report_unsupported_augmented_op(&mut self.context);
|
||||
report_unsupported_augmented_assignment(
|
||||
&self.context,
|
||||
assignment,
|
||||
target_type,
|
||||
value_type,
|
||||
);
|
||||
bindings.return_type(db)
|
||||
}
|
||||
}
|
||||
|
|
@ -9699,7 +9717,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.context.report_lint(&UNSUPPORTED_OPERATOR, unary)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Unary operator `{op}` is not supported for type `{}`",
|
||||
"Unary operator `{op}` is not supported for object of type `{}`",
|
||||
operand_type.display(self.db()),
|
||||
));
|
||||
}
|
||||
|
|
@ -9732,26 +9750,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
|
||||
self.infer_binary_expression_type(binary.into(), false, left_ty, right_ty, *op)
|
||||
.unwrap_or_else(|| {
|
||||
let db = self.db();
|
||||
|
||||
if let Some(builder) = self.context.report_lint(&UNSUPPORTED_OPERATOR, binary) {
|
||||
let mut diag = builder.into_diagnostic(format_args!(
|
||||
"Operator `{op}` is not supported between objects of type `{}` and `{}`",
|
||||
left_ty.display(db),
|
||||
right_ty.display(db)
|
||||
));
|
||||
|
||||
if op == &ast::Operator::BitOr
|
||||
&& (left_ty.is_subtype_of(db, KnownClass::Type.to_instance(db))
|
||||
|| right_ty.is_subtype_of(db, KnownClass::Type.to_instance(db)))
|
||||
&& Program::get(db).python_version(db) < PythonVersion::PY310
|
||||
{
|
||||
diag.info(
|
||||
"Note that `X | Y` PEP 604 union syntax is only available in Python 3.10 and later",
|
||||
);
|
||||
add_inferred_python_version_hint_to_diagnostic(db, &mut diag, "resolving types");
|
||||
}
|
||||
}
|
||||
report_unsupported_binary_operation(&self.context, binary, left_ty, right_ty, *op);
|
||||
Type::unknown()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ use crate::types::tuple::{TupleSpecBuilder, TupleType};
|
|||
use crate::types::{
|
||||
BindingContext, CallableType, DynamicType, GenericContext, IntersectionBuilder, KnownClass,
|
||||
KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType,
|
||||
Type, TypeAliasType, TypeContext, TypeIsType, TypeMapping, UnionBuilder, UnionType,
|
||||
any_over_type, todo_type,
|
||||
Type, TypeAliasType, TypeContext, TypeIsType, TypeMapping, TypeVarKind, UnionBuilder,
|
||||
UnionType, any_over_type, todo_type,
|
||||
};
|
||||
|
||||
/// Type expressions
|
||||
|
|
@ -995,8 +995,26 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
}
|
||||
Type::unknown()
|
||||
}
|
||||
KnownInstanceType::TypeVar(_) => {
|
||||
KnownInstanceType::TypeVar(typevar) => {
|
||||
// The type variable designated as a generic type alias by `typing.TypeAlias` can be explicitly specialized.
|
||||
// ```py
|
||||
// from typing import TypeVar, TypeAlias
|
||||
// T = TypeVar('T')
|
||||
// Annotated: TypeAlias = T
|
||||
// _: Annotated[int] = 1 # valid
|
||||
// ```
|
||||
if typevar.identity(self.db()).kind(self.db()) == TypeVarKind::Pep613Alias {
|
||||
self.infer_explicit_type_alias_specialization(subscript, value_ty, false)
|
||||
} else {
|
||||
if let Some(builder) =
|
||||
self.context.report_lint(&INVALID_TYPE_FORM, subscript)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"A type variable itself cannot be specialized",
|
||||
));
|
||||
}
|
||||
Type::unknown()
|
||||
}
|
||||
}
|
||||
|
||||
KnownInstanceType::UnionType(_)
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
]
|
||||
},
|
||||
"root": {
|
||||
"description": "The root paths of the project, used for finding first-party modules.\n\nAccepts a list of directory paths searched in priority order (first has highest priority).\n\nIf left unspecified, ty will try to detect common project layouts and initialize `root` accordingly:\n\n* if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat)\n* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path\n* otherwise, default to `.` (flat layout)\n\nBesides, if a `./python` or `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file),\nit will also be included in the first party search path.",
|
||||
"description": "The root paths of the project, used for finding first-party modules.\n\nAccepts a list of directory paths searched in priority order (first has highest priority).\n\nIf left unspecified, ty will try to detect common project layouts and initialize `root` accordingly:\n\n* if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat)\n* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path\n* otherwise, default to `.` (flat layout)\n\nAdditionally, if a `./python` directory exists and is not a package (i.e. it does not contain an `__init__.py` or `__init__.pyi` file),\nit will also be included in the first party search path.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
|
|
@ -1171,7 +1171,7 @@
|
|||
]
|
||||
},
|
||||
"root": {
|
||||
"description": "The root of the project, used for finding first-party modules.\n\nIf left unspecified, ty will try to detect common project layouts and initialize `src.root` accordingly:\n\n* if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat)\n* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path\n* otherwise, default to `.` (flat layout)\n\nBesides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),\nit will also be included in the first party search path.",
|
||||
"description": "The root of the project, used for finding first-party modules.\n\nIf left unspecified, ty will try to detect common project layouts and initialize `src.root` accordingly:\n\n* if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat)\n* if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path\n* otherwise, default to `.` (flat layout)\n\nAdditionally, if a `./python` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),\nit will also be included in the first party search path.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/RelativePathBuf"
|
||||
|
|
|
|||
Loading…
Reference in New Issue