1 //! This module provides primitives for showing type and function parameter information when editing
2 //! a call or use-site.
4 use std::collections::BTreeSet;
7 use hir::{AssocItem, GenericParam, HasAttrs, HirDisplay, Semantics, Trait};
8 use ide_db::{active_parameter::callable_for_node, base_db::FilePosition};
12 ast::{self, HasArgList},
13 match_ast, AstNode, Direction, SyntaxToken, TextRange, TextSize,
16 use crate::RootDatabase;
18 /// Contains information about an item signature as seen from a use site.
20 /// This includes the "active parameter", which is the parameter whose value is currently being
23 pub struct SignatureHelp {
24 pub doc: Option<String>,
25 pub signature: String,
26 pub active_parameter: Option<usize>,
27 parameters: Vec<TextRange>,
31 pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
32 self.parameters.iter().map(move |&it| &self.signature[it])
35 pub fn parameter_ranges(&self) -> &[TextRange] {
39 fn push_call_param(&mut self, param: &str) {
40 self.push_param('(', param);
43 fn push_generic_param(&mut self, param: &str) {
44 self.push_param('<', param);
47 fn push_param(&mut self, opening_delim: char, param: &str) {
48 if !self.signature.ends_with(opening_delim) {
49 self.signature.push_str(", ");
51 let start = TextSize::of(&self.signature);
52 self.signature.push_str(param);
53 let end = TextSize::of(&self.signature);
54 self.parameters.push(TextRange::new(start, end))
58 /// Computes parameter information for the given position.
59 pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Option<SignatureHelp> {
60 let sema = Semantics::new(db);
61 let file = sema.parse(position.file_id);
62 let file = file.syntax();
64 .token_at_offset(position.offset)
66 // if the cursor is sandwiched between two space tokens and the call is unclosed
67 // this prevents us from leaving the CallExpression
68 .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
69 let token = sema.descend_into_macros_single(token);
71 for node in token.parent_ancestors() {
74 ast::ArgList(arg_list) => {
75 let cursor_outside = arg_list.r_paren_token().as_ref() == Some(&token);
79 return signature_help_for_call(&sema, arg_list, token);
81 ast::GenericArgList(garg_list) => {
82 let cursor_outside = garg_list.r_angle_token().as_ref() == Some(&token);
86 return signature_help_for_generics(&sema, garg_list, token);
92 // Stop at multi-line expressions, since the signature of the outer call is not very
93 // helpful inside them.
94 if let Some(expr) = ast::Expr::cast(node.clone()) {
95 if expr.syntax().text().contains_char('\n') {
104 fn signature_help_for_call(
105 sema: &Semantics<'_, RootDatabase>,
106 arg_list: ast::ArgList,
108 ) -> Option<SignatureHelp> {
109 // Find the calling expression and its NameRef
110 let mut node = arg_list.syntax().parent()?;
111 let calling_node = loop {
112 if let Some(callable) = ast::CallableExpr::cast(node.clone()) {
115 .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
121 node = node.parent()?;
124 let (callable, active_parameter) = callable_for_node(sema, &calling_node, &token)?;
127 SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter };
130 let mut fn_params = None;
131 match callable.kind() {
132 hir::CallableKind::Function(func) => {
133 res.doc = func.docs(db).map(|it| it.into());
134 format_to!(res.signature, "fn {}", func.name(db));
135 fn_params = Some(match callable.receiver_param(db) {
136 Some(_self) => func.params_without_self(db),
137 None => func.assoc_fn_params(db),
140 hir::CallableKind::TupleStruct(strukt) => {
141 res.doc = strukt.docs(db).map(|it| it.into());
142 format_to!(res.signature, "struct {}", strukt.name(db));
144 hir::CallableKind::TupleEnumVariant(variant) => {
145 res.doc = variant.docs(db).map(|it| it.into());
149 variant.parent_enum(db).name(db),
153 hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
156 res.signature.push('(');
158 if let Some(self_param) = callable.receiver_param(db) {
159 format_to!(res.signature, "{}", self_param)
161 let mut buf = String::new();
162 for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
164 if let Some(pat) = pat {
166 Either::Left(_self) => format_to!(buf, "self: "),
167 Either::Right(pat) => format_to!(buf, "{}: ", pat),
170 // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is
171 // in the middle of entering call arguments.
172 // In that case, fall back to render definitions of the respective parameters.
173 // This is overly conservative: we do not substitute known type vars
174 // (see FIXME in tests::impl_trait) and falling back on any unknowns.
175 match (ty.contains_unknown(), fn_params.as_deref()) {
176 (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)),
177 _ => format_to!(buf, "{}", ty.display(db)),
179 res.push_call_param(&buf);
182 res.signature.push(')');
184 let mut render = |ret_type: hir::Type| {
185 if !ret_type.is_unit() {
186 format_to!(res.signature, " -> {}", ret_type.display(db));
189 match callable.kind() {
190 hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
191 render(func.ret_type(db))
193 hir::CallableKind::Function(_)
194 | hir::CallableKind::Closure
195 | hir::CallableKind::FnPtr
196 | hir::CallableKind::Other => render(callable.return_type()),
197 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
202 fn signature_help_for_generics(
203 sema: &Semantics<'_, RootDatabase>,
204 garg_list: ast::GenericArgList,
206 ) -> Option<SignatureHelp> {
207 let arg_list = garg_list
210 .filter_map(ast::GenericArgList::cast)
211 .find(|list| list.syntax().text_range().contains(token.text_range().start()))?;
213 let mut active_parameter = arg_list
215 .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
218 let first_arg_is_non_lifetime = arg_list
221 .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_)));
223 let mut generics_def = if let Some(path) =
224 arg_list.syntax().ancestors().find_map(ast::Path::cast)
226 let res = sema.resolve_path(&path)?;
227 let generic_def: hir::GenericDef = match res {
228 hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
229 hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
230 hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
231 hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
232 hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(),
233 hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
234 | hir::PathResolution::Def(hir::ModuleDef::Const(_))
235 | hir::PathResolution::Def(hir::ModuleDef::Macro(_))
236 | hir::PathResolution::Def(hir::ModuleDef::Module(_))
237 | hir::PathResolution::Def(hir::ModuleDef::Static(_)) => return None,
238 hir::PathResolution::BuiltinAttr(_)
239 | hir::PathResolution::ToolModule(_)
240 | hir::PathResolution::Local(_)
241 | hir::PathResolution::TypeParam(_)
242 | hir::PathResolution::ConstParam(_)
243 | hir::PathResolution::SelfType(_)
244 | hir::PathResolution::DeriveHelper(_) => return None,
248 } else if let Some(method_call) = arg_list.syntax().parent().and_then(ast::MethodCallExpr::cast)
250 // recv.method::<$0>()
251 let method = sema.resolve_method_call(&method_call)?;
257 let mut res = SignatureHelp {
259 signature: String::new(),
261 active_parameter: None,
266 hir::GenericDef::Function(it) => {
267 res.doc = it.docs(db).map(|it| it.into());
268 format_to!(res.signature, "fn {}", it.name(db));
270 hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
271 res.doc = it.docs(db).map(|it| it.into());
272 format_to!(res.signature, "enum {}", it.name(db));
274 hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
275 res.doc = it.docs(db).map(|it| it.into());
276 format_to!(res.signature, "struct {}", it.name(db));
278 hir::GenericDef::Adt(hir::Adt::Union(it)) => {
279 res.doc = it.docs(db).map(|it| it.into());
280 format_to!(res.signature, "union {}", it.name(db));
282 hir::GenericDef::Trait(it) => {
283 res.doc = it.docs(db).map(|it| it.into());
284 format_to!(res.signature, "trait {}", it.name(db));
286 hir::GenericDef::TypeAlias(it) => {
287 res.doc = it.docs(db).map(|it| it.into());
288 format_to!(res.signature, "type {}", it.name(db));
290 hir::GenericDef::Variant(it) => {
291 // In paths, generics of an enum can be specified *after* one of its variants.
293 // We'll use the signature of the enum, but include the docs of the variant.
294 res.doc = it.docs(db).map(|it| it.into());
295 let it = it.parent_enum(db);
296 format_to!(res.signature, "enum {}", it.name(db));
297 generics_def = it.into();
299 // These don't have generic args that can be specified
300 hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
303 let params = generics_def.params(sema.db);
304 let num_lifetime_params =
305 params.iter().take_while(|param| matches!(param, GenericParam::LifetimeParam(_))).count();
306 if first_arg_is_non_lifetime {
307 // Lifetime parameters were omitted.
308 active_parameter += num_lifetime_params;
310 res.active_parameter = Some(active_parameter);
312 res.signature.push('<');
313 let mut buf = String::new();
314 for param in params {
315 if let hir::GenericParam::TypeParam(ty) = param {
316 if ty.is_implicit(db) {
322 format_to!(buf, "{}", param.display(db));
323 res.push_generic_param(&buf);
325 if let hir::GenericDef::Trait(tr) = generics_def {
326 add_assoc_type_bindings(db, &mut res, tr, arg_list);
328 res.signature.push('>');
333 fn add_assoc_type_bindings(
335 res: &mut SignatureHelp,
337 args: ast::GenericArgList,
339 if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {
340 // Assoc type bindings are only valid in type bound position.
344 let present_bindings = args
346 .filter_map(|arg| match arg {
347 ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()),
350 .collect::<BTreeSet<_>>();
352 let mut buf = String::new();
353 for binding in &present_bindings {
355 format_to!(buf, "{} = …", binding);
356 res.push_generic_param(&buf);
359 for item in tr.items_with_supertraits(db) {
360 if let AssocItem::TypeAlias(ty) = item {
361 let name = ty.name(db).to_smol_str();
362 if !present_bindings.contains(&*name) {
364 format_to!(buf, "{} = …", name);
365 res.push_generic_param(&buf);
375 use expect_test::{expect, Expect};
376 use ide_db::base_db::{fixture::ChangeFixture, FilePosition};
379 use crate::RootDatabase;
381 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
382 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
383 let change_fixture = ChangeFixture::parse(ra_fixture);
384 let mut database = RootDatabase::default();
385 database.apply_change(change_fixture.change);
386 let (file_id, range_or_offset) =
387 change_fixture.file_position.expect("expected a marker ($0)");
388 let offset = range_or_offset.expect_offset();
389 (database, FilePosition { file_id, offset })
392 fn check(ra_fixture: &str, expect: Expect) {
393 let fixture = format!(
395 //- minicore: sized, fn
399 let (db, position) = position(&fixture);
400 let sig_help = crate::signature_help::signature_help(&db, position);
401 let actual = match sig_help {
403 let mut rendered = String::new();
404 if let Some(docs) = &sig_help.doc {
405 format_to!(rendered, "{}\n------\n", docs.as_str());
407 format_to!(rendered, "{}\n", sig_help.signature);
409 for (i, range) in sig_help.parameter_ranges().iter().enumerate() {
410 let is_active = sig_help.active_parameter == Some(i);
412 let start = u32::from(range.start());
413 let gap = start.checked_sub(offset).unwrap_or_else(|| {
414 panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges())
416 rendered.extend(iter::repeat(' ').take(gap as usize));
417 let param_text = &sig_help.signature[*range];
418 let width = param_text.chars().count(); // …
419 let marker = if is_active { '^' } else { '-' };
420 rendered.extend(iter::repeat(marker).take(width));
421 offset += gap + u32::from(range.len());
423 if !sig_help.parameter_ranges().is_empty() {
424 format_to!(rendered, "\n");
428 None => String::new(),
430 expect.assert_eq(&actual);
434 fn test_fn_signature_two_args() {
437 fn foo(x: u32, y: u32) -> u32 {x + y}
438 fn bar() { foo($03, ); }
441 fn foo(x: u32, y: u32) -> u32
447 fn foo(x: u32, y: u32) -> u32 {x + y}
448 fn bar() { foo(3$0, ); }
451 fn foo(x: u32, y: u32) -> u32
457 fn foo(x: u32, y: u32) -> u32 {x + y}
458 fn bar() { foo(3,$0 ); }
461 fn foo(x: u32, y: u32) -> u32
467 fn foo(x: u32, y: u32) -> u32 {x + y}
468 fn bar() { foo(3, $0); }
471 fn foo(x: u32, y: u32) -> u32
478 fn test_fn_signature_two_args_empty() {
481 fn foo(x: u32, y: u32) -> u32 {x + y}
482 fn bar() { foo($0); }
485 fn foo(x: u32, y: u32) -> u32
492 fn test_fn_signature_two_args_first_generics() {
495 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
496 where T: Copy + Display, U: Debug
499 fn bar() { foo($03, ); }
502 fn foo(x: i32, y: U) -> u32
509 fn test_fn_signature_no_params() {
512 fn foo<T>() -> T where T: Copy + Display {}
513 fn bar() { foo($0); }
522 fn test_fn_signature_for_impl() {
526 impl F { pub fn new() { } }
528 let _ : F = F::new($0);
538 fn test_fn_signature_for_method_self() {
542 impl S { pub fn do_it(&self) {} }
556 fn test_fn_signature_for_method_with_arg() {
561 fn foo(&self, x: i32) {}
564 fn main() { S.foo($0); }
567 fn foo(&self, x: i32)
574 fn test_fn_signature_for_generic_method() {
579 fn foo(&self, x: T) {}
582 fn main() { S(1u32).foo($0); }
585 fn foo(&self, x: u32)
592 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
597 fn foo(&self, x: i32) {}
600 fn main() { S::foo($0); }
603 fn foo(self: &S, x: i32)
610 fn test_fn_signature_with_docs_simple() {
615 fn foo(j: u32) -> u32 {
626 fn foo(j: u32) -> u32
633 fn test_fn_signature_with_docs() {
636 /// Adds one to the number given.
643 /// assert_eq!(6, my_crate::add_one(5));
645 pub fn add_one(x: i32) -> i32 {
653 Adds one to the number given.
660 assert_eq!(6, my_crate::add_one(5));
663 fn add_one(x: i32) -> i32
670 fn test_fn_signature_with_docs_impl() {
675 /// Adds one to the number given.
682 /// assert_eq!(6, my_crate::add_one(5));
684 pub fn add_one(x: i32) -> i32 {
695 Adds one to the number given.
702 assert_eq!(6, my_crate::add_one(5));
705 fn add_one(x: i32) -> i32
712 fn test_fn_signature_with_docs_from_actix() {
716 /// Actor execution context type
719 trait WriteHandler<E>
723 /// Method is called when writer finishes.
725 /// By default this method stops actor's `Context`.
726 fn finished(&mut self, ctx: &mut Self::Context) {}
729 fn foo(mut r: impl WriteHandler<()>) {
734 Method is called when writer finishes.
736 By default this method stops actor's `Context`.
738 fn finished(&mut self, ctx: &mut <impl WriteHandler<()> as Actor>::Context)
739 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
745 fn call_info_bad_offset() {
748 fn foo(x: u32, y: u32) -> u32 {x + y}
749 fn bar() { foo $0 (3, ); }
756 fn outside_of_arg_list() {
777 fn foo(a: u8) -> u8 {a}
778 fn bar(a: u8) -> u8 {a}
793 let _: Vec2<Vec<u8>$0>
804 fn test_nested_method_in_lambda() {
808 impl Foo { fn bar(&self, _: u32) { } }
814 std::thread::spawn(move || foo.bar($0));
818 fn bar(&self, _: u32)
825 fn works_for_tuple_structs() {
828 /// A cool tuple struct
844 fn generic_struct() {
860 fn works_for_enum_variants() {
886 fn cant_call_struct_record() {
889 struct S { x: u32, y: i32 }
899 fn cant_call_enum_record() {
920 fn fn_signature_for_call_in_macro() {
923 macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
926 fn bar() { foo($0); }
936 fn call_info_for_lambdas() {
940 fn foo(s: S) -> i32 { 92 }
953 fn call_info_for_fn_ptr() {
956 fn main(f: fn(i32, f64) -> char) {
968 fn call_info_for_unclosed_call() {
971 fn foo(foo: u32, bar: u32) {}
976 fn foo(foo: u32, bar: u32)
980 // check with surrounding space
983 fn foo(foo: u32, bar: u32) {}
988 fn foo(foo: u32, bar: u32)
995 fn test_multiline_argument() {
998 fn callee(a: u8, b: u8) {}
1008 fn callee(a: u8, b: u8) {}
1015 fn callee(a: u8, b: u8)
1021 fn callee(a: u8, b: u8) {}
1028 fn callee(a: u8, b: u8)
1035 fn test_generics_simple() {
1058 fn test_generics_on_variant() {
1085 fn test_lots_of_generics() {
1093 fn f<G, H>(g: G, h: impl Tr<G>) where G: Tr<()> {}
1108 fn test_generics_in_trait_ufcs() {
1131 fn test_generics_in_method_call() {
1152 fn test_generic_param_in_method_call() {
1157 fn test<V>(&mut self, val: V) {}
1164 fn test(&mut self, val: V)
1171 fn test_generic_kinds() {
1174 fn callee<'a, const A: u8, T, const C: u8>() {}
1177 callee::<'static, $0
1181 fn callee<'a, const A: u8, T, const C: u8>
1182 -- ^^^^^^^^^^^ - -----------
1187 fn callee<'a, const A: u8, T, const C: u8>() {}
1190 callee::<NON_LIFETIME$0
1194 fn callee<'a, const A: u8, T, const C: u8>
1195 -- ^^^^^^^^^^^ - -----------
1201 fn test_trait_assoc_types() {
1204 trait Trait<'a, T> {
1207 fn f() -> impl Trait<(), $0
1210 trait Trait<'a, T, Assoc = …>
1219 fn f() -> impl Iterator<$0
1222 trait Iterator<Item = …>
1231 fn f() -> impl Iterator<Item = $0
1234 trait Iterator<Item = …>
1244 fn f() -> impl Tr<$0
1247 trait Tr<A = …, B = …>
1257 fn f() -> impl Tr<B$0
1260 trait Tr<A = …, B = …>
1270 fn f() -> impl Tr<B = $0
1273 trait Tr<B = …, A = …>
1283 fn f() -> impl Tr<B = (), $0
1286 trait Tr<B = …, A = …>
1293 fn test_supertrait_assoc() {
1299 trait Sub: Super + Super {
1302 fn f() -> impl Sub<$0
1305 trait Sub<SubTy = …, SuperTy = …>
1306 ^^^^^^^^^ -----------
1312 fn no_assoc_types_outside_type_bounds() {
1330 // FIXME: Substitute type vars in impl trait (`U` -> `i8`)
1335 fn foo<U>(x: Wrap<impl Trait<U>>) {}
1341 fn foo(x: Wrap<impl Trait<U>>)
1342 ^^^^^^^^^^^^^^^^^^^^^^
1348 fn fully_qualified_syntax() {
1352 trait A { fn foo(&self, other: Self); }
1353 A::foo(&self$0, other);
1357 fn foo(self: &Self, other: Self)
1358 ^^^^^^^^^^^ -----------
1364 fn help_for_generic_call() {
1367 fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
1378 fn f<T, F: FnOnce(&T, u16) -> &T>(f: F) {
1390 fn regression_13579() {
1399 ) -> impl Fn() -> C {