Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 74 additions & 74 deletions crates/ty/docs/rules.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def _(a: object, b: object, flag: bool):
else:
x = g

# error: [unsupported-operator] "Operator `>` is not supported for types `object` and `object`"
# error: [unsupported-operator] "Operator `>` is not supported between two objects of type `object`"
x(f"{'a' if a > b else 'b'}")
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ reveal_type(A - B) # revealed: Unknown
reveal_type(A < B) # revealed: bool
reveal_type(A > B) # revealed: bool

# error: [unsupported-operator] "Operator `<=` is not supported for types `<class 'A'>` and `<class 'B'>`"
# error: [unsupported-operator] "Operator `<=` is not supported between objects of type `<class 'A'>` and `<class 'B'>`"
reveal_type(A <= B) # revealed: Unknown

reveal_type(A[0]) # revealed: str
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ class A:

reveal_type("hello" in A()) # revealed: bool
reveal_type("hello" not in A()) # revealed: bool
# error: [unsupported-operator] "Operator `in` is not supported for types `int` and `A`, in comparing `Literal[42]` with `A`"
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[42]` and `A`"
reveal_type(42 in A()) # revealed: bool
# error: [unsupported-operator] "Operator `not in` is not supported for types `int` and `A`, in comparing `Literal[42]` with `A`"
# error: [unsupported-operator] "Operator `not in` is not supported between objects of type `Literal[42]` and `A`"
reveal_type(42 not in A()) # revealed: bool
```

Expand Down Expand Up @@ -127,9 +127,9 @@ class A:

reveal_type(CheckContains() in A()) # revealed: bool

# error: [unsupported-operator] "Operator `in` is not supported for types `CheckIter` and `A`"
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `CheckIter` and `A`"
reveal_type(CheckIter() in A()) # revealed: bool
# error: [unsupported-operator] "Operator `in` is not supported for types `CheckGetItem` and `A`"
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `CheckGetItem` and `A`"
reveal_type(CheckGetItem() in A()) # revealed: bool

class B:
Expand All @@ -155,9 +155,9 @@ class A:
def __getitem__(self, key: str) -> str:
return "foo"

# error: [unsupported-operator] "Operator `in` is not supported for types `int` and `A`, in comparing `Literal[42]` with `A`"
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[42]` and `A`"
reveal_type(42 in A()) # revealed: bool
# error: [unsupported-operator] "Operator `in` is not supported for types `str` and `A`, in comparing `Literal["hello"]` with `A`"
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal["hello"]` and `A`"
reveal_type("hello" in A()) # revealed: bool
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ reveal_type(A() != object()) # revealed: bool
reveal_type(object() == A()) # revealed: bool
reveal_type(object() != A()) # revealed: bool

# error: [unsupported-operator] "Operator `<` is not supported for types `A` and `object`"
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `A` and `object`"
# revealed: Unknown
reveal_type(A() < object())
```
Expand All @@ -327,13 +327,13 @@ reveal_type(1 >= 1.0) # revealed: bool
reveal_type(1 == 2j) # revealed: bool
reveal_type(1 != 2j) # revealed: bool

# error: [unsupported-operator] "Operator `<` is not supported for types `int` and `complex`, in comparing `Literal[1]` with `complex`"
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `Literal[1]` and `complex`"
reveal_type(1 < 2j) # revealed: Unknown
# error: [unsupported-operator] "Operator `<=` is not supported for types `int` and `complex`, in comparing `Literal[1]` with `complex`"
# error: [unsupported-operator] "Operator `<=` is not supported between objects of type `Literal[1]` and `complex`"
reveal_type(1 <= 2j) # revealed: Unknown
# error: [unsupported-operator] "Operator `>` is not supported for types `int` and `complex`, in comparing `Literal[1]` with `complex`"
# error: [unsupported-operator] "Operator `>` is not supported between objects of type `Literal[1]` and `complex`"
reveal_type(1 > 2j) # revealed: Unknown
# error: [unsupported-operator] "Operator `>=` is not supported for types `int` and `complex`, in comparing `Literal[1]` with `complex`"
# error: [unsupported-operator] "Operator `>=` is not supported between objects of type `Literal[1]` and `complex`"
reveal_type(1 >= 2j) # revealed: Unknown

def f(x: bool, y: int):
Expand Down Expand Up @@ -386,3 +386,29 @@ reveal_type(A() == A()) # revealed: Literal[True]
reveal_type(A() < A()) # revealed: Literal[True]
reveal_type(A() > A()) # revealed: Literal[True]
```

