mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[ty] Rename "possibly unbound" diagnostics to "possibly missing" (#20492)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
parent
4ed8c65d29
commit
bf38e69870
41 changed files with 213 additions and 194 deletions
|
@ -42,6 +42,13 @@ Used to determine if an active Conda environment is the base environment or not.
|
||||||
Used to detect an activated Conda environment location.
|
Used to detect an activated Conda environment location.
|
||||||
If both `VIRTUAL_ENV` and `CONDA_PREFIX` are present, `VIRTUAL_ENV` will be preferred.
|
If both `VIRTUAL_ENV` and `CONDA_PREFIX` are present, `VIRTUAL_ENV` will be preferred.
|
||||||
|
|
||||||
|
### `PYTHONPATH`
|
||||||
|
|
||||||
|
Adds additional directories to ty's search paths.
|
||||||
|
The format is the same as the shell’s PATH:
|
||||||
|
one or more directory pathnames separated by os appropriate pathsep
|
||||||
|
(e.g. colons on Unix or semicolons on Windows).
|
||||||
|
|
||||||
### `RAYON_NUM_THREADS`
|
### `RAYON_NUM_THREADS`
|
||||||
|
|
||||||
Specifies an upper limit for the number of threads ty uses when performing work in parallel.
|
Specifies an upper limit for the number of threads ty uses when performing work in parallel.
|
||||||
|
|
24
crates/ty/docs/rules.md
generated
24
crates/ty/docs/rules.md
generated
|
@ -1863,21 +1863,21 @@ Use instead:
|
||||||
a = 20 / 0 # type: ignore
|
a = 20 / 0 # type: ignore
|
||||||
```
|
```
|
||||||
|
|
||||||
## `possibly-unbound-attribute`
|
## `possibly-missing-attribute`
|
||||||
|
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-attribute) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1328)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1328)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
||||||
Checks for possibly unbound attributes.
|
Checks for possibly missing attributes.
|
||||||
|
|
||||||
**Why is this bad?**
|
**Why is this bad?**
|
||||||
|
|
||||||
Attempting to access an unbound attribute will raise an `AttributeError` at runtime.
|
Attempting to access a missing attribute will raise an `AttributeError` at runtime.
|
||||||
|
|
||||||
**Examples**
|
**Examples**
|
||||||
|
|
||||||
|
@ -1889,23 +1889,23 @@ class A:
|
||||||
A.c # AttributeError: type object 'A' has no attribute 'c'
|
A.c # AttributeError: type object 'A' has no attribute 'c'
|
||||||
```
|
```
|
||||||
|
|
||||||
## `possibly-unbound-implicit-call`
|
## `possibly-missing-implicit-call`
|
||||||
|
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-implicit-call) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-implicit-call) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L132)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L132)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
||||||
Checks for implicit calls to possibly unbound methods.
|
Checks for implicit calls to possibly missing methods.
|
||||||
|
|
||||||
**Why is this bad?**
|
**Why is this bad?**
|
||||||
|
|
||||||
Expressions such as `x[y]` and `x * y` call methods
|
Expressions such as `x[y]` and `x * y` call methods
|
||||||
under the hood (`__getitem__` and `__mul__` respectively).
|
under the hood (`__getitem__` and `__mul__` respectively).
|
||||||
Calling an unbound method will raise an `AttributeError` at runtime.
|
Calling a missing method will raise an `AttributeError` at runtime.
|
||||||
|
|
||||||
**Examples**
|
**Examples**
|
||||||
|
|
||||||
|
@ -1919,21 +1919,21 @@ class A:
|
||||||
A()[0] # TypeError: 'A' object is not subscriptable
|
A()[0] # TypeError: 'A' object is not subscriptable
|
||||||
```
|
```
|
||||||
|
|
||||||
## `possibly-unbound-import`
|
## `possibly-missing-import`
|
||||||
|
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-import) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1350)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1350)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
||||||
Checks for imports of symbols that may be unbound.
|
Checks for imports of symbols that may be missing.
|
||||||
|
|
||||||
**Why is this bad?**
|
**Why is this bad?**
|
||||||
|
|
||||||
Importing an unbound module or name will raise a `ModuleNotFoundError`
|
Importing a missing module or name will raise a `ModuleNotFoundError`
|
||||||
or `ImportError` at runtime.
|
or `ImportError` at runtime.
|
||||||
|
|
||||||
**Examples**
|
**Examples**
|
||||||
|
|
|
@ -914,7 +914,7 @@ def _(flag: bool):
|
||||||
reveal_type(C3.attr2) # revealed: Literal["metaclass value", "class value"]
|
reveal_type(C3.attr2) # revealed: Literal["metaclass value", "class value"]
|
||||||
```
|
```
|
||||||
|
|
||||||
If the *metaclass* attribute is only partially defined, we emit a `possibly-unbound-attribute`
|
If the *metaclass* attribute is only partially defined, we emit a `possibly-missing-attribute`
|
||||||
diagnostic:
|
diagnostic:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
@ -924,12 +924,12 @@ def _(flag: bool):
|
||||||
attr1: str = "metaclass value"
|
attr1: str = "metaclass value"
|
||||||
|
|
||||||
class C4(metaclass=Meta4): ...
|
class C4(metaclass=Meta4): ...
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(C4.attr1) # revealed: str
|
reveal_type(C4.attr1) # revealed: str
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, if both the metaclass attribute and the class-level attribute are only partially defined,
|
Finally, if both the metaclass attribute and the class-level attribute are only partially defined,
|
||||||
we union them and emit a `possibly-unbound-attribute` diagnostic:
|
we union them and emit a `possibly-missing-attribute` diagnostic:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _(flag1: bool, flag2: bool):
|
def _(flag1: bool, flag2: bool):
|
||||||
|
@ -941,7 +941,7 @@ def _(flag1: bool, flag2: bool):
|
||||||
if flag2:
|
if flag2:
|
||||||
attr1 = "class value"
|
attr1 = "class value"
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(C5.attr1) # revealed: Unknown | Literal["metaclass value", "class value"]
|
reveal_type(C5.attr1) # revealed: Unknown | Literal["metaclass value", "class value"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1180,13 +1180,13 @@ def _(flag1: bool, flag2: bool):
|
||||||
|
|
||||||
C = C1 if flag1 else C2 if flag2 else C3
|
C = C1 if flag1 else C2 if flag2 else C3
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `x` on type `<class 'C1'> | <class 'C2'> | <class 'C3'>` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `x` on type `<class 'C1'> | <class 'C2'> | <class 'C3'>` may be missing"
|
||||||
reveal_type(C.x) # revealed: Unknown | Literal[1, 3]
|
reveal_type(C.x) # revealed: Unknown | Literal[1, 3]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `Literal[100]` is not assignable to attribute `x` on type `<class 'C1'> | <class 'C2'> | <class 'C3'>`"
|
# error: [invalid-assignment] "Object of type `Literal[100]` is not assignable to attribute `x` on type `<class 'C1'> | <class 'C2'> | <class 'C3'>`"
|
||||||
C.x = 100
|
C.x = 100
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `x` on type `C1 | C2 | C3` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `x` on type `C1 | C2 | C3` may be missing"
|
||||||
reveal_type(C().x) # revealed: Unknown | Literal[1, 3]
|
reveal_type(C().x) # revealed: Unknown | Literal[1, 3]
|
||||||
|
|
||||||
# error: [invalid-assignment] "Object of type `Literal[100]` is not assignable to attribute `x` on type `C1 | C2 | C3`"
|
# error: [invalid-assignment] "Object of type `Literal[100]` is not assignable to attribute `x` on type `C1 | C2 | C3`"
|
||||||
|
@ -1212,18 +1212,18 @@ def _(flag: bool, flag1: bool, flag2: bool):
|
||||||
|
|
||||||
C = C1 if flag1 else C2 if flag2 else C3
|
C = C1 if flag1 else C2 if flag2 else C3
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `x` on type `<class 'C1'> | <class 'C2'> | <class 'C3'>` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `x` on type `<class 'C1'> | <class 'C2'> | <class 'C3'>` may be missing"
|
||||||
reveal_type(C.x) # revealed: Unknown | Literal[1, 2, 3]
|
reveal_type(C.x) # revealed: Unknown | Literal[1, 2, 3]
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
C.x = 100
|
C.x = 100
|
||||||
|
|
||||||
# Note: we might want to consider ignoring possibly-unbound diagnostics for instance attributes eventually,
|
# Note: we might want to consider ignoring possibly-missing diagnostics for instance attributes eventually,
|
||||||
# see the "Possibly unbound/undeclared instance attribute" section below.
|
# see the "Possibly unbound/undeclared instance attribute" section below.
|
||||||
# error: [possibly-unbound-attribute] "Attribute `x` on type `C1 | C2 | C3` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `x` on type `C1 | C2 | C3` may be missing"
|
||||||
reveal_type(C().x) # revealed: Unknown | Literal[1, 2, 3]
|
reveal_type(C().x) # revealed: Unknown | Literal[1, 2, 3]
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
C().x = 100
|
C().x = 100
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1287,16 +1287,16 @@ def _(flag: bool):
|
||||||
if flag:
|
if flag:
|
||||||
x = 2
|
x = 2
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(Bar.x) # revealed: Unknown | Literal[2, 1]
|
reveal_type(Bar.x) # revealed: Unknown | Literal[2, 1]
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
Bar.x = 3
|
Bar.x = 3
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(Bar().x) # revealed: Unknown | Literal[2, 1]
|
reveal_type(Bar().x) # revealed: Unknown | Literal[2, 1]
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
Bar().x = 3
|
Bar().x = 3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1304,7 +1304,7 @@ def _(flag: bool):
|
||||||
|
|
||||||
We currently treat implicit instance attributes to be bound, even if they are only conditionally
|
We currently treat implicit instance attributes to be bound, even if they are only conditionally
|
||||||
defined within a method. If the class-level definition or the whole method is only conditionally
|
defined within a method. If the class-level definition or the whole method is only conditionally
|
||||||
available, we emit a `possibly-unbound-attribute` diagnostic.
|
available, we emit a `possibly-missing-attribute` diagnostic.
|
||||||
|
|
||||||
#### Possibly unbound and undeclared
|
#### Possibly unbound and undeclared
|
||||||
|
|
||||||
|
@ -1484,17 +1484,17 @@ def _(flag: bool):
|
||||||
class B1: ...
|
class B1: ...
|
||||||
|
|
||||||
def inner1(a_and_b: Intersection[A1, B1]):
|
def inner1(a_and_b: Intersection[A1, B1]):
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(a_and_b.x) # revealed: P
|
reveal_type(a_and_b.x) # revealed: P
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
a_and_b.x = R()
|
a_and_b.x = R()
|
||||||
# Same for class objects
|
# Same for class objects
|
||||||
def inner1_class(a_and_b: Intersection[type[A1], type[B1]]):
|
def inner1_class(a_and_b: Intersection[type[A1], type[B1]]):
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(a_and_b.x) # revealed: P
|
reveal_type(a_and_b.x) # revealed: P
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
a_and_b.x = R()
|
a_and_b.x = R()
|
||||||
|
|
||||||
class A2:
|
class A2:
|
||||||
|
@ -1509,7 +1509,7 @@ def _(flag: bool):
|
||||||
|
|
||||||
# TODO: this should not be an error, we need better intersection
|
# TODO: this should not be an error, we need better intersection
|
||||||
# handling in `validate_attribute_assignment` for this
|
# handling in `validate_attribute_assignment` for this
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
a_and_b.x = R()
|
a_and_b.x = R()
|
||||||
# Same for class objects
|
# Same for class objects
|
||||||
def inner2_class(a_and_b: Intersection[type[A2], type[B1]]):
|
def inner2_class(a_and_b: Intersection[type[A2], type[B1]]):
|
||||||
|
@ -1524,17 +1524,17 @@ def _(flag: bool):
|
||||||
x: Q = Q()
|
x: Q = Q()
|
||||||
|
|
||||||
def inner3(a_and_b: Intersection[A3, B3]):
|
def inner3(a_and_b: Intersection[A3, B3]):
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(a_and_b.x) # revealed: P & Q
|
reveal_type(a_and_b.x) # revealed: P & Q
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
a_and_b.x = R()
|
a_and_b.x = R()
|
||||||
# Same for class objects
|
# Same for class objects
|
||||||
def inner3_class(a_and_b: Intersection[type[A3], type[B3]]):
|
def inner3_class(a_and_b: Intersection[type[A3], type[B3]]):
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(a_and_b.x) # revealed: P & Q
|
reveal_type(a_and_b.x) # revealed: P & Q
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
a_and_b.x = R()
|
a_and_b.x = R()
|
||||||
|
|
||||||
class A4: ...
|
class A4: ...
|
||||||
|
@ -1649,7 +1649,7 @@ If an attribute is defined on the class, it takes precedence over the `__getattr
|
||||||
reveal_type(c.class_attr) # revealed: int
|
reveal_type(c.class_attr) # revealed: int
|
||||||
```
|
```
|
||||||
|
|
||||||
If the class attribute is possibly unbound, we union the type of the attribute with the fallback
|
If the class attribute is possibly missing, we union the type of the attribute with the fallback
|
||||||
type of the `__getattr__` method:
|
type of the `__getattr__` method:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
|
|
@ -26,7 +26,7 @@ In particular, we should raise errors in the "possibly-undeclared-and-unbound" a
|
||||||
| **Diagnostic** | declared | possibly-undeclared | undeclared |
|
| **Diagnostic** | declared | possibly-undeclared | undeclared |
|
||||||
| ---------------- | -------- | ------------------------- | ------------------- |
|
| ---------------- | -------- | ------------------------- | ------------------- |
|
||||||
| bound | | | |
|
| bound | | | |
|
||||||
| possibly-unbound | | `possibly-unbound-import` | ? |
|
| possibly-unbound | | `possibly-missing-import` | ? |
|
||||||
| unbound | | ? | `unresolved-import` |
|
| unbound | | ? | `unresolved-import` |
|
||||||
|
|
||||||
## Declared
|
## Declared
|
||||||
|
@ -158,7 +158,7 @@ a = None
|
||||||
|
|
||||||
If a symbol is possibly undeclared and possibly unbound, we also use the union of the declared and
|
If a symbol is possibly undeclared and possibly unbound, we also use the union of the declared and
|
||||||
inferred types. This case is interesting because the "possibly declared" definition might not be the
|
inferred types. This case is interesting because the "possibly declared" definition might not be the
|
||||||
same as the "possibly bound" definition (symbol `b`). Note that we raise a `possibly-unbound-import`
|
same as the "possibly bound" definition (symbol `b`). Note that we raise a `possibly-missing-import`
|
||||||
error for both `a` and `b`:
|
error for both `a` and `b`:
|
||||||
|
|
||||||
`mod.py`:
|
`mod.py`:
|
||||||
|
@ -177,8 +177,8 @@ else:
|
||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# error: [possibly-unbound-import]
|
# error: [possibly-missing-import] "Member `a` of module `mod` may be missing"
|
||||||
# error: [possibly-unbound-import]
|
# error: [possibly-missing-import] "Member `b` of module `mod` may be missing"
|
||||||
from mod import a, b
|
from mod import a, b
|
||||||
|
|
||||||
reveal_type(a) # revealed: Literal[1] | Any
|
reveal_type(a) # revealed: Literal[1] | Any
|
||||||
|
@ -332,8 +332,8 @@ if flag():
|
||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# error: [possibly-unbound-import]
|
# error: [possibly-missing-import]
|
||||||
# error: [possibly-unbound-import]
|
# error: [possibly-missing-import]
|
||||||
from mod import MyInt, C
|
from mod import MyInt, C
|
||||||
|
|
||||||
reveal_type(MyInt) # revealed: <class 'int'>
|
reveal_type(MyInt) # revealed: <class 'int'>
|
||||||
|
|
|
@ -19,7 +19,7 @@ b = Unit()(3.0) # error: "Object of type `Unit` is not callable"
|
||||||
reveal_type(b) # revealed: Unknown
|
reveal_type(b) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
## Possibly unbound `__call__` method
|
## Possibly missing `__call__` method
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _(flag: bool):
|
def _(flag: bool):
|
||||||
|
@ -29,7 +29,7 @@ def _(flag: bool):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
a = PossiblyNotCallable()
|
a = PossiblyNotCallable()
|
||||||
result = a() # error: "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
result = a() # error: "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
reveal_type(result) # revealed: int
|
reveal_type(result) # revealed: int
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ reveal_type(c()) # revealed: int
|
||||||
|
|
||||||
## Union over callables
|
## Union over callables
|
||||||
|
|
||||||
### Possibly unbound `__call__`
|
### Possibly missing `__call__`
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def outer(cond1: bool):
|
def outer(cond1: bool):
|
||||||
|
@ -122,6 +122,6 @@ def outer(cond1: bool):
|
||||||
else:
|
else:
|
||||||
a = Other()
|
a = Other()
|
||||||
|
|
||||||
# error: [call-non-callable] "Object of type `Test` is not callable (possibly unbound `__call__` method)"
|
# error: [call-non-callable] "Object of type `Test` is not callable (possibly missing `__call__` method)"
|
||||||
a()
|
a()
|
||||||
```
|
```
|
||||||
|
|
|
@ -158,15 +158,15 @@ def _(flag: bool) -> None:
|
||||||
def __new__(cls):
|
def __new__(cls):
|
||||||
return object.__new__(cls)
|
return object.__new__(cls)
|
||||||
|
|
||||||
# error: [possibly-unbound-implicit-call]
|
# error: [possibly-missing-implicit-call]
|
||||||
reveal_type(Foo()) # revealed: Foo
|
reveal_type(Foo()) # revealed: Foo
|
||||||
|
|
||||||
# error: [possibly-unbound-implicit-call]
|
# error: [possibly-missing-implicit-call]
|
||||||
# error: [too-many-positional-arguments]
|
# error: [too-many-positional-arguments]
|
||||||
reveal_type(Foo(1)) # revealed: Foo
|
reveal_type(Foo(1)) # revealed: Foo
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Possibly unbound `__call__` on `__new__` callable
|
#### Possibly missing `__call__` on `__new__` callable
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _(flag: bool) -> None:
|
def _(flag: bool) -> None:
|
||||||
|
@ -178,11 +178,11 @@ def _(flag: bool) -> None:
|
||||||
class Foo:
|
class Foo:
|
||||||
__new__ = Callable()
|
__new__ = Callable()
|
||||||
|
|
||||||
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly unbound `__call__` method)"
|
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly missing `__call__` method)"
|
||||||
reveal_type(Foo(1)) # revealed: Foo
|
reveal_type(Foo(1)) # revealed: Foo
|
||||||
# TODO should be - error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
|
# TODO should be - error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
|
||||||
# but we currently infer the signature of `__call__` as unknown, so it accepts any arguments
|
# but we currently infer the signature of `__call__` as unknown, so it accepts any arguments
|
||||||
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly unbound `__call__` method)"
|
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly missing `__call__` method)"
|
||||||
reveal_type(Foo()) # revealed: Foo
|
reveal_type(Foo()) # revealed: Foo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -294,11 +294,11 @@ def _(flag: bool) -> None:
|
||||||
class Foo:
|
class Foo:
|
||||||
__init__ = Callable()
|
__init__ = Callable()
|
||||||
|
|
||||||
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly unbound `__call__` method)"
|
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly missing `__call__` method)"
|
||||||
reveal_type(Foo(1)) # revealed: Foo
|
reveal_type(Foo(1)) # revealed: Foo
|
||||||
# TODO should be - error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
|
# TODO should be - error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
|
||||||
# but we currently infer the signature of `__call__` as unknown, so it accepts any arguments
|
# but we currently infer the signature of `__call__` as unknown, so it accepts any arguments
|
||||||
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly unbound `__call__` method)"
|
# error: [call-non-callable] "Object of type `Callable` is not callable (possibly missing `__call__` method)"
|
||||||
reveal_type(Foo()) # revealed: Foo
|
reveal_type(Foo()) # revealed: Foo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,11 @@ def _(flag: bool):
|
||||||
|
|
||||||
this_fails = ThisFails()
|
this_fails = ThisFails()
|
||||||
|
|
||||||
# error: [possibly-unbound-implicit-call]
|
# TODO: this would be a friendlier diagnostic if we propagated the error up the stack
|
||||||
|
# and transformed it into a `[not-subscriptable]` error with a subdiagnostic explaining
|
||||||
|
# that the cause of the error was a possibly missing `__getitem__` method
|
||||||
|
#
|
||||||
|
# error: [possibly-missing-implicit-call] "Method `__getitem__` of type `ThisFails` may be missing"
|
||||||
reveal_type(this_fails[0]) # revealed: Unknown | str
|
reveal_type(this_fails[0]) # revealed: Unknown | str
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -270,6 +274,11 @@ def _(flag: bool):
|
||||||
return str(key)
|
return str(key)
|
||||||
|
|
||||||
c = C()
|
c = C()
|
||||||
# error: [possibly-unbound-implicit-call]
|
|
||||||
|
# TODO: this would be a friendlier diagnostic if we propagated the error up the stack
|
||||||
|
# and transformed it into a `[not-subscriptable]` error with a subdiagnostic explaining
|
||||||
|
# that the cause of the error was a possibly missing `__getitem__` method
|
||||||
|
#
|
||||||
|
# error: [possibly-missing-implicit-call] "Method `__getitem__` of type `C` may be missing"
|
||||||
reveal_type(c[0]) # revealed: str
|
reveal_type(c[0]) # revealed: str
|
||||||
```
|
```
|
||||||
|
|
|
@ -325,7 +325,7 @@ class D(metaclass=Meta):
|
||||||
reveal_type(D.f(1)) # revealed: Literal["a"]
|
reveal_type(D.f(1)) # revealed: Literal["a"]
|
||||||
```
|
```
|
||||||
|
|
||||||
If the class method is possibly unbound, we union the return types:
|
If the class method is possibly missing, we union the return types:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def flag() -> bool:
|
def flag() -> bool:
|
||||||
|
|
|
@ -219,7 +219,7 @@ def f(x: C | D):
|
||||||
s = super(A, x)
|
s = super(A, x)
|
||||||
reveal_type(s) # revealed: <super: <class 'A'>, C> | <super: <class 'A'>, D>
|
reveal_type(s) # revealed: <super: <class 'A'>, C> | <super: <class 'A'>, D>
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `b` on type `<super: <class 'A'>, C> | <super: <class 'A'>, D>` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `b` on type `<super: <class 'A'>, C> | <super: <class 'A'>, D>` may be missing"
|
||||||
s.b
|
s.b
|
||||||
|
|
||||||
def f(flag: bool):
|
def f(flag: bool):
|
||||||
|
@ -259,7 +259,7 @@ def f(flag: bool):
|
||||||
reveal_type(s.x) # revealed: Unknown | Literal[1, 2]
|
reveal_type(s.x) # revealed: Unknown | Literal[1, 2]
|
||||||
reveal_type(s.y) # revealed: int | str
|
reveal_type(s.y) # revealed: int | str
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `a` on type `<super: <class 'B'>, B> | <super: <class 'D'>, D>` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `a` on type `<super: <class 'B'>, B> | <super: <class 'D'>, D>` may be missing"
|
||||||
reveal_type(s.a) # revealed: str
|
reveal_type(s.a) # revealed: str
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -351,7 +351,7 @@ reveal_type(C4.meta_attribute) # revealed: Literal["value on metaclass"]
|
||||||
reveal_type(C4.meta_non_data_descriptor) # revealed: Literal["non-data"]
|
reveal_type(C4.meta_non_data_descriptor) # revealed: Literal["non-data"]
|
||||||
```
|
```
|
||||||
|
|
||||||
When a metaclass data descriptor is possibly unbound, we union the result type of its `__get__`
|
When a metaclass data descriptor is possibly missing, we union the result type of its `__get__`
|
||||||
method with an underlying class level attribute, if present:
|
method with an underlying class level attribute, if present:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
@ -365,7 +365,7 @@ def _(flag: bool):
|
||||||
meta_data_descriptor1: Literal["value on class"] = "value on class"
|
meta_data_descriptor1: Literal["value on class"] = "value on class"
|
||||||
|
|
||||||
reveal_type(C5.meta_data_descriptor1) # revealed: Literal["data", "value on class"]
|
reveal_type(C5.meta_data_descriptor1) # revealed: Literal["data", "value on class"]
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(C5.meta_data_descriptor2) # revealed: Literal["data"]
|
reveal_type(C5.meta_data_descriptor2) # revealed: Literal["data"]
|
||||||
|
|
||||||
# TODO: We currently emit two diagnostics here, corresponding to the two states of `flag`. The diagnostics are not
|
# TODO: We currently emit two diagnostics here, corresponding to the two states of `flag`. The diagnostics are not
|
||||||
|
@ -375,11 +375,11 @@ def _(flag: bool):
|
||||||
# error: [invalid-assignment] "Object of type `None` is not assignable to attribute `meta_data_descriptor1` of type `Literal["value on class"]`"
|
# error: [invalid-assignment] "Object of type `None` is not assignable to attribute `meta_data_descriptor1` of type `Literal["value on class"]`"
|
||||||
C5.meta_data_descriptor1 = None
|
C5.meta_data_descriptor1 = None
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
C5.meta_data_descriptor2 = 1
|
C5.meta_data_descriptor2 = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
When a class-level attribute is possibly unbound, we union its (descriptor protocol) type with the
|
When a class-level attribute is possibly missing, we union its (descriptor protocol) type with the
|
||||||
metaclass attribute (unless it's a data descriptor, which always takes precedence):
|
metaclass attribute (unless it's a data descriptor, which always takes precedence):
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
@ -401,7 +401,7 @@ def _(flag: bool):
|
||||||
reveal_type(C6.attribute1) # revealed: Literal["data"]
|
reveal_type(C6.attribute1) # revealed: Literal["data"]
|
||||||
reveal_type(C6.attribute2) # revealed: Literal["non-data", "value on class"]
|
reveal_type(C6.attribute2) # revealed: Literal["non-data", "value on class"]
|
||||||
reveal_type(C6.attribute3) # revealed: Literal["value on metaclass", "value on class"]
|
reveal_type(C6.attribute3) # revealed: Literal["value on metaclass", "value on class"]
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(C6.attribute4) # revealed: Literal["value on class"]
|
reveal_type(C6.attribute4) # revealed: Literal["value on class"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -756,16 +756,16 @@ def _(flag: bool):
|
||||||
non_data: NonDataDescriptor = NonDataDescriptor()
|
non_data: NonDataDescriptor = NonDataDescriptor()
|
||||||
data: DataDescriptor = DataDescriptor()
|
data: DataDescriptor = DataDescriptor()
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `non_data` on type `<class 'PossiblyUnbound'>` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `non_data` on type `<class 'PossiblyUnbound'>` may be missing"
|
||||||
reveal_type(PossiblyUnbound.non_data) # revealed: int
|
reveal_type(PossiblyUnbound.non_data) # revealed: int
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `non_data` on type `PossiblyUnbound` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `non_data` on type `PossiblyUnbound` may be missing"
|
||||||
reveal_type(PossiblyUnbound().non_data) # revealed: int
|
reveal_type(PossiblyUnbound().non_data) # revealed: int
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `data` on type `<class 'PossiblyUnbound'>` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `data` on type `<class 'PossiblyUnbound'>` may be missing"
|
||||||
reveal_type(PossiblyUnbound.data) # revealed: int
|
reveal_type(PossiblyUnbound.data) # revealed: int
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `data` on type `PossiblyUnbound` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `data` on type `PossiblyUnbound` may be missing"
|
||||||
reveal_type(PossiblyUnbound().data) # revealed: int
|
reveal_type(PossiblyUnbound().data) # revealed: int
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ instance = C()
|
||||||
instance.non_existent = 1 # error: [unresolved-attribute]
|
instance.non_existent = 1 # error: [unresolved-attribute]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Possibly-unbound attributes
|
## Possibly-missing attributes
|
||||||
|
|
||||||
When trying to set an attribute that is not defined in all branches, we emit errors:
|
When trying to set an attribute that is not defined in all branches, we emit errors:
|
||||||
|
|
||||||
|
@ -79,10 +79,10 @@ def _(flag: bool) -> None:
|
||||||
if flag:
|
if flag:
|
||||||
attr: int = 0
|
attr: int = 0
|
||||||
|
|
||||||
C.attr = 1 # error: [possibly-unbound-attribute]
|
C.attr = 1 # error: [possibly-missing-attribute]
|
||||||
|
|
||||||
instance = C()
|
instance = C()
|
||||||
instance.attr = 1 # error: [possibly-unbound-attribute]
|
instance.attr = 1 # error: [possibly-missing-attribute]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Data descriptors
|
## Data descriptors
|
||||||
|
|
|
@ -23,7 +23,7 @@ async def main() -> None:
|
||||||
await MissingAwait() # error: [invalid-await]
|
await MissingAwait() # error: [invalid-await]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Custom type with possibly unbound `__await__`
|
## Custom type with possibly missing `__await__`
|
||||||
|
|
||||||
This diagnostic also points to the method definition if available.
|
This diagnostic also points to the method definition if available.
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ def _(n: int):
|
||||||
# error: [invalid-argument-type] "Argument to function `f5` is incorrect: Expected `str`, found `Literal[3]`"
|
# error: [invalid-argument-type] "Argument to function `f5` is incorrect: Expected `str`, found `Literal[3]`"
|
||||||
# error: [no-matching-overload] "No overload of function `f6` matches arguments"
|
# error: [no-matching-overload] "No overload of function `f6` matches arguments"
|
||||||
# error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
# error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
# error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
# error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
x = f(3)
|
x = f(3)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ def _(flag: bool):
|
||||||
|
|
||||||
reveal_type(A.union_declared) # revealed: int | str
|
reveal_type(A.union_declared) # revealed: int | str
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `possibly_unbound` on type `<class 'A'>` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `possibly_unbound` on type `<class 'A'>` may be missing"
|
||||||
reveal_type(A.possibly_unbound) # revealed: str
|
reveal_type(A.possibly_unbound) # revealed: str
|
||||||
|
|
||||||
# error: [unresolved-attribute] "Type `<class 'A'>` has no attribute `non_existent`"
|
# error: [unresolved-attribute] "Type `<class 'A'>` has no attribute `non_existent`"
|
||||||
|
|
|
@ -22,7 +22,7 @@ reveal_type(y)
|
||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# error: [possibly-unbound-import] "Member `y` of module `maybe_unbound` is possibly unbound"
|
# error: [possibly-missing-import] "Member `y` of module `maybe_unbound` may be missing"
|
||||||
from maybe_unbound import x, y
|
from maybe_unbound import x, y
|
||||||
|
|
||||||
reveal_type(x) # revealed: Unknown | Literal[3]
|
reveal_type(x) # revealed: Unknown | Literal[3]
|
||||||
|
@ -53,7 +53,7 @@ reveal_type(y)
|
||||||
Importing an annotated name prefers the declared type over the inferred type:
|
Importing an annotated name prefers the declared type over the inferred type:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# error: [possibly-unbound-import] "Member `y` of module `maybe_unbound_annotated` is possibly unbound"
|
# error: [possibly-missing-import] "Member `y` of module `maybe_unbound_annotated` may be missing"
|
||||||
from maybe_unbound_annotated import x, y
|
from maybe_unbound_annotated import x, y
|
||||||
|
|
||||||
reveal_type(x) # revealed: Unknown | Literal[3]
|
reveal_type(x) # revealed: Unknown | Literal[3]
|
||||||
|
|
|
@ -306,7 +306,7 @@ The following scenarios are when a re-export happens conditionally in a stub fil
|
||||||
### Global import
|
### Global import
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# error: "Member `Foo` of module `a` is possibly unbound"
|
# error: "Member `Foo` of module `a` may be missing"
|
||||||
from a import Foo
|
from a import Foo
|
||||||
|
|
||||||
reveal_type(Foo) # revealed: str
|
reveal_type(Foo) # revealed: str
|
||||||
|
@ -337,7 +337,7 @@ Here, both the branches of the condition are import statements where one of them
|
||||||
the other does not.
|
the other does not.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# error: "Member `Foo` of module `a` is possibly unbound"
|
# error: "Member `Foo` of module `a` may be missing"
|
||||||
from a import Foo
|
from a import Foo
|
||||||
|
|
||||||
reveal_type(Foo) # revealed: <class 'Foo'>
|
reveal_type(Foo) # revealed: <class 'Foo'>
|
||||||
|
@ -365,7 +365,7 @@ class Foo: ...
|
||||||
### Re-export in one branch
|
### Re-export in one branch
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# error: "Member `Foo` of module `a` is possibly unbound"
|
# error: "Member `Foo` of module `a` may be missing"
|
||||||
from a import Foo
|
from a import Foo
|
||||||
|
|
||||||
reveal_type(Foo) # revealed: <class 'Foo'>
|
reveal_type(Foo) # revealed: <class 'Foo'>
|
||||||
|
|
|
@ -88,7 +88,7 @@ async def foo():
|
||||||
reveal_type(x) # revealed: Unknown
|
reveal_type(x) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
### Possibly unbound `__anext__` method
|
### Possibly missing `__anext__` method
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing_extensions import reveal_type
|
from typing_extensions import reveal_type
|
||||||
|
@ -108,7 +108,7 @@ async def foo(flag: bool):
|
||||||
reveal_type(x) # revealed: int
|
reveal_type(x) # revealed: int
|
||||||
```
|
```
|
||||||
|
|
||||||
### Possibly unbound `__aiter__` method
|
### Possibly missing `__aiter__` method
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing_extensions import reveal_type
|
from typing_extensions import reveal_type
|
||||||
|
|
|
@ -363,7 +363,7 @@ for x in Bad():
|
||||||
reveal_type(x) # revealed: Unknown
|
reveal_type(x) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
## `__iter__` returns an object with a possibly unbound `__next__` method
|
## `__iter__` returns an object with a possibly missing `__next__` method
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _(flag: bool):
|
def _(flag: bool):
|
||||||
|
@ -412,7 +412,7 @@ for y in Iterable2():
|
||||||
reveal_type(y) # revealed: Unknown
|
reveal_type(y) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
## Possibly unbound `__iter__` and bad `__getitem__` method
|
## Possibly missing `__iter__` and bad `__getitem__` method
|
||||||
|
|
||||||
<!-- snapshot-diagnostics -->
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
@ -438,12 +438,12 @@ def _(flag: bool):
|
||||||
reveal_type(x) # revealed: int | bytes
|
reveal_type(x) # revealed: int | bytes
|
||||||
```
|
```
|
||||||
|
|
||||||
## Possibly unbound `__iter__` and not-callable `__getitem__`
|
## Possibly missing `__iter__` and not-callable `__getitem__`
|
||||||
|
|
||||||
This snippet tests that we infer the element type correctly in the following edge case:
|
This snippet tests that we infer the element type correctly in the following edge case:
|
||||||
|
|
||||||
- `__iter__` is a method with the correct parameter spec that returns a valid iterator; BUT
|
- `__iter__` is a method with the correct parameter spec that returns a valid iterator; BUT
|
||||||
- `__iter__` is possibly unbound; AND
|
- `__iter__` is possibly missing; AND
|
||||||
- `__getitem__` is set to a non-callable type
|
- `__getitem__` is set to a non-callable type
|
||||||
|
|
||||||
It's important that we emit a diagnostic here, but it's also important that we still use the return
|
It's important that we emit a diagnostic here, but it's also important that we still use the return
|
||||||
|
@ -466,7 +466,7 @@ def _(flag: bool):
|
||||||
reveal_type(x) # revealed: int
|
reveal_type(x) # revealed: int
|
||||||
```
|
```
|
||||||
|
|
||||||
## Possibly unbound `__iter__` and possibly unbound `__getitem__`
|
## Possibly missing `__iter__` and possibly missing `__getitem__`
|
||||||
|
|
||||||
<!-- snapshot-diagnostics -->
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
@ -560,7 +560,7 @@ for x in Iterable():
|
||||||
reveal_type(x) # revealed: int
|
reveal_type(x) # revealed: int
|
||||||
```
|
```
|
||||||
|
|
||||||
## Possibly unbound `__iter__` but definitely bound `__getitem__`
|
## Possibly missing `__iter__` but definitely bound `__getitem__`
|
||||||
|
|
||||||
Here, we should not emit a diagnostic: if `__iter__` is unbound, we should fallback to
|
Here, we should not emit a diagnostic: if `__iter__` is unbound, we should fallback to
|
||||||
`__getitem__`:
|
`__getitem__`:
|
||||||
|
@ -694,7 +694,7 @@ def _(flag: bool):
|
||||||
reveal_type(y) # revealed: str | int
|
reveal_type(y) # revealed: str | int
|
||||||
```
|
```
|
||||||
|
|
||||||
## Possibly unbound `__iter__` and possibly invalid `__getitem__`
|
## Possibly missing `__iter__` and possibly invalid `__getitem__`
|
||||||
|
|
||||||
<!-- snapshot-diagnostics -->
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
|
|
@ -135,9 +135,9 @@ a.b = B()
|
||||||
reveal_type(a.b) # revealed: B
|
reveal_type(a.b) # revealed: B
|
||||||
reveal_type(a.b.c1) # revealed: C | None
|
reveal_type(a.b.c1) # revealed: C | None
|
||||||
reveal_type(a.b.c2) # revealed: C | None
|
reveal_type(a.b.c2) # revealed: C | None
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(a.b.c1.d) # revealed: D | None
|
reveal_type(a.b.c1.d) # revealed: D | None
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(a.b.c2.d) # revealed: D | None
|
reveal_type(a.b.c2.d) # revealed: D | None
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -295,9 +295,9 @@ class C:
|
||||||
reveal_type(b.a.x[0]) # revealed: Literal[0]
|
reveal_type(b.a.x[0]) # revealed: Literal[0]
|
||||||
|
|
||||||
def _():
|
def _():
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(b.a.x[0]) # revealed: Unknown | int | None
|
reveal_type(b.a.x[0]) # revealed: Unknown | int | None
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(b.a.x) # revealed: Unknown | list[int | None]
|
reveal_type(b.a.x) # revealed: Unknown | list[int | None]
|
||||||
reveal_type(b.a) # revealed: Unknown | A | None
|
reveal_type(b.a) # revealed: Unknown | A | None
|
||||||
```
|
```
|
||||||
|
|
|
@ -161,7 +161,7 @@ class _:
|
||||||
a.b = B()
|
a.b = B()
|
||||||
|
|
||||||
class _:
|
class _:
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(a.b.c1.d) # revealed: D | None
|
reveal_type(a.b.c1.d) # revealed: D | None
|
||||||
reveal_type(a.b.c1) # revealed: C | None
|
reveal_type(a.b.c1) # revealed: C | None
|
||||||
```
|
```
|
||||||
|
|
|
@ -60,7 +60,7 @@ def _(obj: WithSpam):
|
||||||
```
|
```
|
||||||
|
|
||||||
When a class may or may not have a `spam` attribute, `hasattr` narrowing can provide evidence that
|
When a class may or may not have a `spam` attribute, `hasattr` narrowing can provide evidence that
|
||||||
the attribute exists. Here, no `possibly-unbound-attribute` error is emitted in the `if` branch:
|
the attribute exists. Here, no `possibly-missing-attribute` error is emitted in the `if` branch:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def returns_bool() -> bool:
|
def returns_bool() -> bool:
|
||||||
|
@ -71,7 +71,7 @@ class MaybeWithSpam:
|
||||||
spam: int = 42
|
spam: int = 42
|
||||||
|
|
||||||
def _(obj: MaybeWithSpam):
|
def _(obj: MaybeWithSpam):
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(obj.spam) # revealed: int
|
reveal_type(obj.spam) # revealed: int
|
||||||
|
|
||||||
if hasattr(obj, "spam"):
|
if hasattr(obj, "spam"):
|
||||||
|
@ -81,7 +81,7 @@ def _(obj: MaybeWithSpam):
|
||||||
reveal_type(obj) # revealed: MaybeWithSpam & ~<Protocol with members 'spam'>
|
reveal_type(obj) # revealed: MaybeWithSpam & ~<Protocol with members 'spam'>
|
||||||
|
|
||||||
# TODO: Ideally, we would emit `[unresolved-attribute]` and reveal `Unknown` here:
|
# TODO: Ideally, we would emit `[unresolved-attribute]` and reveal `Unknown` here:
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(obj.spam) # revealed: int
|
reveal_type(obj.spam) # revealed: int
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ class C:
|
||||||
if flag:
|
if flag:
|
||||||
x = 2
|
x = 2
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute] "Attribute `x` on type `<class 'C'>` is possibly unbound"
|
# error: [possibly-missing-attribute] "Attribute `x` on type `<class 'C'>` may be missing"
|
||||||
reveal_type(C.x) # revealed: Unknown | Literal[2]
|
reveal_type(C.x) # revealed: Unknown | Literal[2]
|
||||||
reveal_type(C.y) # revealed: Unknown | Literal[1]
|
reveal_type(C.y) # revealed: Unknown | Literal[1]
|
||||||
```
|
```
|
||||||
|
@ -52,7 +52,7 @@ class C:
|
||||||
elif coinflip():
|
elif coinflip():
|
||||||
x: str = "abc"
|
x: str = "abc"
|
||||||
|
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
reveal_type(C.x) # revealed: int | str
|
reveal_type(C.x) # revealed: int | str
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
||||||
expression: snapshot
|
expression: snapshot
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
mdtest name: async_for.md - Async - Error cases - Possibly unbound `__aiter__` method
|
mdtest name: async_for.md - Async - Error cases - Possibly missing `__aiter__` method
|
||||||
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/async_for.md
|
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/async_for.md
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
||||||
expression: snapshot
|
expression: snapshot
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
mdtest name: async_for.md - Async - Error cases - Possibly unbound `__anext__` method
|
mdtest name: async_for.md - Async - Error cases - Possibly missing `__anext__` method
|
||||||
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/async_for.md
|
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/async_for.md
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
||||||
expression: snapshot
|
expression: snapshot
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
mdtest name: attribute_assignment.md - Attribute assignment - Possibly-unbound attributes
|
mdtest name: attribute_assignment.md - Attribute assignment - Possibly-missing attributes
|
||||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/attribute_assignment.md
|
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/attribute_assignment.md
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -17,37 +17,37 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/attribute_as
|
||||||
3 | if flag:
|
3 | if flag:
|
||||||
4 | attr: int = 0
|
4 | attr: int = 0
|
||||||
5 |
|
5 |
|
||||||
6 | C.attr = 1 # error: [possibly-unbound-attribute]
|
6 | C.attr = 1 # error: [possibly-missing-attribute]
|
||||||
7 |
|
7 |
|
||||||
8 | instance = C()
|
8 | instance = C()
|
||||||
9 | instance.attr = 1 # error: [possibly-unbound-attribute]
|
9 | instance.attr = 1 # error: [possibly-missing-attribute]
|
||||||
```
|
```
|
||||||
|
|
||||||
# Diagnostics
|
# Diagnostics
|
||||||
|
|
||||||
```
|
```
|
||||||
warning[possibly-unbound-attribute]: Attribute `attr` on type `<class 'C'>` is possibly unbound
|
warning[possibly-missing-attribute]: Attribute `attr` on type `<class 'C'>` may be missing
|
||||||
--> src/mdtest_snippet.py:6:5
|
--> src/mdtest_snippet.py:6:5
|
||||||
|
|
|
|
||||||
4 | attr: int = 0
|
4 | attr: int = 0
|
||||||
5 |
|
5 |
|
||||||
6 | C.attr = 1 # error: [possibly-unbound-attribute]
|
6 | C.attr = 1 # error: [possibly-missing-attribute]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
7 |
|
7 |
|
||||||
8 | instance = C()
|
8 | instance = C()
|
||||||
|
|
|
|
||||||
info: rule `possibly-unbound-attribute` is enabled by default
|
info: rule `possibly-missing-attribute` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
warning[possibly-unbound-attribute]: Attribute `attr` on type `C` is possibly unbound
|
warning[possibly-missing-attribute]: Attribute `attr` on type `C` may be missing
|
||||||
--> src/mdtest_snippet.py:9:5
|
--> src/mdtest_snippet.py:9:5
|
||||||
|
|
|
|
||||||
8 | instance = C()
|
8 | instance = C()
|
||||||
9 | instance.attr = 1 # error: [possibly-unbound-attribute]
|
9 | instance.attr = 1 # error: [possibly-missing-attribute]
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
info: rule `possibly-unbound-attribute` is enabled by default
|
info: rule `possibly-missing-attribute` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
||||||
expression: snapshot
|
expression: snapshot
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
mdtest name: for.md - For loops - Possibly unbound `__iter__` and possibly invalid `__getitem__`
|
mdtest name: for.md - For loops - Possibly missing `__iter__` and possibly invalid `__getitem__`
|
||||||
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/for.md
|
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/for.md
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
||||||
expression: snapshot
|
expression: snapshot
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
mdtest name: for.md - For loops - Possibly unbound `__iter__` and bad `__getitem__` method
|
mdtest name: for.md - For loops - Possibly missing `__iter__` and bad `__getitem__` method
|
||||||
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/for.md
|
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/for.md
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
||||||
expression: snapshot
|
expression: snapshot
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
mdtest name: for.md - For loops - Possibly unbound `__iter__` and possibly unbound `__getitem__`
|
mdtest name: for.md - For loops - Possibly missing `__iter__` and possibly missing `__getitem__`
|
||||||
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/for.md
|
mdtest path: crates/ty_python_semantic/resources/mdtest/loops/for.md
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
||||||
expression: snapshot
|
expression: snapshot
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
mdtest name: invalid_await.md - Invalid await diagnostics - Custom type with possibly unbound `__await__`
|
mdtest name: invalid_await.md - Invalid await diagnostics - Custom type with possibly missing `__await__`
|
||||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_await.md
|
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_await.md
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ error[invalid-await]: `PossiblyUnbound` is not awaitable
|
||||||
| --------------- method defined here
|
| --------------- method defined here
|
||||||
6 | yield
|
6 | yield
|
||||||
|
|
|
|
||||||
info: `__await__` is possibly unbound
|
info: `__await__` may be missing
|
||||||
info: rule `invalid-await` is enabled by default
|
info: rule `invalid-await` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
|
@ -70,7 +70,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/union_call.m
|
||||||
56 | # error: [invalid-argument-type] "Argument to function `f5` is incorrect: Expected `str`, found `Literal[3]`"
|
56 | # error: [invalid-argument-type] "Argument to function `f5` is incorrect: Expected `str`, found `Literal[3]`"
|
||||||
57 | # error: [no-matching-overload] "No overload of function `f6` matches arguments"
|
57 | # error: [no-matching-overload] "No overload of function `f6` matches arguments"
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ error[call-non-callable]: Object of type `Literal[5]` is not callable
|
||||||
--> src/mdtest_snippet.py:60:9
|
--> src/mdtest_snippet.py:60:9
|
||||||
|
|
|
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
|
@ -92,11 +92,11 @@ info: rule `call-non-callable` is enabled by default
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
error[call-non-callable]: Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)
|
error[call-non-callable]: Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)
|
||||||
--> src/mdtest_snippet.py:60:9
|
--> src/mdtest_snippet.py:60:9
|
||||||
|
|
|
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
|
@ -111,7 +111,7 @@ error[missing-argument]: No argument provided for required parameter `b` of func
|
||||||
--> src/mdtest_snippet.py:60:9
|
--> src/mdtest_snippet.py:60:9
|
||||||
|
|
|
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
|
@ -126,7 +126,7 @@ error[no-matching-overload]: No overload of function `f6` matches arguments
|
||||||
--> src/mdtest_snippet.py:60:9
|
--> src/mdtest_snippet.py:60:9
|
||||||
|
|
|
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
|
@ -162,7 +162,7 @@ error[invalid-argument-type]: Argument to function `f2` is incorrect
|
||||||
--> src/mdtest_snippet.py:60:11
|
--> src/mdtest_snippet.py:60:11
|
||||||
|
|
|
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
| ^ Expected `str`, found `Literal[3]`
|
| ^ Expected `str`, found `Literal[3]`
|
||||||
|
|
|
|
||||||
|
@ -186,7 +186,7 @@ error[invalid-argument-type]: Argument to function `f4` is incorrect
|
||||||
--> src/mdtest_snippet.py:60:11
|
--> src/mdtest_snippet.py:60:11
|
||||||
|
|
|
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
| ^ Argument type `Literal[3]` does not satisfy upper bound `str` of type variable `T`
|
| ^ Argument type `Literal[3]` does not satisfy upper bound `str` of type variable `T`
|
||||||
|
|
|
|
||||||
|
@ -210,7 +210,7 @@ error[invalid-argument-type]: Argument to function `f5` is incorrect
|
||||||
--> src/mdtest_snippet.py:60:11
|
--> src/mdtest_snippet.py:60:11
|
||||||
|
|
|
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
| ^ Expected `str`, found `Literal[3]`
|
| ^ Expected `str`, found `Literal[3]`
|
||||||
|
|
|
|
||||||
|
@ -237,7 +237,7 @@ error[too-many-positional-arguments]: Too many positional arguments to function
|
||||||
--> src/mdtest_snippet.py:60:11
|
--> src/mdtest_snippet.py:60:11
|
||||||
|
|
|
|
||||||
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
58 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||||
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
59 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly missing `__call__` method)"
|
||||||
60 | x = f(3)
|
60 | x = f(3)
|
||||||
| ^
|
| ^
|
||||||
|
|
|
|
||||||
|
|
|
@ -1530,7 +1530,7 @@ if flag():
|
||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# error: [possibly-unbound-import]
|
# error: [possibly-missing-import]
|
||||||
from module import symbol
|
from module import symbol
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,11 @@ a = NotSubscriptable()[0] # error: "Cannot subscript object of type `NotSubscri
|
||||||
class NotSubscriptable:
|
class NotSubscriptable:
|
||||||
__getitem__ = None
|
__getitem__ = None
|
||||||
|
|
||||||
# error: "Method `__getitem__` of type `Unknown | None` is possibly not callable on object of type `NotSubscriptable`"
|
# TODO: this would be more user-friendly if the `call-non-callable` diagnostic was
|
||||||
|
# transformed into a `not-subscriptable` diagnostic with a subdiagnostic explaining
|
||||||
|
# that this was because `__getitem__` was possibly not callable
|
||||||
|
#
|
||||||
|
# error: [call-non-callable] "Method `__getitem__` of type `Unknown | None` may not be callable on object of type `NotSubscriptable`"
|
||||||
a = NotSubscriptable()[0]
|
a = NotSubscriptable()[0]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -82,7 +86,7 @@ class NoSetitem:
|
||||||
__setitem__ = None
|
__setitem__ = None
|
||||||
|
|
||||||
a = NoSetitem()
|
a = NoSetitem()
|
||||||
a[0] = 0 # error: "Method `__setitem__` of type `Unknown | None` is possibly not callable on object of type `NoSetitem`"
|
a[0] = 0 # error: "Method `__setitem__` of type `Unknown | None` may not be callable on object of type `NoSetitem`"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Valid `__setitem__` method
|
## Valid `__setitem__` method
|
||||||
|
|
|
@ -198,7 +198,7 @@ import sys
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
# TODO: we should not emit an error here
|
# TODO: we should not emit an error here
|
||||||
# error: [possibly-unbound-attribute]
|
# error: [possibly-missing-attribute]
|
||||||
sys.getwindowsversion()
|
sys.getwindowsversion()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ async def _(flag: bool):
|
||||||
class NotAContextManager: ...
|
class NotAContextManager: ...
|
||||||
context_expr = Manager1() if flag else NotAContextManager()
|
context_expr = Manager1() if flag else NotAContextManager()
|
||||||
|
|
||||||
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `async with` because the methods `__aenter__` and `__aexit__` are possibly unbound"
|
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `async with` because the methods `__aenter__` and `__aexit__` are possibly missing"
|
||||||
async with context_expr as f:
|
async with context_expr as f:
|
||||||
reveal_type(f) # revealed: str
|
reveal_type(f) # revealed: str
|
||||||
```
|
```
|
||||||
|
@ -129,7 +129,7 @@ async def _(flag: bool):
|
||||||
|
|
||||||
async def __exit__(self, *args): ...
|
async def __exit__(self, *args): ...
|
||||||
|
|
||||||
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `async with` because the method `__aenter__` is possibly unbound"
|
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `async with` because the method `__aenter__` may be missing"
|
||||||
async with Manager() as f:
|
async with Manager() as f:
|
||||||
reveal_type(f) # revealed: CoroutineType[Any, Any, str]
|
reveal_type(f) # revealed: CoroutineType[Any, Any, str]
|
||||||
```
|
```
|
||||||
|
|
|
@ -113,7 +113,7 @@ def _(flag: bool):
|
||||||
class NotAContextManager: ...
|
class NotAContextManager: ...
|
||||||
context_expr = Manager1() if flag else NotAContextManager()
|
context_expr = Manager1() if flag else NotAContextManager()
|
||||||
|
|
||||||
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `with` because the methods `__enter__` and `__exit__` are possibly unbound"
|
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `with` because the methods `__enter__` and `__exit__` are possibly missing"
|
||||||
with context_expr as f:
|
with context_expr as f:
|
||||||
reveal_type(f) # revealed: str
|
reveal_type(f) # revealed: str
|
||||||
```
|
```
|
||||||
|
@ -129,7 +129,7 @@ def _(flag: bool):
|
||||||
|
|
||||||
def __exit__(self, *args): ...
|
def __exit__(self, *args): ...
|
||||||
|
|
||||||
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because the method `__enter__` is possibly unbound"
|
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because the method `__enter__` may be missing"
|
||||||
with Manager() as f:
|
with Manager() as f:
|
||||||
reveal_type(f) # revealed: str
|
reveal_type(f) # revealed: str
|
||||||
```
|
```
|
||||||
|
|
|
@ -8,7 +8,7 @@ use bitflags::bitflags;
|
||||||
use call::{CallDunderError, CallError, CallErrorKind};
|
use call::{CallDunderError, CallError, CallErrorKind};
|
||||||
use context::InferContext;
|
use context::InferContext;
|
||||||
use diagnostic::{
|
use diagnostic::{
|
||||||
INVALID_CONTEXT_MANAGER, INVALID_SUPER_ARGUMENT, NOT_ITERABLE, POSSIBLY_UNBOUND_IMPLICIT_CALL,
|
INVALID_CONTEXT_MANAGER, INVALID_SUPER_ARGUMENT, NOT_ITERABLE, POSSIBLY_MISSING_IMPLICIT_CALL,
|
||||||
UNAVAILABLE_IMPLICIT_SUPER_ARGUMENTS,
|
UNAVAILABLE_IMPLICIT_SUPER_ARGUMENTS,
|
||||||
};
|
};
|
||||||
use ruff_db::diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagnosticSeverity};
|
use ruff_db::diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagnosticSeverity};
|
||||||
|
@ -3830,7 +3830,7 @@ impl<'db> Type<'db> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't trust possibly unbound `__bool__` method.
|
// Don't trust possibly missing `__bool__` method.
|
||||||
Ok(Truthiness::Ambiguous)
|
Ok(Truthiness::Ambiguous)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8148,7 +8148,7 @@ impl<'db> AwaitError<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Call(CallDunderError::PossiblyUnbound(bindings)) => {
|
Self::Call(CallDunderError::PossiblyUnbound(bindings)) => {
|
||||||
diag.info("`__await__` is possibly unbound");
|
diag.info("`__await__` may be missing");
|
||||||
if let Some(definition_spans) = bindings.callable_type().function_spans(db) {
|
if let Some(definition_spans) = bindings.callable_type().function_spans(db) {
|
||||||
diag.annotate(
|
diag.annotate(
|
||||||
Annotation::secondary(definition_spans.signature)
|
Annotation::secondary(definition_spans.signature)
|
||||||
|
@ -8255,7 +8255,7 @@ impl<'db> ContextManagerError<'db> {
|
||||||
match call_dunder_error {
|
match call_dunder_error {
|
||||||
CallDunderError::MethodNotAvailable => format!("it does not implement `{name}`"),
|
CallDunderError::MethodNotAvailable => format!("it does not implement `{name}`"),
|
||||||
CallDunderError::PossiblyUnbound(_) => {
|
CallDunderError::PossiblyUnbound(_) => {
|
||||||
format!("the method `{name}` is possibly unbound")
|
format!("the method `{name}` may be missing")
|
||||||
}
|
}
|
||||||
// TODO: Use more specific error messages for the different error cases.
|
// TODO: Use more specific error messages for the different error cases.
|
||||||
// E.g. hint toward the union variant that doesn't correctly implement enter,
|
// E.g. hint toward the union variant that doesn't correctly implement enter,
|
||||||
|
@ -8272,7 +8272,7 @@ impl<'db> ContextManagerError<'db> {
|
||||||
name_b: &str| {
|
name_b: &str| {
|
||||||
match (error_a, error_b) {
|
match (error_a, error_b) {
|
||||||
(CallDunderError::PossiblyUnbound(_), CallDunderError::PossiblyUnbound(_)) => {
|
(CallDunderError::PossiblyUnbound(_), CallDunderError::PossiblyUnbound(_)) => {
|
||||||
format!("the methods `{name_a}` and `{name_b}` are possibly unbound")
|
format!("the methods `{name_a}` and `{name_b}` are possibly missing")
|
||||||
}
|
}
|
||||||
(CallDunderError::MethodNotAvailable, CallDunderError::MethodNotAvailable) => {
|
(CallDunderError::MethodNotAvailable, CallDunderError::MethodNotAvailable) => {
|
||||||
format!("it does not implement `{name_a}` and `{name_b}`")
|
format!("it does not implement `{name_a}` and `{name_b}`")
|
||||||
|
@ -8821,7 +8821,7 @@ pub(super) enum BoolError<'db> {
|
||||||
|
|
||||||
/// Any other reason why the type can't be converted to a bool.
|
/// Any other reason why the type can't be converted to a bool.
|
||||||
/// E.g. because calling `__bool__` returns in a union type and not all variants support `__bool__` or
|
/// E.g. because calling `__bool__` returns in a union type and not all variants support `__bool__` or
|
||||||
/// because `__bool__` points to a type that has a possibly unbound `__call__` method.
|
/// because `__bool__` points to a type that has a possibly missing `__call__` method.
|
||||||
Other { not_boolable_type: Type<'db> },
|
Other { not_boolable_type: Type<'db> },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8994,7 +8994,7 @@ impl<'db> ConstructorCallError<'db> {
|
||||||
let report_init_error = |call_dunder_error: &CallDunderError<'db>| match call_dunder_error {
|
let report_init_error = |call_dunder_error: &CallDunderError<'db>| match call_dunder_error {
|
||||||
CallDunderError::MethodNotAvailable => {
|
CallDunderError::MethodNotAvailable => {
|
||||||
if let Some(builder) =
|
if let Some(builder) =
|
||||||
context.report_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL, context_expression_node)
|
context.report_lint(&POSSIBLY_MISSING_IMPLICIT_CALL, context_expression_node)
|
||||||
{
|
{
|
||||||
// If we are using vendored typeshed, it should be impossible to have missing
|
// If we are using vendored typeshed, it should be impossible to have missing
|
||||||
// or unbound `__init__` method on a class, as all classes have `object` in MRO.
|
// or unbound `__init__` method on a class, as all classes have `object` in MRO.
|
||||||
|
@ -9008,10 +9008,10 @@ impl<'db> ConstructorCallError<'db> {
|
||||||
}
|
}
|
||||||
CallDunderError::PossiblyUnbound(bindings) => {
|
CallDunderError::PossiblyUnbound(bindings) => {
|
||||||
if let Some(builder) =
|
if let Some(builder) =
|
||||||
context.report_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL, context_expression_node)
|
context.report_lint(&POSSIBLY_MISSING_IMPLICIT_CALL, context_expression_node)
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Method `__init__` on type `{}` is possibly unbound.",
|
"Method `__init__` on type `{}` may be missing.",
|
||||||
context_expression_type.display(context.db()),
|
context_expression_type.display(context.db()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -9026,10 +9026,10 @@ impl<'db> ConstructorCallError<'db> {
|
||||||
let report_new_error = |error: &DunderNewCallError<'db>| match error {
|
let report_new_error = |error: &DunderNewCallError<'db>| match error {
|
||||||
DunderNewCallError::PossiblyUnbound(call_error) => {
|
DunderNewCallError::PossiblyUnbound(call_error) => {
|
||||||
if let Some(builder) =
|
if let Some(builder) =
|
||||||
context.report_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL, context_expression_node)
|
context.report_lint(&POSSIBLY_MISSING_IMPLICIT_CALL, context_expression_node)
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Method `__new__` on type `{}` is possibly unbound.",
|
"Method `__new__` on type `{}` may be missing.",
|
||||||
context_expression_type.display(context.db()),
|
context_expression_type.display(context.db()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1758,7 +1758,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
if self.dunder_call_is_possibly_unbound {
|
if self.dunder_call_is_possibly_unbound {
|
||||||
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, node) {
|
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, node) {
|
||||||
let mut diag = builder.into_diagnostic(format_args!(
|
let mut diag = builder.into_diagnostic(format_args!(
|
||||||
"Object of type `{}` is not callable (possibly unbound `__call__` method)",
|
"Object of type `{}` is not callable (possibly missing `__call__` method)",
|
||||||
self.callable_type.display(context.db()),
|
self.callable_type.display(context.db()),
|
||||||
));
|
));
|
||||||
if let Some(union_diag) = union_diag {
|
if let Some(union_diag) = union_diag {
|
||||||
|
|
|
@ -38,7 +38,7 @@ use std::fmt::Formatter;
|
||||||
pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
||||||
registry.register_lint(&AMBIGUOUS_PROTOCOL_MEMBER);
|
registry.register_lint(&AMBIGUOUS_PROTOCOL_MEMBER);
|
||||||
registry.register_lint(&CALL_NON_CALLABLE);
|
registry.register_lint(&CALL_NON_CALLABLE);
|
||||||
registry.register_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL);
|
registry.register_lint(&POSSIBLY_MISSING_IMPLICIT_CALL);
|
||||||
registry.register_lint(&CONFLICTING_ARGUMENT_FORMS);
|
registry.register_lint(&CONFLICTING_ARGUMENT_FORMS);
|
||||||
registry.register_lint(&CONFLICTING_DECLARATIONS);
|
registry.register_lint(&CONFLICTING_DECLARATIONS);
|
||||||
registry.register_lint(&CONFLICTING_METACLASS);
|
registry.register_lint(&CONFLICTING_METACLASS);
|
||||||
|
@ -80,8 +80,8 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
||||||
registry.register_lint(&NOT_ITERABLE);
|
registry.register_lint(&NOT_ITERABLE);
|
||||||
registry.register_lint(&UNSUPPORTED_BOOL_CONVERSION);
|
registry.register_lint(&UNSUPPORTED_BOOL_CONVERSION);
|
||||||
registry.register_lint(&PARAMETER_ALREADY_ASSIGNED);
|
registry.register_lint(&PARAMETER_ALREADY_ASSIGNED);
|
||||||
registry.register_lint(&POSSIBLY_UNBOUND_ATTRIBUTE);
|
registry.register_lint(&POSSIBLY_MISSING_ATTRIBUTE);
|
||||||
registry.register_lint(&POSSIBLY_UNBOUND_IMPORT);
|
registry.register_lint(&POSSIBLY_MISSING_IMPORT);
|
||||||
registry.register_lint(&POSSIBLY_UNRESOLVED_REFERENCE);
|
registry.register_lint(&POSSIBLY_UNRESOLVED_REFERENCE);
|
||||||
registry.register_lint(&SUBCLASS_OF_FINAL_CLASS);
|
registry.register_lint(&SUBCLASS_OF_FINAL_CLASS);
|
||||||
registry.register_lint(&TYPE_ASSERTION_FAILURE);
|
registry.register_lint(&TYPE_ASSERTION_FAILURE);
|
||||||
|
@ -131,12 +131,12 @@ declare_lint! {
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for implicit calls to possibly unbound methods.
|
/// Checks for implicit calls to possibly missing methods.
|
||||||
///
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// Expressions such as `x[y]` and `x * y` call methods
|
/// Expressions such as `x[y]` and `x * y` call methods
|
||||||
/// under the hood (`__getitem__` and `__mul__` respectively).
|
/// under the hood (`__getitem__` and `__mul__` respectively).
|
||||||
/// Calling an unbound method will raise an `AttributeError` at runtime.
|
/// Calling a missing method will raise an `AttributeError` at runtime.
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
/// ```python
|
/// ```python
|
||||||
|
@ -148,8 +148,8 @@ declare_lint! {
|
||||||
///
|
///
|
||||||
/// A()[0] # TypeError: 'A' object is not subscriptable
|
/// A()[0] # TypeError: 'A' object is not subscriptable
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) static POSSIBLY_UNBOUND_IMPLICIT_CALL = {
|
pub(crate) static POSSIBLY_MISSING_IMPLICIT_CALL = {
|
||||||
summary: "detects implicit calls to possibly unbound methods",
|
summary: "detects implicit calls to possibly missing methods",
|
||||||
status: LintStatus::preview("1.0.0"),
|
status: LintStatus::preview("1.0.0"),
|
||||||
default_level: Level::Warn,
|
default_level: Level::Warn,
|
||||||
}
|
}
|
||||||
|
@ -1327,10 +1327,10 @@ declare_lint! {
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for possibly unbound attributes.
|
/// Checks for possibly missing attributes.
|
||||||
///
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// Attempting to access an unbound attribute will raise an `AttributeError` at runtime.
|
/// Attempting to access a missing attribute will raise an `AttributeError` at runtime.
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
/// ```python
|
/// ```python
|
||||||
|
@ -1340,8 +1340,8 @@ declare_lint! {
|
||||||
///
|
///
|
||||||
/// A.c # AttributeError: type object 'A' has no attribute 'c'
|
/// A.c # AttributeError: type object 'A' has no attribute 'c'
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) static POSSIBLY_UNBOUND_ATTRIBUTE = {
|
pub(crate) static POSSIBLY_MISSING_ATTRIBUTE = {
|
||||||
summary: "detects references to possibly unbound attributes",
|
summary: "detects references to possibly missing attributes",
|
||||||
status: LintStatus::preview("1.0.0"),
|
status: LintStatus::preview("1.0.0"),
|
||||||
default_level: Level::Warn,
|
default_level: Level::Warn,
|
||||||
}
|
}
|
||||||
|
@ -1349,10 +1349,10 @@ declare_lint! {
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for imports of symbols that may be unbound.
|
/// Checks for imports of symbols that may be missing.
|
||||||
///
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// Importing an unbound module or name will raise a `ModuleNotFoundError`
|
/// Importing a missing module or name will raise a `ModuleNotFoundError`
|
||||||
/// or `ImportError` at runtime.
|
/// or `ImportError` at runtime.
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
|
@ -1366,8 +1366,8 @@ declare_lint! {
|
||||||
/// # main.py
|
/// # main.py
|
||||||
/// from module import a # ImportError: cannot import name 'a' from 'module'
|
/// from module import a # ImportError: cannot import name 'a' from 'module'
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) static POSSIBLY_UNBOUND_IMPORT = {
|
pub(crate) static POSSIBLY_MISSING_IMPORT = {
|
||||||
summary: "detects possibly unbound imports",
|
summary: "detects possibly missing imports",
|
||||||
status: LintStatus::preview("1.0.0"),
|
status: LintStatus::preview("1.0.0"),
|
||||||
default_level: Level::Warn,
|
default_level: Level::Warn,
|
||||||
}
|
}
|
||||||
|
@ -2197,17 +2197,17 @@ pub(super) fn report_possibly_unresolved_reference(
|
||||||
builder.into_diagnostic(format_args!("Name `{id}` used when possibly not defined"));
|
builder.into_diagnostic(format_args!("Name `{id}` used when possibly not defined"));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn report_possibly_unbound_attribute(
|
pub(super) fn report_possibly_missing_attribute(
|
||||||
context: &InferContext,
|
context: &InferContext,
|
||||||
target: &ast::ExprAttribute,
|
target: &ast::ExprAttribute,
|
||||||
attribute: &str,
|
attribute: &str,
|
||||||
object_ty: Type,
|
object_ty: Type,
|
||||||
) {
|
) {
|
||||||
let Some(builder) = context.report_lint(&POSSIBLY_UNBOUND_ATTRIBUTE, target) else {
|
let Some(builder) = context.report_lint(&POSSIBLY_MISSING_ATTRIBUTE, target) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Attribute `{attribute}` on type `{}` is possibly unbound",
|
"Attribute `{attribute}` on type `{}` may be missing",
|
||||||
object_ty.display(context.db()),
|
object_ty.display(context.db()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -2793,7 +2793,7 @@ pub(crate) fn report_invalid_or_unsupported_base(
|
||||||
CallDunderError::PossiblyUnbound(_) => {
|
CallDunderError::PossiblyUnbound(_) => {
|
||||||
explain_mro_entries(&mut diagnostic);
|
explain_mro_entries(&mut diagnostic);
|
||||||
diagnostic.info(format_args!(
|
diagnostic.info(format_args!(
|
||||||
"Type `{}` has an `__mro_entries__` attribute, but it is possibly unbound",
|
"Type `{}` may have an `__mro_entries__` attribute, but it may be missing",
|
||||||
base_type.display(db)
|
base_type.display(db)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ use crate::types::diagnostic::{
|
||||||
INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION,
|
INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION,
|
||||||
INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_NAMED_TUPLE, INVALID_PARAMETER_DEFAULT,
|
INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_NAMED_TUPLE, INVALID_PARAMETER_DEFAULT,
|
||||||
INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS,
|
INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS,
|
||||||
IncompatibleBases, NON_SUBSCRIPTABLE, POSSIBLY_UNBOUND_IMPLICIT_CALL, POSSIBLY_UNBOUND_IMPORT,
|
IncompatibleBases, NON_SUBSCRIPTABLE, POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT,
|
||||||
UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT,
|
UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT,
|
||||||
UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, report_bad_dunder_set_call,
|
UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, report_bad_dunder_set_call,
|
||||||
report_cannot_pop_required_field_on_typed_dict, report_implicit_return_type,
|
report_cannot_pop_required_field_on_typed_dict, report_implicit_return_type,
|
||||||
|
@ -60,7 +60,7 @@ use crate::types::diagnostic::{
|
||||||
report_invalid_attribute_assignment, report_invalid_generator_function_return_type,
|
report_invalid_attribute_assignment, report_invalid_generator_function_return_type,
|
||||||
report_invalid_key_on_typed_dict, report_invalid_return_type,
|
report_invalid_key_on_typed_dict, report_invalid_return_type,
|
||||||
report_namedtuple_field_without_default_after_field_with_default,
|
report_namedtuple_field_without_default_after_field_with_default,
|
||||||
report_possibly_unbound_attribute,
|
report_possibly_missing_attribute,
|
||||||
};
|
};
|
||||||
use crate::types::diagnostic::{
|
use crate::types::diagnostic::{
|
||||||
INVALID_METACLASS, INVALID_OVERLOAD, INVALID_PROTOCOL, SUBCLASS_OF_FINAL_CLASS,
|
INVALID_METACLASS, INVALID_OVERLOAD, INVALID_PROTOCOL, SUBCLASS_OF_FINAL_CLASS,
|
||||||
|
@ -3222,10 +3222,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
CallDunderError::PossiblyUnbound { .. } => {
|
CallDunderError::PossiblyUnbound { .. } => {
|
||||||
if let Some(builder) =
|
if let Some(builder) =
|
||||||
context.report_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL, &**value)
|
context.report_lint(&POSSIBLY_MISSING_IMPLICIT_CALL, &**value)
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Method `__setitem__` of type `{}` is possibly unbound",
|
"Method `__setitem__` of type `{}` may be missing",
|
||||||
value_ty.display(db),
|
value_ty.display(db),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -3306,7 +3306,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, &**value)
|
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, &**value)
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Method `__setitem__` of type `{}` is possibly not \
|
"Method `__setitem__` of type `{}` may not be \
|
||||||
callable on object of type `{}`",
|
callable on object of type `{}`",
|
||||||
bindings.callable_type().display(db),
|
bindings.callable_type().display(db),
|
||||||
value_ty.display(db),
|
value_ty.display(db),
|
||||||
|
@ -3642,7 +3642,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if boundness == Boundness::PossiblyUnbound {
|
if boundness == Boundness::PossiblyUnbound {
|
||||||
report_possibly_unbound_attribute(
|
report_possibly_missing_attribute(
|
||||||
&self.context,
|
&self.context,
|
||||||
target,
|
target,
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -3672,7 +3672,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if instance_attr_boundness == Boundness::PossiblyUnbound {
|
if instance_attr_boundness == Boundness::PossiblyUnbound {
|
||||||
report_possibly_unbound_attribute(
|
report_possibly_missing_attribute(
|
||||||
&self.context,
|
&self.context,
|
||||||
target,
|
target,
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -3752,7 +3752,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if boundness == Boundness::PossiblyUnbound {
|
if boundness == Boundness::PossiblyUnbound {
|
||||||
report_possibly_unbound_attribute(
|
report_possibly_missing_attribute(
|
||||||
&self.context,
|
&self.context,
|
||||||
target,
|
target,
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -3783,7 +3783,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if class_attr_boundness == Boundness::PossiblyUnbound {
|
if class_attr_boundness == Boundness::PossiblyUnbound {
|
||||||
report_possibly_unbound_attribute(
|
report_possibly_missing_attribute(
|
||||||
&self.context,
|
&self.context,
|
||||||
target,
|
target,
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -4680,10 +4680,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// together if the attribute exists but is possibly-unbound.
|
// together if the attribute exists but is possibly-unbound.
|
||||||
if let Some(builder) = self
|
if let Some(builder) = self
|
||||||
.context
|
.context
|
||||||
.report_lint(&POSSIBLY_UNBOUND_IMPORT, AnyNodeRef::Alias(alias))
|
.report_lint(&POSSIBLY_MISSING_IMPORT, AnyNodeRef::Alias(alias))
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Member `{name}` of module `{module_name}` is possibly unbound",
|
"Member `{name}` of module `{module_name}` may be missing",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6804,7 +6804,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Type::unknown().into()
|
Type::unknown().into()
|
||||||
}
|
}
|
||||||
LookupError::PossiblyUnbound(type_when_bound) => {
|
LookupError::PossiblyUnbound(type_when_bound) => {
|
||||||
report_possibly_unbound_attribute(
|
report_possibly_missing_attribute(
|
||||||
&self.context,
|
&self.context,
|
||||||
attribute,
|
attribute,
|
||||||
&attr.id,
|
&attr.id,
|
||||||
|
@ -8757,10 +8757,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
Err(err @ CallDunderError::PossiblyUnbound { .. }) => {
|
Err(err @ CallDunderError::PossiblyUnbound { .. }) => {
|
||||||
if let Some(builder) =
|
if let Some(builder) =
|
||||||
context.report_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL, value_node)
|
context.report_lint(&POSSIBLY_MISSING_IMPLICIT_CALL, value_node)
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Method `__getitem__` of type `{}` is possibly unbound",
|
"Method `__getitem__` of type `{}` may be missing",
|
||||||
value_ty.display(db),
|
value_ty.display(db),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -8808,7 +8808,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
CallErrorKind::PossiblyNotCallable => {
|
CallErrorKind::PossiblyNotCallable => {
|
||||||
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, value_node) {
|
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, value_node) {
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Method `__getitem__` of type `{}` is possibly not callable on object of type `{}`",
|
"Method `__getitem__` of type `{}` may not be callable on object of type `{}`",
|
||||||
bindings.callable_type().display(db),
|
bindings.callable_type().display(db),
|
||||||
value_ty.display(db),
|
value_ty.display(db),
|
||||||
));
|
));
|
||||||
|
@ -8840,11 +8840,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Place::Type(ty, boundness) => {
|
Place::Type(ty, boundness) => {
|
||||||
if boundness == Boundness::PossiblyUnbound {
|
if boundness == Boundness::PossiblyUnbound {
|
||||||
if let Some(builder) =
|
if let Some(builder) =
|
||||||
context.report_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL, value_node)
|
context.report_lint(&POSSIBLY_MISSING_IMPLICIT_CALL, value_node)
|
||||||
{
|
{
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
"Method `__class_getitem__` of type `{}` \
|
"Method `__class_getitem__` of type `{}` may be missing",
|
||||||
is possibly unbound",
|
|
||||||
value_ty.display(db),
|
value_ty.display(db),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,9 +78,9 @@ Settings: Settings {
|
||||||
"not-iterable": Error (Default),
|
"not-iterable": Error (Default),
|
||||||
"parameter-already-assigned": Error (Default),
|
"parameter-already-assigned": Error (Default),
|
||||||
"positional-only-parameter-as-kwarg": Error (Default),
|
"positional-only-parameter-as-kwarg": Error (Default),
|
||||||
"possibly-unbound-attribute": Warning (Default),
|
"possibly-missing-attribute": Warning (Default),
|
||||||
"possibly-unbound-implicit-call": Warning (Default),
|
"possibly-missing-implicit-call": Warning (Default),
|
||||||
"possibly-unbound-import": Warning (Default),
|
"possibly-missing-import": Warning (Default),
|
||||||
"raw-string-type-annotation": Error (Default),
|
"raw-string-type-annotation": Error (Default),
|
||||||
"redundant-cast": Warning (Default),
|
"redundant-cast": Warning (Default),
|
||||||
"static-assert-error": Error (Default),
|
"static-assert-error": Error (Default),
|
||||||
|
|
18
ty.schema.json
generated
18
ty.schema.json
generated
|
@ -795,9 +795,9 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"possibly-unbound-attribute": {
|
"possibly-missing-attribute": {
|
||||||
"title": "detects references to possibly unbound attributes",
|
"title": "detects references to possibly missing attributes",
|
||||||
"description": "## What it does\nChecks for possibly unbound attributes.\n\n## Why is this bad?\nAttempting to access an unbound attribute will raise an `AttributeError` at runtime.\n\n## Examples\n```python\nclass A:\n if b:\n c = 0\n\nA.c # AttributeError: type object 'A' has no attribute 'c'\n```",
|
"description": "## What it does\nChecks for possibly missing attributes.\n\n## Why is this bad?\nAttempting to access a missing attribute will raise an `AttributeError` at runtime.\n\n## Examples\n```python\nclass A:\n if b:\n c = 0\n\nA.c # AttributeError: type object 'A' has no attribute 'c'\n```",
|
||||||
"default": "warn",
|
"default": "warn",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
|
@ -805,9 +805,9 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"possibly-unbound-implicit-call": {
|
"possibly-missing-implicit-call": {
|
||||||
"title": "detects implicit calls to possibly unbound methods",
|
"title": "detects implicit calls to possibly missing methods",
|
||||||
"description": "## What it does\nChecks for implicit calls to possibly unbound methods.\n\n## Why is this bad?\nExpressions such as `x[y]` and `x * y` call methods\nunder the hood (`__getitem__` and `__mul__` respectively).\nCalling an unbound method will raise an `AttributeError` at runtime.\n\n## Examples\n```python\nimport datetime\n\nclass A:\n if datetime.date.today().weekday() != 6:\n def __getitem__(self, v): ...\n\nA()[0] # TypeError: 'A' object is not subscriptable\n```",
|
"description": "## What it does\nChecks for implicit calls to possibly missing methods.\n\n## Why is this bad?\nExpressions such as `x[y]` and `x * y` call methods\nunder the hood (`__getitem__` and `__mul__` respectively).\nCalling a missing method will raise an `AttributeError` at runtime.\n\n## Examples\n```python\nimport datetime\n\nclass A:\n if datetime.date.today().weekday() != 6:\n def __getitem__(self, v): ...\n\nA()[0] # TypeError: 'A' object is not subscriptable\n```",
|
||||||
"default": "warn",
|
"default": "warn",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
|
@ -815,9 +815,9 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"possibly-unbound-import": {
|
"possibly-missing-import": {
|
||||||
"title": "detects possibly unbound imports",
|
"title": "detects possibly missing imports",
|
||||||
"description": "## What it does\nChecks for imports of symbols that may be unbound.\n\n## Why is this bad?\nImporting an unbound module or name will raise a `ModuleNotFoundError`\nor `ImportError` at runtime.\n\n## Examples\n```python\n# module.py\nimport datetime\n\nif datetime.date.today().weekday() != 6:\n a = 1\n\n# main.py\nfrom module import a # ImportError: cannot import name 'a' from 'module'\n```",
|
"description": "## What it does\nChecks for imports of symbols that may be missing.\n\n## Why is this bad?\nImporting a missing module or name will raise a `ModuleNotFoundError`\nor `ImportError` at runtime.\n\n## Examples\n```python\n# module.py\nimport datetime\n\nif datetime.date.today().weekday() != 6:\n a = 1\n\n# main.py\nfrom module import a # ImportError: cannot import name 'a' from 'module'\n```",
|
||||||
"default": "warn",
|
"default": "warn",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue