Skip to content

Commit 9d3483a

Browse files
bors[bot]adamrk
andauthored
Merge #5846
5846: Add references to fn args during completion r=matklad a=adamrk When completing a function call, if there is an argument taken as a ref or mut ref which matches the name and type of a variable in scope, we will insert a `&` or `&mut` when filling in the function arguments. This addresses #5449. E.g. ```rust fn foo(x: &i32) {} fn main() { let x = 5; foo # completing foo here generates `foo(&x)` now instead of `foo(x)` } ``` Co-authored-by: adamrk <[email protected]>
2 parents 4ddb812 + 5cd2c67 commit 9d3483a

File tree

3 files changed

+161
-8
lines changed

3 files changed

+161
-8
lines changed

crates/hir/src/code_model.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -709,11 +709,23 @@ impl Function {
709709
}
710710

711711
pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> {
712+
let resolver = self.id.resolver(db.upcast());
713+
let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
714+
let environment = TraitEnvironment::lower(db, &resolver);
712715
db.function_data(self.id)
713716
.params
714717
.iter()
715718
.skip(if self.self_param(db).is_some() { 1 } else { 0 })
716-
.map(|_| Param { _ty: () })
719+
.map(|type_ref| {
720+
let ty = Type {
721+
krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate,
722+
ty: InEnvironment {
723+
value: Ty::from_hir_ext(&ctx, type_ref).0,
724+
environment: environment.clone(),
725+
},
726+
};
727+
Param { ty }
728+
})
717729
.collect()
718730
}
719731

@@ -742,15 +754,21 @@ impl From<Mutability> for Access {
742754
}
743755
}
744756

757+
pub struct Param {
758+
ty: Type,
759+
}
760+
761+
impl Param {
762+
pub fn ty(&self) -> &Type {
763+
&self.ty
764+
}
765+
}
766+
745767
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
746768
pub struct SelfParam {
747769
func: FunctionId,
748770
}
749771

750-
pub struct Param {
751-
_ty: (),
752-
}
753-
754772
impl SelfParam {
755773
pub fn access(self, db: &dyn HirDatabase) -> Access {
756774
let func_data = db.function_data(self.func);
@@ -1276,6 +1294,14 @@ impl Type {
12761294
)
12771295
}
12781296

1297+
pub fn remove_ref(&self) -> Option<Type> {
1298+
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(_), .. }) = self.ty.value {
1299+
self.ty.value.substs().map(|substs| self.derived(substs[0].clone()))
1300+
} else {
1301+
None
1302+
}
1303+
}
1304+
12791305
pub fn is_unknown(&self) -> bool {
12801306
matches!(self.ty.value, Ty::Unknown)
12811307
}

crates/ide/src/completion/completion_context.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! FIXME: write short doc here
22
33
use base_db::SourceDatabase;
4-
use hir::{Semantics, SemanticsScope, Type};
4+
use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
55
use ide_db::RootDatabase;
66
use syntax::{
77
algo::{find_covering_element, find_node_at_offset},
@@ -91,6 +91,7 @@ pub(crate) struct CompletionContext<'a> {
9191
pub(super) impl_as_prev_sibling: bool,
9292
pub(super) is_match_arm: bool,
9393
pub(super) has_item_list_or_source_file_parent: bool,
94+
pub(super) locals: Vec<(String, Local)>,
9495
}
9596

9697
impl<'a> CompletionContext<'a> {
@@ -119,6 +120,12 @@ impl<'a> CompletionContext<'a> {
119120
original_file.syntax().token_at_offset(position.offset).left_biased()?;
120121
let token = sema.descend_into_macros(original_token.clone());
121122
let scope = sema.scope_at_offset(&token.parent(), position.offset);
123+
let mut locals = vec![];
124+
scope.process_all_names(&mut |name, scope| {
125+
if let ScopeDef::Local(local) = scope {
126+
locals.push((name.to_string(), local));
127+
}
128+
});
122129
let mut ctx = CompletionContext {
123130
sema,
124131
scope,
@@ -167,6 +174,7 @@ impl<'a> CompletionContext<'a> {
167174
if_is_prev: false,
168175
is_match_arm: false,
169176
has_item_list_or_source_file_parent: false,
177+
locals,
170178
};
171179

172180
let mut original_file = original_file.syntax().clone();

crates/ide/src/completion/presentation.rs

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,17 @@ impl Completions {
191191
func: hir::Function,
192192
local_name: Option<String>,
193193
) {
194+
fn add_arg(arg: &str, ty: &Type, ctx: &CompletionContext) -> String {
195+
if let Some(derefed_ty) = ty.remove_ref() {
196+
for (name, local) in ctx.locals.iter() {
197+
if name == arg && local.ty(ctx.db) == derefed_ty {
198+
return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string()
199+
+ &arg.to_string();
200+
}
201+
}
202+
}
203+
arg.to_string()
204+
};
194205
let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
195206
let ast_node = func.source(ctx.db).value;
196207

@@ -205,12 +216,20 @@ impl Completions {
205216
.set_deprecated(is_deprecated(func, ctx.db))
206217
.detail(function_declaration(&ast_node));
207218

219+
let params_ty = func.params(ctx.db);
208220
let params = ast_node
209221
.param_list()
210222
.into_iter()
211223
.flat_map(|it| it.params())
212-
.flat_map(|it| it.pat())
213-
.map(|pat| pat.to_string().trim_start_matches('_').into())
224+
.zip(params_ty)
225+
.flat_map(|(it, param_ty)| {
226+
if let Some(pat) = it.pat() {
227+
let name = pat.to_string();
228+
let arg = name.trim_start_matches("mut ").trim_start_matches('_');
229+
return Some(add_arg(arg, param_ty.ty(), ctx));
230+
}
231+
None
232+
})
214233
.collect();
215234

216235
builder = builder.add_call_parens(ctx, name, Params::Named(params));
@@ -863,6 +882,106 @@ fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
863882
);
864883
}
865884

885+
#[test]
886+
fn insert_ref_when_matching_local_in_scope() {
887+
check_edit(
888+
"ref_arg",
889+
r#"
890+
struct Foo {}
891+
fn ref_arg(x: &Foo) {}
892+
fn main() {
893+
let x = Foo {};
894+
ref_ar<|>
895+
}
896+
"#,
897+
r#"
898+
struct Foo {}
899+
fn ref_arg(x: &Foo) {}
900+
fn main() {
901+
let x = Foo {};
902+
ref_arg(${1:&x})$0
903+
}
904+
"#,
905+
);
906+
}
907+
908+
#[test]
909+
fn insert_mut_ref_when_matching_local_in_scope() {
910+
check_edit(
911+
"ref_arg",
912+
r#"
913+
struct Foo {}
914+
fn ref_arg(x: &mut Foo) {}
915+
fn main() {
916+
let x = Foo {};
917+
ref_ar<|>
918+
}
919+
"#,
920+
r#"
921+
struct Foo {}
922+
fn ref_arg(x: &mut Foo) {}
923+
fn main() {
924+
let x = Foo {};
925+
ref_arg(${1:&mut x})$0
926+
}
927+
"#,
928+
);
929+
}
930+
931+
#[test]
932+
fn insert_ref_when_matching_local_in_scope_for_method() {
933+
check_edit(
934+
"apply_foo",
935+
r#"
936+
struct Foo {}
937+
struct Bar {}
938+
impl Bar {
939+
fn apply_foo(&self, x: &Foo) {}
940+
}
941+
942+
fn main() {
943+
let x = Foo {};
944+
let y = Bar {};
945+
y.<|>
946+
}
947+
"#,
948+
r#"
949+
struct Foo {}
950+
struct Bar {}
951+
impl Bar {
952+
fn apply_foo(&self, x: &Foo) {}
953+
}
954+
955+
fn main() {
956+
let x = Foo {};
957+
let y = Bar {};
958+
y.apply_foo(${1:&x})$0
959+
}
960+
"#,
961+
);
962+
}
963+
964+
#[test]
965+
fn trim_mut_keyword_in_func_completion() {
966+
check_edit(
967+
"take_mutably",
968+
r#"
969+
fn take_mutably(mut x: &i32) {}
970+
971+
fn main() {
972+
take_m<|>
973+
}
974+
"#,
975+
r#"
976+
fn take_mutably(mut x: &i32) {}
977+
978+
fn main() {
979+
take_mutably(${1:x})$0
980+
}
981+
"#,
982+
);
983+
}
984+
866985
#[test]
867986
fn inserts_parens_for_tuple_enums() {
868987
mark::check!(inserts_parens_for_tuple_enums);

0 commit comments

Comments
 (0)