aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/call_info.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-01-08 15:16:26 +0000
committerAleksey Kladov <[email protected]>2019-01-08 15:16:26 +0000
commite6a4383bb475b866b67df6bb83ecbdf823d73667 (patch)
tree6dbf7da77cd26e44597c40d1b5c802c552007ae8 /crates/ra_analysis/src/call_info.rs
parent2f07976cb51f7be216678f410175ba4c09bc7e71 (diff)
move call-info to a separate file
Diffstat (limited to 'crates/ra_analysis/src/call_info.rs')
-rw-r--r--crates/ra_analysis/src/call_info.rs119
1 files changed, 119 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/call_info.rs b/crates/ra_analysis/src/call_info.rs
new file mode 100644
index 000000000..a31a54ef6
--- /dev/null
+++ b/crates/ra_analysis/src/call_info.rs
@@ -0,0 +1,119 @@
1use ra_db::{SyntaxDatabase, Cancelable};
2use ra_syntax::{
3 AstNode, SyntaxNode, TextUnit, TextRange,
4 SyntaxKind::FN_DEF,
5 ast::{self, ArgListOwner},
6};
7use ra_editor::find_node_at_offset;
8use hir::FnSignatureInfo;
9
10use crate::{FilePosition, db::RootDatabase};
11
12/// Computes parameter information for the given call expression.
13pub(crate) fn call_info(
14 db: &RootDatabase,
15 position: FilePosition,
16) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
17 let file = db.source_file(position.file_id);
18 let syntax = file.syntax();
19
20 // Find the calling expression and it's NameRef
21 let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset));
22 let name_ref = ctry!(calling_node.name_ref());
23
24 // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
25 let file_symbols = db.index_resolve(name_ref)?;
26 for symbol in file_symbols {
27 if symbol.ptr.kind() == FN_DEF {
28 let fn_file = db.source_file(symbol.file_id);
29 let fn_def = symbol.ptr.resolve(&fn_file);
30 let fn_def = ast::FnDef::cast(&fn_def).unwrap();
31 let descr = ctry!(hir::source_binder::function_from_source(
32 db,
33 symbol.file_id,
34 fn_def
35 )?);
36 if let Some(descriptor) = descr.signature_info(db) {
37 // If we have a calling expression let's find which argument we are on
38 let mut current_parameter = None;
39
40 let num_params = descriptor.params.len();
41 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
42
43 if num_params == 1 {
44 if !has_self {
45 current_parameter = Some(0);
46 }
47 } else if num_params > 1 {
48 // Count how many parameters into the call we are.
49 // TODO: This is best effort for now and should be fixed at some point.
50 // It may be better to see where we are in the arg_list and then check
51 // where offset is in that list (or beyond).
52 // Revisit this after we get documentation comments in.
53 if let Some(ref arg_list) = calling_node.arg_list() {
54 let start = arg_list.syntax().range().start();
55
56 let range_search = TextRange::from_to(start, position.offset);
57 let mut commas: usize = arg_list
58 .syntax()
59 .text()
60 .slice(range_search)
61 .to_string()
62 .matches(',')
63 .count();
64
65 // If we have a method call eat the first param since it's just self.
66 if has_self {
67 commas += 1;
68 }
69
70 current_parameter = Some(commas);
71 }
72 }
73
74 return Ok(Some((descriptor, current_parameter)));
75 }
76 }
77 }
78
79 Ok(None)
80}
81
82enum FnCallNode<'a> {
83 CallExpr(&'a ast::CallExpr),
84 MethodCallExpr(&'a ast::MethodCallExpr),
85}
86
87impl<'a> FnCallNode<'a> {
88 pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
89 if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
90 return Some(FnCallNode::CallExpr(expr));
91 }
92 if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
93 return Some(FnCallNode::MethodCallExpr(expr));
94 }
95 None
96 }
97
98 pub fn name_ref(&self) -> Option<&'a ast::NameRef> {
99 match *self {
100 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
101 ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
102 _ => return None,
103 }),
104
105 FnCallNode::MethodCallExpr(call_expr) => call_expr
106 .syntax()
107 .children()
108 .filter_map(ast::NameRef::cast)
109 .nth(0),
110 }
111 }
112
113 pub fn arg_list(&self) -> Option<&'a ast::ArgList> {
114 match *self {
115 FnCallNode::CallExpr(expr) => expr.arg_list(),
116 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
117 }
118 }
119}