From e170fe493dfdffbb833629d7565a5e0555c528a7 Mon Sep 17 00:00:00 2001 From: David Peter Date: Wed, 23 Apr 2025 20:07:29 +0200 Subject: [PATCH] [red-knot] Trust all symbols in stub files (#17588) ## Summary *Generally* trust undeclared symbols in stubs, not just at the module level. Follow-up on the discussion [here](https://github.com/astral-sh/ruff/pull/17577#discussion_r2055945909). ## Test Plan New Markdown test. --- .../mdtest/boundness_declaredness/public.md | 63 +++++++++++++++++++ .../src/semantic_index/symbol.rs | 8 --- crates/red_knot_python_semantic/src/symbol.rs | 2 +- 3 files changed, 64 insertions(+), 9 deletions(-) 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 3c41c72e44..956fa1cc7f 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 10f6e9c5d1..ec9ef3886e 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 4e713a7fde..3be4ce8d02 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