@@ -92,58 +92,52 @@ pub(crate) fn get_match(
92
92
sema : & Semantics < ra_ide_db:: RootDatabase > ,
93
93
) -> Result < Match , MatchFailed > {
94
94
record_match_fails_reasons_scope ( debug_active, || {
95
- MatchState :: try_match ( rule, code, restrict_range, sema)
95
+ Matcher :: try_match ( rule, code, restrict_range, sema)
96
96
} )
97
97
}
98
98
99
- /// Inputs to matching. This cannot be part of `MatchState`, since we mutate `MatchState` and in at
100
- /// least one case need to hold a borrow of a placeholder from the input pattern while calling a
101
- /// mutable `MatchState` method.
102
- struct MatchInputs < ' pattern > {
103
- ssr_pattern : & ' pattern SsrPattern ,
104
- }
105
-
106
- /// State used while attempting to match our search pattern against a particular node of the AST.
107
- struct MatchState < ' db , ' sema > {
99
+ /// Checks if our search pattern matches a particular node of the AST.
100
+ struct Matcher < ' db , ' sema > {
108
101
sema : & ' sema Semantics < ' db , ra_ide_db:: RootDatabase > ,
109
102
/// If any placeholders come from anywhere outside of this range, then the match will be
110
103
/// rejected.
111
104
restrict_range : Option < FileRange > ,
112
- /// The match that we're building. We do two passes for a successful match. On the first pass,
113
- /// this is None so that we can avoid doing things like storing copies of what placeholders
114
- /// matched to. If that pass succeeds, then we do a second pass where we collect those details.
115
- /// This means that if we have a pattern like `$a.foo()` we won't do an insert into the
116
- /// placeholders map for every single method call in the codebase. Instead we'll discard all the
117
- /// method calls that aren't calls to `foo` on the first pass and only insert into the
118
- /// placeholders map on the second pass. Likewise for ignored comments.
119
- match_out : Option < Match > ,
105
+ rule : & ' sema SsrRule ,
106
+ }
107
+
108
+ /// Which phase of matching we're currently performing. We do two phases because most attempted
109
+ /// matches will fail and it means we can defer more expensive checks to the second phase.
110
+ enum Phase < ' a > {
111
+ /// On the first phase, we perform cheap checks. No state is mutated and nothing is recorded.
112
+ First ,
113
+ /// On the second phase, we construct the `Match`. Things like what placeholders bind to is
114
+ /// recorded.
115
+ Second ( & ' a mut Match ) ,
120
116
}
121
117
122
- impl < ' db , ' sema > MatchState < ' db , ' sema > {
118
+ impl < ' db , ' sema > Matcher < ' db , ' sema > {
123
119
fn try_match (
124
- rule : & SsrRule ,
120
+ rule : & ' sema SsrRule ,
125
121
code : & SyntaxNode ,
126
122
restrict_range : & Option < FileRange > ,
127
123
sema : & ' sema Semantics < ' db , ra_ide_db:: RootDatabase > ,
128
124
) -> Result < Match , MatchFailed > {
129
- let mut match_state =
130
- MatchState { sema, restrict_range : restrict_range. clone ( ) , match_out : None } ;
131
- let match_inputs = MatchInputs { ssr_pattern : & rule. pattern } ;
125
+ let match_state = Matcher { sema, restrict_range : restrict_range. clone ( ) , rule } ;
132
126
let pattern_tree = rule. pattern . tree_for_kind ( code. kind ( ) ) ?;
133
127
// First pass at matching, where we check that node types and idents match.
134
- match_state. attempt_match_node ( & match_inputs , & pattern_tree, code) ?;
128
+ match_state. attempt_match_node ( & mut Phase :: First , & pattern_tree, code) ?;
135
129
match_state. validate_range ( & sema. original_range ( code) ) ?;
136
- match_state . match_out = Some ( Match {
130
+ let mut the_match = Match {
137
131
range : sema. original_range ( code) ,
138
132
matched_node : code. clone ( ) ,
139
133
placeholder_values : FxHashMap :: default ( ) ,
140
134
ignored_comments : Vec :: new ( ) ,
141
135
template : rule. template . clone ( ) ,
142
- } ) ;
136
+ } ;
143
137
// Second matching pass, where we record placeholder matches, ignored comments and maybe do
144
138
// any other more expensive checks that we didn't want to do on the first pass.
145
- match_state. attempt_match_node ( & match_inputs , & pattern_tree, code) ?;
146
- Ok ( match_state . match_out . unwrap ( ) )
139
+ match_state. attempt_match_node ( & mut Phase :: Second ( & mut the_match ) , & pattern_tree, code) ?;
140
+ Ok ( the_match )
147
141
}
148
142
149
143
/// Checks that `range` is within the permitted range if any. This is applicable when we're
@@ -161,27 +155,22 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
161
155
}
162
156
163
157
fn attempt_match_node (
164
- & mut self ,
165
- match_inputs : & MatchInputs ,
158
+ & self ,
159
+ phase : & mut Phase ,
166
160
pattern : & SyntaxNode ,
167
161
code : & SyntaxNode ,
168
162
) -> Result < ( ) , MatchFailed > {
169
163
// Handle placeholders.
170
- if let Some ( placeholder) =
171
- match_inputs. get_placeholder ( & SyntaxElement :: Node ( pattern. clone ( ) ) )
172
- {
164
+ if let Some ( placeholder) = self . get_placeholder ( & SyntaxElement :: Node ( pattern. clone ( ) ) ) {
173
165
for constraint in & placeholder. constraints {
174
166
self . check_constraint ( constraint, code) ?;
175
167
}
176
- if self . match_out . is_none ( ) {
177
- return Ok ( ( ) ) ;
178
- }
179
- let original_range = self . sema . original_range ( code) ;
180
- // We validated the range for the node when we started the match, so the placeholder
181
- // probably can't fail range validation, but just to be safe...
182
- self . validate_range ( & original_range) ?;
183
- if let Some ( match_out) = & mut self . match_out {
184
- match_out. placeholder_values . insert (
168
+ if let Phase :: Second ( matches_out) = phase {
169
+ let original_range = self . sema . original_range ( code) ;
170
+ // We validated the range for the node when we started the match, so the placeholder
171
+ // probably can't fail range validation, but just to be safe...
172
+ self . validate_range ( & original_range) ?;
173
+ matches_out. placeholder_values . insert (
185
174
Var ( placeholder. ident . to_string ( ) ) ,
186
175
PlaceholderMatch :: new ( code, original_range) ,
187
176
) ;
@@ -190,53 +179,59 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
190
179
}
191
180
// Non-placeholders.
192
181
if pattern. kind ( ) != code. kind ( ) {
193
- fail_match ! ( "Pattern had a {:?}, code had {:?}" , pattern. kind( ) , code. kind( ) ) ;
182
+ fail_match ! (
183
+ "Pattern had a `{}` ({:?}), code had `{}` ({:?})" ,
184
+ pattern. text( ) ,
185
+ pattern. kind( ) ,
186
+ code. text( ) ,
187
+ code. kind( )
188
+ ) ;
194
189
}
195
190
// Some kinds of nodes have special handling. For everything else, we fall back to default
196
191
// matching.
197
192
match code. kind ( ) {
198
193
SyntaxKind :: RECORD_FIELD_LIST => {
199
- self . attempt_match_record_field_list ( match_inputs , pattern, code)
194
+ self . attempt_match_record_field_list ( phase , pattern, code)
200
195
}
201
- SyntaxKind :: TOKEN_TREE => self . attempt_match_token_tree ( match_inputs , pattern, code) ,
202
- _ => self . attempt_match_node_children ( match_inputs , pattern, code) ,
196
+ SyntaxKind :: TOKEN_TREE => self . attempt_match_token_tree ( phase , pattern, code) ,
197
+ _ => self . attempt_match_node_children ( phase , pattern, code) ,
203
198
}
204
199
}
205
200
206
201
fn attempt_match_node_children (
207
- & mut self ,
208
- match_inputs : & MatchInputs ,
202
+ & self ,
203
+ phase : & mut Phase ,
209
204
pattern : & SyntaxNode ,
210
205
code : & SyntaxNode ,
211
206
) -> Result < ( ) , MatchFailed > {
212
207
self . attempt_match_sequences (
213
- match_inputs ,
208
+ phase ,
214
209
PatternIterator :: new ( pattern) ,
215
210
code. children_with_tokens ( ) ,
216
211
)
217
212
}
218
213
219
214
fn attempt_match_sequences (
220
- & mut self ,
221
- match_inputs : & MatchInputs ,
215
+ & self ,
216
+ phase : & mut Phase ,
222
217
pattern_it : PatternIterator ,
223
218
mut code_it : SyntaxElementChildren ,
224
219
) -> Result < ( ) , MatchFailed > {
225
220
let mut pattern_it = pattern_it. peekable ( ) ;
226
221
loop {
227
- match self . next_non_trivial ( & mut code_it) {
222
+ match phase . next_non_trivial ( & mut code_it) {
228
223
None => {
229
224
if let Some ( p) = pattern_it. next ( ) {
230
225
fail_match ! ( "Part of the pattern was unmatched: {:?}" , p) ;
231
226
}
232
227
return Ok ( ( ) ) ;
233
228
}
234
229
Some ( SyntaxElement :: Token ( c) ) => {
235
- self . attempt_match_token ( & mut pattern_it, & c) ?;
230
+ self . attempt_match_token ( phase , & mut pattern_it, & c) ?;
236
231
}
237
232
Some ( SyntaxElement :: Node ( c) ) => match pattern_it. next ( ) {
238
233
Some ( SyntaxElement :: Node ( p) ) => {
239
- self . attempt_match_node ( match_inputs , & p, & c) ?;
234
+ self . attempt_match_node ( phase , & p, & c) ?;
240
235
}
241
236
Some ( p) => fail_match ! ( "Pattern wanted '{}', code has {}" , p, c. text( ) ) ,
242
237
None => fail_match ! ( "Pattern reached end, code has {}" , c. text( ) ) ,
@@ -246,11 +241,12 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
246
241
}
247
242
248
243
fn attempt_match_token (
249
- & mut self ,
244
+ & self ,
245
+ phase : & mut Phase ,
250
246
pattern : & mut Peekable < PatternIterator > ,
251
247
code : & ra_syntax:: SyntaxToken ,
252
248
) -> Result < ( ) , MatchFailed > {
253
- self . record_ignored_comments ( code) ;
249
+ phase . record_ignored_comments ( code) ;
254
250
// Ignore whitespace and comments.
255
251
if code. kind ( ) . is_trivia ( ) {
256
252
return Ok ( ( ) ) ;
@@ -317,8 +313,8 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
317
313
/// We want to allow the records to match in any order, so we have special matching logic for
318
314
/// them.
319
315
fn attempt_match_record_field_list (
320
- & mut self ,
321
- match_inputs : & MatchInputs ,
316
+ & self ,
317
+ phase : & mut Phase ,
322
318
pattern : & SyntaxNode ,
323
319
code : & SyntaxNode ,
324
320
) -> Result < ( ) , MatchFailed > {
@@ -334,11 +330,11 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
334
330
for p in pattern. children_with_tokens ( ) {
335
331
if let SyntaxElement :: Node ( p) = p {
336
332
if let Some ( name_element) = p. first_child_or_token ( ) {
337
- if match_inputs . get_placeholder ( & name_element) . is_some ( ) {
333
+ if self . get_placeholder ( & name_element) . is_some ( ) {
338
334
// If the pattern is using placeholders for field names then order
339
335
// independence doesn't make sense. Fall back to regular ordered
340
336
// matching.
341
- return self . attempt_match_node_children ( match_inputs , pattern, code) ;
337
+ return self . attempt_match_node_children ( phase , pattern, code) ;
342
338
}
343
339
if let Some ( ident) = only_ident ( name_element) {
344
340
let code_record = fields_by_name. remove ( ident. text ( ) ) . ok_or_else ( || {
@@ -347,7 +343,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
347
343
ident
348
344
)
349
345
} ) ?;
350
- self . attempt_match_node ( match_inputs , & p, & code_record) ?;
346
+ self . attempt_match_node ( phase , & p, & code_record) ?;
351
347
}
352
348
}
353
349
}
@@ -367,16 +363,15 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
367
363
/// pattern matches the macro invocation. For matches within the macro call, we'll already have
368
364
/// expanded the macro.
369
365
fn attempt_match_token_tree (
370
- & mut self ,
371
- match_inputs : & MatchInputs ,
366
+ & self ,
367
+ phase : & mut Phase ,
372
368
pattern : & SyntaxNode ,
373
369
code : & ra_syntax:: SyntaxNode ,
374
370
) -> Result < ( ) , MatchFailed > {
375
371
let mut pattern = PatternIterator :: new ( pattern) . peekable ( ) ;
376
372
let mut children = code. children_with_tokens ( ) ;
377
373
while let Some ( child) = children. next ( ) {
378
- if let Some ( placeholder) = pattern. peek ( ) . and_then ( |p| match_inputs. get_placeholder ( p) )
379
- {
374
+ if let Some ( placeholder) = pattern. peek ( ) . and_then ( |p| self . get_placeholder ( p) ) {
380
375
pattern. next ( ) ;
381
376
let next_pattern_token = pattern
382
377
. peek ( )
@@ -402,7 +397,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
402
397
if Some ( first_token. to_string ( ) ) == next_pattern_token {
403
398
if let Some ( SyntaxElement :: Node ( p) ) = pattern. next ( ) {
404
399
// We have a subtree that starts with the next token in our pattern.
405
- self . attempt_match_token_tree ( match_inputs , & p, & n) ?;
400
+ self . attempt_match_token_tree ( phase , & p, & n) ?;
406
401
break ;
407
402
}
408
403
}
@@ -411,7 +406,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
411
406
} ;
412
407
last_matched_token = next;
413
408
}
414
- if let Some ( match_out) = & mut self . match_out {
409
+ if let Phase :: Second ( match_out) = phase {
415
410
match_out. placeholder_values . insert (
416
411
Var ( placeholder. ident . to_string ( ) ) ,
417
412
PlaceholderMatch :: from_range ( FileRange {
@@ -427,11 +422,11 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
427
422
// Match literal (non-placeholder) tokens.
428
423
match child {
429
424
SyntaxElement :: Token ( token) => {
430
- self . attempt_match_token ( & mut pattern, & token) ?;
425
+ self . attempt_match_token ( phase , & mut pattern, & token) ?;
431
426
}
432
427
SyntaxElement :: Node ( node) => match pattern. next ( ) {
433
428
Some ( SyntaxElement :: Node ( p) ) => {
434
- self . attempt_match_token_tree ( match_inputs , & p, & node) ?;
429
+ self . attempt_match_token_tree ( phase , & p, & node) ?;
435
430
}
436
431
Some ( SyntaxElement :: Token ( p) ) => fail_match ! (
437
432
"Pattern has token '{}', code has subtree '{}'" ,
@@ -448,6 +443,13 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
448
443
Ok ( ( ) )
449
444
}
450
445
446
+ fn get_placeholder ( & self , element : & SyntaxElement ) -> Option < & Placeholder > {
447
+ only_ident ( element. clone ( ) )
448
+ . and_then ( |ident| self . rule . pattern . placeholders_by_stand_in . get ( ident. text ( ) ) )
449
+ }
450
+ }
451
+
452
+ impl Phase < ' _ > {
451
453
fn next_non_trivial ( & mut self , code_it : & mut SyntaxElementChildren ) -> Option < SyntaxElement > {
452
454
loop {
453
455
let c = code_it. next ( ) ;
@@ -463,7 +465,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
463
465
464
466
fn record_ignored_comments ( & mut self , token : & SyntaxToken ) {
465
467
if token. kind ( ) == SyntaxKind :: COMMENT {
466
- if let Some ( match_out) = & mut self . match_out {
468
+ if let Phase :: Second ( match_out) = self {
467
469
if let Some ( comment) = ast:: Comment :: cast ( token. clone ( ) ) {
468
470
match_out. ignored_comments . push ( comment) ;
469
471
}
@@ -472,13 +474,6 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
472
474
}
473
475
}
474
476
475
- impl MatchInputs < ' _ > {
476
- fn get_placeholder ( & self , element : & SyntaxElement ) -> Option < & Placeholder > {
477
- only_ident ( element. clone ( ) )
478
- . and_then ( |ident| self . ssr_pattern . placeholders_by_stand_in . get ( ident. text ( ) ) )
479
- }
480
- }
481
-
482
477
fn is_closing_token ( kind : SyntaxKind ) -> bool {
483
478
kind == SyntaxKind :: R_PAREN || kind == SyntaxKind :: R_CURLY || kind == SyntaxKind :: R_BRACK
484
479
}
@@ -596,12 +591,12 @@ impl PatternIterator {
596
591
#[ cfg( test) ]
597
592
mod tests {
598
593
use super :: * ;
599
- use crate :: MatchFinder ;
594
+ use crate :: { MatchFinder , SsrRule } ;
600
595
601
596
#[ test]
602
597
fn parse_match_replace ( ) {
603
598
let rule: SsrRule = "foo($x) ==>> bar($x)" . parse ( ) . unwrap ( ) ;
604
- let input = "fn main() { foo(1+2); }" ;
599
+ let input = "fn foo() {} fn main() { foo(1+2); }" ;
605
600
606
601
use ra_db:: fixture:: WithFixture ;
607
602
let ( db, file_id) = ra_ide_db:: RootDatabase :: with_single_file ( input) ;
@@ -623,6 +618,6 @@ mod tests {
623
618
let edit = crate :: replacing:: matches_to_edit ( & matches, input) ;
624
619
let mut after = input. to_string ( ) ;
625
620
edit. apply ( & mut after) ;
626
- assert_eq ! ( after, "fn main() { bar(1+2); }" ) ;
621
+ assert_eq ! ( after, "fn foo() {} fn main() { bar(1+2); }" ) ;
627
622
}
628
623
}
0 commit comments