Skip to content

Commit d707e9f

Browse files
committed
Handle nested imports with as clause correctly
1 parent 370c133 commit d707e9f

File tree

3 files changed

+74
-19
lines changed

3 files changed

+74
-19
lines changed

crates/red_knot_python_semantic/resources/mdtest/import/basic.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,36 @@ reveal_type(a.b.c.C) # revealed: Literal[C]
5858
```py path=a/b/c.py
5959
class C: ...
6060
```
61+
62+
## Nested with rename
63+
64+
```py
65+
import a.b as b
66+
67+
reveal_type(b.C) # revealed: Literal[C]
68+
```
69+
70+
```py path=a/__init__.py
71+
```
72+
73+
```py path=a/b.py
74+
class C: ...
75+
```
76+
77+
## Deeply nested with rename
78+
79+
```py
80+
import a.b.c as c
81+
82+
reveal_type(c.C) # revealed: Literal[C]
83+
```
84+
85+
```py path=a/__init__.py
86+
```
87+
88+
```py path=a/b/__init__.py
89+
```
90+
91+
```py path=a/b/c.py
92+
class C: ...
93+
```

crates/red_knot_python_semantic/resources/mdtest/import/tracking.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,5 @@ class C: ...
6666

6767
```py path=q.py
6868
import a as a
69-
import a.b
70-
# XXX: fix as
71-
b = a.b
69+
import a.b as b
7270
```

crates/red_knot_python_semantic/src/types/infer.rs

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,31 +2117,55 @@ impl<'db> TypeInferenceBuilder<'db> {
21172117
let ast::Alias {
21182118
range: _,
21192119
name,
2120-
asname: _,
2120+
asname,
21212121
} = alias;
21222122

2123-
// When importing a nested module a.b.c, we create a binding for (and need to infer the
2124-
// type of) the top-most parent module. We also try to resolve the full nested module now,
2125-
// so that if we run into problems, any diagnostics are correctly associated with the
2126-
// import statement.
2127-
let module_ty = if let Some(module_name) = ModuleName::new(name) {
2128-
if self.module_ty_from_name(&module_name).is_none() {
2129-
self.diagnostics.add_unresolved_module(alias, 0, Some(name));
2130-
}
2123+
// The name of the module being imported
2124+
let Some(full_module_name) = ModuleName::new(name) else {
2125+
tracing::debug!("Failed to resolve import due to invalid syntax");
2126+
self.add_declaration_with_binding(
2127+
alias.into(),
2128+
definition,
2129+
Type::Unknown,
2130+
Type::Unknown,
2131+
);
2132+
return;
2133+
};
21312134

2132-
let bound_module_name =
2133-
ModuleName::new(module_name.components().next().unwrap()).unwrap();
2134-
if let Some(module) = self.module_ty_from_name(&bound_module_name) {
2135-
module
2135+
// Resolve the module being imported.
2136+
let Some(full_module_ty) = self.module_ty_from_name(&full_module_name) else {
2137+
self.diagnostics.add_unresolved_module(alias, 0, Some(name));
2138+
self.add_declaration_with_binding(
2139+
alias.into(),
2140+
definition,
2141+
Type::Unknown,
2142+
Type::Unknown,
2143+
);
2144+
return;
2145+
};
2146+
2147+
let binding_ty = if let Some(_) = asname {
2148+
// If we are renaming the imported module via an `as` clause, then we bind the resolved
2149+
// module's type to that name, even if that module is nested.
2150+
full_module_ty
2151+
} else if full_module_name.contains('.') {
2152+
// If there's no `as` clause and the imported module is nested, we're not going to bind
2153+
// the resolved module itself into the current scope; we're going to bind the top-most
2154+
// parent package of that module.
2155+
let topmost_parent_name =
2156+
ModuleName::new(full_module_name.components().next().unwrap()).unwrap();
2157+
if let Some(topmost_parent_ty) = self.module_ty_from_name(&topmost_parent_name) {
2158+
topmost_parent_ty
21362159
} else {
21372160
Type::Unknown
21382161
}
21392162
} else {
2140-
tracing::debug!("Failed to resolve import due to invalid syntax");
2141-
Type::Unknown
2163+
// If there's no `as` clause and the imported module isn't nested, then the imported
2164+
// module _is_ what we bind into the current scope.
2165+
full_module_ty
21422166
};
21432167

2144-
self.add_declaration_with_binding(alias.into(), definition, module_ty, module_ty);
2168+
self.add_declaration_with_binding(alias.into(), definition, binding_ty, binding_ty);
21452169
}
21462170

21472171
fn infer_import_from_statement(&mut self, import: &ast::StmtImportFrom) {

0 commit comments

Comments
 (0)