]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide/src/call_info.rs
Merge #2018
[rust.git] / crates / ra_ide / src / call_info.rs
1 //! FIXME: write short doc here
2
3 use ra_db::SourceDatabase;
4 use ra_syntax::{
5     algo::ancestors_at_offset,
6     ast::{self, ArgListOwner},
7     match_ast, AstNode, SyntaxNode, TextUnit,
8 };
9 use test_utils::tested_by;
10
11 use crate::{db::RootDatabase, CallInfo, FilePosition, FunctionSignature};
12
13 /// Computes parameter information for the given call expression.
14 pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
15     let parse = db.parse(position.file_id);
16     let syntax = parse.tree().syntax().clone();
17
18     // Find the calling expression and it's NameRef
19     let calling_node = FnCallNode::with_node(&syntax, position.offset)?;
20     let name_ref = calling_node.name_ref()?;
21     let name_ref = hir::InFile::new(position.file_id.into(), name_ref.syntax());
22
23     let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
24     let (mut call_info, has_self) = match &calling_node {
25         FnCallNode::CallExpr(expr) => {
26             //FIXME: Type::as_callable is broken
27             let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
28             match callable_def {
29                 hir::CallableDef::FunctionId(it) => {
30                     let fn_def = it.into();
31                     (CallInfo::with_fn(db, fn_def), fn_def.has_self_param(db))
32                 }
33                 hir::CallableDef::StructId(it) => (CallInfo::with_struct(db, it.into())?, false),
34                 hir::CallableDef::EnumVariantId(it) => {
35                     (CallInfo::with_enum_variant(db, it.into())?, false)
36                 }
37             }
38         }
39         FnCallNode::MethodCallExpr(expr) => {
40             let function = analyzer.resolve_method_call(&expr)?;
41             (CallInfo::with_fn(db, function), function.has_self_param(db))
42         }
43         FnCallNode::MacroCallExpr(expr) => {
44             let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?;
45             (CallInfo::with_macro(db, macro_def)?, false)
46         }
47     };
48
49     // If we have a calling expression let's find which argument we are on
50     let num_params = call_info.parameters().len();
51
52     if num_params == 1 {
53         if !has_self {
54             call_info.active_parameter = Some(0);
55         }
56     } else if num_params > 1 {
57         // Count how many parameters into the call we are.
58         if let Some(arg_list) = calling_node.arg_list() {
59             // Number of arguments specified at the call site
60             let num_args_at_callsite = arg_list.args().count();
61
62             let arg_list_range = arg_list.syntax().text_range();
63             if !arg_list_range.contains_inclusive(position.offset) {
64                 tested_by!(call_info_bad_offset);
65                 return None;
66             }
67
68             let mut param = std::cmp::min(
69                 num_args_at_callsite,
70                 arg_list
71                     .args()
72                     .take_while(|arg| arg.syntax().text_range().end() < position.offset)
73                     .count(),
74             );
75
76             // If we are in a method account for `self`
77             if has_self {
78                 param += 1;
79             }
80
81             call_info.active_parameter = Some(param);
82         }
83     }
84
85     Some(call_info)
86 }
87
88 #[derive(Debug)]
89 enum FnCallNode {
90     CallExpr(ast::CallExpr),
91     MethodCallExpr(ast::MethodCallExpr),
92     MacroCallExpr(ast::MacroCall),
93 }
94
95 impl FnCallNode {
96     fn with_node(syntax: &SyntaxNode, offset: TextUnit) -> Option<FnCallNode> {
97         ancestors_at_offset(syntax, offset).find_map(|node| {
98             match_ast! {
99                 match node {
100                     ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
101                     ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) },
102                     ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) },
103                     _ => { None },
104                 }
105             }
106         })
107     }
108
109     fn name_ref(&self) -> Option<ast::NameRef> {
110         match self {
111             FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
112                 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
113                 _ => return None,
114             }),
115
116             FnCallNode::MethodCallExpr(call_expr) => {
117                 call_expr.syntax().children().filter_map(ast::NameRef::cast).nth(0)
118             }
119
120             FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
121         }
122     }
123
124     fn arg_list(&self) -> Option<ast::ArgList> {
125         match self {
126             FnCallNode::CallExpr(expr) => expr.arg_list(),
127             FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
128             FnCallNode::MacroCallExpr(_) => None,
129         }
130     }
131 }
132
133 impl CallInfo {
134     fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
135         let signature = FunctionSignature::from_hir(db, function);
136
137         CallInfo { signature, active_parameter: None }
138     }
139
140     fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
141         let signature = FunctionSignature::from_struct(db, st)?;
142
143         Some(CallInfo { signature, active_parameter: None })
144     }
145
146     fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
147         let signature = FunctionSignature::from_enum_variant(db, variant)?;
148
149         Some(CallInfo { signature, active_parameter: None })
150     }
151
152     fn with_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
153         let signature = FunctionSignature::from_macro(db, macro_def)?;
154
155         Some(CallInfo { signature, active_parameter: None })
156     }
157
158     fn parameters(&self) -> &[String] {
159         &self.signature.parameters
160     }
161 }
162
163 #[cfg(test)]
164 mod tests {
165     use test_utils::covers;
166
167     use crate::mock_analysis::single_file_with_position;
168
169     use super::*;
170
171     // These are only used when testing
172     impl CallInfo {
173         fn doc(&self) -> Option<hir::Documentation> {
174             self.signature.doc.clone()
175         }
176
177         fn label(&self) -> String {
178             self.signature.to_string()
179         }
180     }
181
182     fn call_info(text: &str) -> CallInfo {
183         let (analysis, position) = single_file_with_position(text);
184         analysis.call_info(position).unwrap().unwrap()
185     }
186
187     #[test]
188     fn test_fn_signature_two_args_firstx() {
189         let info = call_info(
190             r#"fn foo(x: u32, y: u32) -> u32 {x + y}
191 fn bar() { foo(<|>3, ); }"#,
192         );
193
194         assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
195         assert_eq!(info.active_parameter, Some(0));
196     }
197
198     #[test]
199     fn test_fn_signature_two_args_second() {
200         let info = call_info(
201             r#"fn foo(x: u32, y: u32) -> u32 {x + y}
202 fn bar() { foo(3, <|>); }"#,
203         );
204
205         assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
206         assert_eq!(info.active_parameter, Some(1));
207     }
208
209     #[test]
210     fn test_fn_signature_two_args_empty() {
211         let info = call_info(
212             r#"fn foo(x: u32, y: u32) -> u32 {x + y}
213 fn bar() { foo(<|>); }"#,
214         );
215
216         assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
217         assert_eq!(info.active_parameter, Some(0));
218     }
219
220     #[test]
221     fn test_fn_signature_two_args_first_generics() {
222         let info = call_info(
223             r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
224 fn bar() { foo(<|>3, ); }"#,
225         );
226
227         assert_eq!(info.parameters(), ["x: T", "y: U"]);
228         assert_eq!(
229             info.label(),
230             r#"
231 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
232 where T: Copy + Display,
233       U: Debug
234     "#
235             .trim()
236         );
237         assert_eq!(info.active_parameter, Some(0));
238     }
239
240     #[test]
241     fn test_fn_signature_no_params() {
242         let info = call_info(
243             r#"fn foo<T>() -> T where T: Copy + Display {}
244 fn bar() { foo(<|>); }"#,
245         );
246
247         assert!(info.parameters().is_empty());
248         assert_eq!(
249             info.label(),
250             r#"
251 fn foo<T>() -> T
252 where T: Copy + Display
253     "#
254             .trim()
255         );
256         assert!(info.active_parameter.is_none());
257     }
258
259     #[test]
260     fn test_fn_signature_for_impl() {
261         let info = call_info(
262             r#"struct F; impl F { pub fn new() { F{}} }
263 fn bar() {let _ : F = F::new(<|>);}"#,
264         );
265
266         assert!(info.parameters().is_empty());
267         assert_eq!(info.active_parameter, None);
268     }
269
270     #[test]
271     fn test_fn_signature_for_method_self() {
272         let info = call_info(
273             r#"struct F;
274 impl F {
275     pub fn new() -> F{
276         F{}
277     }
278
279     pub fn do_it(&self) {}
280 }
281
282 fn bar() {
283     let f : F = F::new();
284     f.do_it(<|>);
285 }"#,
286         );
287
288         assert_eq!(info.parameters(), ["&self"]);
289         assert_eq!(info.active_parameter, None);
290     }
291
292     #[test]
293     fn test_fn_signature_for_method_with_arg() {
294         let info = call_info(
295             r#"struct F;
296 impl F {
297     pub fn new() -> F{
298         F{}
299     }
300
301     pub fn do_it(&self, x: i32) {}
302 }
303
304 fn bar() {
305     let f : F = F::new();
306     f.do_it(<|>);
307 }"#,
308         );
309
310         assert_eq!(info.parameters(), ["&self", "x: i32"]);
311         assert_eq!(info.active_parameter, Some(1));
312     }
313
314     #[test]
315     fn test_fn_signature_with_docs_simple() {
316         let info = call_info(
317             r#"
318 /// test
319 // non-doc-comment
320 fn foo(j: u32) -> u32 {
321     j
322 }
323
324 fn bar() {
325     let _ = foo(<|>);
326 }
327 "#,
328         );
329
330         assert_eq!(info.parameters(), ["j: u32"]);
331         assert_eq!(info.active_parameter, Some(0));
332         assert_eq!(info.label(), "fn foo(j: u32) -> u32");
333         assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
334     }
335
336     #[test]
337     fn test_fn_signature_with_docs() {
338         let info = call_info(
339             r#"
340 /// Adds one to the number given.
341 ///
342 /// # Examples
343 ///
344 /// ```
345 /// let five = 5;
346 ///
347 /// assert_eq!(6, my_crate::add_one(5));
348 /// ```
349 pub fn add_one(x: i32) -> i32 {
350     x + 1
351 }
352
353 pub fn do() {
354     add_one(<|>
355 }"#,
356         );
357
358         assert_eq!(info.parameters(), ["x: i32"]);
359         assert_eq!(info.active_parameter, Some(0));
360         assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
361         assert_eq!(
362             info.doc().map(|it| it.into()),
363             Some(
364                 r#"Adds one to the number given.
365
366 # Examples
367
368 ```
369 let five = 5;
370
371 assert_eq!(6, my_crate::add_one(5));
372 ```"#
373                     .to_string()
374             )
375         );
376     }
377
378     #[test]
379     fn test_fn_signature_with_docs_impl() {
380         let info = call_info(
381             r#"
382 struct addr;
383 impl addr {
384     /// Adds one to the number given.
385     ///
386     /// # Examples
387     ///
388     /// ```
389     /// let five = 5;
390     ///
391     /// assert_eq!(6, my_crate::add_one(5));
392     /// ```
393     pub fn add_one(x: i32) -> i32 {
394         x + 1
395     }
396 }
397
398 pub fn do_it() {
399     addr {};
400     addr::add_one(<|>);
401 }"#,
402         );
403
404         assert_eq!(info.parameters(), ["x: i32"]);
405         assert_eq!(info.active_parameter, Some(0));
406         assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
407         assert_eq!(
408             info.doc().map(|it| it.into()),
409             Some(
410                 r#"Adds one to the number given.
411
412 # Examples
413
414 ```
415 let five = 5;
416
417 assert_eq!(6, my_crate::add_one(5));
418 ```"#
419                     .to_string()
420             )
421         );
422     }
423
424     #[test]
425     fn test_fn_signature_with_docs_from_actix() {
426         let info = call_info(
427             r#"
428 struct WriteHandler<E>;
429
430 impl<E> WriteHandler<E> {
431     /// Method is called when writer emits error.
432     ///
433     /// If this method returns `ErrorAction::Continue` writer processing
434     /// continues otherwise stream processing stops.
435     fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
436         Running::Stop
437     }
438
439     /// Method is called when writer finishes.
440     ///
441     /// By default this method stops actor's `Context`.
442     fn finished(&mut self, ctx: &mut Self::Context) {
443         ctx.stop()
444     }
445 }
446
447 pub fn foo(mut r: WriteHandler<()>) {
448     r.finished(<|>);
449 }
450
451 "#,
452         );
453
454         assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string());
455         assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]);
456         assert_eq!(info.active_parameter, Some(1));
457         assert_eq!(
458             info.doc().map(|it| it.into()),
459             Some(
460                 r#"Method is called when writer finishes.
461
462 By default this method stops actor's `Context`."#
463                     .to_string()
464             )
465         );
466     }
467
468     #[test]
469     fn call_info_bad_offset() {
470         covers!(call_info_bad_offset);
471         let (analysis, position) = single_file_with_position(
472             r#"fn foo(x: u32, y: u32) -> u32 {x + y}
473                fn bar() { foo <|> (3, ); }"#,
474         );
475         let call_info = analysis.call_info(position).unwrap();
476         assert!(call_info.is_none());
477     }
478
479     #[test]
480     fn test_nested_method_in_lamba() {
481         let info = call_info(
482             r#"struct Foo;
483
484 impl Foo {
485     fn bar(&self, _: u32) { }
486 }
487
488 fn bar(_: u32) { }
489
490 fn main() {
491     let foo = Foo;
492     std::thread::spawn(move || foo.bar(<|>));
493 }"#,
494         );
495
496         assert_eq!(info.parameters(), ["&self", "_: u32"]);
497         assert_eq!(info.active_parameter, Some(1));
498         assert_eq!(info.label(), "fn bar(&self, _: u32)");
499     }
500
501     #[test]
502     fn works_for_tuple_structs() {
503         let info = call_info(
504             r#"
505 /// A cool tuple struct
506 struct TS(u32, i32);
507 fn main() {
508     let s = TS(0, <|>);
509 }"#,
510         );
511
512         assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
513         assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
514         assert_eq!(info.active_parameter, Some(1));
515     }
516
517     #[test]
518     #[should_panic]
519     fn cant_call_named_structs() {
520         let _ = call_info(
521             r#"
522 struct TS { x: u32, y: i32 }
523 fn main() {
524     let s = TS(<|>);
525 }"#,
526         );
527     }
528
529     #[test]
530     fn works_for_enum_variants() {
531         let info = call_info(
532             r#"
533 enum E {
534     /// A Variant
535     A(i32),
536     /// Another
537     B,
538     /// And C
539     C { a: i32, b: i32 }
540 }
541
542 fn main() {
543     let a = E::A(<|>);
544 }
545             "#,
546         );
547
548         assert_eq!(info.label(), "E::A(0: i32)");
549         assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string()));
550         assert_eq!(info.active_parameter, Some(0));
551     }
552
553     #[test]
554     #[should_panic]
555     fn cant_call_enum_records() {
556         let _ = call_info(
557             r#"
558 enum E {
559     /// A Variant
560     A(i32),
561     /// Another
562     B,
563     /// And C
564     C { a: i32, b: i32 }
565 }
566
567 fn main() {
568     let a = E::C(<|>);
569 }
570             "#,
571         );
572     }
573
574     #[test]
575     fn fn_signature_for_macro() {
576         let info = call_info(
577             r#"
578 /// empty macro
579 macro_rules! foo {
580     () => {}
581 }
582
583 fn f() {
584     foo!(<|>);
585 }
586         "#,
587         );
588
589         assert_eq!(info.label(), "foo!()");
590         assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string()));
591     }
592 }