1 //! This module provides primitives for tracking the information about a call site.
4 use hir::{HasAttrs, HirDisplay, Semantics};
5 use ide_db::{active_parameter::callable_for_token, base_db::FilePosition};
7 use syntax::{algo, AstNode, Direction, TextRange, TextSize};
9 use crate::RootDatabase;
11 /// Contains information about a call site. Specifically the
12 /// `FunctionSignature`and current parameter.
15 pub doc: Option<String>,
16 pub signature: String,
17 pub active_parameter: Option<usize>,
18 parameters: Vec<TextRange>,
22 pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
23 self.parameters.iter().map(move |&it| &self.signature[it])
26 pub fn parameter_ranges(&self) -> &[TextRange] {
30 fn push_param(&mut self, param: &str) {
31 if !self.signature.ends_with('(') {
32 self.signature.push_str(", ");
34 let start = TextSize::of(&self.signature);
35 self.signature.push_str(param);
36 let end = TextSize::of(&self.signature);
37 self.parameters.push(TextRange::new(start, end))
41 /// Computes parameter information for the given call expression.
42 pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
43 let sema = Semantics::new(db);
44 let file = sema.parse(position.file_id);
45 let file = file.syntax();
47 .token_at_offset(position.offset)
49 // if the cursor is sandwiched between two space tokens and the call is unclosed
50 // this prevents us from leaving the CallExpression
51 .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
52 let token = sema.descend_into_macros_single(token);
54 let (callable, active_parameter) = callable_for_token(&sema, token)?;
57 CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
59 match callable.kind() {
60 hir::CallableKind::Function(func) => {
61 res.doc = func.docs(db).map(|it| it.into());
62 format_to!(res.signature, "fn {}", func.name(db));
64 hir::CallableKind::TupleStruct(strukt) => {
65 res.doc = strukt.docs(db).map(|it| it.into());
66 format_to!(res.signature, "struct {}", strukt.name(db));
68 hir::CallableKind::TupleEnumVariant(variant) => {
69 res.doc = variant.docs(db).map(|it| it.into());
73 variant.parent_enum(db).name(db),
77 hir::CallableKind::Closure => (),
80 res.signature.push('(');
82 if let Some(self_param) = callable.receiver_param(db) {
83 format_to!(res.signature, "{}", self_param)
85 let mut buf = String::new();
86 for (pat, ty) in callable.params(db) {
88 if let Some(pat) = pat {
90 Either::Left(_self) => format_to!(buf, "self: "),
91 Either::Right(pat) => format_to!(buf, "{}: ", pat),
94 format_to!(buf, "{}", ty.display(db));
98 res.signature.push(')');
100 match callable.kind() {
101 hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
102 let ret_type = callable.return_type();
103 if !ret_type.is_unit() {
104 format_to!(res.signature, " -> {}", ret_type.display(db));
107 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
114 use expect_test::{expect, Expect};
115 use ide_db::base_db::{fixture::ChangeFixture, FilePosition};
117 use crate::RootDatabase;
119 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
120 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
121 let change_fixture = ChangeFixture::parse(ra_fixture);
122 let mut database = RootDatabase::default();
123 database.apply_change(change_fixture.change);
124 let (file_id, range_or_offset) =
125 change_fixture.file_position.expect("expected a marker ($0)");
126 let offset = range_or_offset.expect_offset();
127 (database, FilePosition { file_id, offset })
130 fn check(ra_fixture: &str, expect: Expect) {
131 let (db, position) = position(ra_fixture);
132 let call_info = crate::call_info::call_info(&db, position);
133 let actual = match call_info {
135 let docs = match &call_info.doc {
136 None => "".to_string(),
137 Some(docs) => format!("{}\n------\n", docs.as_str()),
139 let params = call_info
143 if Some(i) == call_info.active_parameter {
144 format!("<{}>", param)
151 format!("{}{}\n({})\n", docs, call_info.signature, params)
153 None => String::new(),
155 expect.assert_eq(&actual);
159 fn test_fn_signature_two_args() {
162 fn foo(x: u32, y: u32) -> u32 {x + y}
163 fn bar() { foo($03, ); }
166 fn foo(x: u32, y: u32) -> u32
172 fn foo(x: u32, y: u32) -> u32 {x + y}
173 fn bar() { foo(3$0, ); }
176 fn foo(x: u32, y: u32) -> u32
182 fn foo(x: u32, y: u32) -> u32 {x + y}
183 fn bar() { foo(3,$0 ); }
186 fn foo(x: u32, y: u32) -> u32
192 fn foo(x: u32, y: u32) -> u32 {x + y}
193 fn bar() { foo(3, $0); }
196 fn foo(x: u32, y: u32) -> u32
203 fn test_fn_signature_two_args_empty() {
206 fn foo(x: u32, y: u32) -> u32 {x + y}
207 fn bar() { foo($0); }
210 fn foo(x: u32, y: u32) -> u32
217 fn test_fn_signature_two_args_first_generics() {
220 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
221 where T: Copy + Display, U: Debug
224 fn bar() { foo($03, ); }
227 fn foo(x: i32, y: {unknown}) -> u32
228 (<x: i32>, y: {unknown})
234 fn test_fn_signature_no_params() {
237 fn foo<T>() -> T where T: Copy + Display {}
238 fn bar() { foo($0); }
241 fn foo() -> {unknown}
248 fn test_fn_signature_for_impl() {
252 impl F { pub fn new() { } }
254 let _ : F = F::new($0);
265 fn test_fn_signature_for_method_self() {
269 impl S { pub fn do_it(&self) {} }
284 fn test_fn_signature_for_method_with_arg() {
289 fn foo(&self, x: i32) {}
292 fn main() { S.foo($0); }
295 fn foo(&self, x: i32)
302 fn test_fn_signature_for_generic_method() {
307 fn foo(&self, x: T) {}
310 fn main() { S(1u32).foo($0); }
313 fn foo(&self, x: u32)
320 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
325 fn foo(&self, x: i32) {}
328 fn main() { S::foo($0); }
331 fn foo(self: &S, x: i32)
338 fn test_fn_signature_with_docs_simple() {
343 fn foo(j: u32) -> u32 {
354 fn foo(j: u32) -> u32
361 fn test_fn_signature_with_docs() {
364 /// Adds one to the number given.
371 /// assert_eq!(6, my_crate::add_one(5));
373 pub fn add_one(x: i32) -> i32 {
381 Adds one to the number given.
388 assert_eq!(6, my_crate::add_one(5));
391 fn add_one(x: i32) -> i32
398 fn test_fn_signature_with_docs_impl() {
403 /// Adds one to the number given.
410 /// assert_eq!(6, my_crate::add_one(5));
412 pub fn add_one(x: i32) -> i32 {
423 Adds one to the number given.
430 assert_eq!(6, my_crate::add_one(5));
433 fn add_one(x: i32) -> i32
440 fn test_fn_signature_with_docs_from_actix() {
443 struct WriteHandler<E>;
445 impl<E> WriteHandler<E> {
446 /// Method is called when writer emits error.
448 /// If this method returns `ErrorAction::Continue` writer processing
449 /// continues otherwise stream processing stops.
450 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
454 /// Method is called when writer finishes.
456 /// By default this method stops actor's `Context`.
457 fn finished(&mut self, ctx: &mut Self::Context) {
462 pub fn foo(mut r: WriteHandler<()>) {
467 Method is called when writer finishes.
469 By default this method stops actor's `Context`.
471 fn finished(&mut self, ctx: &mut {unknown})
472 (<ctx: &mut {unknown}>)
478 fn call_info_bad_offset() {
481 fn foo(x: u32, y: u32) -> u32 {x + y}
482 fn bar() { foo $0 (3, ); }
489 fn test_nested_method_in_lambda() {
493 impl Foo { fn bar(&self, _: u32) { } }
499 std::thread::spawn(move || foo.bar($0));
503 fn bar(&self, _: u32)
510 fn works_for_tuple_structs() {
513 /// A cool tuple struct
529 fn generic_struct() {
545 fn works_for_enum_variants() {
571 fn cant_call_struct_record() {
574 struct S { x: u32, y: i32 }
584 fn cant_call_enum_record() {
605 fn fn_signature_for_call_in_macro() {
608 macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
611 fn bar() { foo($0); }
622 fn call_info_for_lambdas() {
626 fn foo(s: S) -> i32 { 92 }
639 fn call_info_for_fn_ptr() {
642 fn main(f: fn(i32, f64) -> char) {
654 fn call_info_for_unclosed_call() {
657 fn foo(foo: u32, bar: u32) {}
662 fn foo(foo: u32, bar: u32)
663 (<foo: u32>, bar: u32)
666 // check with surrounding space
669 fn foo(foo: u32, bar: u32) {}
674 fn foo(foo: u32, bar: u32)
675 (<foo: u32>, bar: u32)