mirror of https://github.com/astral-sh/ruff
[ty] Improve diagnostics for unsupported binary operations and unsupported augmented assignments (#21947)
## Summary This PR takes the improvements we made to unsupported-comparison diagnostics in https://github.com/astral-sh/ruff/pull/21737, and extends them to other `unsupported-operator` diagnostics. ## Test Plan Mdtests and snapshots
This commit is contained in:
parent
ca5f099481
commit
5a2aba237b
|
|
@ -38,6 +38,8 @@ reveal_type(x) # revealed: int
|
||||||
|
|
||||||
## Unsupported types
|
## Unsupported types
|
||||||
|
|
||||||
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
```py
|
```py
|
||||||
class C:
|
class C:
|
||||||
def __isub__(self, other: str) -> int:
|
def __isub__(self, other: str) -> int:
|
||||||
|
|
|
||||||
|
|
@ -79,31 +79,31 @@ reveal_type(Sub() & Sub()) # revealed: Literal["&"]
|
||||||
reveal_type(Sub() // Sub()) # revealed: Literal["//"]
|
reveal_type(Sub() // Sub()) # revealed: Literal["//"]
|
||||||
|
|
||||||
# No does not implement any of the dunder methods.
|
# 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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
reveal_type(No() // No()) # revealed: Unknown
|
||||||
|
|
||||||
# Yes does not implement any of the reflected dunder methods.
|
# Yes does not implement any of the reflected dunder methods.
|
||||||
|
|
@ -293,6 +293,8 @@ reveal_type(Yes() // No()) # revealed: Literal["//"]
|
||||||
|
|
||||||
## Classes
|
## Classes
|
||||||
|
|
||||||
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
Dunder methods defined in a class are available to instances of that class, but not to the class
|
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
|
itself. (For these operators to work on the class itself, they would have to be defined on the
|
||||||
class's type, i.e. `type`.)
|
class's type, i.e. `type`.)
|
||||||
|
|
@ -307,11 +309,11 @@ class Yes:
|
||||||
class Sub(Yes): ...
|
class Sub(Yes): ...
|
||||||
class No: ...
|
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
|
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
|
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
|
reveal_type(No + No) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -336,11 +338,11 @@ def sub() -> type[Sub]:
|
||||||
def no() -> type[No]:
|
def no() -> type[No]:
|
||||||
return 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
|
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
|
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
|
reveal_type(no() + no()) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -350,30 +352,54 @@ reveal_type(no() + no()) # revealed: Unknown
|
||||||
def f():
|
def f():
|
||||||
pass
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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):
|
def __init__(self):
|
||||||
self.__add__ = add_impl
|
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
|
# revealed: Unknown
|
||||||
reveal_type(A() + A())
|
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):
|
def f2(i: int, s: str, int_or_str: int | str):
|
||||||
i + i
|
i + i
|
||||||
s + s
|
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
|
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:
|
def same_constrained_types(t1: T, t2: T) -> T:
|
||||||
# TODO: no error
|
# 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
|
return t1 + t2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -287,7 +287,7 @@ and an `int` and a `str` cannot be added together:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def unions_are_different(t1: int | str, t2: int | str) -> int | str:
|
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
|
return t1 + t2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ methods that are compatible with the return type, so the `return` expression is
|
||||||
```py
|
```py
|
||||||
def same_constrained_types[T: (int, str)](t1: T, t2: T) -> T:
|
def same_constrained_types[T: (int, str)](t1: T, t2: T) -> T:
|
||||||
# TODO: no error
|
# 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
|
return t1 + t2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -256,7 +256,7 @@ and an `int` and a `str` cannot be added together:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def unions_are_different(t1: int | str, t2: int | str) -> int | str:
|
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
|
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:
|
`NoneType` has no special or-operator behavior, so this is an error:
|
||||||
|
|
||||||
```py
|
```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
|
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
|
# 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
|
--> src/mdtest_snippet.py:2:12
|
||||||
|
|
|
|
||||||
1 | # error: [unsupported-operator]
|
1 | # error: [unsupported-operator]
|
||||||
2 | IntOrStr = int | str
|
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: 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
|
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: str
|
||||||
reveal_type(~Sub()) # revealed: int
|
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
|
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
|
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
|
reveal_type(~No()) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -52,25 +52,25 @@ class Yes:
|
||||||
class Sub(Yes): ...
|
class Sub(Yes): ...
|
||||||
class No: ...
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
reveal_type(~No) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -80,11 +80,11 @@ reveal_type(~No) # revealed: Unknown
|
||||||
def f():
|
def f():
|
||||||
pass
|
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
|
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
|
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
|
reveal_type(~f) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -113,25 +113,25 @@ def sub() -> type[Sub]:
|
||||||
def no() -> type[No]:
|
def no() -> type[No]:
|
||||||
return 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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
reveal_type(~no()) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -160,10 +160,10 @@ reveal_type(+Sub) # revealed: bool
|
||||||
reveal_type(-Sub) # revealed: str
|
reveal_type(-Sub) # revealed: str
|
||||||
reveal_type(~Sub) # revealed: int
|
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
|
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
|
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
|
reveal_type(~No) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ reveal_type(~a) # revealed: Literal[True]
|
||||||
class NoDunder: ...
|
class NoDunder: ...
|
||||||
|
|
||||||
b = 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 object of 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 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_diagnostics::{Edit, Fix};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::token::parentheses_iterator;
|
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 ruff_text_size::{Ranged, TextRange};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use std::fmt::{self, Formatter};
|
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,
|
/// This function receives an unresolved `from foo import bar` import,
|
||||||
/// where `foo` can be resolved to a module but that module does not
|
/// where `foo` can be resolved to a module but that module does not
|
||||||
/// have a `bar` member or submodule.
|
/// 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_invalid_type_checking_constant, report_named_tuple_field_with_leading_underscore,
|
||||||
report_namedtuple_field_without_default_after_field_with_default, report_non_subscriptable,
|
report_namedtuple_field_without_default_after_field_with_default, report_non_subscriptable,
|
||||||
report_possibly_missing_attribute, report_possibly_unresolved_reference,
|
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::{
|
use crate::types::function::{
|
||||||
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
|
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
|
||||||
|
|
@ -5925,22 +5926,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let op = assignment.op;
|
let op = assignment.op;
|
||||||
let db = self.db();
|
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.
|
// Fall back to non-augmented binary operator inference.
|
||||||
let mut binary_return_ty = || {
|
let mut binary_return_ty = || {
|
||||||
self.infer_binary_expression_type(assignment.into(), false, target_type, value_type, op)
|
self.infer_binary_expression_type(assignment.into(), false, target_type, value_type, op)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
report_unsupported_augmented_op(&mut self.context);
|
report_unsupported_augmented_assignment(
|
||||||
|
&self.context,
|
||||||
|
assignment,
|
||||||
|
target_type,
|
||||||
|
value_type,
|
||||||
|
);
|
||||||
Type::unknown()
|
Type::unknown()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
@ -5964,7 +5959,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
UnionType::from_elements(db, [outcome.return_type(db), binary_return_ty()])
|
UnionType::from_elements(db, [outcome.return_type(db), binary_return_ty()])
|
||||||
}
|
}
|
||||||
Err(CallDunderError::CallError(_, bindings)) => {
|
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)
|
bindings.return_type(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9699,7 +9699,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
self.context.report_lint(&UNSUPPORTED_OPERATOR, unary)
|
self.context.report_lint(&UNSUPPORTED_OPERATOR, unary)
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!(
|
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()),
|
operand_type.display(self.db()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -9732,26 +9732,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
self.infer_binary_expression_type(binary.into(), false, left_ty, right_ty, *op)
|
self.infer_binary_expression_type(binary.into(), false, left_ty, right_ty, *op)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let db = self.db();
|
report_unsupported_binary_operation(&self.context, binary, left_ty, right_ty, *op);
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Type::unknown()
|
Type::unknown()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue