diff --git a/crates/red_knot_python_semantic/resources/mdtest/boundness_declaredness/public.md b/crates/red_knot_python_semantic/resources/mdtest/boundness_declaredness/public.md index 3c41c72e440da..956fa1cc7fd82 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/boundness_declaredness/public.md +++ b/crates/red_knot_python_semantic/resources/mdtest/boundness_declaredness/public.md @@ -292,3 +292,66 @@ reveal_type(a) # revealed: Unknown # Modifications allowed in this case: a = None ``` + +## In stub files + +In stub files, we have a minor modification to the rules above: we do not union with `Unknown` for +undeclared symbols. + +### Undeclared and bound + +`mod.pyi`: + +```pyi +MyInt = int + +class C: + MyStr = str +``` + +```py +from mod import MyInt, C + +reveal_type(MyInt) # revealed: Literal[int] +reveal_type(C.MyStr) # revealed: Literal[str] +``` + +### Undeclared and possibly unbound + +`mod.pyi`: + +```pyi +def flag() -> bool: + return True + +if flag(): + MyInt = int + + class C: + MyStr = str +``` + +```py +# error: [possibly-unbound-import] +# error: [possibly-unbound-import] +from mod import MyInt, C + +reveal_type(MyInt) # revealed: Literal[int] +reveal_type(C.MyStr) # revealed: Literal[str] +``` + +### Undeclared and unbound + +`mod.pyi`: + +```pyi +if False: + MyInt = int +``` + +```py +# error: [unresolved-import] +from mod import MyInt + +reveal_type(MyInt) # revealed: Unknown +``` diff --git a/crates/red_knot_python_semantic/src/semantic_index/symbol.rs b/crates/red_knot_python_semantic/src/semantic_index/symbol.rs index 10f6e9c5d190f..ec9ef3886eff3 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/symbol.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/symbol.rs @@ -115,10 +115,6 @@ impl<'db> ScopeId<'db> { self.node(db).scope_kind().is_function_like() } - pub(crate) fn is_module_scope(self, db: &'db dyn Db) -> bool { - self.node(db).scope_kind().is_module() - } - pub(crate) fn is_type_parameter(self, db: &'db dyn Db) -> bool { self.node(db).scope_kind().is_type_parameter() } @@ -267,10 +263,6 @@ impl ScopeKind { matches!(self, ScopeKind::Class) } - pub(crate) fn is_module(self) -> bool { - matches!(self, ScopeKind::Module) - } - pub(crate) fn is_type_parameter(self) -> bool { matches!(self, ScopeKind::Annotation | ScopeKind::TypeAlias) } diff --git a/crates/red_knot_python_semantic/src/symbol.rs b/crates/red_knot_python_semantic/src/symbol.rs index 4e713a7fdea15..3be4ce8d02fc5 100644 --- a/crates/red_knot_python_semantic/src/symbol.rs +++ b/crates/red_knot_python_semantic/src/symbol.rs @@ -593,7 +593,7 @@ fn symbol_by_id<'db>( "__slots__" | "TYPE_CHECKING" ); - if scope.is_module_scope(db) && scope.file(db).is_stub(db.upcast()) { + if scope.file(db).is_stub(db.upcast()) { // We generally trust module-level undeclared symbols in stubs and do not union // with `Unknown`. If we don't do this, simple aliases like `IOError = OSError` in // stubs would result in `IOError` being a union of `OSError` and `Unknown`, which