diff options
Diffstat (limited to 'crates/ra_ide_api/src/call_info.rs')
-rw-r--r-- | crates/ra_ide_api/src/call_info.rs | 142 |
1 files changed, 133 insertions, 9 deletions
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index c95133343..175af3fd9 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs | |||
@@ -20,24 +20,30 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal | |||
20 | let name_ref = calling_node.name_ref()?; | 20 | let name_ref = calling_node.name_ref()?; |
21 | 21 | ||
22 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); | 22 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); |
23 | let function = match &calling_node { | 23 | let (mut call_info, has_self) = match &calling_node { |
24 | FnCallNode::CallExpr(expr) => { | 24 | FnCallNode::CallExpr(expr) => { |
25 | //FIXME: apply subst | 25 | //FIXME: apply subst |
26 | let (callable_def, _subst) = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 26 | let (callable_def, _subst) = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; |
27 | match callable_def { | 27 | match callable_def { |
28 | hir::CallableDef::Function(it) => it, | 28 | hir::CallableDef::Function(it) => { |
29 | //FIXME: handle other callables | 29 | (CallInfo::with_fn(db, it), it.data(db).has_self_param()) |
30 | _ => return None, | 30 | } |
31 | hir::CallableDef::Struct(it) => (CallInfo::with_struct(db, it)?, false), | ||
32 | hir::CallableDef::EnumVariant(it) => (CallInfo::with_enum_variant(db, it)?, false), | ||
31 | } | 33 | } |
32 | } | 34 | } |
33 | FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(&expr)?, | 35 | FnCallNode::MethodCallExpr(expr) => { |
36 | let function = analyzer.resolve_method_call(&expr)?; | ||
37 | (CallInfo::with_fn(db, function), function.data(db).has_self_param()) | ||
38 | } | ||
39 | FnCallNode::MacroCallExpr(expr) => { | ||
40 | let macro_def = analyzer.resolve_macro_call(db, &expr)?; | ||
41 | (CallInfo::with_macro(db, macro_def)?, false) | ||
42 | } | ||
34 | }; | 43 | }; |
35 | 44 | ||
36 | let mut call_info = CallInfo::new(db, function); | ||
37 | |||
38 | // If we have a calling expression let's find which argument we are on | 45 | // If we have a calling expression let's find which argument we are on |
39 | let num_params = call_info.parameters().len(); | 46 | let num_params = call_info.parameters().len(); |
40 | let has_self = function.data(db).has_self_param(); | ||
41 | 47 | ||
42 | if num_params == 1 { | 48 | if num_params == 1 { |
43 | if !has_self { | 49 | if !has_self { |
@@ -75,9 +81,11 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal | |||
75 | Some(call_info) | 81 | Some(call_info) |
76 | } | 82 | } |
77 | 83 | ||
84 | #[derive(Debug)] | ||
78 | enum FnCallNode { | 85 | enum FnCallNode { |
79 | CallExpr(ast::CallExpr), | 86 | CallExpr(ast::CallExpr), |
80 | MethodCallExpr(ast::MethodCallExpr), | 87 | MethodCallExpr(ast::MethodCallExpr), |
88 | MacroCallExpr(ast::MacroCall), | ||
81 | } | 89 | } |
82 | 90 | ||
83 | impl FnCallNode { | 91 | impl FnCallNode { |
@@ -87,6 +95,8 @@ impl FnCallNode { | |||
87 | Some(FnCallNode::CallExpr(expr)) | 95 | Some(FnCallNode::CallExpr(expr)) |
88 | } else if let Some(expr) = ast::MethodCallExpr::cast(node.clone()) { | 96 | } else if let Some(expr) = ast::MethodCallExpr::cast(node.clone()) { |
89 | Some(FnCallNode::MethodCallExpr(expr)) | 97 | Some(FnCallNode::MethodCallExpr(expr)) |
98 | } else if let Some(expr) = ast::MacroCall::cast(node.clone()) { | ||
99 | Some(FnCallNode::MacroCallExpr(expr)) | ||
90 | } else { | 100 | } else { |
91 | None | 101 | None |
92 | } | 102 | } |
@@ -103,6 +113,8 @@ impl FnCallNode { | |||
103 | FnCallNode::MethodCallExpr(call_expr) => { | 113 | FnCallNode::MethodCallExpr(call_expr) => { |
104 | call_expr.syntax().children().filter_map(ast::NameRef::cast).nth(0) | 114 | call_expr.syntax().children().filter_map(ast::NameRef::cast).nth(0) |
105 | } | 115 | } |
116 | |||
117 | FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(), | ||
106 | } | 118 | } |
107 | } | 119 | } |
108 | 120 | ||
@@ -110,17 +122,36 @@ impl FnCallNode { | |||
110 | match self { | 122 | match self { |
111 | FnCallNode::CallExpr(expr) => expr.arg_list(), | 123 | FnCallNode::CallExpr(expr) => expr.arg_list(), |
112 | FnCallNode::MethodCallExpr(expr) => expr.arg_list(), | 124 | FnCallNode::MethodCallExpr(expr) => expr.arg_list(), |
125 | FnCallNode::MacroCallExpr(_) => None, | ||
113 | } | 126 | } |
114 | } | 127 | } |
115 | } | 128 | } |
116 | 129 | ||
117 | impl CallInfo { | 130 | impl CallInfo { |
118 | fn new(db: &RootDatabase, function: hir::Function) -> Self { | 131 | fn with_fn(db: &RootDatabase, function: hir::Function) -> Self { |
119 | let signature = FunctionSignature::from_hir(db, function); | 132 | let signature = FunctionSignature::from_hir(db, function); |
120 | 133 | ||
121 | CallInfo { signature, active_parameter: None } | 134 | CallInfo { signature, active_parameter: None } |
122 | } | 135 | } |
123 | 136 | ||
137 | fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> { | ||
138 | let signature = FunctionSignature::from_struct(db, st)?; | ||
139 | |||
140 | Some(CallInfo { signature, active_parameter: None }) | ||
141 | } | ||
142 | |||
143 | fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> { | ||
144 | let signature = FunctionSignature::from_enum_variant(db, variant)?; | ||
145 | |||
146 | Some(CallInfo { signature, active_parameter: None }) | ||
147 | } | ||
148 | |||
149 | fn with_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> { | ||
150 | let signature = FunctionSignature::from_macro(db, macro_def)?; | ||
151 | |||
152 | Some(CallInfo { signature, active_parameter: None }) | ||
153 | } | ||
154 | |||
124 | fn parameters(&self) -> &[String] { | 155 | fn parameters(&self) -> &[String] { |
125 | &self.signature.parameters | 156 | &self.signature.parameters |
126 | } | 157 | } |
@@ -417,6 +448,7 @@ pub fn foo(mut r: WriteHandler<()>) { | |||
417 | "#, | 448 | "#, |
418 | ); | 449 | ); |
419 | 450 | ||
451 | assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string()); | ||
420 | assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]); | 452 | assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]); |
421 | assert_eq!(info.active_parameter, Some(1)); | 453 | assert_eq!(info.active_parameter, Some(1)); |
422 | assert_eq!( | 454 | assert_eq!( |
@@ -462,4 +494,96 @@ fn main() { | |||
462 | assert_eq!(info.active_parameter, Some(1)); | 494 | assert_eq!(info.active_parameter, Some(1)); |
463 | assert_eq!(info.label(), "fn bar(&self, _: u32)"); | 495 | assert_eq!(info.label(), "fn bar(&self, _: u32)"); |
464 | } | 496 | } |
497 | |||
498 | #[test] | ||
499 | fn works_for_tuple_structs() { | ||
500 | let info = call_info( | ||
501 | r#" | ||
502 | /// A cool tuple struct | ||
503 | struct TS(u32, i32); | ||
504 | fn main() { | ||
505 | let s = TS(0, <|>); | ||
506 | }"#, | ||
507 | ); | ||
508 | |||
509 | assert_eq!(info.label(), "struct TS(u32, i32) -> TS"); | ||
510 | assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string())); | ||
511 | assert_eq!(info.active_parameter, Some(1)); | ||
512 | } | ||
513 | |||
514 | #[test] | ||
515 | #[should_panic] | ||
516 | fn cant_call_named_structs() { | ||
517 | let _ = call_info( | ||
518 | r#" | ||
519 | struct TS { x: u32, y: i32 } | ||
520 | fn main() { | ||
521 | let s = TS(<|>); | ||
522 | }"#, | ||
523 | ); | ||
524 | } | ||
525 | |||
526 | #[test] | ||
527 | fn works_for_enum_variants() { | ||
528 | let info = call_info( | ||
529 | r#" | ||
530 | enum E { | ||
531 | /// A Variant | ||
532 | A(i32), | ||
533 | /// Another | ||
534 | B, | ||
535 | /// And C | ||
536 | C { a: i32, b: i32 } | ||
537 | } | ||
538 | |||
539 | fn main() { | ||
540 | let a = E::A(<|>); | ||
541 | } | ||
542 | "#, | ||
543 | ); | ||
544 | |||
545 | assert_eq!(info.label(), "E::A(0: i32)"); | ||
546 | assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string())); | ||
547 | assert_eq!(info.active_parameter, Some(0)); | ||
548 | } | ||
549 | |||
550 | #[test] | ||
551 | #[should_panic] | ||
552 | fn cant_call_enum_records() { | ||
553 | let _ = call_info( | ||
554 | r#" | ||
555 | enum E { | ||
556 | /// A Variant | ||
557 | A(i32), | ||
558 | /// Another | ||
559 | B, | ||
560 | /// And C | ||
561 | C { a: i32, b: i32 } | ||
562 | } | ||
563 | |||
564 | fn main() { | ||
565 | let a = E::C(<|>); | ||
566 | } | ||
567 | "#, | ||
568 | ); | ||
569 | } | ||
570 | |||
571 | #[test] | ||
572 | fn fn_signature_for_macro() { | ||
573 | let info = call_info( | ||
574 | r#" | ||
575 | /// empty macro | ||
576 | macro_rules! foo { | ||
577 | () => {} | ||
578 | } | ||
579 | |||
580 | fn f() { | ||
581 | foo!(<|>); | ||
582 | } | ||
583 | "#, | ||
584 | ); | ||
585 | |||
586 | assert_eq!(info.label(), "foo!()"); | ||
587 | assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string())); | ||
588 | } | ||
465 | } | 589 | } |