mirror of https://github.com/astral-sh/ruff
[ty] Fix goto-declaration on the RHS of `from module import submodule` (#22042)
This commit is contained in:
parent
06305f3c02
commit
7bb5dd87ff
|
|
@ -386,6 +386,29 @@ FOO = 0
|
|||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_from_import_rhs_is_module() {
|
||||
let test = CursorTest::builder()
|
||||
.source("lib/__init__.py", r#""#)
|
||||
.source("lib/module.py", r#""#)
|
||||
.source("main.py", r#"from lib import module<CURSOR>"#)
|
||||
.build();
|
||||
|
||||
// Should resolve to the actual function definition, not the import statement
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Go to declaration
|
||||
--> main.py:1:17
|
||||
|
|
||||
1 | from lib import module
|
||||
| ^^^^^^ Clicking here
|
||||
|
|
||||
info: Found 1 declaration
|
||||
--> lib/module.py:1:1
|
||||
|
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_from_import_as() {
|
||||
let test = CursorTest::builder()
|
||||
|
|
|
|||
|
|
@ -1041,21 +1041,26 @@ mod resolve_definition {
|
|||
}
|
||||
}
|
||||
|
||||
// Resolve the target module file
|
||||
let module_file = {
|
||||
// Resolve the module being imported from (handles both relative and absolute imports)
|
||||
let Some(module_name) = ModuleName::from_import_statement(db, file, import_node).ok()
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(resolved_module) = resolve_module(db, file, &module_name) else {
|
||||
return Vec::new();
|
||||
};
|
||||
resolved_module.file(db)
|
||||
// Resolve the module being imported from (handles both relative and absolute imports)
|
||||
let Some(module_name) = ModuleName::from_import_statement(db, file, import_node).ok()
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(resolved_module) = resolve_module(db, file, &module_name) else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
// Resolve the target module file
|
||||
let module_file = resolved_module.file(db);
|
||||
|
||||
let Some(module_file) = module_file else {
|
||||
return Vec::new(); // Module resolution failed
|
||||
// No file means this is a namespace package, try to import the submodule
|
||||
return Vec::from_iter(resolve_from_import_submodule_definitions(
|
||||
db,
|
||||
file,
|
||||
symbol_name,
|
||||
module_name,
|
||||
));
|
||||
};
|
||||
|
||||
// Find the definition of this symbol in the imported module's global scope
|
||||
|
|
@ -1064,22 +1069,38 @@ mod resolve_definition {
|
|||
|
||||
// Recursively resolve any import definitions found in the target module
|
||||
if definitions_in_module.is_empty() {
|
||||
// If we can't find the specific symbol, return empty list
|
||||
Vec::new()
|
||||
} else {
|
||||
let mut resolved_definitions = Vec::new();
|
||||
for def in definitions_in_module {
|
||||
let resolved = resolve_definition_recursive(
|
||||
db,
|
||||
def,
|
||||
visited,
|
||||
Some(symbol_name),
|
||||
alias_resolution,
|
||||
);
|
||||
resolved_definitions.extend(resolved);
|
||||
}
|
||||
resolved_definitions
|
||||
// This might be importing a submodule, try that
|
||||
return Vec::from_iter(resolve_from_import_submodule_definitions(
|
||||
db,
|
||||
file,
|
||||
symbol_name,
|
||||
module_name,
|
||||
));
|
||||
}
|
||||
|
||||
let mut resolved_definitions = Vec::new();
|
||||
for def in definitions_in_module {
|
||||
let resolved =
|
||||
resolve_definition_recursive(db, def, visited, Some(symbol_name), alias_resolution);
|
||||
resolved_definitions.extend(resolved);
|
||||
}
|
||||
resolved_definitions
|
||||
}
|
||||
|
||||
// Helper to resolve `from x.y import z` assuming `x.y.z` is a module.
|
||||
fn resolve_from_import_submodule_definitions<'db>(
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
symbol_name: &str,
|
||||
module_name: ModuleName,
|
||||
) -> Option<ResolvedDefinition<'db>> {
|
||||
let submodule_name = ModuleName::new(symbol_name)?;
|
||||
let mut full_submodule_name = module_name;
|
||||
full_submodule_name.extend(&submodule_name);
|
||||
let module = resolve_module(db, file, &full_submodule_name)?;
|
||||
let file = module.file(db)?;
|
||||
|
||||
Some(ResolvedDefinition::Module(file))
|
||||
}
|
||||
|
||||
/// Find definitions for a symbol name in a specific scope.
|
||||
|
|
|
|||
Loading…
Reference in New Issue