mirror of https://github.com/astral-sh/ruff
[ty] Improve diagnostics for unsupported binary operations and unsupported augmented assignments
This commit is contained in:
parent
c8851ecf70
commit
1f0649aa86
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -5911,22 +5912,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()
|
||||
})
|
||||
};
|
||||
|
|
@ -5950,7 +5945,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)
|
||||
}
|
||||
}
|
||||
|
|
@ -9685,7 +9685,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()),
|
||||
));
|
||||
}
|
||||
|
|
@ -9718,26 +9718,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()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue