Skip to content

Commit 1253006

Browse files
committed
Implement expand_glob_reexport assist
1 parent 6e7838d commit 1253006

File tree

2 files changed

+208
-19
lines changed

2 files changed

+208
-19
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs

+207-19
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ide_db::{
77
};
88
use stdx::never;
99
use syntax::{
10-
ast::{self, make, Use, UseTree},
10+
ast::{self, make, Use, UseTree, VisibilityKind},
1111
ted, AstNode, Direction, SyntaxNode, SyntaxToken, T,
1212
};
1313

@@ -65,7 +65,76 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) ->
6565
"Expand glob import",
6666
target.text_range(),
6767
|builder| {
68-
build_expanded_import(ctx, builder, use_tree, use_item, target_module, current_module)
68+
build_expanded_import(
69+
ctx,
70+
builder,
71+
use_tree,
72+
use_item,
73+
target_module,
74+
current_module,
75+
false,
76+
)
77+
},
78+
)
79+
}
80+
81+
// Assist: expand_glob_reexport
82+
//
83+
// Expands non-private glob imports.
84+
//
85+
// ```
86+
// mod foo {
87+
// pub struct Bar;
88+
// pub struct Baz;
89+
// }
90+
//
91+
// pub use foo::*$0;
92+
// ```
93+
// ->
94+
// ```
95+
// mod foo {
96+
// pub struct Bar;
97+
// pub struct Baz;
98+
// }
99+
//
100+
// pub use foo::{Bar, Baz};
101+
// ```
102+
pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
103+
let star = ctx.find_token_syntax_at_offset(T![*])?;
104+
let use_tree = star.parent().and_then(ast::UseTree::cast)?;
105+
let use_item = star.parent_ancestors().find_map(ast::Use::cast)?;
106+
let (parent, mod_path) = find_parent_and_path(&star)?;
107+
let target_module = match ctx.sema.resolve_path(&mod_path)? {
108+
PathResolution::Def(ModuleDef::Module(it)) => Expandable::Module(it),
109+
PathResolution::Def(ModuleDef::Adt(hir::Adt::Enum(e))) => Expandable::Enum(e),
110+
_ => return None,
111+
};
112+
113+
let current_scope = ctx.sema.scope(&star.parent()?)?;
114+
let current_module = current_scope.module();
115+
116+
if let VisibilityKind::PubSelf = get_export_visibility_kind(&use_item) {
117+
return None;
118+
}
119+
if !is_visible_from(ctx, &target_module, current_module) {
120+
return None;
121+
}
122+
123+
let target = parent.either(|n| n.syntax().clone(), |n| n.syntax().clone());
124+
acc.add(
125+
AssistId("expand_glob_reexport", AssistKind::RefactorRewrite),
126+
"Expand glob reexport",
127+
target.text_range(),
128+
|builder| {
129+
build_expanded_import(
130+
ctx,
131+
builder,
132+
use_tree,
133+
use_item,
134+
target_module,
135+
current_module,
136+
true,
137+
)
69138
},
70139
)
71140
}
@@ -77,13 +146,27 @@ fn build_expanded_import(
77146
use_item: Use,
78147
target_module: Expandable,
79148
current_module: Module,
149+
reexport_public_items: bool,
80150
) {
81-
let refs_in_target = find_refs_in_mod(ctx, target_module, current_module);
151+
let (must_be_pub, visible_from) = if !reexport_public_items {
152+
(false, current_module)
153+
} else {
154+
match get_export_visibility_kind(&use_item) {
155+
VisibilityKind::Pub => (true, current_module.krate().root_module()),
156+
VisibilityKind::PubCrate => (false, current_module.krate().root_module()),
157+
_ => (false, current_module),
158+
}
159+
};
160+
161+
let refs_in_target = find_refs_in_mod(ctx, target_module, visible_from, must_be_pub);
82162
let imported_defs = find_imported_defs(ctx, use_item);
83163

164+
let filtered_defs =
165+
if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) };
166+
84167
let use_tree = builder.make_mut(use_tree);
85168

86-
let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
169+
let names_to_import = find_names_to_import(filtered_defs, imported_defs);
87170
let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
88171
let path = make::ext::ident_path(
89172
&n.display(ctx.db(), current_module.krate().edition(ctx.db())).to_string(),
@@ -110,6 +193,21 @@ fn build_expanded_import(
110193
}
111194
}
112195