## Diagnostics where classes have the same name

We use the fully qualified names of classes to disambiguate them where necessary:

`a.py`:

```py
class Foo: ...
```

`b.py`:

```py
class Foo: ...
```

`main.py`:

```py
import a
import b

# error: [unsupported-operator] "Operator `<` is not supported between objects of type `a.Foo` and `b.Foo`"
a.Foo() < b.Foo()
```
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ reveal_type(1 is 1) # revealed: bool
reveal_type(1 is not 1) # revealed: bool
reveal_type(1 is 2) # revealed: Literal[False]
reveal_type(1 is not 7) # revealed: Literal[True]
# error: [unsupported-operator] "Operator `<=` is not supported for types `int` and `str`, in comparing `Literal[1]` with `Literal[""]`"
# error: [unsupported-operator] "Operator `<=` is not supported between objects of type `Literal[1]` and `Literal[""]`"
reveal_type(1 <= "" and 0 < 1) # revealed: (Unknown & ~AlwaysTruthy) | Literal[True]
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ def _(o: object):

### Unsupported operators for positive contributions

<!-- snapshot-diagnostics -->

Raise an error if the given operator is unsupported for all positive contributions to the
intersection type:

Expand All @@ -121,7 +123,7 @@ def _(x: object):
if isinstance(x, NonContainer2):
reveal_type(x) # revealed: NonContainer1 & NonContainer2

# error: [unsupported-operator] "Operator `in` is not supported for types `int` and `NonContainer1`"
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[2]` and `NonContainer1 & NonContainer2`"
reveal_type(2 in x) # revealed: bool
```

Expand Down Expand Up @@ -149,7 +151,7 @@ def _(x: object):
if not isinstance(x, NonContainer1):
reveal_type(x) # revealed: ~NonContainer1

# error: [unsupported-operator] "Operator `in` is not supported for types `int` and `object`, in comparing `Literal[2]` with `~NonContainer1`"
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[2]` and `~NonContainer1`"
reveal_type(2 in x) # revealed: bool

reveal_type(2 is x) # revealed: bool
Expand Down
29 changes: 17 additions & 12 deletions crates/ty_python_semantic/resources/mdtest/comparison/tuples.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ def _(x: bool, y: int):

#### Comparison Unsupported

<!-- snapshot-diagnostics -->

If two tuples contain types that do not support comparison, the result may be `Unknown`. However,
`==` and `!=` are exceptions and can still provide definite results.

Expand All @@ -92,14 +94,17 @@ reveal_type(a == b) # revealed: bool
# TODO: should be Literal[True], once we implement (in)equality for mismatched literals
reveal_type(a != b) # revealed: bool

# error: [unsupported-operator] "Operator `<` is not supported for types `int` and `str`, in comparing `tuple[Literal[1], Literal[2]]` with `tuple[Literal[1], Literal["hello"]]`"
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[Literal[1], Literal[2]]` and `tuple[Literal[1], Literal["hello"]]`"
reveal_type(a < b) # revealed: Unknown
# error: [unsupported-operator] "Operator `<=` is not supported for types `int` and `str`, in comparing `tuple[Literal[1], Literal[2]]` with `tuple[Literal[1], Literal["hello"]]`"
# error: [unsupported-operator] "Operator `<=` is not supported between objects of type `tuple[Literal[1], Literal[2]]` and `tuple[Literal[1], Literal["hello"]]`"
reveal_type(a <= b) # revealed: Unknown
# error: [unsupported-operator] "Operator `>` is not supported for types `int` and `str`, in comparing `tuple[Literal[1], Literal[2]]` with `tuple[Literal[1], Literal["hello"]]`"
# error: [unsupported-operator] "Operator `>` is not supported between objects of type `tuple[Literal[1], Literal[2]]` and `tuple[Literal[1], Literal["hello"]]`"
reveal_type(a > b) # revealed: Unknown
# error: [unsupported-operator] "Operator `>=` is not supported for types `int` and `str`, in comparing `tuple[Literal[1], Literal[2]]` with `tuple[Literal[1], Literal["hello"]]`"
# error: [unsupported-operator] "Operator `>=` is not supported between objects of type `tuple[Literal[1], Literal[2]]` and `tuple[Literal[1], Literal["hello"]]`"
reveal_type(a >= b) # revealed: Unknown
# error: [unsupported-operator]
# error: [unsupported-operator]
reveal_type((object(),) < (object(),) < (object(),)) # revealed: Unknown
```

However, if the lexicographic comparison completes without reaching a point where str and int are
Expand Down Expand Up @@ -257,24 +262,24 @@ comparison can clearly conclude before encountering an error, the error should n
```py
def _(n: int, s: str):
class A: ...
# error: [unsupported-operator] "Operator `<` is not supported for types `A` and `A`"
# error: [unsupported-operator] "Operator `<` is not supported between two objects of type `A`"
A() < A()
# error: [unsupported-operator] "Operator `<=` is not supported for types `A` and `A`"
# error: [unsupported-operator] "Operator `<=` is not supported between two objects of type `A`"
A() <= A()
# error: [unsupported-operator] "Operator `>` is not supported for types `A` and `A`"
# error: [unsupported-operator] "Operator `>` is not supported between two objects of type `A`"
A() > A()
# error: [unsupported-operator] "Operator `>=` is not supported for types `A` and `A`"
# error: [unsupported-operator] "Operator `>=` is not supported between two objects of type `A`"
A() >= A()

