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(_)
241 | hir::PathResolution::DeriveHelper(_) => return None,
245 } else if let Some(method_call) = arg_list.syntax().parent().and_then(ast::MethodCallExpr::cast)
247 // recv.method::<$0>()
248 let method = sema.resolve_method_call(&method_call)?;
254 let mut res = SignatureHelp {
256 signature: String::new(),
258 active_parameter: None,
263 hir::GenericDef::Function(it) => {
264 res.doc = it.docs(db).map(|it| it.into());
265 format_to!(res.signature, "fn {}", it.name(db));
267 hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
268 res.doc = it.docs(db).map(|it| it.into());
269 format_to!(res.signature, "enum {}", it.name(db));
271 hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
272 res.doc = it.docs(db).map(|it| it.into());
273 format_to!(res.signature, "struct {}", it.name(db));
275 hir::GenericDef::Adt(hir::Adt::Union(it)) => {
276 res.doc = it.docs(db).map(|it| it.into());
277 format_to!(res.signature, "union {}", it.name(db));
279 hir::GenericDef::Trait(it) => {
280 res.doc = it.docs(db).map(|it| it.into());
281 format_to!(res.signature, "trait {}", it.name(db));
283 hir::GenericDef::TypeAlias(it) => {
284 res.doc = it.docs(db).map(|it| it.into());
285 format_to!(res.signature, "type {}", it.name(db));
287 hir::GenericDef::Variant(it) => {
288 // In paths, generics of an enum can be specified *after* one of its variants.
290 // We'll use the signature of the enum, but include the docs of the variant.
291 res.doc = it.docs(db).map(|it| it.into());
292 let it = it.parent_enum(db);
293 format_to!(res.signature, "enum {}", it.name(db));
294 generics_def = it.into();
296 // These don't have generic args that can be specified
297 hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
300 let params = generics_def.params(sema.db);
301 let num_lifetime_params =
302 params.iter().take_while(|param| matches!(param, GenericParam::LifetimeParam(_))).count();
303 if first_arg_is_non_lifetime {
304 // Lifetime parameters were omitted.
305 active_parameter += num_lifetime_params;
307 res.active_parameter = Some(active_parameter);
309 res.signature.push('<');
310 let mut buf = String::new();
311 for param in params {
312 if let hir::GenericParam::TypeParam(ty) = param {
313 if ty.is_implicit(db) {
319 format_to!(buf, "{}", param.display(db));
320 res.push_generic_param(&buf);
322 if let hir::GenericDef::Trait(tr) = generics_def {
323 add_assoc_type_bindings(db, &mut res, tr, arg_list);
325 res.signature.push('>');
330 fn add_assoc_type_bindings(
332 res: &mut SignatureHelp,
334 args: ast::GenericArgList,
336 if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {
337 // Assoc type bindings are only valid in type bound position.
341 let present_bindings = args
343 .filter_map(|arg| match arg {
344 ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()),
347 .collect::<BTreeSet<_>>();
349 let mut buf = String::new();
350 for binding in &present_bindings {
352 format_to!(buf, "{} = …", binding);
353 res.push_generic_param(&buf);
356 for item in tr.items_with_supertraits(db) {
357 if let AssocItem::TypeAlias(ty) = item {
358 let name = ty.name(db).to_smol_str();
359 if !present_bindings.contains(&*name) {
361 format_to!(buf, "{} = …", name);
362 res.push_generic_param(&buf);
372 use expect_test::{expect, Expect};
373 use ide_db::base_db::{fixture::ChangeFixture, FilePosition};
376 use crate::RootDatabase;
378 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
379 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
380 let change_fixture = ChangeFixture::parse(ra_fixture);
381 let mut database = RootDatabase::default();
382 database.apply_change(change_fixture.change);
383 let (file_id, range_or_offset) =
384 change_fixture.file_position.expect("expected a marker ($0)");
385 let offset = range_or_offset.expect_offset();
386 (database, FilePosition { file_id, offset })
389 fn check(ra_fixture: &str, expect: Expect) {
390 // Implicitly add `Sized` to avoid noisy `T: ?Sized` in the results.
391 let fixture = format!(
393 #[lang = "sized"] trait Sized {{}}
397 let (db, position) = position(&fixture);
398 let sig_help = crate::signature_help::signature_help(&db, position);
399 let actual = match sig_help {
401 let mut rendered = String::new();
402 if let Some(docs) = &sig_help.doc {
403 format_to!(rendered, "{}\n------\n", docs.as_str());
405 format_to!(rendered, "{}\n", sig_help.signature);
407 for (i, range) in sig_help.parameter_ranges().iter().enumerate() {
408 let is_active = sig_help.active_parameter == Some(i);
410 let start = u32::from(range.start());
411 let gap = start.checked_sub(offset).unwrap_or_else(|| {
412 panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges())
414 rendered.extend(iter::repeat(' ').take(gap as usize));
415 let param_text = &sig_help.signature[*range];
416 let width = param_text.chars().count(); // …
417 let marker = if is_active { '^' } else { '-' };
418 rendered.extend(iter::repeat(marker).take(width));
419 offset += gap + u32::from(range.len());
421 if !sig_help.parameter_ranges().is_empty() {
422 format_to!(rendered, "\n");
426 None => String::new(),
428 expect.assert_eq(&actual);
432 fn test_fn_signature_two_args() {
435 fn foo(x: u32, y: u32) -> u32 {x + y}
436 fn bar() { foo($03, ); }
439 fn foo(x: u32, y: u32) -> u32
445 fn foo(x: u32, y: u32) -> u32 {x + y}
446 fn bar() { foo(3$0, ); }
449 fn foo(x: u32, y: u32) -> u32
455 fn foo(x: u32, y: u32) -> u32 {x + y}
456 fn bar() { foo(3,$0 ); }
459 fn foo(x: u32, y: u32) -> u32
465 fn foo(x: u32, y: u32) -> u32 {x + y}
466 fn bar() { foo(3, $0); }
469 fn foo(x: u32, y: u32) -> u32
476 fn test_fn_signature_two_args_empty() {
479 fn foo(x: u32, y: u32) -> u32 {x + y}
480 fn bar() { foo($0); }
483 fn foo(x: u32, y: u32) -> u32
490 fn test_fn_signature_two_args_first_generics() {
493 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
494 where T: Copy + Display, U: Debug
497 fn bar() { foo($03, ); }
500 fn foo(x: i32, y: U) -> u32
507 fn test_fn_signature_no_params() {
510 fn foo<T>() -> T where T: Copy + Display {}
511 fn bar() { foo($0); }
520 fn test_fn_signature_for_impl() {
524 impl F { pub fn new() { } }
526 let _ : F = F::new($0);
536 fn test_fn_signature_for_method_self() {
540 impl S { pub fn do_it(&self) {} }
554 fn test_fn_signature_for_method_with_arg() {
559 fn foo(&self, x: i32) {}
562 fn main() { S.foo($0); }
565 fn foo(&self, x: i32)
572 fn test_fn_signature_for_generic_method() {
577 fn foo(&self, x: T) {}
580 fn main() { S(1u32).foo($0); }
583 fn foo(&self, x: u32)
590 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
595 fn foo(&self, x: i32) {}
598 fn main() { S::foo($0); }
601 fn foo(self: &S, x: i32)
608 fn test_fn_signature_with_docs_simple() {
613 fn foo(j: u32) -> u32 {
624 fn foo(j: u32) -> u32
631 fn test_fn_signature_with_docs() {
634 /// Adds one to the number given.
641 /// assert_eq!(6, my_crate::add_one(5));
643 pub fn add_one(x: i32) -> i32 {
651 Adds one to the number given.
658 assert_eq!(6, my_crate::add_one(5));
661 fn add_one(x: i32) -> i32
668 fn test_fn_signature_with_docs_impl() {
673 /// Adds one to the number given.
680 /// assert_eq!(6, my_crate::add_one(5));
682 pub fn add_one(x: i32) -> i32 {
693 Adds one to the number given.
700 assert_eq!(6, my_crate::add_one(5));
703 fn add_one(x: i32) -> i32
710 fn test_fn_signature_with_docs_from_actix() {
714 /// Actor execution context type
717 trait WriteHandler<E>
721 /// Method is called when writer finishes.
723 /// By default this method stops actor's `Context`.
724 fn finished(&mut self, ctx: &mut Self::Context) {}
727 fn foo(mut r: impl WriteHandler<()>) {
732 Method is called when writer finishes.
734 By default this method stops actor's `Context`.
736 fn finished(&mut self, ctx: &mut <impl WriteHandler<()> as Actor>::Context)
737 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
743 fn call_info_bad_offset() {
746 fn foo(x: u32, y: u32) -> u32 {x + y}
747 fn bar() { foo $0 (3, ); }
754 fn outside_of_arg_list() {
776 fn test_nested_method_in_lambda() {
780 impl Foo { fn bar(&self, _: u32) { } }
786 std::thread::spawn(move || foo.bar($0));
790 fn bar(&self, _: u32)
797 fn works_for_tuple_structs() {
800 /// A cool tuple struct
816 fn generic_struct() {
832 fn works_for_enum_variants() {
858 fn cant_call_struct_record() {
861 struct S { x: u32, y: i32 }
871 fn cant_call_enum_record() {
892 fn fn_signature_for_call_in_macro() {
895 macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
898 fn bar() { foo($0); }
908 fn call_info_for_lambdas() {
912 fn foo(s: S) -> i32 { 92 }
925 fn call_info_for_fn_ptr() {
928 fn main(f: fn(i32, f64) -> char) {
940 fn call_info_for_unclosed_call() {
943 fn foo(foo: u32, bar: u32) {}
948 fn foo(foo: u32, bar: u32)
952 // check with surrounding space
955 fn foo(foo: u32, bar: u32) {}
960 fn foo(foo: u32, bar: u32)
967 fn test_multiline_argument() {
970 fn callee(a: u8, b: u8) {}
980 fn callee(a: u8, b: u8) {}
987 fn callee(a: u8, b: u8)
993 fn callee(a: u8, b: u8) {}
1000 fn callee(a: u8, b: u8)
1007 fn test_generics_simple() {
1030 fn test_generics_on_variant() {
1057 fn test_lots_of_generics() {
1065 fn f<G, H>(g: G, h: impl Tr<G>) where G: Tr<()> {}
1080 fn test_generics_in_trait_ufcs() {
1103 fn test_generics_in_method_call() {
1124 fn test_generic_param_in_method_call() {
1129 fn test<V>(&mut self, val: V) {}
1136 fn test(&mut self, val: V)
1143 fn test_generic_kinds() {
1146 fn callee<'a, const A: u8, T, const C: u8>() {}
1149 callee::<'static, $0
1153 fn callee<'a, const A: u8, T, const C: u8>
1154 -- ^^^^^^^^^^^ - -----------
1159 fn callee<'a, const A: u8, T, const C: u8>() {}
1162 callee::<NON_LIFETIME$0
1166 fn callee<'a, const A: u8, T, const C: u8>
1167 -- ^^^^^^^^^^^ - -----------
1173 fn test_trait_assoc_types() {
1176 trait Trait<'a, T> {
1179 fn f() -> impl Trait<(), $0
1182 trait Trait<'a, T, Assoc = …>
1191 fn f() -> impl Iterator<$0
1194 trait Iterator<Item = …>
1203 fn f() -> impl Iterator<Item = $0
1206 trait Iterator<Item = …>
1216 fn f() -> impl Tr<$0
1219 trait Tr<A = …, B = …>
1229 fn f() -> impl Tr<B$0
1232 trait Tr<A = …, B = …>
1242 fn f() -> impl Tr<B = $0
1245 trait Tr<B = …, A = …>
1255 fn f() -> impl Tr<B = (), $0
1258 trait Tr<B = …, A = …>
1265 fn test_supertrait_assoc() {
1271 trait Sub: Super + Super {
1274 fn f() -> impl Sub<$0
1277 trait Sub<SubTy = …, SuperTy = …>
1278 ^^^^^^^^^ -----------
1284 fn no_assoc_types_outside_type_bounds() {
1302 // FIXME: Substitute type vars in impl trait (`U` -> `i8`)
1307 fn foo<U>(x: Wrap<impl Trait<U>>) {}
1313 fn foo(x: Wrap<impl Trait<U>>)
1314 ^^^^^^^^^^^^^^^^^^^^^^
1320 fn fully_qualified_syntax() {
1324 trait A { fn foo(&self, other: Self); }
1325 A::foo(&self$0, other);
1329 fn foo(self: &Self, other: Self)
1330 ^^^^^^^^^^^ -----------