@@ -68,7 +68,7 @@ pub(crate) fn highlight(
6868 // When we leave a node, the we use it to flatten the highlighted ranges.
6969 let mut stack = HighlightedRangeStack :: new ( ) ;
7070
71- let mut current_macro_call: Option < ast:: MacroCall > = None ;
71+ let mut current_macro_call: Option < ( ast:: MacroCall , Option < MacroMatcherParseState > ) > = None ;
7272 let mut format_string: Option < SyntaxElement > = None ;
7373
7474 // Walk all nodes, keeping track of whether we are inside a macro or not.
@@ -92,15 +92,16 @@ pub(crate) fn highlight(
9292 // Track "inside macro" state
9393 match event. clone ( ) . map ( |it| it. into_node ( ) . and_then ( ast:: MacroCall :: cast) ) {
9494 WalkEvent :: Enter ( Some ( mc) ) => {
95- current_macro_call = Some ( mc. clone ( ) ) ;
9695 if let Some ( range) = macro_call_range ( & mc) {
9796 stack. add ( HighlightedRange {
9897 range,
9998 highlight : HighlightTag :: Macro . into ( ) ,
10099 binding_hash : None ,
101100 } ) ;
102101 }
102+ let mut is_macro_rules = None ;
103103 if let Some ( name) = mc. is_macro_rules ( ) {
104+ is_macro_rules = Some ( MacroMatcherParseState :: new ( ) ) ;
104105 if let Some ( ( highlight, binding_hash) ) = highlight_element (
105106 & sema,
106107 & mut bindings_shadow_count,
@@ -114,10 +115,11 @@ pub(crate) fn highlight(
114115 } ) ;
115116 }
116117 }
118+ current_macro_call = Some ( ( mc. clone ( ) , is_macro_rules) ) ;
117119 continue ;
118120 }
119121 WalkEvent :: Leave ( Some ( mc) ) => {
120- assert ! ( current_macro_call == Some ( mc) ) ;
122+ assert ! ( current_macro_call. map ( |it| it . 0 ) == Some ( mc) ) ;
121123 current_macro_call = None ;
122124 format_string = None ;
123125 }
@@ -146,6 +148,20 @@ pub(crate) fn highlight(
146148 WalkEvent :: Leave ( _) => continue ,
147149 } ;
148150
151+ // check if in matcher part of a macro_rules rule
152+ if let Some ( ( _, Some ( ref mut state) ) ) = current_macro_call {
153+ if let Some ( tok) = element. as_token ( ) {
154+ if matches ! (
155+ update_macro_rules_state( tok, state) ,
156+ RuleState :: Matcher | RuleState :: Expander
157+ ) {
158+ if skip_metavariables ( element. clone ( ) ) {
159+ continue ;
160+ }
161+ }
162+ }
163+ }
164+
149165 let range = element. text_range ( ) ;
150166
151167 let element_to_highlight = if current_macro_call. is_some ( ) && element. kind ( ) != COMMENT {
@@ -918,3 +934,99 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
918934 _ => default. into ( ) ,
919935 }
920936}
937+
938+ struct MacroMatcherParseState {
939+ /// Opening and corresponding closing bracket of the matcher or expander of the current rule
940+ paren_ty : Option < ( SyntaxKind , SyntaxKind ) > ,
941+ paren_level : usize ,
942+ rule_state : RuleState ,
943+ /// Whether we are inside the outer `{` `}` macro block that holds the rules
944+ in_invoc_body : bool ,
945+ }
946+
947+ impl MacroMatcherParseState {
948+ fn new ( ) -> Self {
949+ MacroMatcherParseState {
950+ paren_ty : None ,
951+ paren_level : 0 ,
952+ in_invoc_body : false ,
953+ rule_state : RuleState :: None ,
954+ }
955+ }
956+ }
957+
958+ #[ derive( Copy , Clone , PartialEq ) ]
959+ enum RuleState {
960+ Matcher ,
961+ Expander ,
962+ Between ,
963+ None ,
964+ }
965+
966+ impl RuleState {
967+ fn transition ( & mut self ) {
968+ * self = match self {
969+ RuleState :: Matcher => RuleState :: Between ,
970+ RuleState :: Expander => RuleState :: None ,
971+ RuleState :: Between => RuleState :: Expander ,
972+ RuleState :: None => RuleState :: Matcher ,
973+ } ;
974+ }
975+ }
976+
977+ fn update_macro_rules_state ( tok : & SyntaxToken , state : & mut MacroMatcherParseState ) -> RuleState {
978+ if !state. in_invoc_body {
979+ if tok. kind ( ) == T ! [ '{' ] {
980+ state. in_invoc_body = true ;
981+ }
982+ return state. rule_state ;
983+ }
984+
985+ match state. paren_ty {
986+ Some ( ( open, close) ) => {
987+ if tok. kind ( ) == open {
988+ state. paren_level += 1 ;
989+ } else if tok. kind ( ) == close {
990+ state. paren_level -= 1 ;
991+ if state. paren_level == 0 {
992+ let res = state. rule_state ;
993+ state. rule_state . transition ( ) ;
994+ state. paren_ty = None ;
995+ return res;
996+ }
997+ }
998+ }
999+ None => {
1000+ match tok. kind ( ) {
1001+ T ! [ '(' ] => {
1002+ state. paren_ty = Some ( ( T ! [ '(' ] , T ! [ ')' ] ) ) ;
1003+ }
1004+ T ! [ '{' ] => {
1005+ state. paren_ty = Some ( ( T ! [ '{' ] , T ! [ '}' ] ) ) ;
1006+ }
1007+ T ! [ '[' ] => {
1008+ state. paren_ty = Some ( ( T ! [ '[' ] , T ! [ ']' ] ) ) ;
1009+ }
1010+ _ => ( ) ,
1011+ }
1012+ if state. paren_ty . is_some ( ) {
1013+ state. paren_level = 1 ;
1014+ state. rule_state . transition ( ) ;
1015+ }
1016+ }
1017+ }
1018+ state. rule_state
1019+ }
1020+
1021+ fn skip_metavariables ( element : SyntaxElement ) -> bool {
1022+ let tok = match element. as_token ( ) {
1023+ Some ( tok) => tok,
1024+ None => return false ,
1025+ } ;
1026+ let is_fragment = || tok. prev_token ( ) . map ( |tok| tok. kind ( ) ) == Some ( T ! [ $] ) ;
1027+ match tok. kind ( ) {
1028+ IDENT if is_fragment ( ) => true ,
1029+ kind if kind. is_keyword ( ) && is_fragment ( ) => true ,
1030+ _ => false ,
1031+ }
1032+ }
0 commit comments