• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! This module is responsible for matching a search pattern against a node in the AST. In the
2 //! process of matching, placeholder values are recorded.
3 
4 use crate::{
5     parsing::{Constraint, NodeKind, Placeholder, Var},
6     resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo},
7     SsrMatches,
8 };
9 use hir::Semantics;
10 use ide_db::{base_db::FileRange, FxHashMap};
11 use std::{cell::Cell, iter::Peekable};
12 use syntax::{
13     ast::{self, AstNode, AstToken},
14     SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken,
15 };
16 
17 // Creates a match error. If we're currently attempting to match some code that we thought we were
18 // going to match, as indicated by the --debug-snippet flag, then populate the reason field.
19 macro_rules! match_error {
20     ($e:expr) => {{
21             MatchFailed {
22                 reason: if recording_match_fail_reasons() {
23                     Some(format!("{}", $e))
24                 } else {
25                     None
26                 }
27             }
28     }};
29     ($fmt:expr, $($arg:tt)+) => {{
30         MatchFailed {
31             reason: if recording_match_fail_reasons() {
32                 Some(format!($fmt, $($arg)+))
33             } else {
34                 None
35             }
36         }
37     }};
38 }
39 
40 // Fails the current match attempt, recording the supplied reason if we're recording match fail reasons.
41 macro_rules! fail_match {
42     ($($args:tt)*) => {return Err(match_error!($($args)*))};
43 }
44 
45 /// Information about a match that was found.
46 #[derive(Debug)]
47 pub struct Match {
48     pub(crate) range: FileRange,
49     pub(crate) matched_node: SyntaxNode,
50     pub(crate) placeholder_values: FxHashMap<Var, PlaceholderMatch>,
51     pub(crate) ignored_comments: Vec<ast::Comment>,
52     pub(crate) rule_index: usize,
53     /// The depth of matched_node.
54     pub(crate) depth: usize,
55     // Each path in the template rendered for the module in which the match was found.
56     pub(crate) rendered_template_paths: FxHashMap<SyntaxNode, hir::ModPath>,
57 }
58 
59 /// Information about a placeholder bound in a match.
60 #[derive(Debug)]
61 pub(crate) struct PlaceholderMatch {
62     pub(crate) range: FileRange,
63     /// More matches, found within `node`.
64     pub(crate) inner_matches: SsrMatches,
65     /// How many times the code that the placeholder matched needed to be dereferenced. Will only be
66     /// non-zero if the placeholder matched to the receiver of a method call.
67     pub(crate) autoderef_count: usize,
68     pub(crate) autoref_kind: ast::SelfParamKind,
69 }
70 
71 #[derive(Debug)]
72 pub(crate) struct MatchFailureReason {
73     pub(crate) reason: String,
74 }
75 
76 /// An "error" indicating that matching failed. Use the fail_match! macro to create and return this.
77 #[derive(Clone)]
78 pub(crate) struct MatchFailed {
79     /// The reason why we failed to match. Only present when debug_active true in call to
80     /// `get_match`.
81     pub(crate) reason: Option<String>,
82 }
83 
84 /// Checks if `code` matches the search pattern found in `search_scope`, returning information about
85 /// the match, if it does. Since we only do matching in this module and searching is done by the
86 /// parent module, we don't populate nested matches.
get_match( debug_active: bool, rule: &ResolvedRule, code: &SyntaxNode, restrict_range: &Option<FileRange>, sema: &Semantics<'_, ide_db::RootDatabase>, ) -> Result<Match, MatchFailed>87 pub(crate) fn get_match(
88     debug_active: bool,
89     rule: &ResolvedRule,
90     code: &SyntaxNode,
91     restrict_range: &Option<FileRange>,
92     sema: &Semantics<'_, ide_db::RootDatabase>,
93 ) -> Result<Match, MatchFailed> {
94     record_match_fails_reasons_scope(debug_active, || {
95         Matcher::try_match(rule, code, restrict_range, sema)
96     })
97 }
98 
99 /// Checks if our search pattern matches a particular node of the AST.
100 struct Matcher<'db, 'sema> {
101     sema: &'sema Semantics<'db, ide_db::RootDatabase>,
102     /// If any placeholders come from anywhere outside of this range, then the match will be
103     /// rejected.
104     restrict_range: Option<FileRange>,
105     rule: &'sema ResolvedRule,
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),
116 }
117 
118 impl<'db, 'sema> Matcher<'db, 'sema> {
try_match( rule: &ResolvedRule, code: &SyntaxNode, restrict_range: &Option<FileRange>, sema: &'sema Semantics<'db, ide_db::RootDatabase>, ) -> Result<Match, MatchFailed>119     fn try_match(
120         rule: &ResolvedRule,
121         code: &SyntaxNode,
122         restrict_range: &Option<FileRange>,
123         sema: &'sema Semantics<'db, ide_db::RootDatabase>,
124     ) -> Result<Match, MatchFailed> {
125         let match_state = Matcher { sema, restrict_range: *restrict_range, rule };
126         // First pass at matching, where we check that node types and idents match.
127         match_state.attempt_match_node(&mut Phase::First, &rule.pattern.node, code)?;
128         match_state.validate_range(&sema.original_range(code))?;
129         let mut the_match = Match {
130             range: sema.original_range(code),
131             matched_node: code.clone(),
132             placeholder_values: FxHashMap::default(),
133             ignored_comments: Vec::new(),
134             rule_index: rule.index,
135             depth: 0,
136             rendered_template_paths: FxHashMap::default(),
137         };
138         // Second matching pass, where we record placeholder matches, ignored comments and maybe do
139         // any other more expensive checks that we didn't want to do on the first pass.
140         match_state.attempt_match_node(
141             &mut Phase::Second(&mut the_match),
142             &rule.pattern.node,
143             code,
144         )?;
145         the_match.depth = sema.ancestors_with_macros(the_match.matched_node.clone()).count();
146         if let Some(template) = &rule.template {
147             the_match.render_template_paths(template, sema)?;
148         }
149         Ok(the_match)
150     }
151 
152     /// Checks that `range` is within the permitted range if any. This is applicable when we're
153     /// processing a macro expansion and we want to fail the match if we're working with a node that
154     /// didn't originate from the token tree of the macro call.
validate_range(&self, range: &FileRange) -> Result<(), MatchFailed>155     fn validate_range(&self, range: &FileRange) -> Result<(), MatchFailed> {
156         if let Some(restrict_range) = &self.restrict_range {
157             if restrict_range.file_id != range.file_id
158                 || !restrict_range.range.contains_range(range.range)
159             {
160                 fail_match!("Node originated from a macro");
161             }
162         }
163         Ok(())
164     }
165 
attempt_match_node( &self, phase: &mut Phase<'_>, pattern: &SyntaxNode, code: &SyntaxNode, ) -> Result<(), MatchFailed>166     fn attempt_match_node(
167         &self,
168         phase: &mut Phase<'_>,
169         pattern: &SyntaxNode,
170         code: &SyntaxNode,
171     ) -> Result<(), MatchFailed> {
172         // Handle placeholders.
173         if let Some(placeholder) = self.get_placeholder_for_node(pattern) {
174             for constraint in &placeholder.constraints {
175                 self.check_constraint(constraint, code)?;
176             }
177             if let Phase::Second(matches_out) = phase {
178                 let original_range = self.sema.original_range(code);
179                 // We validated the range for the node when we started the match, so the placeholder
180                 // probably can't fail range validation, but just to be safe...
181                 self.validate_range(&original_range)?;
182                 matches_out.placeholder_values.insert(
183                     placeholder.ident.clone(),
184                     PlaceholderMatch::from_range(original_range),
185                 );
186             }
187             return Ok(());
188         }
189         // We allow a UFCS call to match a method call, provided they resolve to the same function.
190         if let Some(pattern_ufcs) = self.rule.pattern.ufcs_function_calls.get(pattern) {
191             if let Some(code) = ast::MethodCallExpr::cast(code.clone()) {
192                 return self.attempt_match_ufcs_to_method_call(phase, pattern_ufcs, &code);
193             }
194             if let Some(code) = ast::CallExpr::cast(code.clone()) {
195                 return self.attempt_match_ufcs_to_ufcs(phase, pattern_ufcs, &code);
196             }
197         }
198         if pattern.kind() != code.kind() {
199             fail_match!(
200                 "Pattern had `{}` ({:?}), code had `{}` ({:?})",
201                 pattern.text(),
202                 pattern.kind(),
203                 code.text(),
204                 code.kind()
205             );
206         }
207         // Some kinds of nodes have special handling. For everything else, we fall back to default
208         // matching.
209         match code.kind() {
210             SyntaxKind::RECORD_EXPR_FIELD_LIST => {
211                 self.attempt_match_record_field_list(phase, pattern, code)
212             }
213             SyntaxKind::TOKEN_TREE => self.attempt_match_token_tree(phase, pattern, code),
214             SyntaxKind::PATH => self.attempt_match_path(phase, pattern, code),
215             _ => self.attempt_match_node_children(phase, pattern, code),
216         }
217     }
218 
attempt_match_node_children( &self, phase: &mut Phase<'_>, pattern: &SyntaxNode, code: &SyntaxNode, ) -> Result<(), MatchFailed>219     fn attempt_match_node_children(
220         &self,
221         phase: &mut Phase<'_>,
222         pattern: &SyntaxNode,
223         code: &SyntaxNode,
224     ) -> Result<(), MatchFailed> {
225         self.attempt_match_sequences(
226             phase,
227             PatternIterator::new(pattern),
228             code.children_with_tokens(),
229         )
230     }
231 
attempt_match_sequences( &self, phase: &mut Phase<'_>, pattern_it: PatternIterator, mut code_it: SyntaxElementChildren, ) -> Result<(), MatchFailed>232     fn attempt_match_sequences(
233         &self,
234         phase: &mut Phase<'_>,
235         pattern_it: PatternIterator,
236         mut code_it: SyntaxElementChildren,
237     ) -> Result<(), MatchFailed> {
238         let mut pattern_it = pattern_it.peekable();
239         loop {
240             match phase.next_non_trivial(&mut code_it) {
241                 None => {
242                     if let Some(p) = pattern_it.next() {
243                         fail_match!("Part of the pattern was unmatched: {:?}", p);
244                     }
245                     return Ok(());
246                 }
247                 Some(SyntaxElement::Token(c)) => {
248                     self.attempt_match_token(phase, &mut pattern_it, &c)?;
249                 }
250                 Some(SyntaxElement::Node(c)) => match pattern_it.next() {
251                     Some(SyntaxElement::Node(p)) => {
252                         self.attempt_match_node(phase, &p, &c)?;
253                     }
254                     Some(p) => fail_match!("Pattern wanted '{}', code has {}", p, c.text()),
255                     None => fail_match!("Pattern reached end, code has {}", c.text()),
256                 },
257             }
258         }
259     }
260 
attempt_match_token( &self, phase: &mut Phase<'_>, pattern: &mut Peekable<PatternIterator>, code: &syntax::SyntaxToken, ) -> Result<(), MatchFailed>261     fn attempt_match_token(
262         &self,
263         phase: &mut Phase<'_>,
264         pattern: &mut Peekable<PatternIterator>,
265         code: &syntax::SyntaxToken,
266     ) -> Result<(), MatchFailed> {
267         phase.record_ignored_comments(code);
268         // Ignore whitespace and comments.
269         if code.kind().is_trivia() {
270             return Ok(());
271         }
272         if let Some(SyntaxElement::Token(p)) = pattern.peek() {
273             // If the code has a comma and the pattern is about to close something, then accept the
274             // comma without advancing the pattern. i.e. ignore trailing commas.
275             if code.kind() == SyntaxKind::COMMA && is_closing_token(p.kind()) {
276                 return Ok(());
277             }
278             // Conversely, if the pattern has a comma and the code doesn't, skip that part of the
279             // pattern and continue to match the code.
280             if p.kind() == SyntaxKind::COMMA && is_closing_token(code.kind()) {
281                 pattern.next();
282             }
283         }
284         // Consume an element from the pattern and make sure it matches.
285         match pattern.next() {
286             Some(SyntaxElement::Token(p)) => {
287                 if p.kind() != code.kind() || p.text() != code.text() {
288                     fail_match!(
289                         "Pattern wanted token '{}' ({:?}), but code had token '{}' ({:?})",
290                         p.text(),
291                         p.kind(),
292                         code.text(),
293                         code.kind()
294                     )
295                 }
296             }
297             Some(SyntaxElement::Node(p)) => {
298                 // Not sure if this is actually reachable.
299                 fail_match!(
300                     "Pattern wanted {:?}, but code had token '{}' ({:?})",
301                     p,
302                     code.text(),
303                     code.kind()
304                 );
305             }
306             None => {
307                 fail_match!("Pattern exhausted, while code remains: `{}`", code.text());
308             }
309         }
310         Ok(())
311     }
312 
check_constraint( &self, constraint: &Constraint, code: &SyntaxNode, ) -> Result<(), MatchFailed>313     fn check_constraint(
314         &self,
315         constraint: &Constraint,
316         code: &SyntaxNode,
317     ) -> Result<(), MatchFailed> {
318         match constraint {
319             Constraint::Kind(kind) => {
320                 kind.matches(code)?;
321             }
322             Constraint::Not(sub) => {
323                 if self.check_constraint(&*sub, code).is_ok() {
324                     fail_match!("Constraint {:?} failed for '{}'", constraint, code.text());
325                 }
326             }
327         }
328         Ok(())
329     }
330 
331     /// Paths are matched based on whether they refer to the same thing, even if they're written
332     /// differently.
attempt_match_path( &self, phase: &mut Phase<'_>, pattern: &SyntaxNode, code: &SyntaxNode, ) -> Result<(), MatchFailed>333     fn attempt_match_path(
334         &self,
335         phase: &mut Phase<'_>,
336         pattern: &SyntaxNode,
337         code: &SyntaxNode,
338     ) -> Result<(), MatchFailed> {
339         if let Some(pattern_resolved) = self.rule.pattern.resolved_paths.get(pattern) {
340             let pattern_path = ast::Path::cast(pattern.clone()).unwrap();
341             let code_path = ast::Path::cast(code.clone()).unwrap();
342             if let (Some(pattern_segment), Some(code_segment)) =
343                 (pattern_path.segment(), code_path.segment())
344             {
345                 // Match everything within the segment except for the name-ref, which is handled
346                 // separately via comparing what the path resolves to below.
347                 self.attempt_match_opt(
348                     phase,
349                     pattern_segment.generic_arg_list(),
350                     code_segment.generic_arg_list(),
351                 )?;
352                 self.attempt_match_opt(
353                     phase,
354                     pattern_segment.param_list(),
355                     code_segment.param_list(),
356                 )?;
357             }
358             if matches!(phase, Phase::Second(_)) {
359                 let resolution = self
360                     .sema
361                     .resolve_path(&code_path)
362                     .ok_or_else(|| match_error!("Failed to resolve path `{}`", code.text()))?;
363                 if pattern_resolved.resolution != resolution {
364                     fail_match!("Pattern had path `{}` code had `{}`", pattern.text(), code.text());
365                 }
366             }
367         } else {
368             return self.attempt_match_node_children(phase, pattern, code);
369         }
370         Ok(())
371     }
372 
attempt_match_opt<T: AstNode>( &self, phase: &mut Phase<'_>, pattern: Option<T>, code: Option<T>, ) -> Result<(), MatchFailed>373     fn attempt_match_opt<T: AstNode>(
374         &self,
375         phase: &mut Phase<'_>,
376         pattern: Option<T>,
377         code: Option<T>,
378     ) -> Result<(), MatchFailed> {
379         match (pattern, code) {
380             (Some(p), Some(c)) => self.attempt_match_node(phase, p.syntax(), c.syntax()),
381             (None, None) => Ok(()),
382             (Some(p), None) => fail_match!("Pattern `{}` had nothing to match", p.syntax().text()),
383             (None, Some(c)) => {
384                 fail_match!("Nothing in pattern to match code `{}`", c.syntax().text())
385             }
386         }
387     }
388 
389     /// We want to allow the records to match in any order, so we have special matching logic for
390     /// them.
attempt_match_record_field_list( &self, phase: &mut Phase<'_>, pattern: &SyntaxNode, code: &SyntaxNode, ) -> Result<(), MatchFailed>391     fn attempt_match_record_field_list(
392         &self,
393         phase: &mut Phase<'_>,
394         pattern: &SyntaxNode,
395         code: &SyntaxNode,
396     ) -> Result<(), MatchFailed> {
397         // Build a map keyed by field name.
398         let mut fields_by_name: FxHashMap<SmolStr, SyntaxNode> = FxHashMap::default();
399         for child in code.children() {
400             if let Some(record) = ast::RecordExprField::cast(child.clone()) {
401                 if let Some(name) = record.field_name() {
402                     fields_by_name.insert(name.text().into(), child.clone());
403                 }
404             }
405         }
406         for p in pattern.children_with_tokens() {
407             if let SyntaxElement::Node(p) = p {
408                 if let Some(name_element) = p.first_child_or_token() {
409                     if self.get_placeholder(&name_element).is_some() {
410                         // If the pattern is using placeholders for field names then order
411                         // independence doesn't make sense. Fall back to regular ordered
412                         // matching.
413                         return self.attempt_match_node_children(phase, pattern, code);
414                     }
415                     if let Some(ident) = only_ident(name_element) {
416                         let code_record = fields_by_name.remove(ident.text()).ok_or_else(|| {
417                             match_error!(
418                                 "Placeholder has record field '{}', but code doesn't",
419                                 ident
420                             )
421                         })?;
422                         self.attempt_match_node(phase, &p, &code_record)?;
423                     }
424                 }
425             }
426         }
427         if let Some(unmatched_fields) = fields_by_name.keys().next() {
428             fail_match!(
429                 "{} field(s) of a record literal failed to match, starting with {}",
430                 fields_by_name.len(),
431                 unmatched_fields
432             );
433         }
434         Ok(())
435     }
436 
437     /// Outside of token trees, a placeholder can only match a single AST node, whereas in a token
438     /// tree it can match a sequence of tokens. Note, that this code will only be used when the
439     /// pattern matches the macro invocation. For matches within the macro call, we'll already have
440     /// expanded the macro.
attempt_match_token_tree( &self, phase: &mut Phase<'_>, pattern: &SyntaxNode, code: &syntax::SyntaxNode, ) -> Result<(), MatchFailed>441     fn attempt_match_token_tree(
442         &self,
443         phase: &mut Phase<'_>,
444         pattern: &SyntaxNode,
445         code: &syntax::SyntaxNode,
446     ) -> Result<(), MatchFailed> {
447         let mut pattern = PatternIterator::new(pattern).peekable();
448         let mut children = code.children_with_tokens();
449         while let Some(child) = children.next() {
450             if let Some(placeholder) = pattern.peek().and_then(|p| self.get_placeholder(p)) {
451                 pattern.next();
452                 let next_pattern_token = pattern
453                     .peek()
454                     .and_then(|p| match p {
455                         SyntaxElement::Token(t) => Some(t.clone()),
456                         SyntaxElement::Node(n) => n.first_token(),
457                     })
458                     .map(|p| p.text().to_string());
459                 let first_matched_token = child.clone();
460                 let mut last_matched_token = child;
461                 // Read code tokens util we reach one equal to the next token from our pattern
462                 // or we reach the end of the token tree.
463                 for next in &mut children {
464                     match &next {
465                         SyntaxElement::Token(t) => {
466                             if Some(t.to_string()) == next_pattern_token {
467                                 pattern.next();
468                                 break;
469                             }
470                         }
471                         SyntaxElement::Node(n) => {
472                             if let Some(first_token) = n.first_token() {
473                                 if Some(first_token.text()) == next_pattern_token.as_deref() {
474                                     if let Some(SyntaxElement::Node(p)) = pattern.next() {
475                                         // We have a subtree that starts with the next token in our pattern.
476                                         self.attempt_match_token_tree(phase, &p, n)?;
477                                         break;
478                                     }
479                                 }
480                             }
481                         }
482                     };
483                     last_matched_token = next;
484                 }
485                 if let Phase::Second(match_out) = phase {
486                     match_out.placeholder_values.insert(
487                         placeholder.ident.clone(),
488                         PlaceholderMatch::from_range(FileRange {
489                             file_id: self.sema.original_range(code).file_id,
490                             range: first_matched_token
491                                 .text_range()
492                                 .cover(last_matched_token.text_range()),
493                         }),
494                     );
495                 }
496                 continue;
497             }
498             // Match literal (non-placeholder) tokens.
499             match child {
500                 SyntaxElement::Token(token) => {
501                     self.attempt_match_token(phase, &mut pattern, &token)?;
502                 }
503                 SyntaxElement::Node(node) => match pattern.next() {
504                     Some(SyntaxElement::Node(p)) => {
505                         self.attempt_match_token_tree(phase, &p, &node)?;
506                     }
507                     Some(SyntaxElement::Token(p)) => fail_match!(
508                         "Pattern has token '{}', code has subtree '{}'",
509                         p.text(),
510                         node.text()
511                     ),
512                     None => fail_match!("Pattern has nothing, code has '{}'", node.text()),
513                 },
514             }
515         }
516         if let Some(p) = pattern.next() {
517             fail_match!("Reached end of token tree in code, but pattern still has {:?}", p);
518         }
519         Ok(())
520     }
521 
attempt_match_ufcs_to_method_call( &self, phase: &mut Phase<'_>, pattern_ufcs: &UfcsCallInfo, code: &ast::MethodCallExpr, ) -> Result<(), MatchFailed>522     fn attempt_match_ufcs_to_method_call(
523         &self,
524         phase: &mut Phase<'_>,
525         pattern_ufcs: &UfcsCallInfo,
526         code: &ast::MethodCallExpr,
527     ) -> Result<(), MatchFailed> {
528         use ast::HasArgList;
529         let code_resolved_function = self
530             .sema
531             .resolve_method_call(code)
532             .ok_or_else(|| match_error!("Failed to resolve method call"))?;
533         if pattern_ufcs.function != code_resolved_function {
534             fail_match!("Method call resolved to a different function");
535         }
536         // Check arguments.
537         let mut pattern_args = pattern_ufcs
538             .call_expr
539             .arg_list()
540             .ok_or_else(|| match_error!("Pattern function call has no args"))?
541             .args();
542         // If the function we're calling takes a self parameter, then we store additional
543         // information on the placeholder match about autoderef and autoref. This allows us to use
544         // the placeholder in a context where autoderef and autoref don't apply.
545         if code_resolved_function.self_param(self.sema.db).is_some() {
546             if let (Some(pattern_type), Some(expr)) =
547                 (&pattern_ufcs.qualifier_type, &code.receiver())
548             {
549                 let deref_count = self.check_expr_type(pattern_type, expr)?;
550                 let pattern_receiver = pattern_args.next();
551                 self.attempt_match_opt(phase, pattern_receiver.clone(), code.receiver())?;
552                 if let Phase::Second(match_out) = phase {
553                     if let Some(placeholder_value) = pattern_receiver
554                         .and_then(|n| self.get_placeholder_for_node(n.syntax()))
555                         .and_then(|placeholder| {
556                             match_out.placeholder_values.get_mut(&placeholder.ident)
557                         })
558                     {
559                         placeholder_value.autoderef_count = deref_count;
560                         placeholder_value.autoref_kind = self
561                             .sema
562                             .resolve_method_call_as_callable(code)
563                             .and_then(|callable| callable.receiver_param(self.sema.db))
564                             .map(|(self_param, _)| self_param.kind())
565                             .unwrap_or(ast::SelfParamKind::Owned);
566                     }
567                 }
568             }
569         } else {
570             self.attempt_match_opt(phase, pattern_args.next(), code.receiver())?;
571         }
572         let mut code_args =
573             code.arg_list().ok_or_else(|| match_error!("Code method call has no args"))?.args();
574         loop {
575             match (pattern_args.next(), code_args.next()) {
576                 (None, None) => return Ok(()),
577                 (p, c) => self.attempt_match_opt(phase, p, c)?,
578             }
579         }
580     }
581 
attempt_match_ufcs_to_ufcs( &self, phase: &mut Phase<'_>, pattern_ufcs: &UfcsCallInfo, code: &ast::CallExpr, ) -> Result<(), MatchFailed>582     fn attempt_match_ufcs_to_ufcs(
583         &self,
584         phase: &mut Phase<'_>,
585         pattern_ufcs: &UfcsCallInfo,
586         code: &ast::CallExpr,
587     ) -> Result<(), MatchFailed> {
588         use ast::HasArgList;
589         // Check that the first argument is the expected type.
590         if let (Some(pattern_type), Some(expr)) = (
591             &pattern_ufcs.qualifier_type,
592             &code.arg_list().and_then(|code_args| code_args.args().next()),
593         ) {
594             self.check_expr_type(pattern_type, expr)?;
595         }
596         self.attempt_match_node_children(phase, pattern_ufcs.call_expr.syntax(), code.syntax())
597     }
598 
599     /// Verifies that `expr` matches `pattern_type`, possibly after dereferencing some number of
600     /// times. Returns the number of times it needed to be dereferenced.
check_expr_type( &self, pattern_type: &hir::Type, expr: &ast::Expr, ) -> Result<usize, MatchFailed>601     fn check_expr_type(
602         &self,
603         pattern_type: &hir::Type,
604         expr: &ast::Expr,
605     ) -> Result<usize, MatchFailed> {
606         use hir::HirDisplay;
607         let code_type = self
608             .sema
609             .type_of_expr(expr)
610             .ok_or_else(|| {
611                 match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
612             })?
613             .original;
614         // Temporary needed to make the borrow checker happy.
615         let res = code_type
616             .autoderef(self.sema.db)
617             .enumerate()
618             .find(|(_, deref_code_type)| pattern_type == deref_code_type)
619             .map(|(count, _)| count)
620             .ok_or_else(|| {
621                 match_error!(
622                     "Pattern type `{}` didn't match code type `{}`",
623                     pattern_type.display(self.sema.db),
624                     code_type.display(self.sema.db)
625                 )
626             });
627         res
628     }
629 
get_placeholder_for_node(&self, node: &SyntaxNode) -> Option<&Placeholder>630     fn get_placeholder_for_node(&self, node: &SyntaxNode) -> Option<&Placeholder> {
631         self.get_placeholder(&SyntaxElement::Node(node.clone()))
632     }
633 
get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder>634     fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> {
635         only_ident(element.clone()).and_then(|ident| self.rule.get_placeholder(&ident))
636     }
637 }
638 
639 impl Match {
render_template_paths( &mut self, template: &ResolvedPattern, sema: &Semantics<'_, ide_db::RootDatabase>, ) -> Result<(), MatchFailed>640     fn render_template_paths(
641         &mut self,
642         template: &ResolvedPattern,
643         sema: &Semantics<'_, ide_db::RootDatabase>,
644     ) -> Result<(), MatchFailed> {
645         let module = sema
646             .scope(&self.matched_node)
647             .ok_or_else(|| match_error!("Matched node isn't in a module"))?
648             .module();
649         for (path, resolved_path) in &template.resolved_paths {
650             if let hir::PathResolution::Def(module_def) = resolved_path.resolution {
651                 let mod_path =
652                     module.find_use_path(sema.db, module_def, false).ok_or_else(|| {
653                         match_error!("Failed to render template path `{}` at match location")
654                     })?;
655                 self.rendered_template_paths.insert(path.clone(), mod_path);
656             }
657         }
658         Ok(())
659     }
660 }
661 
662 impl Phase<'_> {
next_non_trivial(&mut self, code_it: &mut SyntaxElementChildren) -> Option<SyntaxElement>663     fn next_non_trivial(&mut self, code_it: &mut SyntaxElementChildren) -> Option<SyntaxElement> {
664         loop {
665             let c = code_it.next();
666             if let Some(SyntaxElement::Token(t)) = &c {
667                 self.record_ignored_comments(t);
668                 if t.kind().is_trivia() {
669                     continue;
670                 }
671             }
672             return c;
673         }
674     }
675 
record_ignored_comments(&mut self, token: &SyntaxToken)676     fn record_ignored_comments(&mut self, token: &SyntaxToken) {
677         if token.kind() == SyntaxKind::COMMENT {
678             if let Phase::Second(match_out) = self {
679                 if let Some(comment) = ast::Comment::cast(token.clone()) {
680                     match_out.ignored_comments.push(comment);
681                 }
682             }
683         }
684     }
685 }
686 
is_closing_token(kind: SyntaxKind) -> bool687 fn is_closing_token(kind: SyntaxKind) -> bool {
688     kind == SyntaxKind::R_PAREN || kind == SyntaxKind::R_CURLY || kind == SyntaxKind::R_BRACK
689 }
690 
record_match_fails_reasons_scope<F, T>(debug_active: bool, f: F) -> T where F: Fn() -> T,691 pub(crate) fn record_match_fails_reasons_scope<F, T>(debug_active: bool, f: F) -> T
692 where
693     F: Fn() -> T,
694 {
695     RECORDING_MATCH_FAIL_REASONS.with(|c| c.set(debug_active));
696     let res = f();
697     RECORDING_MATCH_FAIL_REASONS.with(|c| c.set(false));
698     res
699 }
700 
701 // For performance reasons, we don't want to record the reason why every match fails, only the bit
702 // of code that the user indicated they thought would match. We use a thread local to indicate when
703 // we are trying to match that bit of code. This saves us having to pass a boolean into all the bits
704 // of code that can make the decision to not match.
705 thread_local! {
706     pub static RECORDING_MATCH_FAIL_REASONS: Cell<bool> = Cell::new(false);
707 }
708 
recording_match_fail_reasons() -> bool709 fn recording_match_fail_reasons() -> bool {
710     RECORDING_MATCH_FAIL_REASONS.with(|c| c.get())
711 }
712 
713 impl PlaceholderMatch {
from_range(range: FileRange) -> Self714     fn from_range(range: FileRange) -> Self {
715         Self {
716             range,
717             inner_matches: SsrMatches::default(),
718             autoderef_count: 0,
719             autoref_kind: ast::SelfParamKind::Owned,
720         }
721     }
722 }
723 
724 impl NodeKind {
matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed>725     fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> {
726         let ok = match self {
727             Self::Literal => {
728                 cov_mark::hit!(literal_constraint);
729                 ast::Literal::can_cast(node.kind())
730             }
731         };
732         if !ok {
733             fail_match!("Code '{}' isn't of kind {:?}", node.text(), self);
734         }
735         Ok(())
736     }
737 }
738 
739 // If `node` contains nothing but an ident then return it, otherwise return None.
only_ident(element: SyntaxElement) -> Option<SyntaxToken>740 fn only_ident(element: SyntaxElement) -> Option<SyntaxToken> {
741     match element {
742         SyntaxElement::Token(t) => {
743             if t.kind() == SyntaxKind::IDENT {
744                 return Some(t);
745             }
746         }
747         SyntaxElement::Node(n) => {
748             let mut children = n.children_with_tokens();
749             if let (Some(only_child), None) = (children.next(), children.next()) {
750                 return only_ident(only_child);
751             }
752         }
753     }
754     None
755 }
756 
757 struct PatternIterator {
758     iter: SyntaxElementChildren,
759 }
760 
761 impl Iterator for PatternIterator {
762     type Item = SyntaxElement;
763 
next(&mut self) -> Option<SyntaxElement>764     fn next(&mut self) -> Option<SyntaxElement> {
765         for element in &mut self.iter {
766             if !element.kind().is_trivia() {
767                 return Some(element);
768             }
769         }
770         None
771     }
772 }
773 
774 impl PatternIterator {
new(parent: &SyntaxNode) -> Self775     fn new(parent: &SyntaxNode) -> Self {
776         Self { iter: parent.children_with_tokens() }
777     }
778 }
779 
780 #[cfg(test)]
781 mod tests {
782     use crate::{MatchFinder, SsrRule};
783 
784     #[test]
parse_match_replace()785     fn parse_match_replace() {
786         let rule: SsrRule = "foo($x) ==>> bar($x)".parse().unwrap();
787         let input = "fn foo() {} fn bar() {} fn main() { foo(1+2); }";
788 
789         let (db, position, selections) = crate::tests::single_file(input);
790         let mut match_finder = MatchFinder::in_context(&db, position, selections).unwrap();
791         match_finder.add_rule(rule).unwrap();
792         let matches = match_finder.matches();
793         assert_eq!(matches.matches.len(), 1);
794         assert_eq!(matches.matches[0].matched_node.text(), "foo(1+2)");
795         assert_eq!(matches.matches[0].placeholder_values.len(), 1);
796 
797         let edits = match_finder.edits();
798         assert_eq!(edits.len(), 1);
799         let edit = &edits[&position.file_id];
800         let mut after = input.to_string();
801         edit.apply(&mut after);
802         assert_eq!(after, "fn foo() {} fn bar() {} fn main() { bar(1+2); }");
803     }
804 }
805