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, 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, token);
96 fn signature_help_for_call(
97 sema: &Semantics<'_, RootDatabase>,
99 ) -> Option<SignatureHelp> {
100 // Find the calling expression and its NameRef
101 let mut node = token.parent()?;
102 let calling_node = loop {
103 if let Some(callable) = ast::CallableExpr::cast(node.clone()) {
106 .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
112 // Stop at multi-line expressions, since the signature of the outer call is not very
113 // helpful inside them.
114 if let Some(expr) = ast::Expr::cast(node.clone()) {
115 if expr.syntax().text().contains_char('\n') {
120 node = node.parent()?;
123 let (callable, active_parameter) = callable_for_node(sema, &calling_node, &token)?;
126 SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter };
129 let mut fn_params = None;
130 match callable.kind() {
131 hir::CallableKind::Function(func) => {
132 res.doc = func.docs(db).map(|it| it.into());
133 format_to!(res.signature, "fn {}", func.name(db));
134 fn_params = Some(match callable.receiver_param(db) {
135 Some(_self) => func.params_without_self(db),
136 None => func.assoc_fn_params(db),
139 hir::CallableKind::TupleStruct(strukt) => {
140 res.doc = strukt.docs(db).map(|it| it.into());
141 format_to!(res.signature, "struct {}", strukt.name(db));
143 hir::CallableKind::TupleEnumVariant(variant) => {
144 res.doc = variant.docs(db).map(|it| it.into());
148 variant.parent_enum(db).name(db),
152 hir::CallableKind::Closure | hir::CallableKind::FnPtr => (),
155 res.signature.push('(');
157 if let Some(self_param) = callable.receiver_param(db) {
158 format_to!(res.signature, "{}", self_param)
160 let mut buf = String::new();
161 for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
163 if let Some(pat) = pat {
165 Either::Left(_self) => format_to!(buf, "self: "),
166 Either::Right(pat) => format_to!(buf, "{}: ", pat),
169 // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is
170 // in the middle of entering call arguments.
171 // In that case, fall back to render definitions of the respective parameters.
172 // This is overly conservative: we do not substitute known type vars
173 // (see FIXME in tests::impl_trait) and falling back on any unknowns.
174 match (ty.contains_unknown(), fn_params.as_deref()) {
175 (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)),
176 _ => format_to!(buf, "{}", ty.display(db)),
178 res.push_call_param(&buf);
181 res.signature.push(')');
183 let mut render = |ret_type: hir::Type| {
184 if !ret_type.is_unit() {
185 format_to!(res.signature, " -> {}", ret_type.display(db));
188 match callable.kind() {
189 hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
190 render(func.ret_type(db))
192 hir::CallableKind::Function(_) | hir::CallableKind::Closure | hir::CallableKind::FnPtr => {
193 render(callable.return_type())
195 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
200 fn signature_help_for_generics(
201 sema: &Semantics<'_, RootDatabase>,
203 ) -> Option<SignatureHelp> {
204 let parent = token.parent()?;
205 let arg_list = parent
207 .filter_map(ast::GenericArgList::cast)
208 .find(|list| list.syntax().text_range().contains(token.text_range().start()))?;
210 let mut active_parameter = arg_list
212 .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
215 let first_arg_is_non_lifetime = arg_list
218 .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_)));
220 let mut generics_def = if let Some(path) =
221 arg_list.syntax().ancestors().find_map(ast::Path::cast)
223 let res = sema.resolve_path(&path)?;
224 let generic_def: hir::GenericDef = match res {
225 hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
226 hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
227 hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
228 hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
229 hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(),
230 hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
231 | hir::PathResolution::Def(hir::ModuleDef::Const(_))
232 | hir::PathResolution::Def(hir::ModuleDef::Macro(_))
233 | hir::PathResolution::Def(hir::ModuleDef::Module(_))
234 | hir::PathResolution::Def(hir::ModuleDef::Static(_)) => return None,
235 hir::PathResolution::BuiltinAttr(_)
236 | hir::PathResolution::ToolModule(_)
237 | hir::PathResolution::Local(_)
238 | hir::PathResolution::TypeParam(_)
239 | hir::PathResolution::ConstParam(_)
240 | hir::PathResolution::SelfType(_) => return None,
244 } else if let Some(method_call) = arg_list.syntax().parent().and_then(ast::MethodCallExpr::cast)
246 // recv.method::<$0>()
247 let method = sema.resolve_method_call(&method_call)?;
253 let mut res = SignatureHelp {
255 signature: String::new(),
257 active_parameter: None,
262 hir::GenericDef::Function(it) => {
263 res.doc = it.docs(db).map(|it| it.into());
264 format_to!(res.signature, "fn {}", it.name(db));
266 hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
267 res.doc = it.docs(db).map(|it| it.into());
268 format_to!(res.signature, "enum {}", it.name(db));
270 hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
271 res.doc = it.docs(db).map(|it| it.into());
272 format_to!(res.signature, "struct {}", it.name(db));
274 hir::GenericDef::Adt(hir::Adt::Union(it)) => {
275 res.doc = it.docs(db).map(|it| it.into());
276 format_to!(res.signature, "union {}", it.name(db));
278 hir::GenericDef::Trait(it) => {
279 res.doc = it.docs(db).map(|it| it.into());
280 format_to!(res.signature, "trait {}", it.name(db));
282 hir::GenericDef::TypeAlias(it) => {
283 res.doc = it.docs(db).map(|it| it.into());
284 format_to!(res.signature, "type {}", it.name(db));
286 hir::GenericDef::Variant(it) => {
287 // In paths, generics of an enum can be specified *after* one of its variants.
289 // We'll use the signature of the enum, but include the docs of the variant.
290 res.doc = it.docs(db).map(|it| it.into());
291 let it = it.parent_enum(db);
292 format_to!(res.signature, "enum {}", it.name(db));
293 generics_def = it.into();
295 // These don't have generic args that can be specified
296 hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
299 let params = generics_def.params(sema.db);
300 let num_lifetime_params =
301 params.iter().take_while(|param| matches!(param, GenericParam::LifetimeParam(_))).count();
302 if first_arg_is_non_lifetime {
303 // Lifetime parameters were omitted.
304 active_parameter += num_lifetime_params;
306 res.active_parameter = Some(active_parameter);
308 res.signature.push('<');
309 let mut buf = String::new();
310 for param in params {
311 if let hir::GenericParam::TypeParam(ty) = param {
312 if ty.is_implicit(db) {
318 format_to!(buf, "{}", param.display(db));
319 res.push_generic_param(&buf);
321 if let hir::GenericDef::Trait(tr) = generics_def {
322 add_assoc_type_bindings(db, &mut res, tr, arg_list);
324 res.signature.push('>');
329 fn add_assoc_type_bindings(
331 res: &mut SignatureHelp,
333 args: ast::GenericArgList,
335 if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {
336 // Assoc type bindings are only valid in type bound position.
340 let present_bindings = args
342 .filter_map(|arg| match arg {
343 ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()),
346 .collect::<BTreeSet<_>>();
348 let mut buf = String::new();
349 for binding in &present_bindings {
351 format_to!(buf, "{} = …", binding);
352 res.push_generic_param(&buf);
355 for item in tr.items_with_supertraits(db) {
356 if let AssocItem::TypeAlias(ty) = item {
357 let name = ty.name(db).to_smol_str();
358 if !present_bindings.contains(&*name) {
360 format_to!(buf, "{} = …", name);
361 res.push_generic_param(&buf);
371 use expect_test::{expect, Expect};
372 use ide_db::base_db::{fixture::ChangeFixture, FilePosition};
375 use crate::RootDatabase;
377 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
378 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
379 let change_fixture = ChangeFixture::parse(ra_fixture);
380 let mut database = RootDatabase::default();
381 database.apply_change(change_fixture.change);
382 let (file_id, range_or_offset) =
383 change_fixture.file_position.expect("expected a marker ($0)");
384 let offset = range_or_offset.expect_offset();
385 (database, FilePosition { file_id, offset })
388 fn check(ra_fixture: &str, expect: Expect) {
389 // Implicitly add `Sized` to avoid noisy `T: ?Sized` in the results.
390 let fixture = format!(
392 #[lang = "sized"] trait Sized {{}}
396 let (db, position) = position(&fixture);
397 let sig_help = crate::signature_help::signature_help(&db, position);
398 let actual = match sig_help {
400 let mut rendered = String::new();
401 if let Some(docs) = &sig_help.doc {
402 format_to!(rendered, "{}\n------\n", docs.as_str());
404 format_to!(rendered, "{}\n", sig_help.signature);
406 for (i, range) in sig_help.parameter_ranges().iter().enumerate() {
407 let is_active = sig_help.active_parameter == Some(i);
409 let start = u32::from(range.start());
410 let gap = start.checked_sub(offset).unwrap_or_else(|| {
411 panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges())
413 rendered.extend(iter::repeat(' ').take(gap as usize));
414 let param_text = &sig_help.signature[*range];
415 let width = param_text.chars().count(); // …
416 let marker = if is_active { '^' } else { '-' };
417 rendered.extend(iter::repeat(marker).take(width));
418 offset += gap + u32::from(range.len());
420 if !sig_help.parameter_ranges().is_empty() {
421 format_to!(rendered, "\n");
425 None => String::new(),
427 expect.assert_eq(&actual);
431 fn test_fn_signature_two_args() {
434 fn foo(x: u32, y: u32) -> u32 {x + y}
435 fn bar() { foo($03, ); }
438 fn foo(x: u32, y: u32) -> u32
444 fn foo(x: u32, y: u32) -> u32 {x + y}
445 fn bar() { foo(3$0, ); }
448 fn foo(x: u32, y: u32) -> u32
454 fn foo(x: u32, y: u32) -> u32 {x + y}
455 fn bar() { foo(3,$0 ); }
458 fn foo(x: u32, y: u32) -> u32
464 fn foo(x: u32, y: u32) -> u32 {x + y}
465 fn bar() { foo(3, $0); }
468 fn foo(x: u32, y: u32) -> u32
475 fn test_fn_signature_two_args_empty() {
478 fn foo(x: u32, y: u32) -> u32 {x + y}
479 fn bar() { foo($0); }
482 fn foo(x: u32, y: u32) -> u32
489 fn test_fn_signature_two_args_first_generics() {
492 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
493 where T: Copy + Display, U: Debug
496 fn bar() { foo($03, ); }
499 fn foo(x: i32, y: U) -> u32
506 fn test_fn_signature_no_params() {
509 fn foo<T>() -> T where T: Copy + Display {}
510 fn bar() { foo($0); }
519 fn test_fn_signature_for_impl() {
523 impl F { pub fn new() { } }
525 let _ : F = F::new($0);
535 fn test_fn_signature_for_method_self() {
539 impl S { pub fn do_it(&self) {} }
553 fn test_fn_signature_for_method_with_arg() {
558 fn foo(&self, x: i32) {}
561 fn main() { S.foo($0); }
564 fn foo(&self, x: i32)
571 fn test_fn_signature_for_generic_method() {
576 fn foo(&self, x: T) {}
579 fn main() { S(1u32).foo($0); }
582 fn foo(&self, x: u32)
589 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
594 fn foo(&self, x: i32) {}
597 fn main() { S::foo($0); }
600 fn foo(self: &S, x: i32)
607 fn test_fn_signature_with_docs_simple() {
612 fn foo(j: u32) -> u32 {
623 fn foo(j: u32) -> u32
630 fn test_fn_signature_with_docs() {
633 /// Adds one to the number given.
640 /// assert_eq!(6, my_crate::add_one(5));
642 pub fn add_one(x: i32) -> i32 {
650 Adds one to the number given.
657 assert_eq!(6, my_crate::add_one(5));
660 fn add_one(x: i32) -> i32
667 fn test_fn_signature_with_docs_impl() {
672 /// Adds one to the number given.
679 /// assert_eq!(6, my_crate::add_one(5));
681 pub fn add_one(x: i32) -> i32 {
692 Adds one to the number given.
699 assert_eq!(6, my_crate::add_one(5));
702 fn add_one(x: i32) -> i32
709 fn test_fn_signature_with_docs_from_actix() {
713 /// Actor execution context type
716 trait WriteHandler<E>
720 /// Method is called when writer finishes.
722 /// By default this method stops actor's `Context`.
723 fn finished(&mut self, ctx: &mut Self::Context) {}
726 fn foo(mut r: impl WriteHandler<()>) {
731 Method is called when writer finishes.
733 By default this method stops actor's `Context`.
735 fn finished(&mut self, ctx: &mut <impl WriteHandler<()> as Actor>::Context)
736 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
742 fn call_info_bad_offset() {
745 fn foo(x: u32, y: u32) -> u32 {x + y}
746 fn bar() { foo $0 (3, ); }
753 fn outside_of_arg_list() {
775 fn test_nested_method_in_lambda() {
779 impl Foo { fn bar(&self, _: u32) { } }
785 std::thread::spawn(move || foo.bar($0));
789 fn bar(&self, _: u32)
796 fn works_for_tuple_structs() {
799 /// A cool tuple struct
815 fn generic_struct() {
831 fn works_for_enum_variants() {
857 fn cant_call_struct_record() {
860 struct S { x: u32, y: i32 }
870 fn cant_call_enum_record() {
891 fn fn_signature_for_call_in_macro() {
894 macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
897 fn bar() { foo($0); }
907 fn call_info_for_lambdas() {
911 fn foo(s: S) -> i32 { 92 }
924 fn call_info_for_fn_ptr() {
927 fn main(f: fn(i32, f64) -> char) {
939 fn call_info_for_unclosed_call() {
942 fn foo(foo: u32, bar: u32) {}
947 fn foo(foo: u32, bar: u32)
951 // check with surrounding space
954 fn foo(foo: u32, bar: u32) {}
959 fn foo(foo: u32, bar: u32)
966 fn test_multiline_argument() {
969 fn callee(a: u8, b: u8) {}
979 fn callee(a: u8, b: u8) {}
986 fn callee(a: u8, b: u8)
992 fn callee(a: u8, b: u8) {}
999 fn callee(a: u8, b: u8)
1006 fn test_generics_simple() {
1029 fn test_generics_on_variant() {
1056 fn test_lots_of_generics() {
1064 fn f<G, H>(g: G, h: impl Tr<G>) where G: Tr<()> {}
1079 fn test_generics_in_trait_ufcs() {
1102 fn test_generics_in_method_call() {
1123 fn test_generic_param_in_method_call() {
1128 fn test<V>(&mut self, val: V) {}
1135 fn test(&mut self, val: V)
1142 fn test_generic_kinds() {
1145 fn callee<'a, const A: u8, T, const C: u8>() {}
1148 callee::<'static, $0
1152 fn callee<'a, const A: u8, T, const C: u8>
1153 -- ^^^^^^^^^^^ - -----------
1158 fn callee<'a, const A: u8, T, const C: u8>() {}
1161 callee::<NON_LIFETIME$0
1165 fn callee<'a, const A: u8, T, const C: u8>
1166 -- ^^^^^^^^^^^ - -----------
1172 fn test_trait_assoc_types() {
1175 trait Trait<'a, T> {
1178 fn f() -> impl Trait<(), $0
1181 trait Trait<'a, T, Assoc = …>
1190 fn f() -> impl Iterator<$0
1193 trait Iterator<Item = …>
1202 fn f() -> impl Iterator<Item = $0
1205 trait Iterator<Item = …>
1215 fn f() -> impl Tr<$0
1218 trait Tr<A = …, B = …>
1228 fn f() -> impl Tr<B$0
1231 trait Tr<A = …, B = …>
1241 fn f() -> impl Tr<B = $0
1244 trait Tr<B = …, A = …>
1254 fn f() -> impl Tr<B = (), $0
1257 trait Tr<B = …, A = …>
1264 fn test_supertrait_assoc() {
1270 trait Sub: Super + Super {
1273 fn f() -> impl Sub<$0
1276 trait Sub<SubTy = …, SuperTy = …>
1277 ^^^^^^^^^ -----------
1283 fn no_assoc_types_outside_type_bounds() {
1301 // FIXME: Substitute type vars in impl trait (`U` -> `i8`)
1306 fn foo<U>(x: Wrap<impl Trait<U>>) {}
1312 fn foo(x: Wrap<impl Trait<U>>)
1313 ^^^^^^^^^^^^^^^^^^^^^^
1319 fn fully_qualified_syntax() {
1323 trait A { fn foo(&self, other: Self); }
1324 A::foo(&self$0, other);
1328 fn foo(self: &Self, other: Self)
1329 ^^^^^^^^^^^ -----------