a = (0, n, A())

# error: [unsupported-operator] "Operator `<` is not supported for types `A` and `A`, in comparing `tuple[Literal[0], int, A]` with `tuple[Literal[0], int, A]`"
# error: [unsupported-operator] "Operator `<` is not supported between two objects of type `tuple[Literal[0], int, A]`"
reveal_type(a < a) # revealed: Unknown
# error: [unsupported-operator] "Operator `<=` is not supported for types `A` and `A`, in comparing `tuple[Literal[0], int, A]` with `tuple[Literal[0], int, A]`"
# error: [unsupported-operator] "Operator `<=` is not supported between two objects of type `tuple[Literal[0], int, A]`"
reveal_type(a <= a) # revealed: Unknown
# error: [unsupported-operator] "Operator `>` is not supported for types `A` and `A`, in comparing `tuple[Literal[0], int, A]` with `tuple[Literal[0], int, A]`"
# error: [unsupported-operator] "Operator `>` is not supported between two objects of type `tuple[Literal[0], int, A]`"
reveal_type(a > a) # revealed: Unknown
# error: [unsupported-operator] "Operator `>=` is not supported for types `A` and `A`, in comparing `tuple[Literal[0], int, A]` with `tuple[Literal[0], int, A]`"
# error: [unsupported-operator] "Operator `>=` is not supported between two objects of type `tuple[Literal[0], int, A]`"
reveal_type(a >= a) # revealed: Unknown

# Comparison between `a` and `b` should only involve the first elements, `Literal[0]` and `Literal[99999]`,
Expand Down
21 changes: 18 additions & 3 deletions crates/ty_python_semantic/resources/mdtest/comparison/unions.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,29 @@ def _(flag_s: bool, flag_l: bool):

## Unsupported operations

<!-- snapshot-diagnostics -->

Make sure we emit a diagnostic if *any* of the possible comparisons is unsupported. For now, we fall
back to `bool` for the result type instead of trying to infer something more precise from the other
(supported) variants:

```py
def _(flag: bool):
x = [1, 2] if flag else 1

from typing import Literal

def _(
x: list[int] | Literal[1],
y: list[int] | Literal[1],
aa: tuple[int],
bb: tuple[int] | tuple[int, int],
cc: tuple[str] | tuple[str, str],
):
result = 1 in x # error: "Operator `in` is not supported"
reveal_type(result) # revealed: bool

result2 = y in x # error: [unsupported-operator]
reveal_type(result) # revealed: bool

result3 = aa < cc # error: [unsupported-operator]
result4 = cc < aa # error: [unsupported-operator]
result5 = bb < cc # error: [unsupported-operator]
```
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
# Comparison: Unsupported operators

<!-- snapshot-diagnostics -->

