aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr/src/matching.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ssr/src/matching.rs')
-rw-r--r--crates/ra_ssr/src/matching.rs105
1 files changed, 101 insertions, 4 deletions
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs
index 265b6d793..bdaba9f1b 100644
--- a/crates/ra_ssr/src/matching.rs
+++ b/crates/ra_ssr/src/matching.rs
@@ -61,8 +61,9 @@ pub(crate) struct Var(pub String);
61/// Information about a placeholder bound in a match. 61/// Information about a placeholder bound in a match.
62#[derive(Debug)] 62#[derive(Debug)]
63pub(crate) struct PlaceholderMatch { 63pub(crate) struct PlaceholderMatch {
64 /// The node that the placeholder matched to. 64 /// The node that the placeholder matched to. If set, then we'll search for further matches
65 pub(crate) node: SyntaxNode, 65 /// within this node. It isn't set when we match tokens within a macro call's token tree.
66 pub(crate) node: Option<SyntaxNode>,
66 pub(crate) range: FileRange, 67 pub(crate) range: FileRange,
67 /// More matches, found within `node`. 68 /// More matches, found within `node`.
68 pub(crate) inner_matches: SsrMatches, 69 pub(crate) inner_matches: SsrMatches,
@@ -195,6 +196,7 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
195 SyntaxKind::RECORD_FIELD_LIST => { 196 SyntaxKind::RECORD_FIELD_LIST => {
196 self.attempt_match_record_field_list(match_inputs, pattern, code) 197 self.attempt_match_record_field_list(match_inputs, pattern, code)
197 } 198 }
199 SyntaxKind::TOKEN_TREE => self.attempt_match_token_tree(match_inputs, pattern, code),
198 _ => self.attempt_match_node_children(match_inputs, pattern, code), 200 _ => self.attempt_match_node_children(match_inputs, pattern, code),
199 } 201 }
200 } 202 }
@@ -340,6 +342,90 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
340 Ok(()) 342 Ok(())
341 } 343 }
342 344
345 /// Outside of token trees, a placeholder can only match a single AST node, whereas in a token
346 /// tree it can match a sequence of tokens.
347 fn attempt_match_token_tree(
348 &mut self,
349 match_inputs: &MatchInputs,
350 pattern: &SyntaxNode,
351 code: &ra_syntax::SyntaxNode,
352 ) -> Result<(), MatchFailed> {
353 let mut pattern = PatternIterator::new(pattern).peekable();
354 let mut children = code.children_with_tokens();
355 while let Some(child) = children.next() {
356 if let Some(placeholder) = pattern.peek().and_then(|p| match_inputs.get_placeholder(p))
357 {
358 pattern.next();
359 let next_pattern_token = pattern
360 .peek()
361 .and_then(|p| match p {
362 SyntaxElement::Token(t) => Some(t.clone()),
363 SyntaxElement::Node(n) => n.first_token(),
364 })
365 .map(|p| p.text().to_string());
366 let first_matched_token = child.clone();
367 let mut last_matched_token = child;
368 // Read code tokens util we reach one equal to the next token from our pattern
369 // or we reach the end of the token tree.
370 while let Some(next) = children.next() {
371 match &next {
372 SyntaxElement::Token(t) => {
373 if Some(t.to_string()) == next_pattern_token {
374 pattern.next();
375 break;
376 }
377 }
378 SyntaxElement::Node(n) => {
379 if let Some(first_token) = n.first_token() {
380 if Some(first_token.to_string()) == next_pattern_token {
381 if let Some(SyntaxElement::Node(p)) = pattern.next() {
382 // We have a subtree that starts with the next token in our pattern.
383 self.attempt_match_token_tree(match_inputs, &p, &n)?;
384 break;
385 }
386 }
387 }
388 }
389 };
390 last_matched_token = next;
391 }
392 if let Some(match_out) = &mut self.match_out {
393 match_out.placeholder_values.insert(
394 Var(placeholder.ident.to_string()),
395 PlaceholderMatch::from_range(FileRange {
396 file_id: self.sema.original_range(code).file_id,
397 range: first_matched_token
398 .text_range()
399 .cover(last_matched_token.text_range()),
400 }),
401 );
402 }
403 continue;
404 }
405 // Match literal (non-placeholder) tokens.
406 match child {
407 SyntaxElement::Token(token) => {
408 self.attempt_match_token(&mut pattern, &token)?;
409 }
410 SyntaxElement::Node(node) => match pattern.next() {
411 Some(SyntaxElement::Node(p)) => {
412 self.attempt_match_token_tree(match_inputs, &p, &node)?;
413 }
414 Some(SyntaxElement::Token(p)) => fail_match!(
415 "Pattern has token '{}', code has subtree '{}'",
416 p.text(),
417 node.text()
418 ),
419 None => fail_match!("Pattern has nothing, code has '{}'", node.text()),
420 },
421 }
422 }
423 if let Some(p) = pattern.next() {
424 fail_match!("Reached end of token tree in code, but pattern still has {:?}", p);
425 }
426 Ok(())
427 }
428
343 fn next_non_trivial(&mut self, code_it: &mut SyntaxElementChildren) -> Option<SyntaxElement> { 429 fn next_non_trivial(&mut self, code_it: &mut SyntaxElementChildren) -> Option<SyntaxElement> {
344 loop { 430 loop {
345 let c = code_it.next(); 431 let c = code_it.next();
@@ -399,7 +485,11 @@ fn recording_match_fail_reasons() -> bool {
399 485
400impl PlaceholderMatch { 486impl PlaceholderMatch {
401 fn new(node: &SyntaxNode, range: FileRange) -> Self { 487 fn new(node: &SyntaxNode, range: FileRange) -> Self {
402 Self { node: node.clone(), range, inner_matches: SsrMatches::default() } 488 Self { node: Some(node.clone()), range, inner_matches: SsrMatches::default() }
489 }
490
491 fn from_range(range: FileRange) -> Self {
492 Self { node: None, range, inner_matches: SsrMatches::default() }
403 } 493 }
404} 494}
405 495
@@ -484,7 +574,14 @@ mod tests {
484 assert_eq!(matches.matches.len(), 1); 574 assert_eq!(matches.matches.len(), 1);
485 assert_eq!(matches.matches[0].matched_node.text(), "foo(1+2)"); 575 assert_eq!(matches.matches[0].matched_node.text(), "foo(1+2)");
486 assert_eq!(matches.matches[0].placeholder_values.len(), 1); 576 assert_eq!(matches.matches[0].placeholder_values.len(), 1);
487 assert_eq!(matches.matches[0].placeholder_values[&Var("x".to_string())].node.text(), "1+2"); 577 assert_eq!(
578 matches.matches[0].placeholder_values[&Var("x".to_string())]
579 .node
580 .as_ref()
581 .unwrap()
582 .text(),
583 "1+2"
584 );
488 585
489 let edit = crate::replacing::matches_to_edit(&matches); 586 let edit = crate::replacing::matches_to_edit(&matches);
490 let mut after = input.to_string(); 587 let mut after = input.to_string();