22//! process of matching, placeholder values are recorded.
33
44use crate :: {
5- parsing:: { Constraint , NodeKind , ParsedRule , Placeholder } ,
5+ parsing:: { Constraint , NodeKind , Placeholder } ,
6+ resolving:: { ResolvedPattern , ResolvedRule } ,
67 SsrMatches ,
78} ;
89use hir:: Semantics ;
@@ -51,6 +52,8 @@ pub struct Match {
5152 pub ( crate ) rule_index : usize ,
5253 /// The depth of matched_node.
5354 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 > ,
5457}
5558
5659/// Represents a `$var` in an SSR query.
@@ -86,7 +89,7 @@ pub(crate) struct MatchFailed {
8689/// parent module, we don't populate nested matches.
8790pub ( crate ) fn get_match (
8891 debug_active : bool ,
89- rule : & ParsedRule ,
92+ rule : & ResolvedRule ,
9093 code : & SyntaxNode ,
9194 restrict_range : & Option < FileRange > ,
9295 sema : & Semantics < ra_ide_db:: RootDatabase > ,
@@ -102,7 +105,7 @@ struct Matcher<'db, 'sema> {
102105 /// If any placeholders come from anywhere outside of this range, then the match will be
103106 /// rejected.
104107 restrict_range : Option < FileRange > ,
105- rule : & ' sema ParsedRule ,
108+ rule : & ' sema ResolvedRule ,
106109}
107110
108111/// Which phase of matching we're currently performing. We do two phases because most attempted
@@ -117,14 +120,14 @@ enum Phase<'a> {
117120
118121impl < ' db , ' sema > Matcher < ' db , ' sema > {
119122 fn try_match (
120- rule : & ParsedRule ,
123+ rule : & ResolvedRule ,
121124 code : & SyntaxNode ,
122125 restrict_range : & Option < FileRange > ,
123126 sema : & ' sema Semantics < ' db , ra_ide_db:: RootDatabase > ,
124127 ) -> Result < Match , MatchFailed > {
125128 let match_state = Matcher { sema, restrict_range : restrict_range. clone ( ) , rule } ;
126129 // First pass at matching, where we check that node types and idents match.
127- match_state. attempt_match_node ( & mut Phase :: First , & rule. pattern , code) ?;
130+ match_state. attempt_match_node ( & mut Phase :: First , & rule. pattern . node , code) ?;
128131 match_state. validate_range ( & sema. original_range ( code) ) ?;
129132 let mut the_match = Match {
130133 range : sema. original_range ( code) ,
@@ -133,11 +136,19 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
133136 ignored_comments : Vec :: new ( ) ,
134137 rule_index : rule. index ,
135138 depth : 0 ,
139+ rendered_template_paths : FxHashMap :: default ( ) ,
136140 } ;
137141 // Second matching pass, where we record placeholder matches, ignored comments and maybe do
138142 // any other more expensive checks that we didn't want to do on the first pass.
139- match_state. attempt_match_node ( & mut Phase :: Second ( & mut the_match) , & rule. pattern , code) ?;
143+ match_state. attempt_match_node (
144+ & mut Phase :: Second ( & mut the_match) ,
145+ & rule. pattern . node ,
146+ code,
147+ ) ?;
140148 the_match. depth = sema. ancestors_with_macros ( the_match. matched_node . clone ( ) ) . count ( ) ;
149+ if let Some ( template) = & rule. template {
150+ the_match. render_template_paths ( template, sema) ?;
151+ }
141152 Ok ( the_match)
142153 }
143154
@@ -195,6 +206,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
195206 self . attempt_match_record_field_list ( phase, pattern, code)
196207 }
197208 SyntaxKind :: TOKEN_TREE => self . attempt_match_token_tree ( phase, pattern, code) ,
209+ SyntaxKind :: PATH => self . attempt_match_path ( phase, pattern, code) ,
198210 _ => self . attempt_match_node_children ( phase, pattern, code) ,
199211 }
200212 }
@@ -311,6 +323,64 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
311323 Ok ( ( ) )
312324 }
313325
326+ /// Paths are matched based on whether they refer to the same thing, even if they're written
327+ /// differently.
328+ fn attempt_match_path (
329+ & self ,
330+ phase : & mut Phase ,
331+ pattern : & SyntaxNode ,
332+ code : & SyntaxNode ,
333+ ) -> Result < ( ) , MatchFailed > {
334+ if let Some ( pattern_resolved) = self . rule . pattern . resolved_paths . get ( pattern) {
335+ let pattern_path = ast:: Path :: cast ( pattern. clone ( ) ) . unwrap ( ) ;
336+ let code_path = ast:: Path :: cast ( code. clone ( ) ) . unwrap ( ) ;
337+ if let ( Some ( pattern_segment) , Some ( code_segment) ) =
338+ ( pattern_path. segment ( ) , code_path. segment ( ) )
339+ {
340+ // Match everything within the segment except for the name-ref, which is handled
341+ // separately via comparing what the path resolves to below.
342+ self . attempt_match_opt (
343+ phase,
344+ pattern_segment. type_arg_list ( ) ,
345+ code_segment. type_arg_list ( ) ,
346+ ) ?;
347+ self . attempt_match_opt (
348+ phase,
349+ pattern_segment. param_list ( ) ,
350+ code_segment. param_list ( ) ,
351+ ) ?;
352+ }
353+ if matches ! ( phase, Phase :: Second ( _) ) {
354+ let resolution = self
355+ . sema
356+ . resolve_path ( & code_path)
357+ . ok_or_else ( || match_error ! ( "Failed to resolve path `{}`" , code. text( ) ) ) ?;
358+ if pattern_resolved. resolution != resolution {
359+ fail_match ! ( "Pattern had path `{}` code had `{}`" , pattern. text( ) , code. text( ) ) ;
360+ }
361+ }
362+ } else {
363+ return self . attempt_match_node_children ( phase, pattern, code) ;
364+ }
365+ Ok ( ( ) )
366+ }
367+
368+ fn attempt_match_opt < T : AstNode > (
369+ & self ,
370+ phase : & mut Phase ,
371+ pattern : Option < T > ,
372+ code : Option < T > ,
373+ ) -> Result < ( ) , MatchFailed > {
374+ match ( pattern, code) {
375+ ( Some ( p) , Some ( c) ) => self . attempt_match_node ( phase, & p. syntax ( ) , & c. syntax ( ) ) ,
376+ ( None , None ) => Ok ( ( ) ) ,
377+ ( Some ( p) , None ) => fail_match ! ( "Pattern `{}` had nothing to match" , p. syntax( ) . text( ) ) ,
378+ ( None , Some ( c) ) => {
379+ fail_match ! ( "Nothing in pattern to match code `{}`" , c. syntax( ) . text( ) )
380+ }
381+ }
382+ }
383+
314384 /// We want to allow the records to match in any order, so we have special matching logic for
315385 /// them.
316386 fn attempt_match_record_field_list (
@@ -449,6 +519,28 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
449519 }
450520}
451521
522+ impl Match {
523+ fn render_template_paths (
524+ & mut self ,
525+ template : & ResolvedPattern ,
526+ sema : & Semantics < ra_ide_db:: RootDatabase > ,
527+ ) -> Result < ( ) , MatchFailed > {
528+ let module = sema
529+ . scope ( & self . matched_node )
530+ . module ( )
531+ . ok_or_else ( || match_error ! ( "Matched node isn't in a module" ) ) ?;
532+ for ( path, resolved_path) in & template. resolved_paths {
533+ if let hir:: PathResolution :: Def ( module_def) = resolved_path. resolution {
534+ let mod_path = module. find_use_path ( sema. db , module_def) . ok_or_else ( || {
535+ match_error ! ( "Failed to render template path `{}` at match location" )
536+ } ) ?;
537+ self . rendered_template_paths . insert ( path. clone ( ) , mod_path) ;
538+ }
539+ }
540+ Ok ( ( ) )
541+ }
542+ }
543+
452544impl Phase < ' _ > {
453545 fn next_non_trivial ( & mut self , code_it : & mut SyntaxElementChildren ) -> Option < SyntaxElement > {
454546 loop {
@@ -578,7 +670,7 @@ mod tests {
578670
579671 let ( db, position) = crate :: tests:: single_file ( input) ;
580672 let mut match_finder = MatchFinder :: in_context ( & db, position) ;
581- match_finder. add_rule ( rule) ;
673+ match_finder. add_rule ( rule) . unwrap ( ) ;
582674 let matches = match_finder. matches ( ) ;
583675 assert_eq ! ( matches. matches. len( ) , 1 ) ;
584676 assert_eq ! ( matches. matches[ 0 ] . matched_node. text( ) , "foo(1+2)" ) ;
0 commit comments