196+
fn get_export_visibility_kind(use_item: &Use) -> VisibilityKind {
197+
use syntax::ast::HasVisibility as _;
198+
match use_item.visibility() {
199+
Some(vis) => match vis.kind() {
200+
VisibilityKind::PubCrate => VisibilityKind::PubCrate,
201+
VisibilityKind::Pub => VisibilityKind::Pub,
202+
VisibilityKind::PubSelf => VisibilityKind::PubSelf,
203+
// We don't handle pub(in ...) and pub(super) yet
204+
VisibilityKind::In(_) => VisibilityKind::PubSelf,
205+
VisibilityKind::PubSuper => VisibilityKind::PubSelf,
206+
},
207+
None => VisibilityKind::PubSelf,
208+
}
209+
}
210+
113211
enum Expandable {
114212
Module(Module),
115213
Enum(Enum),
@@ -147,14 +245,17 @@ struct Ref {
147245
// could be alias
148246
visible_name: Name,
149247
def: Definition,
248+
is_pub: bool,
150249
}
151250

152251
impl Ref {
153-
fn from_scope_def(name: Name, scope_def: ScopeDef) -> Option<Self> {
252+
fn from_scope_def(ctx: &AssistContext<'_>, name: Name, scope_def: ScopeDef) -> Option<Self> {
154253
match scope_def {
155-
ScopeDef::ModuleDef(def) => {
156-
Some(Ref { visible_name: name, def: Definition::from(def) })
157-
}
254+
ScopeDef::ModuleDef(def) => Some(Ref {
255+
visible_name: name,
256+
def: Definition::from(def),
257+
is_pub: matches!(def.visibility(ctx.db()), hir::Visibility::Public),
258+
}),
158259
_ => None,
159260
}
160261
}
@@ -193,18 +294,30 @@ impl Refs {
193294
}
194295
}
195296

196-
fn find_refs_in_mod(ctx: &AssistContext<'_>, expandable: Expandable, visible_from: Module) -> Refs {
297+
fn find_refs_in_mod(
298+
ctx: &AssistContext<'_>,
299+
expandable: Expandable,
300+
visible_from: Module,
301+
must_be_pub: bool,
302+
) -> Refs {
197303
match expandable {
198304
Expandable::Module(module) => {
199305
let module_scope = module.scope(ctx.db(), Some(visible_from));
200-
let refs =
201-
module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect();
306+
let refs = module_scope
307+
.into_iter()
308+
.filter_map(|(n, d)| Ref::from_scope_def(ctx, n, d))
309+
.filter(|r| !must_be_pub || r.is_pub)
310+
.collect();
202311
Refs(refs)
203312
}
204313
Expandable::Enum(enm) => Refs(
205314
enm.variants(ctx.db())
206315
.into_iter()
207-
.map(|v| Ref { visible_name: v.name(ctx.db()), def: Definition::Variant(v) })
316+
.map(|v| Ref {
317+
visible_name: v.name(ctx.db()),
318+
def: Definition::Variant(v),
319+
is_pub: true,
320+
})
208321
.collect(),
209322
),
210323
}
@@ -276,13 +389,9 @@ fn find_imported_defs(ctx: &AssistContext<'_>, use_item: Use) -> Vec<Definition>
276389
.collect()
277390
}
278391

279-
fn find_names_to_import(
280-
ctx: &AssistContext<'_>,
281-
refs_in_target: Refs,
282-
imported_defs: Vec<Definition>,
283-
) -> Vec<Name> {
284-
let used_refs = refs_in_target.used_refs(ctx).filter_out_by_defs(imported_defs);
285-
used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
392+
fn find_names_to_import(refs_in_target: Refs, imported_defs: Vec<Definition>) -> Vec<Name> {
393+
let final_refs = refs_in_target.filter_out_by_defs(imported_defs);
394+
final_refs.0.iter().map(|r| r.visible_name.clone()).collect()
286395
}
287396

288397
#[cfg(test)]
@@ -1029,4 +1138,83 @@ mod abc {
10291138
}"#,
10301139
)
10311140
}
1141+
1142+
#[test]
1143+
fn expanding_glob_reexport() {
1144+
check_assist(
1145+
expand_glob_reexport,
1146+
r"
1147+
mod foo {
1148+
pub struct Bar;
1149+
pub struct Baz;
1150+
struct Qux;
1151+
1152+
pub fn f() {}
1153+
1154+
pub(crate) fn g() {}
1155+
pub(self) fn h() {}
1156+
}
1157+
1158+
pub use foo::*$0;
1159+
",
1160+
r"
1161+
mod foo {
1162+
pub struct Bar;
1163+
pub struct Baz;
1164+
struct Qux;
1165+
1166+
pub fn f() {}
1167+
1168+
pub(crate) fn g() {}
1169+
pub(self) fn h() {}
1170+
}
1171+
1172+
pub use foo::{Bar, Baz, f};
1173+
",
1174+
)
1175+
}
1176+
1177+
#[test]
1178+
fn expanding_recursive_glob_reexport() {
1179+
check_assist(
1180+
expand_glob_reexport,
1181+
r"
1182+
mod foo {
1183+
pub use bar::*;
1184+
mod bar {
1185+
pub struct Bar;
1186+
pub struct Baz;
1187+
}
1188+
}
1189+
1190+
pub use foo::*$0;
1191+
",
1192+
r"
1193+
mod foo {
1194+
pub use bar::*;
1195+
mod bar {
1196+
pub struct Bar;
1197+
pub struct Baz;
1198+
}
1199+
}
1200+
1201+
pub use foo::{Bar, Baz};
1202+
",
1203+
)
1204+
}
1205+
1206+
#[test]
1207+
fn expanding_reexport_is_not_applicable_for_private_import() {
1208+
check_assist_not_applicable(
1209+
expand_glob_reexport,
1210+
r"
1211+
mod foo {
1212+
pub struct Bar;
1213+
pub struct Baz;
1214+
}
1215+
1216+
use foo::*$0;
1217+
",
1218+
);
1219+
}
10321220
}

src/tools/rust-analyzer/crates/ide-assists/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ mod handlers {
270270
destructure_tuple_binding::destructure_tuple_binding,
271271
destructure_struct_binding::destructure_struct_binding,
272272
expand_glob_import::expand_glob_import,
273+
expand_glob_import::expand_glob_reexport,
273274
explicit_enum_discriminant::explicit_enum_discriminant,
274275
extract_expressions_from_format_string::extract_expressions_from_format_string,
275276
extract_struct_from_enum_variant::extract_struct_from_enum_variant,

0 commit comments

Comments
 (0)