aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/call_info.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/call_info.rs')
-rw-r--r--crates/ra_ide_api/src/call_info.rs142
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)]
78enum FnCallNode { 85enum 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
83impl FnCallNode { 91impl 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
117impl CallInfo { 130impl 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
503struct TS(u32, i32);
504fn 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#"
519struct TS { x: u32, y: i32 }
520fn main() {
521 let s = TS(<|>);
522}"#,
523 );
524 }
525
526 #[test]
527 fn works_for_enum_variants() {
528 let info = call_info(
529 r#"
530enum E {
531 /// A Variant
532 A(i32),
533 /// Another
534 B,
535 /// And C
536 C { a: i32, b: i32 }
537}
538
539fn 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#"
555enum E {
556 /// A Variant
557 A(i32),
558 /// Another
559 B,
560 /// And C
561 C { a: i32, b: i32 }
562}
563
564fn 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
576macro_rules! foo {
577 () => {}
578}
579
580fn 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}