Skip to content

Commit 3b857e1

Browse files
committed
Auto merge of #14238 - lowr:feat/allow-generate-fn-across-local-crates, r=Veykril
feat: allow `generate_function` to generate in different local crate Closes #14224 This PR allows `generate_function` assist to generate in crates other than the current one. I took a step further from the original request and made it allow to generate in any local crates since it looked reasonable and IDE layer doesn't really know about packages. (actually we have been checking which crate we're generating in only for methods and not for freestanding functions, so we were providing the assist for `std::foo$0()`; it's both feature and fix in a sense) The first commit is a drive-by fix unrelated to the feature.
2 parents 9b441b9 + bda2af7 commit 3b857e1

File tree

8 files changed

+143
-34
lines changed

8 files changed

+143
-34
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-assists/src/handlers/generate_new.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use ide_db::{
2-
imports::import_assets::item_for_path_search, use_trivial_contructor::use_trivial_constructor,
2+
imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
33
};
44
use itertools::Itertools;
55
use stdx::format_to;

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+
}

crates/ide-db/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub mod source_change;
2222
pub mod symbol_index;
2323
pub mod traits;
2424
pub mod ty_filter;
25-
pub mod use_trivial_contructor;
25+
pub mod use_trivial_constructor;
2626

2727
pub mod imports {
2828
pub mod import_assets;

crates/ide-diagnostics/src/handlers/missing_fields.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use hir::{
55
};
66
use ide_db::{
77
assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search,
8-
source_change::SourceChange, use_trivial_contructor::use_trivial_constructor, FxHashMap,
8+
source_change::SourceChange, use_trivial_constructor::use_trivial_constructor, FxHashMap,
99
};
1010
use stdx::format_to;
1111
use syntax::{

0 commit comments

Comments
 (0)