diff --git a/crates/ty_python_semantic/resources/mdtest/import/tracking.md b/crates/ty_python_semantic/resources/mdtest/import/tracking.md index f31193b762..16c2473a7e 100644 --- a/crates/ty_python_semantic/resources/mdtest/import/tracking.md +++ b/crates/ty_python_semantic/resources/mdtest/import/tracking.md @@ -116,3 +116,43 @@ b = 1 ```py ``` + +## Submodule is loaded in a non-global scope + +We recognise submodules as being available as attributes even if they are loaded in a function +scope. The function might never be executed, which means that the submodule might never be loaded; +however, we prefer to prioritise avoiding false positives over catching all possible errors here. + +`a/b.py`: + +```py +``` + +`a/c.py`: + +```py +d = 42 +``` + +`a/e/f.py`: + +```py +``` + +`main.py`: + +```py +import a + +def f(): + import a.b + from a.c import d + from a.e import f + +f() + +reveal_type(a.b) # revealed: +reveal_type(a.c) # revealed: +reveal_type(a.e) # revealed: +reveal_type(a.e.f) # revealed: +``` diff --git a/crates/ty_python_semantic/src/semantic_index/builder.rs b/crates/ty_python_semantic/src/semantic_index/builder.rs index 646d3aa871..3c16a991b2 100644 --- a/crates/ty_python_semantic/src/semantic_index/builder.rs +++ b/crates/ty_python_semantic/src/semantic_index/builder.rs @@ -1535,11 +1535,12 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { is_self_import = &module_name == thispackage; } - if self.current_scope().is_global() && node.module.is_some() { + if node.module.is_some() { if let Ok(thispackage) = this_package && let Some(relative_submodule) = module_name.relative_to(&thispackage) { if is_package + && self.current_scope().is_global() && let Some(direct_submodule) = relative_submodule.components().next() && !self.seen_submodule_imports.contains(direct_submodule)