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