[red-knot] Diagnostic for possibly unbound imports (#14281)

## Summary

This adds a new diagnostic when possibly unbound symbols are imported.
The `TODO` comment had a question mark, do I'm not sure if this is
really something that we want.

This does not touch the un*declared* case, yet.

relates to: #14022

## Test Plan

Updated already existing tests with new diagnostics
This commit is contained in:
David Peter 2024-11-11 20:26:01 +01:00 committed by GitHub
parent f82ee8ea59
commit 3bef23669f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 20 additions and 10 deletions

View file

@ -21,6 +21,7 @@ reveal_type(y)
``` ```
```py ```py
# error: [possibly-unbound-import] "Member `y` of module `maybe_unbound` is possibly unbound"
from maybe_unbound import x, y from maybe_unbound import x, y
reveal_type(x) # revealed: Literal[3] reveal_type(x) # revealed: Literal[3]
@ -50,6 +51,7 @@ reveal_type(y)
Importing an annotated name prefers the declared type over the inferred type: Importing an annotated name prefers the declared type over the inferred type:
```py ```py
# error: [possibly-unbound-import] "Member `y` of module `maybe_unbound_annotated` is possibly unbound"
from maybe_unbound_annotated import x, y from maybe_unbound_annotated import x, y
reveal_type(x) # revealed: Literal[3] reveal_type(x) # revealed: Literal[3]

View file

@ -102,6 +102,9 @@ else:
### Handling of `None` ### Handling of `None`
```py ```py
# TODO: this error should ideally go away once we (1) understand `sys.version_info` branches,
# and (2) set the target Python version for this test to 3.10.
# error: [possibly-unbound-import] "Member `NoneType` of module `types` is possibly unbound"
from types import NoneType from types import NoneType
def flag() -> bool: ... def flag() -> bool: ...

View file

@ -10,8 +10,6 @@ reveal_type(not not None) # revealed: Literal[False]
## Function ## Function
```py ```py
from typing import reveal_type
def f(): def f():
return 1 return 1

View file

@ -2008,20 +2008,27 @@ impl<'db> TypeInferenceBuilder<'db> {
asname: _, asname: _,
} = alias; } = alias;
// For possibly-unbound names, just eliminate Unbound from the type; we match module_ty.member(self.db, &ast::name::Name::new(&name.id)) {
// must be in a bound path. TODO diagnostic for possibly-unbound import? Symbol::Type(ty, boundness) => {
module_ty if boundness == Boundness::PossiblyUnbound {
.member(self.db, &ast::name::Name::new(&name.id)) self.diagnostics.add(
.ignore_possibly_unbound() AnyNodeRef::Alias(alias),
.unwrap_or_else(|| { "possibly-unbound-import",
format_args!("Member `{name}` of module `{module_name}` is possibly unbound",),
);
}
ty
}
Symbol::Unbound => {
self.diagnostics.add( self.diagnostics.add(
AnyNodeRef::Alias(alias), AnyNodeRef::Alias(alias),
"unresolved-import", "unresolved-import",
format_args!("Module `{module_name}` has no member `{name}`",), format_args!("Module `{module_name}` has no member `{name}`",),
); );
Type::Unknown Type::Unknown
}) }
}
} else { } else {
self.diagnostics self.diagnostics
.add_unresolved_module(import_from, *level, module); .add_unresolved_module(import_from, *level, module);