Skip to content

Commit a4fc6e8

Browse files
authored
fix: (LSP) check visibility of module that re-exports item, if any (#6371)
1 parent e909dcb commit a4fc6e8

File tree

4 files changed

+34
-5
lines changed

4 files changed

+34
-5
lines changed

tooling/lsp/src/requests/code_action/import_or_qualify.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ impl<'a> CodeActionFinder<'a> {
4646
*module_def_id,
4747
self.module_id,
4848
*visibility,
49+
*defining_module,
4950
self.interner,
5051
self.def_maps,
5152
) {

tooling/lsp/src/requests/completion/auto_import.rs

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ impl<'a> NodeFinder<'a> {
3838
*module_def_id,
3939
self.module_id,
4040
*visibility,
41+
*defining_module,
4142
self.interner,
4243
self.def_maps,
4344
) {

tooling/lsp/src/requests/completion/tests.rs

+23
Original file line numberDiff line numberDiff line change
@@ -1642,6 +1642,29 @@ fn main() {
16421642
assert!(items.is_empty());
16431643
}
16441644

1645+
#[test]
1646+
async fn checks_visibility_of_module_that_exports_item_if_any() {
1647+
let src = r#"
1648+
mod foo {
1649+
mod bar {
1650+
pub fn hello_world() {}
1651+
}
1652+
1653+
pub use bar::hello_world;
1654+
}
1655+
1656+
fn main() {
1657+
hello_w>|<
1658+
}
1659+
"#;
1660+
let mut items = get_completions(src).await;
1661+
assert_eq!(items.len(), 1);
1662+
1663+
let item = items.remove(0);
1664+
assert_eq!(item.label, "hello_world()");
1665+
assert_eq!(item.label_details.unwrap().detail.unwrap(), "(use foo::hello_world)");
1666+
}
1667+
16451668
#[test]
16461669
async fn test_auto_import_suggests_modules_too() {
16471670
let src = r#"mod foo {

tooling/lsp/src/visibility.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,13 @@ pub(super) fn item_in_module_is_visible(
3737

3838
/// Returns true if the given ModuleDefId is visible from the current module, given its visibility.
3939
/// This will in turn check if the ModuleDefId parent modules are visible from the current module.
40+
/// If `defining_module` is Some, it will be considered as the parent of the item to check
41+
/// (this is the case when the item is re-exported with `pub use` or similar).
4042
pub(super) fn module_def_id_is_visible(
4143
module_def_id: ModuleDefId,
4244
current_module_id: ModuleId,
4345
mut visibility: ItemVisibility,
46+
mut defining_module: Option<ModuleId>,
4447
interner: &NodeInterner,
4548
def_maps: &BTreeMap<CrateId, CrateDefMap>,
4649
) -> bool {
@@ -49,7 +52,7 @@ pub(super) fn module_def_id_is_visible(
4952
let mut target_module_id = if let ModuleDefId::ModuleId(module_id) = module_def_id {
5053
Some(module_id)
5154
} else {
52-
get_parent_module(interner, module_def_id)
55+
std::mem::take(&mut defining_module).or_else(|| get_parent_module(interner, module_def_id))
5356
};
5457

5558
// Then check if it's visible, and upwards
@@ -58,10 +61,11 @@ pub(super) fn module_def_id_is_visible(
5861
return false;
5962
}
6063

61-
let module_data = &def_maps[&module_id.krate].modules()[module_id.local_id.0];
62-
let parent_local_id = module_data.parent;
63-
target_module_id =
64-
parent_local_id.map(|local_id| ModuleId { krate: module_id.krate, local_id });
64+
target_module_id = std::mem::take(&mut defining_module).or_else(|| {
65+
let module_data = &def_maps[&module_id.krate].modules()[module_id.local_id.0];
66+
let parent_local_id = module_data.parent;
67+
parent_local_id.map(|local_id| ModuleId { krate: module_id.krate, local_id })
68+
});
6569

6670
// This is a bit strange, but the visibility is always that of the item inside another module,
6771
// so the visibility we update here is for the next loop check.

0 commit comments

Comments
 (0)