mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +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.
|
||||
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`
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## `possibly-unbound-attribute`
|
||||
## `possibly-missing-attribute`
|
||||
|
||||
<small>
|
||||
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)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
||||
Checks for possibly unbound attributes.
|
||||
Checks for possibly missing attributes.
|
||||
|
||||
**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**
|
||||
|
||||
|
@ -1889,23 +1889,23 @@ class A:
|
|||
A.c # AttributeError: type object 'A' has no attribute 'c'
|
||||
```
|
||||
|
||||
## `possibly-unbound-implicit-call`
|
||||
## `possibly-missing-implicit-call`
|
||||
|
||||
<small>
|
||||
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)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
||||
Checks for implicit calls to possibly unbound methods.
|
||||
Checks for implicit calls to possibly missing methods.
|
||||
|
||||
**Why is this bad?**
|
||||
|
||||
Expressions such as `x[y]` and `x * y` call methods
|
||||
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**
|
||||
|
||||
|
@ -1919,21 +1919,21 @@ class A:
|
|||
A()[0] # TypeError: 'A' object is not subscriptable
|
||||
```
|
||||
|
||||
## `possibly-unbound-import`
|
||||
## `possibly-missing-import`
|
||||
|
||||
<small>
|
||||
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)
|
||||
</small>
|
||||
|
||||
**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?**
|
||||
|
||||
Importing an unbound module or name will raise a `ModuleNotFoundError`
|
||||
Importing a missing module or name will raise a `ModuleNotFoundError`
|
||||
or `ImportError` at runtime.
|
||||
|
||||
**Examples**
|
||||
|
|
|
@ -914,7 +914,7 @@ def _(flag: bool):
|
|||
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:
|
||||
|
||||
```py
|
||||
|
@ -924,12 +924,12 @@ def _(flag: bool):
|
|||
attr1: str = "metaclass value"
|
||||
|
||||
class C4(metaclass=Meta4): ...
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
reveal_type(C4.attr1) # revealed: str
|
||||
```
|
||||
|
||||
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
|
||||
def _(flag1: bool, flag2: bool):
|
||||
|
@ -941,7 +941,7 @@ def _(flag1: bool, flag2: bool):
|
|||
if flag2:
|
||||
attr1 = "class value"
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
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
|
||||
|
||||
# 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]
|
||||
|
||||
# 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
|
||||
|
||||
# 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]
|
||||
|
||||
# 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
|
||||
|
||||
# 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]
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
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.
|
||||
# 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]
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
C().x = 100
|
||||
```
|
||||
|
||||
|
@ -1287,16 +1287,16 @@ def _(flag: bool):
|
|||
if flag:
|
||||
x = 2
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
reveal_type(Bar.x) # revealed: Unknown | Literal[2, 1]
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
Bar.x = 3
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
reveal_type(Bar().x) # revealed: Unknown | Literal[2, 1]
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -1484,17 +1484,17 @@ def _(flag: bool):
|
|||
class 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
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
a_and_b.x = R()
|
||||
# Same for class objects
|
||||
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
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
a_and_b.x = R()
|
||||
|
||||
class A2:
|
||||
|
@ -1509,7 +1509,7 @@ def _(flag: bool):
|
|||
|
||||
# TODO: this should not be an error, we need better intersection
|
||||
# handling in `validate_attribute_assignment` for this
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
a_and_b.x = R()
|
||||
# Same for class objects
|
||||
def inner2_class(a_and_b: Intersection[type[A2], type[B1]]):
|
||||
|
@ -1524,17 +1524,17 @@ def _(flag: bool):
|
|||
x: Q = Q()
|
||||
|
||||
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
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
a_and_b.x = R()
|
||||
# Same for class objects
|
||||
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
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
a_and_b.x = R()
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```py
|
||||
|
|
|
@ -26,7 +26,7 @@ In particular, we should raise errors in the "possibly-undeclared-and-unbound" a
|
|||
| **Diagnostic** | declared | possibly-undeclared | undeclared |
|
||||
| ---------------- | -------- | ------------------------- | ------------------- |
|
||||
| bound | | | |
|
||||
| possibly-unbound | | `possibly-unbound-import` | ? |
|
||||
| possibly-unbound | | `possibly-missing-import` | ? |
|
||||
| unbound | | ? | `unresolved-import` |
|
||||
|
||||
## 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
|
||||
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`:
|
||||
|
||||
`mod.py`:
|
||||
|
@ -177,8 +177,8 @@ else:
|
|||
```
|
||||
|
||||
```py
|
||||
# error: [possibly-unbound-import]
|
||||
# error: [possibly-unbound-import]
|
||||
# error: [possibly-missing-import] "Member `a` of module `mod` may be missing"
|
||||
# error: [possibly-missing-import] "Member `b` of module `mod` may be missing"
|
||||
from mod import a, b
|
||||
|
||||
reveal_type(a) # revealed: Literal[1] | Any
|
||||
|
@ -332,8 +332,8 @@ if flag():
|
|||
```
|
||||
|
||||
```py
|
||||
# error: [possibly-unbound-import]
|
||||
# error: [possibly-unbound-import]
|
||||
# error: [possibly-missing-import]
|
||||
# error: [possibly-missing-import]
|
||||
from mod import MyInt, C
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## Possibly unbound `__call__` method
|
||||
## Possibly missing `__call__` method
|
||||
|
||||
```py
|
||||
def _(flag: bool):
|
||||
|
@ -29,7 +29,7 @@ def _(flag: bool):
|
|||
return 1
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
|
@ -105,7 +105,7 @@ reveal_type(c()) # revealed: int
|
|||
|
||||
## Union over callables
|
||||
|
||||
### Possibly unbound `__call__`
|
||||
### Possibly missing `__call__`
|
||||
|
||||
```py
|
||||
def outer(cond1: bool):
|
||||
|
@ -122,6 +122,6 @@ def outer(cond1: bool):
|
|||
else:
|
||||
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()
|
||||
```
|
||||
|
|
|
@ -158,15 +158,15 @@ def _(flag: bool) -> None:
|
|||
def __new__(cls):
|
||||
return object.__new__(cls)
|
||||
|
||||
# error: [possibly-unbound-implicit-call]
|
||||
# error: [possibly-missing-implicit-call]
|
||||
reveal_type(Foo()) # revealed: Foo
|
||||
|
||||
# error: [possibly-unbound-implicit-call]
|
||||
# error: [possibly-missing-implicit-call]
|
||||
# error: [too-many-positional-arguments]
|
||||
reveal_type(Foo(1)) # revealed: Foo
|
||||
```
|
||||
|
||||
#### Possibly unbound `__call__` on `__new__` callable
|
||||
#### Possibly missing `__call__` on `__new__` callable
|
||||
|
||||
```py
|
||||
def _(flag: bool) -> None:
|
||||
|
@ -178,11 +178,11 @@ def _(flag: bool) -> None:
|
|||
class Foo:
|
||||
__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
|
||||
# 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
|
||||
# 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
|
||||
```
|
||||
|
||||
|
@ -294,11 +294,11 @@ def _(flag: bool) -> None:
|
|||
class Foo:
|
||||
__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
|
||||
# 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
|
||||
# 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
|
||||
```
|
||||
|
||||
|
|
|
@ -114,7 +114,11 @@ def _(flag: bool):
|
|||
|
||||
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
|
||||
```
|
||||
|
||||
|
@ -270,6 +274,11 @@ def _(flag: bool):
|
|||
return str(key)
|
||||
|
||||
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
|
||||
```
|
||||
|
|
|
@ -325,7 +325,7 @@ class D(metaclass=Meta):
|
|||
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
|
||||
def flag() -> bool:
|
||||
|
|
|
@ -219,7 +219,7 @@ def f(x: C | D):
|
|||
s = super(A, x)
|
||||
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
|
||||
|
||||
def f(flag: bool):
|
||||
|
@ -259,7 +259,7 @@ def f(flag: bool):
|
|||
reveal_type(s.x) # revealed: Unknown | Literal[1, 2]
|
||||
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
|
||||
```
|
||||
|
||||
|
|
|
@ -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"]
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```py
|
||||
|
@ -365,7 +365,7 @@ def _(flag: bool):
|
|||
meta_data_descriptor1: Literal["value on class"] = "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"]
|
||||
|
||||
# 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"]`"
|
||||
C5.meta_data_descriptor1 = None
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
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):
|
||||
|
||||
```py
|
||||
|
@ -401,7 +401,7 @@ def _(flag: bool):
|
|||
reveal_type(C6.attribute1) # revealed: Literal["data"]
|
||||
reveal_type(C6.attribute2) # revealed: Literal["non-data", "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"]
|
||||
```
|
||||
|
||||
|
@ -756,16 +756,16 @@ def _(flag: bool):
|
|||
non_data: NonDataDescriptor = NonDataDescriptor()
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
```
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ instance = C()
|
|||
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:
|
||||
|
||||
|
@ -79,10 +79,10 @@ def _(flag: bool) -> None:
|
|||
if flag:
|
||||
attr: int = 0
|
||||
|
||||
C.attr = 1 # error: [possibly-unbound-attribute]
|
||||
C.attr = 1 # error: [possibly-missing-attribute]
|
||||
|
||||
instance = C()
|
||||
instance.attr = 1 # error: [possibly-unbound-attribute]
|
||||
instance.attr = 1 # error: [possibly-missing-attribute]
|
||||
```
|
||||
|
||||
## Data descriptors
|
||||
|
|
|
@ -23,7 +23,7 @@ async def main() -> None:
|
|||
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.
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ def _(n: int):
|
|||
# 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: [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)
|
||||
```
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ def _(flag: bool):
|
|||
|
||||
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
|
||||
|
||||
# error: [unresolved-attribute] "Type `<class 'A'>` has no attribute `non_existent`"
|
||||
|
|
|
@ -22,7 +22,7 @@ reveal_type(y)
|
|||
```
|
||||
|
||||
```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
|
||||
|
||||
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:
|
||||
|
||||
```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
|
||||
|
||||
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
|
||||
|
||||
```py
|
||||
# error: "Member `Foo` of module `a` is possibly unbound"
|
||||
# error: "Member `Foo` of module `a` may be missing"
|
||||
from a import Foo
|
||||
|
||||
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.
|
||||
|
||||
```py
|
||||
# error: "Member `Foo` of module `a` is possibly unbound"
|
||||
# error: "Member `Foo` of module `a` may be missing"
|
||||
from a import Foo
|
||||
|
||||
reveal_type(Foo) # revealed: <class 'Foo'>
|
||||
|
@ -365,7 +365,7 @@ class Foo: ...
|
|||
### Re-export in one branch
|
||||
|
||||
```py
|
||||
# error: "Member `Foo` of module `a` is possibly unbound"
|
||||
# error: "Member `Foo` of module `a` may be missing"
|
||||
from a import Foo
|
||||
|
||||
reveal_type(Foo) # revealed: <class 'Foo'>
|
||||
|
|
|
@ -88,7 +88,7 @@ async def foo():
|
|||
reveal_type(x) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Possibly unbound `__anext__` method
|
||||
### Possibly missing `__anext__` method
|
||||
|
||||
```py
|
||||
from typing_extensions import reveal_type
|
||||
|
@ -108,7 +108,7 @@ async def foo(flag: bool):
|
|||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
||||
### Possibly unbound `__aiter__` method
|
||||
### Possibly missing `__aiter__` method
|
||||
|
||||
```py
|
||||
from typing_extensions import reveal_type
|
||||
|
|
|
@ -363,7 +363,7 @@ for x in Bad():
|
|||
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
|
||||
def _(flag: bool):
|
||||
|
@ -412,7 +412,7 @@ for y in Iterable2():
|
|||
reveal_type(y) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Possibly unbound `__iter__` and bad `__getitem__` method
|
||||
## Possibly missing `__iter__` and bad `__getitem__` method
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
|
@ -438,12 +438,12 @@ def _(flag: bool):
|
|||
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:
|
||||
|
||||
- `__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
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## Possibly unbound `__iter__` and possibly unbound `__getitem__`
|
||||
## Possibly missing `__iter__` and possibly missing `__getitem__`
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
|
@ -560,7 +560,7 @@ for x in Iterable():
|
|||
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
|
||||
`__getitem__`:
|
||||
|
@ -694,7 +694,7 @@ def _(flag: bool):
|
|||
reveal_type(y) # revealed: str | int
|
||||
```
|
||||
|
||||
## Possibly unbound `__iter__` and possibly invalid `__getitem__`
|
||||
## Possibly missing `__iter__` and possibly invalid `__getitem__`
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
|
|
|
@ -135,9 +135,9 @@ a.b = B()
|
|||
reveal_type(a.b) # revealed: B
|
||||
reveal_type(a.b.c1) # 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
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
reveal_type(a.b.c2.d) # revealed: D | None
|
||||
```
|
||||
|
||||
|
@ -295,9 +295,9 @@ class C:
|
|||
reveal_type(b.a.x[0]) # revealed: Literal[0]
|
||||
|
||||
def _():
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
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) # revealed: Unknown | A | None
|
||||
```
|
||||
|
|
|
@ -161,7 +161,7 @@ class _:
|
|||
a.b = B()
|
||||
|
||||
class _:
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
reveal_type(a.b.c1.d) # revealed: D | 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
|
||||
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
|
||||
def returns_bool() -> bool:
|
||||
|
@ -71,7 +71,7 @@ class MaybeWithSpam:
|
|||
spam: int = 42
|
||||
|
||||
def _(obj: MaybeWithSpam):
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
reveal_type(obj.spam) # revealed: int
|
||||
|
||||
if hasattr(obj, "spam"):
|
||||
|
@ -81,7 +81,7 @@ def _(obj: MaybeWithSpam):
|
|||
reveal_type(obj) # revealed: MaybeWithSpam & ~<Protocol with members 'spam'>
|
||||
|
||||
# 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
|
||||
```
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class C:
|
|||
if flag:
|
||||
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.y) # revealed: Unknown | Literal[1]
|
||||
```
|
||||
|
@ -52,7 +52,7 @@ class C:
|
|||
elif coinflip():
|
||||
x: str = "abc"
|
||||
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
reveal_type(C.x) # revealed: int | str
|
||||
```
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
|||
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
|
||||
---
|
||||
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
|||
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
|
||||
---
|
||||
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
|||
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
|
||||
---
|
||||
|
||||
|
@ -17,37 +17,37 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/attribute_as
|
|||
3 | if flag:
|
||||
4 | attr: int = 0
|
||||
5 |
|
||||
6 | C.attr = 1 # error: [possibly-unbound-attribute]
|
||||
6 | C.attr = 1 # error: [possibly-missing-attribute]
|
||||
7 |
|
||||
8 | instance = C()
|
||||
9 | instance.attr = 1 # error: [possibly-unbound-attribute]
|
||||
9 | instance.attr = 1 # error: [possibly-missing-attribute]
|
||||
```
|
||||
|
||||
# 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
|
||||
|
|
||||
4 | attr: int = 0
|
||||
5 |
|
||||
6 | C.attr = 1 # error: [possibly-unbound-attribute]
|
||||
6 | C.attr = 1 # error: [possibly-missing-attribute]
|
||||
| ^^^^^^
|
||||
7 |
|
||||
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
|
||||
|
|
||||
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
|
||||
---
|
||||
---
|
||||
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
|
||||
---
|
||||
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
|||
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
|
||||
---
|
||||
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
|||
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
|
||||
---
|
||||
|
|
@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
|
|||
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
|
||||
---
|
||||
|
||||
|
@ -41,7 +41,7 @@ error[invalid-await]: `PossiblyUnbound` is not awaitable
|
|||
| --------------- method defined here
|
||||
6 | yield
|
||||
|
|
||||
info: `__await__` is possibly unbound
|
||||
info: `__await__` may be missing
|
||||
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]`"
|
||||
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"
|
||||
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)
|
||||
```
|
||||
|
||||
|
@ -81,7 +81,7 @@ error[call-non-callable]: Object of type `Literal[5]` is not callable
|
|||
--> src/mdtest_snippet.py:60:9
|
||||
|
|
||||
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)
|
||||
| ^^^^
|
||||
|
|
||||
|
@ -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
|
||||
|
|
||||
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)
|
||||
| ^^^^
|
||||
|
|
||||
|
@ -111,7 +111,7 @@ error[missing-argument]: No argument provided for required parameter `b` of func
|
|||
--> src/mdtest_snippet.py:60:9
|
||||
|
|
||||
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)
|
||||
| ^^^^
|
||||
|
|
||||
|
@ -126,7 +126,7 @@ error[no-matching-overload]: No overload of function `f6` matches arguments
|
|||
--> src/mdtest_snippet.py:60:9
|
||||
|
|
||||
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)
|
||||
| ^^^^
|
||||
|
|
||||
|
@ -162,7 +162,7 @@ error[invalid-argument-type]: Argument to function `f2` is incorrect
|
|||
--> src/mdtest_snippet.py:60:11
|
||||
|
|
||||
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)
|
||||
| ^ 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
|
||||
|
|
||||
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)
|
||||
| ^ 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
|
||||
|
|
||||
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)
|
||||
| ^ 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
|
||||
|
|
||||
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)
|
||||
| ^
|
||||
|
|
||||
|
|
|
@ -1530,7 +1530,7 @@ if flag():
|
|||
```
|
||||
|
||||
```py
|
||||
# error: [possibly-unbound-import]
|
||||
# error: [possibly-missing-import]
|
||||
from module import symbol
|
||||
```
|
||||
|
||||
|
|
|
@ -14,7 +14,11 @@ a = NotSubscriptable()[0] # error: "Cannot subscript object of type `NotSubscri
|
|||
class NotSubscriptable:
|
||||
__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]
|
||||
```
|
||||
|
||||
|
@ -82,7 +86,7 @@ class NoSetitem:
|
|||
__setitem__ = None
|
||||
|
||||
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
|
||||
|
|
|
@ -198,7 +198,7 @@ import sys
|
|||
|
||||
if sys.platform == "win32":
|
||||
# TODO: we should not emit an error here
|
||||
# error: [possibly-unbound-attribute]
|
||||
# error: [possibly-missing-attribute]
|
||||
sys.getwindowsversion()
|
||||
```
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ async def _(flag: bool):
|
|||
class 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:
|
||||
reveal_type(f) # revealed: str
|
||||
```
|
||||
|
@ -129,7 +129,7 @@ async def _(flag: bool):
|
|||
|
||||
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:
|
||||
reveal_type(f) # revealed: CoroutineType[Any, Any, str]
|
||||
```
|
||||
|
|
|
@ -113,7 +113,7 @@ def _(flag: bool):
|
|||
class 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:
|
||||
reveal_type(f) # revealed: str
|
||||
```
|
||||
|
@ -129,7 +129,7 @@ def _(flag: bool):
|
|||
|
||||
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:
|
||||
reveal_type(f) # revealed: str
|
||||
```
|
||||
|
|
|
@ -8,7 +8,7 @@ use bitflags::bitflags;
|
|||
use call::{CallDunderError, CallError, CallErrorKind};
|
||||
use context::InferContext;
|
||||
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,
|
||||
};
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -8148,7 +8148,7 @@ impl<'db> AwaitError<'db> {
|
|||
}
|
||||
}
|
||||
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) {
|
||||
diag.annotate(
|
||||
Annotation::secondary(definition_spans.signature)
|
||||
|
@ -8255,7 +8255,7 @@ impl<'db> ContextManagerError<'db> {
|
|||
match call_dunder_error {
|
||||
CallDunderError::MethodNotAvailable => format!("it does not implement `{name}`"),
|
||||
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.
|
||||
// E.g. hint toward the union variant that doesn't correctly implement enter,
|
||||
|
@ -8272,7 +8272,7 @@ impl<'db> ContextManagerError<'db> {
|
|||
name_b: &str| {
|
||||
match (error_a, error_b) {
|
||||
(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) => {
|
||||
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.
|
||||
/// 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> },
|
||||
}
|
||||
|
||||
|
@ -8994,7 +8994,7 @@ impl<'db> ConstructorCallError<'db> {
|
|||
let report_init_error = |call_dunder_error: &CallDunderError<'db>| match call_dunder_error {
|
||||
CallDunderError::MethodNotAvailable => {
|
||||
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
|
||||
// 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) => {
|
||||
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!(
|
||||
"Method `__init__` on type `{}` is possibly unbound.",
|
||||
"Method `__init__` on type `{}` may be missing.",
|
||||
context_expression_type.display(context.db()),
|
||||
));
|
||||
}
|
||||
|
@ -9026,10 +9026,10 @@ impl<'db> ConstructorCallError<'db> {
|
|||
let report_new_error = |error: &DunderNewCallError<'db>| match error {
|
||||
DunderNewCallError::PossiblyUnbound(call_error) => {
|
||||
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!(
|
||||
"Method `__new__` on type `{}` is possibly unbound.",
|
||||
"Method `__new__` on type `{}` may be missing.",
|
||||
context_expression_type.display(context.db()),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -1758,7 +1758,7 @@ impl<'db> CallableBinding<'db> {
|
|||
if self.dunder_call_is_possibly_unbound {
|
||||
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, node) {
|
||||
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()),
|
||||
));
|
||||
if let Some(union_diag) = union_diag {
|
||||
|
|
|
@ -38,7 +38,7 @@ use std::fmt::Formatter;
|
|||
pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
||||
registry.register_lint(&AMBIGUOUS_PROTOCOL_MEMBER);
|
||||
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_DECLARATIONS);
|
||||
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(&UNSUPPORTED_BOOL_CONVERSION);
|
||||
registry.register_lint(&PARAMETER_ALREADY_ASSIGNED);
|
||||
registry.register_lint(&POSSIBLY_UNBOUND_ATTRIBUTE);
|
||||
registry.register_lint(&POSSIBLY_UNBOUND_IMPORT);
|
||||
registry.register_lint(&POSSIBLY_MISSING_ATTRIBUTE);
|
||||
registry.register_lint(&POSSIBLY_MISSING_IMPORT);
|
||||
registry.register_lint(&POSSIBLY_UNRESOLVED_REFERENCE);
|
||||
registry.register_lint(&SUBCLASS_OF_FINAL_CLASS);
|
||||
registry.register_lint(&TYPE_ASSERTION_FAILURE);
|
||||
|
@ -131,12 +131,12 @@ declare_lint! {
|
|||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for implicit calls to possibly unbound methods.
|
||||
/// Checks for implicit calls to possibly missing methods.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Expressions such as `x[y]` and `x * y` call methods
|
||||
/// 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
|
||||
/// ```python
|
||||
|
@ -148,8 +148,8 @@ declare_lint! {
|
|||
///
|
||||
/// A()[0] # TypeError: 'A' object is not subscriptable
|
||||
/// ```
|
||||
pub(crate) static POSSIBLY_UNBOUND_IMPLICIT_CALL = {
|
||||
summary: "detects implicit calls to possibly unbound methods",
|
||||
pub(crate) static POSSIBLY_MISSING_IMPLICIT_CALL = {
|
||||
summary: "detects implicit calls to possibly missing methods",
|
||||
status: LintStatus::preview("1.0.0"),
|
||||
default_level: Level::Warn,
|
||||
}
|
||||
|
@ -1327,10 +1327,10 @@ declare_lint! {
|
|||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for possibly unbound attributes.
|
||||
/// Checks for possibly missing attributes.
|
||||
///
|
||||
/// ## 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
|
||||
/// ```python
|
||||
|
@ -1340,8 +1340,8 @@ declare_lint! {
|
|||
///
|
||||
/// A.c # AttributeError: type object 'A' has no attribute 'c'
|
||||
/// ```
|
||||
pub(crate) static POSSIBLY_UNBOUND_ATTRIBUTE = {
|
||||
summary: "detects references to possibly unbound attributes",
|
||||
pub(crate) static POSSIBLY_MISSING_ATTRIBUTE = {
|
||||
summary: "detects references to possibly missing attributes",
|
||||
status: LintStatus::preview("1.0.0"),
|
||||
default_level: Level::Warn,
|
||||
}
|
||||
|
@ -1349,10 +1349,10 @@ declare_lint! {
|
|||
|
||||
declare_lint! {
|
||||
/// ## 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?
|
||||
/// Importing an unbound module or name will raise a `ModuleNotFoundError`
|
||||
/// Importing a missing module or name will raise a `ModuleNotFoundError`
|
||||
/// or `ImportError` at runtime.
|
||||
///
|
||||
/// ## Examples
|
||||
|
@ -1366,8 +1366,8 @@ declare_lint! {
|
|||
/// # main.py
|
||||
/// from module import a # ImportError: cannot import name 'a' from 'module'
|
||||
/// ```
|
||||
pub(crate) static POSSIBLY_UNBOUND_IMPORT = {
|
||||
summary: "detects possibly unbound imports",
|
||||
pub(crate) static POSSIBLY_MISSING_IMPORT = {
|
||||
summary: "detects possibly missing imports",
|
||||
status: LintStatus::preview("1.0.0"),
|
||||
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"));
|
||||
}
|
||||
|
||||
pub(super) fn report_possibly_unbound_attribute(
|
||||
pub(super) fn report_possibly_missing_attribute(
|
||||
context: &InferContext,
|
||||
target: &ast::ExprAttribute,
|
||||
attribute: &str,
|
||||
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;
|
||||
};
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Attribute `{attribute}` on type `{}` is possibly unbound",
|
||||
"Attribute `{attribute}` on type `{}` may be missing",
|
||||
object_ty.display(context.db()),
|
||||
));
|
||||
}
|
||||
|
@ -2793,7 +2793,7 @@ pub(crate) fn report_invalid_or_unsupported_base(
|
|||
CallDunderError::PossiblyUnbound(_) => {
|
||||
explain_mro_entries(&mut diagnostic);
|
||||
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)
|
||||
));
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ use crate::types::diagnostic::{
|
|||
INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION,
|
||||
INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_NAMED_TUPLE, INVALID_PARAMETER_DEFAULT,
|
||||
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,
|
||||
UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, report_bad_dunder_set_call,
|
||||
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_key_on_typed_dict, report_invalid_return_type,
|
||||
report_namedtuple_field_without_default_after_field_with_default,
|
||||
report_possibly_unbound_attribute,
|
||||
report_possibly_missing_attribute,
|
||||
};
|
||||
use crate::types::diagnostic::{
|
||||
INVALID_METACLASS, INVALID_OVERLOAD, INVALID_PROTOCOL, SUBCLASS_OF_FINAL_CLASS,
|
||||
|
@ -3222,10 +3222,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
Err(err) => match err {
|
||||
CallDunderError::PossiblyUnbound { .. } => {
|
||||
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!(
|
||||
"Method `__setitem__` of type `{}` is possibly unbound",
|
||||
"Method `__setitem__` of type `{}` may be missing",
|
||||
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)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Method `__setitem__` of type `{}` is possibly not \
|
||||
"Method `__setitem__` of type `{}` may not be \
|
||||
callable on object of type `{}`",
|
||||
bindings.callable_type().display(db),
|
||||
value_ty.display(db),
|
||||
|
@ -3642,7 +3642,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
};
|
||||
|
||||
if boundness == Boundness::PossiblyUnbound {
|
||||
report_possibly_unbound_attribute(
|
||||
report_possibly_missing_attribute(
|
||||
&self.context,
|
||||
target,
|
||||
attribute,
|
||||
|
@ -3672,7 +3672,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
}
|
||||
|
||||
if instance_attr_boundness == Boundness::PossiblyUnbound {
|
||||
report_possibly_unbound_attribute(
|
||||
report_possibly_missing_attribute(
|
||||
&self.context,
|
||||
target,
|
||||
attribute,
|
||||
|
@ -3752,7 +3752,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
};
|
||||
|
||||
if boundness == Boundness::PossiblyUnbound {
|
||||
report_possibly_unbound_attribute(
|
||||
report_possibly_missing_attribute(
|
||||
&self.context,
|
||||
target,
|
||||
attribute,
|
||||
|
@ -3783,7 +3783,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
}
|
||||
|
||||
if class_attr_boundness == Boundness::PossiblyUnbound {
|
||||
report_possibly_unbound_attribute(
|
||||
report_possibly_missing_attribute(
|
||||
&self.context,
|
||||
target,
|
||||
attribute,
|
||||
|
@ -4680,10 +4680,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
// together if the attribute exists but is possibly-unbound.
|
||||
if let Some(builder) = self
|
||||
.context
|
||||
.report_lint(&POSSIBLY_UNBOUND_IMPORT, AnyNodeRef::Alias(alias))
|
||||
.report_lint(&POSSIBLY_MISSING_IMPORT, AnyNodeRef::Alias(alias))
|
||||
{
|
||||
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()
|
||||
}
|
||||
LookupError::PossiblyUnbound(type_when_bound) => {
|
||||
report_possibly_unbound_attribute(
|
||||
report_possibly_missing_attribute(
|
||||
&self.context,
|
||||
attribute,
|
||||
&attr.id,
|
||||
|
@ -8757,10 +8757,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
}
|
||||
Err(err @ CallDunderError::PossiblyUnbound { .. }) => {
|
||||
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!(
|
||||
"Method `__getitem__` of type `{}` is possibly unbound",
|
||||
"Method `__getitem__` of type `{}` may be missing",
|
||||
value_ty.display(db),
|
||||
));
|
||||
}
|
||||
|
@ -8808,7 +8808,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
CallErrorKind::PossiblyNotCallable => {
|
||||
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, value_node) {
|
||||
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),
|
||||
value_ty.display(db),
|
||||
));
|
||||
|
@ -8840,11 +8840,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
Place::Type(ty, boundness) => {
|
||||
if boundness == Boundness::PossiblyUnbound {
|
||||
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!(
|
||||
"Method `__class_getitem__` of type `{}` \
|
||||
is possibly unbound",
|
||||
"Method `__class_getitem__` of type `{}` may be missing",
|
||||
value_ty.display(db),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -78,9 +78,9 @@ Settings: Settings {
|
|||
"not-iterable": Error (Default),
|
||||
"parameter-already-assigned": Error (Default),
|
||||
"positional-only-parameter-as-kwarg": Error (Default),
|
||||
"possibly-unbound-attribute": Warning (Default),
|
||||
"possibly-unbound-implicit-call": Warning (Default),
|
||||
"possibly-unbound-import": Warning (Default),
|
||||
"possibly-missing-attribute": Warning (Default),
|
||||
"possibly-missing-implicit-call": Warning (Default),
|
||||
"possibly-missing-import": Warning (Default),
|
||||
"raw-string-type-annotation": Error (Default),
|
||||
"redundant-cast": Warning (Default),
|
||||
"static-assert-error": Error (Default),
|
||||
|
|
18
ty.schema.json
generated
18
ty.schema.json
generated
|
@ -795,9 +795,9 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"possibly-unbound-attribute": {
|
||||
"title": "detects references to possibly unbound 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```",
|
||||
"possibly-missing-attribute": {
|
||||
"title": "detects references to possibly missing attributes",
|
||||
"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",
|
||||
"oneOf": [
|
||||
{
|
||||
|
@ -805,9 +805,9 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"possibly-unbound-implicit-call": {
|
||||
"title": "detects implicit calls to possibly unbound 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```",
|
||||
"possibly-missing-implicit-call": {
|
||||
"title": "detects implicit calls to possibly missing methods",
|
||||
"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",
|
||||
"oneOf": [
|
||||
{
|
||||
|
@ -815,9 +815,9 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"possibly-unbound-import": {
|
||||
"title": "detects possibly unbound 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```",
|
||||
"possibly-missing-import": {
|
||||
"title": "detects possibly missing imports",
|
||||
"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",
|
||||
"oneOf": [
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue