diff options
author | David Lattimore <[email protected]> | 2020-07-24 11:53:48 +0100 |
---|---|---|
committer | David Lattimore <[email protected]> | 2020-07-24 12:34:00 +0100 |
commit | 3dac31fe80b9d7279e87b94615b0d55805e83412 (patch) | |
tree | d172c092d9ca93c182b2d88c30c3086a9ea797b0 /crates/ra_ssr/src/search.rs | |
parent | 8d09ab86edfc01405fd0045bef82e0642efd5f01 (diff) |
SSR: Allow function calls to match method calls
This differs from how this used to work before I removed it in that:
a) It's only one direction. Function calls in the pattern can match
method calls in the code, but not the other way around.
b) We now check that the function call in the pattern resolves to the
same function as the method call in the code.
The lack of (b) was the reason I felt the need to remove the feature
before.
Diffstat (limited to 'crates/ra_ssr/src/search.rs')
-rw-r--r-- | crates/ra_ssr/src/search.rs | 65 |
1 files changed, 44 insertions, 21 deletions
diff --git a/crates/ra_ssr/src/search.rs b/crates/ra_ssr/src/search.rs index 141e7d026..bcf0f0468 100644 --- a/crates/ra_ssr/src/search.rs +++ b/crates/ra_ssr/src/search.rs | |||
@@ -46,35 +46,58 @@ impl<'db> MatchFinder<'db> { | |||
46 | usage_cache: &mut UsageCache, | 46 | usage_cache: &mut UsageCache, |
47 | matches_out: &mut Vec<Match>, | 47 | matches_out: &mut Vec<Match>, |
48 | ) { | 48 | ) { |
49 | if let Some(first_path) = pick_path_for_usages(pattern) { | 49 | if let Some(resolved_path) = pick_path_for_usages(pattern) { |
50 | let definition: Definition = first_path.resolution.clone().into(); | 50 | let definition: Definition = resolved_path.resolution.clone().into(); |
51 | for reference in self.find_usages(usage_cache, definition) { | 51 | for reference in self.find_usages(usage_cache, definition) { |
52 | let file = self.sema.parse(reference.file_range.file_id); | 52 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, reference) { |
53 | if let Some(path) = self.sema.find_node_at_offset_with_descend::<ast::Path>( | 53 | if !is_search_permitted_ancestors(&node_to_match) { |
54 | file.syntax(), | 54 | mark::hit!(use_declaration_with_braces); |
55 | reference.file_range.range.start(), | 55 | continue; |
56 | ) { | 56 | } |
57 | if let Some(node_to_match) = self | 57 | if let Ok(m) = |
58 | .sema | 58 | matching::get_match(false, rule, &node_to_match, &None, &self.sema) |
59 | .ancestors_with_macros(path.syntax().clone()) | ||
60 | .skip(first_path.depth as usize) | ||
61 | .next() | ||
62 | { | 59 | { |
63 | if !is_search_permitted_ancestors(&node_to_match) { | 60 | matches_out.push(m); |
64 | mark::hit!(use_declaration_with_braces); | ||
65 | continue; | ||
66 | } | ||
67 | if let Ok(m) = | ||
68 | matching::get_match(false, rule, &node_to_match, &None, &self.sema) | ||
69 | { | ||
70 | matches_out.push(m); | ||
71 | } | ||
72 | } | 61 | } |
73 | } | 62 | } |
74 | } | 63 | } |
75 | } | 64 | } |
76 | } | 65 | } |
77 | 66 | ||
67 | fn find_node_to_match( | ||
68 | &self, | ||
69 | resolved_path: &ResolvedPath, | ||
70 | reference: &Reference, | ||
71 | ) -> Option<SyntaxNode> { | ||
72 | let file = self.sema.parse(reference.file_range.file_id); | ||
73 | let depth = resolved_path.depth as usize; | ||
74 | let offset = reference.file_range.range.start(); | ||
75 | if let Some(path) = | ||
76 | self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset) | ||
77 | { | ||
78 | self.sema.ancestors_with_macros(path.syntax().clone()).skip(depth).next() | ||
79 | } else if let Some(path) = | ||
80 | self.sema.find_node_at_offset_with_descend::<ast::MethodCallExpr>(file.syntax(), offset) | ||
81 | { | ||
82 | // If the pattern contained a path and we found a reference to that path that wasn't | ||
83 | // itself a path, but was a method call, then we need to adjust how far up to try | ||
84 | // matching by how deep the path was within a CallExpr. The structure would have been | ||
85 | // CallExpr, PathExpr, Path - i.e. a depth offset of 2. We don't need to check if the | ||
86 | // path was part of a CallExpr because if it wasn't then all that will happen is we'll | ||
87 | // fail to match, which is the desired behavior. | ||
88 | const PATH_DEPTH_IN_CALL_EXPR: usize = 2; | ||
89 | if depth < PATH_DEPTH_IN_CALL_EXPR { | ||
90 | return None; | ||
91 | } | ||
92 | self.sema | ||
93 | .ancestors_with_macros(path.syntax().clone()) | ||
94 | .skip(depth - PATH_DEPTH_IN_CALL_EXPR) | ||
95 | .next() | ||
96 | } else { | ||
97 | None | ||
98 | } | ||
99 | } | ||
100 | |||
78 | fn find_usages<'a>( | 101 | fn find_usages<'a>( |
79 | &self, | 102 | &self, |
80 | usage_cache: &'a mut UsageCache, | 103 | usage_cache: &'a mut UsageCache, |