Skip to content

Commit bda2af7

Browse files
committed
feat: allow generate_function to generate in different local crate
1 parent 943de55 commit bda2af7

File tree

4 files changed

+140
-31
lines changed

4 files changed

+140
-31
lines changed

crates/hir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ impl Module {
489489
}
490490

491491
/// Finds nearest non-block ancestor `Module` (`self` included).
492-
fn nearest_non_block_module(self, db: &dyn HirDatabase) -> Module {
492+
pub fn nearest_non_block_module(self, db: &dyn HirDatabase) -> Module {
493493
let mut id = self.id;
494494
loop {
495495
let def_map = id.def_map(db.upcast());

crates/ide-assists/src/handlers/generate_function.rs

Lines changed: 125 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use ide_db::{
55
base_db::FileId,
66
defs::{Definition, NameRefClass},
77
famous_defs::FamousDefs,
8+
helpers::is_editable_crate,
89
path_transform::PathTransform,
910
FxHashMap, FxHashSet, RootDatabase, SnippetCap,
1011
};
@@ -65,6 +66,13 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
6566
let fn_name = &*name_ref.text();
6667
let TargetInfo { target_module, adt_name, target, file, insert_offset } =
6768
fn_target_info(ctx, path, &call, fn_name)?;
69+
70+
if let Some(m) = target_module {
71+
if !is_editable_crate(m.krate(), ctx.db()) {
72+
return None;
73+
}
74+
}
75+
6876
let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
6977
let text_range = call.syntax().text_range();
7078
let label = format!("Generate {} function", function_builder.fn_name);
@@ -141,12 +149,11 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
141149
let receiver_ty = ctx.sema.type_of_expr(&call.receiver()?)?.original().strip_references();
142150
let adt = receiver_ty.as_adt()?;
143151

144-
let current_module = ctx.sema.scope(call.syntax())?.module();
145152
let target_module = adt.module(ctx.sema.db);
146-
147-
if current_module.krate() != target_module.krate() {
153+
if !is_editable_crate(target_module.krate(), ctx.db()) {
148154
return None;
149155
}
156+
150157
let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
151158
let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?;
152159

@@ -253,7 +260,7 @@ struct FunctionBuilder {
253260
params: ast::ParamList,
254261
ret_type: Option<ast::RetType>,
255262
should_focus_return_type: bool,
256-
needs_pub: bool,
263+
visibility: Visibility,
257264
is_async: bool,
258265
}
259266

@@ -264,12 +271,14 @@ impl FunctionBuilder {
264271
ctx: &AssistContext<'_>,
265272
call: &ast::CallExpr,
266273
fn_name: &str,
267-
target_module: Option<hir::Module>,
274+
target_module: Option<Module>,
268275
target: GeneratedFunctionTarget,
269276
) -> Option<Self> {
270-
let needs_pub = target_module.is_some();
271277
let target_module =
272278
target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?;
279+
280+
let current_module = ctx.sema.scope(call.syntax())?.module();
281+
let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
273282
let fn_name = make::name(fn_name);
274283
let mut necessary_generic_params = FxHashSet::default();
275284
let params = fn_args(
@@ -300,7 +309,7 @@ impl FunctionBuilder {
300309
params,
301310
ret_type,
302311
should_focus_return_type,
303-
needs_pub,
312+
visibility,
304313
is_async,
305314
})
306315
}
@@ -313,8 +322,9 @@ impl FunctionBuilder {
313322
target_module: Module,
314323
target: GeneratedFunctionTarget,
315324
) -> Option<Self> {
316-
let needs_pub =
317-
!module_is_descendant(&ctx.sema.scope(call.syntax())?.module(), &target_module, ctx);
325+
let current_module = ctx.sema.scope(call.syntax())?.module();
326+
let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
327+
318328
let fn_name = make::name(&name.text());
319329
let mut necessary_generic_params = FxHashSet::default();
320330
necessary_generic_params.extend(receiver_ty.generic_params(ctx.db()));
@@ -346,15 +356,19 @@ impl FunctionBuilder {
346356
params,
347357
ret_type,
348358
should_focus_return_type,
349-
needs_pub,
359+
visibility,
350360
is_async,
351361
})
352362
}
353363

354364
fn render(self, is_method: bool) -> FunctionTemplate {
355365
let placeholder_expr = make::ext::expr_todo();
356366
let fn_body = make::block_expr(vec![], Some(placeholder_expr));
357-
let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
367+
let visibility = match self.visibility {
368+
Visibility::None => None,
369+
Visibility::Crate => Some(make::visibility_pub_crate()),
370+
Visibility::Pub => Some(make::visibility_pub()),
371+
};
358372
let mut fn_def = make::fn_(
359373
visibility,
360374
self.fn_name,
@@ -527,7 +541,7 @@ impl GeneratedFunctionTarget {
527541
/// Computes parameter list for the generated function.
528542
fn fn_args(
529543
ctx: &AssistContext<'_>,
530-
target_module: hir::Module,
544+
target_module: Module,
531545
call: ast::CallableExpr,
532546
necessary_generic_params: &mut FxHashSet<hir::GenericParam>,
533547
) -> Option<ast::ParamList> {
@@ -957,13 +971,13 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri
957971

958972
fn fn_arg_type(
959973
ctx: &AssistContext<'_>,
960-
target_module: hir::Module,
974+
target_module: Module,
961975
fn_arg: &ast::Expr,
962976
generic_params: &mut FxHashSet<hir::GenericParam>,
963977
) -> String {
964978
fn maybe_displayed_type(
965979
ctx: &AssistContext<'_>,
966-
target_module: hir::Module,
980+
target_module: Module,
967981
fn_arg: &ast::Expr,
968982
generic_params: &mut FxHashSet<hir::GenericParam>,
969983
) -> Option<String> {
@@ -1048,16 +1062,29 @@ fn next_space_for_fn_in_impl(impl_: &ast::Impl) -> Option<GeneratedFunctionTarge
10481062
}
10491063
}
10501064

1051-
fn module_is_descendant(module: &hir::Module, ans: &hir::Module, ctx: &AssistContext<'_>) -> bool {
1052-
if module == ans {
1053-
return true;
1054-
}
1055-
for c in ans.children(ctx.sema.db) {
1056-
if module_is_descendant(module, &c, ctx) {
1057-
return true;
1058-
}
1065+
#[derive(Clone, Copy)]
1066+
enum Visibility {
1067+
None,
1068+
Crate,
1069+
Pub,
1070+
}
1071+
1072+
fn calculate_necessary_visibility(
1073+
current_module: Module,
1074+
target_module: Module,
1075+
ctx: &AssistContext<'_>,
1076+
) -> Visibility {
1077+
let db = ctx.db();
1078+
let current_module = current_module.nearest_non_block_module(db);
1079+
let target_module = target_module.nearest_non_block_module(db);
1080+
1081+
if target_module.krate() != current_module.krate() {
1082+
Visibility::Pub
1083+
} else if current_module.path_to_root(db).contains(&target_module) {
1084+
Visibility::None
1085+
} else {
1086+
Visibility::Crate
10591087
}
1060-
false
10611088
}
10621089

10631090
// This is never intended to be used as a generic graph strucuture. If there's ever another need of
@@ -2656,4 +2683,79 @@ fn main() {
26562683
",
26572684
)
26582685
}
2686+
2687+
#[test]
2688+
fn applicable_in_different_local_crate() {
2689+
check_assist(
2690+
generate_function,
2691+
r"
2692+
//- /lib.rs crate:lib new_source_root:local
2693+
fn dummy() {}
2694+
//- /main.rs crate:main deps:lib new_source_root:local
2695+
fn main() {
2696+
lib::foo$0();
2697+
}
2698+
",
2699+
r"
2700+
fn dummy() {}
2701+
2702+
pub fn foo() ${0:-> _} {
2703+
todo!()
2704+
}
2705+
",
2706+
);
2707+
}
2708+
2709+
#[test]
2710+
fn applicable_in_different_local_crate_method() {
2711+
check_assist(
2712+
generate_function,
2713+
r"
2714+
//- /lib.rs crate:lib new_source_root:local
2715+
pub struct S;
2716+
//- /main.rs crate:main deps:lib new_source_root:local
2717+
fn main() {
2718+
lib::S.foo$0();
2719+
}
2720+
",
2721+
r"
2722+
pub struct S;
2723+
impl S {
2724+
pub fn foo(&self) ${0:-> _} {
2725+
todo!()
2726+
}
2727+
}
2728+
",
2729+
);
2730+
}
2731+
2732+
#[test]
2733+
fn not_applicable_in_different_library_crate() {
2734+
check_assist_not_applicable(
2735+
generate_function,
2736+
r"
2737+
//- /lib.rs crate:lib new_source_root:library
2738+
fn dummy() {}
2739+
//- /main.rs crate:main deps:lib new_source_root:local
2740+
fn main() {
2741+
lib::foo$0();
2742+
}
2743+
",
2744+
);
2745+
}
2746+
2747+
#[test]
2748+
fn not_applicable_in_different_library_crate_method() {
2749+
check_assist_not_applicable(
2750+
generate_function,
2751+
r"
2752+
//- /lib.rs crate:lib new_source_root:library
2753+
pub struct S;
2754+
//- /main.rs crate:main deps:lib new_source_root:local
2755+
fn main() {
2756+
lib::S.foo$0();
2757+
}
2758+
",
2759+
);
2760+
}
26592761
}

crates/ide-completion/src/context.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ mod tests;
66

77
use std::iter;
88

9-
use base_db::SourceDatabaseExt;
109
use hir::{
1110
HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
1211
};
1312
use ide_db::{
1413
base_db::{FilePosition, SourceDatabase},
1514
famous_defs::FamousDefs,
15+
helpers::is_editable_crate,
1616
FxHashMap, FxHashSet, RootDatabase,
1717
};
1818
use syntax::{
@@ -525,10 +525,11 @@ impl<'a> CompletionContext<'a> {
525525
return Visible::No;
526526
}
527527
// If the definition location is editable, also show private items
528-
let root_file = defining_crate.root_file(self.db);
529-
let source_root_id = self.db.file_source_root(root_file);
530-
let is_editable = !self.db.source_root(source_root_id).is_library;
531-
return if is_editable { Visible::Editable } else { Visible::No };
528+
return if is_editable_crate(defining_crate, self.db) {
529+
Visible::Editable
530+
} else {
531+
Visible::No
532+
};
532533
}
533534

534535
if self.is_doc_hidden(attrs, defining_crate) {

crates/ide-db/src/helpers.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
33
use std::collections::VecDeque;
44

5-
use base_db::FileId;
6-
use hir::{ItemInNs, ModuleDef, Name, Semantics};
5+
use base_db::{FileId, SourceDatabaseExt};
6+
use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
77
use syntax::{
88
ast::{self, make},
99
AstToken, SyntaxKind, SyntaxToken, TokenAtOffset,
@@ -103,3 +103,9 @@ pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool {
103103
false
104104
}
105105
}
106+
107+
pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool {
108+
let root_file = krate.root_file(db);
109+
let source_root_id = db.file_source_root(root_file);
110+
!db.source_root(source_root_id).is_library
111+
}

0 commit comments

Comments
 (0)