support absolute imports
This commit is contained in:
@@ -333,7 +333,7 @@ reveal_type(mypackage.nested.X) # revealed: Unknown
|
||||
|
||||
### In Non-Stub
|
||||
|
||||
`from mypackage.submodule import nested` in an `__init__.py` only creates `nested`.
|
||||
`from mypackage.submodule import nested` in an `__init__.py` only creates `submodule` and `nested`.
|
||||
|
||||
`mypackage/__init__.py`:
|
||||
|
||||
@@ -357,12 +357,11 @@ X: int = 42
|
||||
```py
|
||||
import mypackage
|
||||
|
||||
reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'>
|
||||
# TODO: this would be nice to support
|
||||
# error: "has no member `submodule`"
|
||||
reveal_type(mypackage.submodule) # revealed: Unknown
|
||||
# error: "has no member `submodule`"
|
||||
# error: "has no member `nested`"
|
||||
reveal_type(mypackage.submodule.nested) # revealed: Unknown
|
||||
# error: "has no member `submodule`"
|
||||
# error: "has no member `nested`"
|
||||
reveal_type(mypackage.submodule.nested.X) # revealed: Unknown
|
||||
reveal_type(mypackage.nested) # revealed: <module 'mypackage.submodule.nested'>
|
||||
reveal_type(mypackage.nested.X) # revealed: int
|
||||
|
||||
@@ -1451,7 +1451,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||
|
||||
// If we see:
|
||||
//
|
||||
// * `from .x.y import z` (must be relative!)
|
||||
// * `from .x.y import z` (or `from whatever.thispackage.x.y`)
|
||||
// * And we are in an `__init__.py(i)` (hereafter `thispackage`)
|
||||
// * And this is the first time we've seen `from .x` in this module
|
||||
// * And we're in the global scope
|
||||
@@ -1465,14 +1465,19 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||
// reasons but it works well for most practical purposes. In particular it's nice
|
||||
// that `x` can be freely overwritten, and that we don't assume that an import
|
||||
// in one function is visible in another function.
|
||||
//
|
||||
// TODO: Also support `from thispackage.x.y import z`?
|
||||
if self.current_scope() == FileScopeId::global()
|
||||
&& node.level == 1
|
||||
&& let Some(submodule) = &node.module
|
||||
&& let Some(parsed_submodule) = ModuleName::new(submodule.as_str())
|
||||
&& let Some(direct_submodule) = parsed_submodule.components().next()
|
||||
if let Some(submodule) = &node.module
|
||||
&& self.current_scope() == FileScopeId::global()
|
||||
&& self.file.is_package(self.db)
|
||||
&& let Ok(module_name) = ModuleName::from_identifier_parts(
|
||||
self.db,
|
||||
self.file,
|
||||
node.module.as_deref(),
|
||||
node.level,
|
||||
)
|
||||
&& let Ok(thispackage) =
|
||||
ModuleName::from_identifier_parts(self.db, self.file, None, 1)
|
||||
&& let Some(relative_submodule) = module_name.relative_to(&thispackage)
|
||||
&& let Some(direct_submodule) = relative_submodule.components().next()
|
||||
&& !self.seen_submodule_imports.contains(direct_submodule)
|
||||
{
|
||||
self.seen_submodule_imports
|
||||
|
||||
@@ -5916,13 +5916,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
// this type of Definition is only created when a `from..import` is in global scope.)
|
||||
|
||||
// Get this package's module by resolving `.`
|
||||
let Ok(module_name) = ModuleName::from_identifier_parts(self.db(), self.file(), None, 1)
|
||||
let Ok(thispackage_name) =
|
||||
ModuleName::from_identifier_parts(self.db(), self.file(), None, 1)
|
||||
else {
|
||||
self.add_binding(import_from.into(), definition, |_, _| Type::unknown());
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(module) = resolve_module(self.db(), &module_name) else {
|
||||
let Some(module) = resolve_module(self.db(), &thispackage_name) else {
|
||||
self.add_binding(import_from.into(), definition, |_, _| Type::unknown());
|
||||
return;
|
||||
};
|
||||
@@ -5932,12 +5933,26 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
!submodule.is_empty(),
|
||||
"ImportFromSubmoduleDefinitionKind constructed with empty module"
|
||||
);
|
||||
let name = submodule
|
||||
.split_once('.')
|
||||
.map(|(first, _)| first)
|
||||
.unwrap_or(submodule.as_str());
|
||||
|
||||
let Ok(submodule_name) = ModuleName::from_identifier_parts(
|
||||
self.db(),
|
||||
self.file(),
|
||||
import_from.module.as_deref(),
|
||||
import_from.level,
|
||||
) else {
|
||||
self.add_binding(import_from.into(), definition, |_, _| Type::unknown());
|
||||
return;
|
||||
};
|
||||
let Some(relative_submodule_name) = submodule_name.relative_to(&thispackage_name) else {
|
||||
self.add_binding(import_from.into(), definition, |_, _| Type::unknown());
|
||||
return;
|
||||
};
|
||||
let Some(name) = relative_submodule_name.components().next() else {
|
||||
self.add_binding(import_from.into(), definition, |_, _| Type::unknown());
|
||||
return;
|
||||
};
|
||||
let full_submodule_name = ModuleName::new(name).map(|final_part| {
|
||||
let mut ret = module_name.clone();
|
||||
let mut ret = thispackage_name.clone();
|
||||
ret.extend(&final_part);
|
||||
ret
|
||||
});
|
||||
@@ -5970,7 +5985,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
};
|
||||
|
||||
let diagnostic = builder.into_diagnostic(format_args!(
|
||||
"Module `{module_name}` has no submodule `{name}`"
|
||||
"Module `{thispackage_name}` has no submodule `{name}`"
|
||||
));
|
||||
|
||||
if let Some(full_submodule_name) = full_submodule_name {
|
||||
|
||||
Reference in New Issue
Block a user