[ty] Rename "possibly unbound" diagnostics to "possibly missing" (#20492)

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
Renkai Ge 2025-09-23 22:26:55 +08:00 committed by GitHub
parent 4ed8c65d29
commit bf38e69870
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 213 additions and 194 deletions

View file

@ -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

View file

@ -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'>

View file

@ -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()
```

View file

@ -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
```

View file

@ -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
```

View file

@ -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:

View file

@ -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
```

View file

@ -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
```

View file

@ -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

View file

@ -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.

View file

@ -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)
```

View file

@ -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`"

View file

@ -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]

View file

@ -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'>

View file

@ -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

View file

@ -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 -->

View file

@ -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
```

View file

@ -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
```

View file

@ -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
```

View file

@ -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
```

View file

@ -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
---

View file

@ -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
---

View file

@ -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
```

View file

@ -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
---

View file

@ -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
---

View file

@ -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
---

View file

@ -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
```

View file

@ -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)
| ^
|

View file

@ -1530,7 +1530,7 @@ if flag():
```
```py
# error: [possibly-unbound-import]
# error: [possibly-missing-import]
from module import symbol
```

View file

@ -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

View file

@ -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()
```

View file

@ -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]
```

View file

@ -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
```

View file

@ -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()),
));
}

View file

@ -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 {

View file

@ -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)
));
}

View file

@ -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),
));
}