```py
def _(flag: bool, flag1: bool, flag2: bool):
class A: ...
a = 1 in 7 # error: "Operator `in` is not supported for types `Literal[1]` and `Literal[7]`"
a = 1 in 7 # error: "Operator `in` is not supported between objects of type `Literal[1]` and `Literal[7]`"
reveal_type(a) # revealed: bool

b = 0 not in 10 # error: "Operator `not in` is not supported for types `Literal[0]` and `Literal[10]`"
b = 0 not in 10 # error: "Operator `not in` is not supported between objects of type `Literal[0]` and `Literal[10]`"
reveal_type(b) # revealed: bool

# error: [unsupported-operator] "Operator `<` is not supported for types `object` and `int`, in comparing `object` with `Literal[5]`"
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `object` and `Literal[5]`"
c = object() < 5
reveal_type(c) # revealed: Unknown

# error: [unsupported-operator] "Operator `<` is not supported for types `int` and `object`, in comparing `Literal[5]` with `object`"
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `Literal[5]` and `object`"
d = 5 < object()
reveal_type(d) # revealed: Unknown

int_literal_or_str_literal = 1 if flag else "foo"
# error: "Operator `in` is not supported for types `Literal[42]` and `Literal[1]`, in comparing `Literal[42]` with `Literal[1, "foo"]`"
# error: "Operator `in` is not supported between objects of type `Literal[42]` and `Literal[1, "foo"]`"
e = 42 in int_literal_or_str_literal
reveal_type(e) # revealed: bool

# error: [unsupported-operator] "Operator `<` is not supported for types `int` and `str`, in comparing `tuple[Literal[1], Literal[2]]` with `tuple[Literal[1], Literal["hello"]]`"
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[Literal[1], Literal[2]]` and `tuple[Literal[1], Literal["hello"]]`"
f = (1, 2) < (1, "hello")
reveal_type(f) # revealed: Unknown

# error: [unsupported-operator] "Operator `<` is not supported for types `A` and `A`, in comparing `tuple[bool, A]` with `tuple[bool, A]`"
# error: [unsupported-operator] "Operator `<` is not supported between two objects of type `tuple[bool, A]`"
g = (flag1, A()) < (flag2, A())
reveal_type(g) # revealed: Unknown
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
source: crates/ty_test/src/lib.rs
expression: snapshot
---
---
mdtest name: intersections.md - Comparison: Intersections - Diagnostics - Unsupported operators for positive contributions
mdtest path: crates/ty_python_semantic/resources/mdtest/comparison/intersections.md
---

# Python source files

## mdtest_snippet.py

```
1 | class NonContainer1: ...
2 | class NonContainer2: ...
3 |
4 | def _(x: object):
5 | if isinstance(x, NonContainer1):
6 | if isinstance(x, NonContainer2):
7 | reveal_type(x) # revealed: NonContainer1 & NonContainer2
8 |
9 | # error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[2]` and `NonContainer1 & NonContainer2`"
10 | reveal_type(2 in x) # revealed: bool
11 | class Container:
12 | def __contains__(self, x) -> bool:
13 | return False
14 |
15 | def _(x: object):
16 | if isinstance(x, NonContainer1):
17 | if isinstance(x, Container):
18 | if isinstance(x, NonContainer2):
19 | reveal_type(x) # revealed: NonContainer1 & Container & NonContainer2
20 | reveal_type(2 in x) # revealed: bool
21 | def _(x: object):
22 | if not isinstance(x, NonContainer1):
23 | reveal_type(x) # revealed: ~NonContainer1
24 |
25 | # error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[2]` and `~NonContainer1`"
26 | reveal_type(2 in x) # revealed: bool
27 |
28 | reveal_type(2 is x) # revealed: bool
```

# Diagnostics

```
error[unsupported-operator]: Unsupported `in` operation
--> src/mdtest_snippet.py:10:25
|
9 | # error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[2]` and `NonContainer1 & …
10 | reveal_type(2 in x) # revealed: bool
| -^^^^-
| | |
| | Has type `NonContainer1 & NonContainer2`
| Has type `Literal[2]`
11 | class Container:
12 | def __contains__(self, x) -> bool:
|
info: rule `unsupported-operator` is enabled by default

```

```
error[unsupported-operator]: Unsupported `in` operation
--> src/mdtest_snippet.py:26:21
|
25 | # error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[2]` and `~NonContainer1`"
26 | reveal_type(2 in x) # revealed: bool
| -^^^^-
| | |
| | Has type `~NonContainer1`
| Has type `Literal[2]`
27 |
28 | reveal_type(2 is x) # revealed: bool
|
info: rule `unsupported-operator` is enabled by default

```
Loading
Loading