diff options
-rw-r--r-- | crates/ra_ssr/src/lib.rs | 50 | ||||
-rw-r--r-- | crates/ra_ssr/src/search.rs | 54 |
2 files changed, 56 insertions, 48 deletions
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs index b28913a65..dac73c07c 100644 --- a/crates/ra_ssr/src/lib.rs +++ b/crates/ra_ssr/src/lib.rs | |||
@@ -6,6 +6,7 @@ | |||
6 | mod matching; | 6 | mod matching; |
7 | mod parsing; | 7 | mod parsing; |
8 | mod replacing; | 8 | mod replacing; |
9 | mod search; | ||
9 | #[macro_use] | 10 | #[macro_use] |
10 | mod errors; | 11 | mod errors; |
11 | #[cfg(test)] | 12 | #[cfg(test)] |
@@ -83,7 +84,7 @@ impl<'db> MatchFinder<'db> { | |||
83 | let file = self.sema.parse(file_id); | 84 | let file = self.sema.parse(file_id); |
84 | let code = file.syntax(); | 85 | let code = file.syntax(); |
85 | let mut matches = SsrMatches::default(); | 86 | let mut matches = SsrMatches::default(); |
86 | self.find_matches(code, &None, &mut matches); | 87 | self.slow_scan_node(code, &None, &mut matches.matches); |
87 | matches | 88 | matches |
88 | } | 89 | } |
89 | 90 | ||
@@ -120,53 +121,6 @@ impl<'db> MatchFinder<'db> { | |||
120 | } | 121 | } |
121 | } | 122 | } |
122 | 123 | ||
123 | fn find_matches( | ||
124 | &self, | ||
125 | code: &SyntaxNode, | ||
126 | restrict_range: &Option<FileRange>, | ||
127 | matches_out: &mut SsrMatches, | ||
128 | ) { | ||
129 | for rule in &self.rules { | ||
130 | if let Ok(mut m) = matching::get_match(false, rule, &code, restrict_range, &self.sema) { | ||
131 | // Continue searching in each of our placeholders. | ||
132 | for placeholder_value in m.placeholder_values.values_mut() { | ||
133 | if let Some(placeholder_node) = &placeholder_value.node { | ||
134 | // Don't search our placeholder if it's the entire matched node, otherwise we'd | ||
135 | // find the same match over and over until we got a stack overflow. | ||
136 | if placeholder_node != code { | ||
137 | self.find_matches( | ||
138 | placeholder_node, | ||
139 | restrict_range, | ||
140 | &mut placeholder_value.inner_matches, | ||
141 | ); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | matches_out.matches.push(m); | ||
146 | return; | ||
147 | } | ||
148 | } | ||
149 | // If we've got a macro call, we already tried matching it pre-expansion, which is the only | ||
150 | // way to match the whole macro, now try expanding it and matching the expansion. | ||
151 | if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { | ||
152 | if let Some(expanded) = self.sema.expand(¯o_call) { | ||
153 | if let Some(tt) = macro_call.token_tree() { | ||
154 | // When matching within a macro expansion, we only want to allow matches of | ||
155 | // nodes that originated entirely from within the token tree of the macro call. | ||
156 | // i.e. we don't want to match something that came from the macro itself. | ||
157 | self.find_matches( | ||
158 | &expanded, | ||
159 | &Some(self.sema.original_range(tt.syntax())), | ||
160 | matches_out, | ||
161 | ); | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | for child in code.children() { | ||
166 | self.find_matches(&child, restrict_range, matches_out); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | fn output_debug_for_nodes_at_range( | 124 | fn output_debug_for_nodes_at_range( |
171 | &self, | 125 | &self, |
172 | node: &SyntaxNode, | 126 | node: &SyntaxNode, |
diff --git a/crates/ra_ssr/src/search.rs b/crates/ra_ssr/src/search.rs new file mode 100644 index 000000000..6f21452ac --- /dev/null +++ b/crates/ra_ssr/src/search.rs | |||
@@ -0,0 +1,54 @@ | |||
1 | //! Searching for matches. | ||
2 | |||
3 | use crate::{matching, Match, MatchFinder}; | ||
4 | use ra_db::FileRange; | ||
5 | use ra_syntax::{ast, AstNode, SyntaxNode}; | ||
6 | |||
7 | impl<'db> MatchFinder<'db> { | ||
8 | pub(crate) fn slow_scan_node( | ||
9 | &self, | ||
10 | code: &SyntaxNode, | ||
11 | restrict_range: &Option<FileRange>, | ||
12 | matches_out: &mut Vec<Match>, | ||
13 | ) { | ||
14 | for rule in &self.rules { | ||
15 | if let Ok(mut m) = matching::get_match(false, rule, &code, restrict_range, &self.sema) { | ||
16 | // Continue searching in each of our placeholders. | ||
17 | for placeholder_value in m.placeholder_values.values_mut() { | ||
18 | if let Some(placeholder_node) = &placeholder_value.node { | ||
19 | // Don't search our placeholder if it's the entire matched node, otherwise we'd | ||
20 | // find the same match over and over until we got a stack overflow. | ||
21 | if placeholder_node != code { | ||
22 | self.slow_scan_node( | ||
23 | placeholder_node, | ||
24 | restrict_range, | ||
25 | &mut placeholder_value.inner_matches.matches, | ||
26 | ); | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | matches_out.push(m); | ||
31 | return; | ||
32 | } | ||
33 | } | ||
34 | // If we've got a macro call, we already tried matching it pre-expansion, which is the only | ||
35 | // way to match the whole macro, now try expanding it and matching the expansion. | ||
36 | if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { | ||
37 | if let Some(expanded) = self.sema.expand(¯o_call) { | ||
38 | if let Some(tt) = macro_call.token_tree() { | ||
39 | // When matching within a macro expansion, we only want to allow matches of | ||
40 | // nodes that originated entirely from within the token tree of the macro call. | ||
41 | // i.e. we don't want to match something that came from the macro itself. | ||
42 | self.slow_scan_node( | ||
43 | &expanded, | ||
44 | &Some(self.sema.original_range(tt.syntax())), | ||
45 | matches_out, | ||
46 | ); | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | for child in code.children() { | ||
51 | self.slow_scan_node(&child, restrict_range, matches_out); | ||
52 | } | ||
53 | } | ||
54 | } | ||