aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/call_info.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-07-16 12:00:56 +0100
committerAleksey Kladov <[email protected]>2020-07-16 17:03:04 +0100
commitff0312fa32715ce42f134fd9f049c4df5956d042 (patch)
treeb514e34d3486a650fef35e29396a810ee7741cd1 /crates/ra_ide/src/call_info.rs
parent9210fcc076808e53e9bde84be26307fc0dc7d688 (diff)
Semantical call info
Diffstat (limited to 'crates/ra_ide/src/call_info.rs')
-rw-r--r--crates/ra_ide/src/call_info.rs332
1 files changed, 168 insertions, 164 deletions
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 1fe1c21de..a2d23b2ec 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -1,20 +1,41 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use hir::Semantics; 2use hir::{Docs, HirDisplay, Semantics, Type};
3use ra_ide_db::RootDatabase; 3use ra_ide_db::RootDatabase;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner}, 5 ast::{self, ArgListOwner},
6 match_ast, AstNode, SyntaxNode, SyntaxToken, 6 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
7}; 7};
8use stdx::format_to;
8use test_utils::mark; 9use test_utils::mark;
9 10
10use crate::{FilePosition, FunctionSignature}; 11use crate::FilePosition;
11 12
12/// Contains information about a call site. Specifically the 13/// Contains information about a call site. Specifically the
13/// `FunctionSignature`and current parameter. 14/// `FunctionSignature`and current parameter.
14#[derive(Debug)] 15#[derive(Debug)]
15pub struct CallInfo { 16pub struct CallInfo {
16 pub signature: FunctionSignature, 17 pub doc: Option<String>,
18 pub signature: String,
17 pub active_parameter: Option<usize>, 19 pub active_parameter: Option<usize>,
20 parameters: Vec<TextRange>,
21}
22
23impl CallInfo {
24 pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
25 self.parameters.iter().map(move |&it| &self.signature[it])
26 }
27 pub fn parameter_ranges(&self) -> &[TextRange] {
28 &self.parameters
29 }
30 fn push_param(&mut self, param: &str) {
31 if !self.signature.ends_with('(') {
32 self.signature.push_str(", ");
33 }
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))
38 }
18} 39}
19 40
20/// Computes parameter information for the given call expression. 41/// Computes parameter information for the given call expression.
@@ -24,105 +45,127 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
24 let file = file.syntax(); 45 let file = file.syntax();
25 let token = file.token_at_offset(position.offset).next()?; 46 let token = file.token_at_offset(position.offset).next()?;
26 let token = sema.descend_into_macros(token); 47 let token = sema.descend_into_macros(token);
27 call_info_for_token(&sema, token)
28}
29 48
30#[derive(Debug)] 49 let (callable, active_parameter) = call_info_impl(&sema, token)?;
31pub(crate) struct ActiveParameter {
32 /// FIXME: should be `Type` and `Name
33 pub(crate) ty: String,
34 pub(crate) name: String,
35}
36 50
37impl ActiveParameter { 51 let mut res =
38 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> { 52 CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
39 call_info(db, position)?.into_active_parameter() 53
54 match callable.kind() {
55 hir::CallableKind::Function(func) => {
56 res.doc = func.docs(db).map(|it| it.as_str().to_string());
57 format_to!(res.signature, "fn {}", func.name(db));
58 }
59 hir::CallableKind::TupleStruct(strukt) => {
60 res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
61 format_to!(res.signature, "struct {}", strukt.name(db));
62 }
63 hir::CallableKind::TupleEnumVariant(variant) => {
64 res.doc = variant.docs(db).map(|it| it.as_str().to_string());
65 format_to!(
66 res.signature,
67 "enum {}::{}",
68 variant.parent_enum(db).name(db),
69 variant.name(db)
70 );
71 }
40 } 72 }
41 73
42 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> { 74 res.signature.push('(');
43 call_info_for_token(sema, token)?.into_active_parameter() 75 {
76 if let Some(self_param) = callable.receiver_param(db) {
77 format_to!(res.signature, "{}", self_param)
78 }
79 let mut buf = String::new();
80 for (pat, ty) in callable.params(db) {
81 buf.clear();
82 if let Some(pat) = pat {
83 format_to!(buf, "{}: ", pat);
84 }
85 format_to!(buf, "{}", ty.display(db));
86 res.push_param(&buf);
87 }
44 } 88 }
89 res.signature.push(')');
90
91 match callable.kind() {
92 hir::CallableKind::Function(_) => {
93 let ret_type = callable.return_type();
94 if !ret_type.is_unit() {
95 format_to!(res.signature, " -> {}", ret_type.display(db));
96 }
97 }
98 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
99 }
100 Some(res)
45} 101}
46 102
47fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<CallInfo> { 103fn call_info_impl(
104 sema: &Semantics<RootDatabase>,
105 token: SyntaxToken,
106) -> Option<(hir::Callable, Option<usize>)> {
48 // Find the calling expression and it's NameRef 107 // Find the calling expression and it's NameRef
49 let calling_node = FnCallNode::with_node(&token.parent())?; 108 let calling_node = FnCallNode::with_node(&token.parent())?;
50 109
51 let signature = match &calling_node { 110 let callable = match &calling_node {
52 FnCallNode::CallExpr(call) => { 111 FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
53 //FIXME: Type::as_callable is broken 112 FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
54 let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?; 113 };
55 match callable_def { 114 let active_param = if let Some(arg_list) = calling_node.arg_list() {
56 hir::CallableDefId::FunctionId(it) => { 115 // Number of arguments specified at the call site
57 let fn_def = it.into(); 116 let num_args_at_callsite = arg_list.args().count();
58 FunctionSignature::from_hir(sema.db, fn_def) 117
59 } 118 let arg_list_range = arg_list.syntax().text_range();
60 hir::CallableDefId::StructId(it) => { 119 if !arg_list_range.contains_inclusive(token.text_range().start()) {
61 FunctionSignature::from_struct(sema.db, it.into())? 120 mark::hit!(call_info_bad_offset);
62 } 121 return None;
63 hir::CallableDefId::EnumVariantId(it) => {
64 FunctionSignature::from_enum_variant(sema.db, it.into())?
65 }
66 }
67 }
68 FnCallNode::MethodCallExpr(method_call) => {
69 let function = sema.resolve_method_call(&method_call)?;
70 FunctionSignature::from_hir(sema.db, function)
71 }
72 FnCallNode::MacroCallExpr(macro_call) => {
73 let macro_def = sema.resolve_macro_call(&macro_call)?;
74 FunctionSignature::from_macro(sema.db, macro_def)?
75 } 122 }
123 let param = std::cmp::min(
124 num_args_at_callsite,
125 arg_list
126 .args()
127 .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
128 .count(),
129 );
130
131 Some(param)
132 } else {
133 None
76 }; 134 };
135 Some((callable, active_param))
136}
77 137
78 // If we have a calling expression let's find which argument we are on 138#[derive(Debug)]
79 let num_params = signature.parameters.len(); 139pub(crate) struct ActiveParameter {
80 140 pub(crate) ty: Type,
81 let active_parameter = match num_params { 141 pub(crate) name: String,
82 0 => None, 142}
83 1 if signature.has_self_param => None,
84 1 => Some(0),
85 _ => {
86 if let Some(arg_list) = calling_node.arg_list() {
87 // Number of arguments specified at the call site
88 let num_args_at_callsite = arg_list.args().count();
89
90 let arg_list_range = arg_list.syntax().text_range();
91 if !arg_list_range.contains_inclusive(token.text_range().start()) {
92 mark::hit!(call_info_bad_offset);
93 return None;
94 }
95 143
96 let mut param = std::cmp::min( 144impl ActiveParameter {
97 num_args_at_callsite, 145 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
98 arg_list 146 let sema = Semantics::new(db);
99 .args() 147 let file = sema.parse(position.file_id);
100 .take_while(|arg| { 148 let file = file.syntax();
101 arg.syntax().text_range().end() <= token.text_range().start() 149 let token = file.token_at_offset(position.offset).next()?;
102 }) 150 let token = sema.descend_into_macros(token);
103 .count(), 151 Self::at_token(&sema, token)
104 ); 152 }
105
106 // If we are in a method account for `self`
107 if signature.has_self_param {
108 param += 1;
109 }
110 153
111 Some(param) 154 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
112 } else { 155 let (signature, active_parameter) = call_info_impl(&sema, token)?;
113 None
114 }
115 }
116 };
117 156
118 Some(CallInfo { signature, active_parameter }) 157 let idx = active_parameter?;
158 let mut params = signature.params(sema.db);
159 let (pat, ty) = params.swap_remove(idx);
160 let name = pat?.to_string();
161 Some(ActiveParameter { ty, name })
162 }
119} 163}
120 164
121#[derive(Debug)] 165#[derive(Debug)]
122pub(crate) enum FnCallNode { 166pub(crate) enum FnCallNode {
123 CallExpr(ast::CallExpr), 167 CallExpr(ast::CallExpr),
124 MethodCallExpr(ast::MethodCallExpr), 168 MethodCallExpr(ast::MethodCallExpr),
125 MacroCallExpr(ast::MacroCall),
126} 169}
127 170
128impl FnCallNode { 171impl FnCallNode {
@@ -138,7 +181,6 @@ impl FnCallNode {
138 } 181 }
139 Some(FnCallNode::MethodCallExpr(it)) 182 Some(FnCallNode::MethodCallExpr(it))
140 }, 183 },
141 ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
142 _ => None, 184 _ => None,
143 } 185 }
144 } 186 }
@@ -150,7 +192,6 @@ impl FnCallNode {
150 match node { 192 match node {
151 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)), 193 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
152 ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)), 194 ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
153 ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
154 _ => None, 195 _ => None,
155 } 196 }
156 } 197 }
@@ -166,8 +207,6 @@ impl FnCallNode {
166 FnCallNode::MethodCallExpr(call_expr) => { 207 FnCallNode::MethodCallExpr(call_expr) => {
167 call_expr.syntax().children().filter_map(ast::NameRef::cast).next() 208 call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
168 } 209 }
169
170 FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
171 } 210 }
172 } 211 }
173 212
@@ -175,21 +214,10 @@ impl FnCallNode {
175 match self { 214 match self {
176 FnCallNode::CallExpr(expr) => expr.arg_list(), 215 FnCallNode::CallExpr(expr) => expr.arg_list(),
177 FnCallNode::MethodCallExpr(expr) => expr.arg_list(), 216 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
178 FnCallNode::MacroCallExpr(_) => None,
179 } 217 }
180 } 218 }
181} 219}
182 220
183impl CallInfo {
184 fn into_active_parameter(self) -> Option<ActiveParameter> {
185 let idx = self.active_parameter?;
186 let ty = self.signature.parameter_types.get(idx)?.clone();
187 let name = self.signature.parameter_names.get(idx)?.clone();
188 let res = ActiveParameter { ty, name };
189 Some(res)
190 }
191}
192
193#[cfg(test)] 221#[cfg(test)]
194mod tests { 222mod tests {
195 use expect::{expect, Expect}; 223 use expect::{expect, Expect};
@@ -202,20 +230,18 @@ mod tests {
202 let call_info = analysis.call_info(position).unwrap(); 230 let call_info = analysis.call_info(position).unwrap();
203 let actual = match call_info { 231 let actual = match call_info {
204 Some(call_info) => { 232 Some(call_info) => {
205 let docs = match &call_info.signature.doc { 233 let docs = match &call_info.doc {
206 None => "".to_string(), 234 None => "".to_string(),
207 Some(docs) => format!("{}\n------\n", docs.as_str()), 235 Some(docs) => format!("{}\n------\n", docs.as_str()),
208 }; 236 };
209 let params = call_info 237 let params = call_info
210 .signature 238 .parameter_labels()
211 .parameters
212 .iter()
213 .enumerate() 239 .enumerate()
214 .map(|(i, param)| { 240 .map(|(i, param)| {
215 if Some(i) == call_info.active_parameter { 241 if Some(i) == call_info.active_parameter {
216 format!("<{}>", param) 242 format!("<{}>", param)
217 } else { 243 } else {
218 param.clone() 244 param.to_string()
219 } 245 }
220 }) 246 })
221 .collect::<Vec<_>>() 247 .collect::<Vec<_>>()
@@ -296,10 +322,8 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
296fn bar() { foo(<|>3, ); } 322fn bar() { foo(<|>3, ); }
297"#, 323"#,
298 expect![[r#" 324 expect![[r#"
299 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 325 fn foo(x: i32, y: {unknown}) -> u32
300 where T: Copy + Display, 326 (<x: i32>, y: {unknown})
301 U: Debug
302 (<x: T>, y: U)
303 "#]], 327 "#]],
304 ); 328 );
305 } 329 }
@@ -312,8 +336,7 @@ fn foo<T>() -> T where T: Copy + Display {}
312fn bar() { foo(<|>); } 336fn bar() { foo(<|>); }
313"#, 337"#,
314 expect![[r#" 338 expect![[r#"
315 fn foo<T>() -> T 339 fn foo() -> {unknown}
316 where T: Copy + Display
317 () 340 ()
318 "#]], 341 "#]],
319 ); 342 );
@@ -323,11 +346,14 @@ fn bar() { foo(<|>); }
323 fn test_fn_signature_for_impl() { 346 fn test_fn_signature_for_impl() {
324 check( 347 check(
325 r#" 348 r#"
326struct F; impl F { pub fn new() { F{}} } 349struct F;
327fn bar() {let _ : F = F::new(<|>);} 350impl F { pub fn new() { } }
351fn bar() {
352 let _ : F = F::new(<|>);
353}
328"#, 354"#,
329 expect![[r#" 355 expect![[r#"
330 pub fn new() 356 fn new()
331 () 357 ()
332 "#]], 358 "#]],
333 ); 359 );
@@ -346,8 +372,8 @@ fn bar() {
346} 372}
347"#, 373"#,
348 expect![[r#" 374 expect![[r#"
349 pub fn do_it(&self) 375 fn do_it(&self)
350 (&self) 376 ()
351 "#]], 377 "#]],
352 ); 378 );
353 } 379 }
@@ -365,8 +391,8 @@ fn bar() {
365} 391}
366"#, 392"#,
367 expect![[r#" 393 expect![[r#"
368 pub fn do_it(&self, x: i32) 394 fn do_it(&self, x: i32)
369 (&self, <x: i32>) 395 (<x: i32>)
370 "#]], 396 "#]],
371 ); 397 );
372 } 398 }
@@ -425,7 +451,7 @@ pub fn do() {
425 assert_eq!(6, my_crate::add_one(5)); 451 assert_eq!(6, my_crate::add_one(5));
426 ``` 452 ```
427 ------ 453 ------
428 pub fn add_one(x: i32) -> i32 454 fn add_one(x: i32) -> i32
429 (<x: i32>) 455 (<x: i32>)
430 "##]], 456 "##]],
431 ); 457 );
@@ -467,7 +493,7 @@ pub fn do_it() {
467 assert_eq!(6, my_crate::add_one(5)); 493 assert_eq!(6, my_crate::add_one(5));
468 ``` 494 ```
469 ------ 495 ------
470 pub fn add_one(x: i32) -> i32 496 fn add_one(x: i32) -> i32
471 (<x: i32>) 497 (<x: i32>)
472 "##]], 498 "##]],
473 ); 499 );
@@ -505,8 +531,8 @@ pub fn foo(mut r: WriteHandler<()>) {
505 531
506 By default this method stops actor's `Context`. 532 By default this method stops actor's `Context`.
507 ------ 533 ------
508 fn finished(&mut self, ctx: &mut Self::Context) 534 fn finished(&mut self, ctx: &mut {unknown})
509 (&mut self, <ctx: &mut Self::Context>) 535 (<ctx: &mut {unknown}>)
510 "#]], 536 "#]],
511 ); 537 );
512 } 538 }
@@ -539,7 +565,7 @@ fn main() {
539"#, 565"#,
540 expect![[r#" 566 expect![[r#"
541 fn bar(&self, _: u32) 567 fn bar(&self, _: u32)
542 (&self, <_: u32>) 568 (<_: u32>)
543 "#]], 569 "#]],
544 ); 570 );
545 } 571 }
@@ -549,15 +575,15 @@ fn main() {
549 check( 575 check(
550 r#" 576 r#"
551/// A cool tuple struct 577/// A cool tuple struct
552struct TS(u32, i32); 578struct S(u32, i32);
553fn main() { 579fn main() {
554 let s = TS(0, <|>); 580 let s = S(0, <|>);
555} 581}
556"#, 582"#,
557 expect![[r#" 583 expect![[r#"
558 A cool tuple struct 584 A cool tuple struct
559 ------ 585 ------
560 struct TS(u32, i32) -> TS 586 struct S(u32, i32)
561 (u32, <i32>) 587 (u32, <i32>)
562 "#]], 588 "#]],
563 ); 589 );
@@ -567,32 +593,19 @@ fn main() {
567 fn generic_struct() { 593 fn generic_struct() {
568 check( 594 check(
569 r#" 595 r#"
570struct TS<T>(T); 596struct S<T>(T);
571fn main() { 597fn main() {
572 let s = TS(<|>); 598 let s = S(<|>);
573} 599}
574"#, 600"#,
575 expect![[r#" 601 expect![[r#"
576 struct TS<T>(T) -> TS 602 struct S({unknown})
577 (<T>) 603 (<{unknown}>)
578 "#]], 604 "#]],
579 ); 605 );
580 } 606 }
581 607
582 #[test] 608 #[test]
583 fn cant_call_named_structs() {
584 check(
585 r#"
586struct TS { x: u32, y: i32 }
587fn main() {
588 let s = TS(<|>);
589}
590"#,
591 expect![[""]],
592 );
593 }
594
595 #[test]
596 fn works_for_enum_variants() { 609 fn works_for_enum_variants() {
597 check( 610 check(
598 r#" 611 r#"
@@ -612,27 +625,19 @@ fn main() {
612 expect![[r#" 625 expect![[r#"
613 A Variant 626 A Variant
614 ------ 627 ------
615 E::A(0: i32) 628 enum E::A(i32)
616 (<0: i32>) 629 (<i32>)
617 "#]], 630 "#]],
618 ); 631 );
619 } 632 }
620 633
621 #[test] 634 #[test]
622 fn cant_call_enum_records() { 635 fn cant_call_struct_record() {
623 check( 636 check(
624 r#" 637 r#"
625enum E { 638struct S { x: u32, y: i32 }
626 /// A Variant
627 A(i32),
628 /// Another
629 B,
630 /// And C
631 C { a: i32, b: i32 }
632}
633
634fn main() { 639fn main() {
635 let a = E::C(<|>); 640 let s = S(<|>);
636} 641}
637"#, 642"#,
638 expect![[""]], 643 expect![[""]],
@@ -640,24 +645,23 @@ fn main() {
640 } 645 }
641 646
642 #[test] 647 #[test]
643 fn fn_signature_for_macro() { 648 fn cant_call_enum_record() {
644 check( 649 check(
645 r#" 650 r#"
646/// empty macro 651enum E {
647macro_rules! foo { 652 /// A Variant
648 () => {} 653 A(i32),
654 /// Another
655 B,
656 /// And C
657 C { a: i32, b: i32 }
649} 658}
650 659
651fn f() { 660fn main() {
652 foo!(<|>); 661 let a = E::C(<|>);
653} 662}
654"#, 663"#,
655 expect![[r#" 664 expect![[""]],
656 empty macro
657 ------
658 foo!()
659 ()
660 "#]],
661 ); 665 );
662 } 666 }
663 667