diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/relative.md b/crates/red_knot_python_semantic/resources/mdtest/import/relative.md index f21c470646..c82d8db7a2 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/relative.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/relative.md @@ -219,7 +219,11 @@ import package reveal_type(package.foo.X) # revealed: Unknown ``` -## In the src-root +## Relative imports at the top of a search path + +Relative imports at the top of a search path result in a runtime error: +`ImportError: attempted relative import with no known parent package`. That's why Red Knot should +disallow them. `parser.py`: @@ -230,21 +234,5 @@ X: int = 42 `__main__.py`: ```py -from .parser import X - -reveal_type(X) # revealed: int -``` - -## Beyond the src-root - -`parser.py`: - -```py -X: int = 42 -``` - -`__main__.py`: - -```py -from ..parser import X # error: [unresolved-import] +from .parser import X # error: [unresolved-import] ``` diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 8768c056c5..7de554f538 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -2512,30 +2512,19 @@ impl<'db> TypeInferenceBuilder<'db> { let module = file_to_module(self.db(), self.file()) .ok_or(ModuleNameResolutionError::UnknownCurrentModule)?; let mut level = level.get(); + if module.kind().is_package() { level = level.saturating_sub(1); } - let mut module_name = module.name().clone(); - let tail = tail - .map(|tail| ModuleName::new(tail).ok_or(ModuleNameResolutionError::InvalidSyntax)) - .transpose()?; - - for remaining_dots in (0..level).rev() { - if let Some(parent) = module_name.parent() { - module_name = parent; - } else if remaining_dots == 0 { - // If we reached a search path root and this was the last dot return the tail if any. - // If there's no tail, then we have a relative import that's too deep. - return tail.ok_or(ModuleNameResolutionError::TooManyDots); - } else { - // We're at a search path root. This is a too deep relative import if there's more than - // one dot remaining. - return Err(ModuleNameResolutionError::TooManyDots); - } - } + let mut module_name = module + .name() + .ancestors() + .nth(level as usize) + .ok_or(ModuleNameResolutionError::TooManyDots)?; if let Some(tail) = tail { + let tail = ModuleName::new(tail).ok_or(ModuleNameResolutionError::InvalidSyntax)?; module_name.extend(&tail); }