@@ -789,6 +789,9 @@ impl<'a> Iterator for TokenIter<'a> {
789789 }
790790}
791791
792+ /// Used to know if a keyword followed by a `!` should never be treated as a macro.
793+ const KEYWORDS_FOLLOWABLE_BY_VALUE : & [ & str ] = & [ "if" , "while" , "match" , "break" , "return" ] ;
794+
792795/// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
793796/// just the next item by using `peek_next`. The `peek` method always returns the next item after
794797/// the current one whereas `peek_next` will return the next item after the last one peeked.
@@ -1010,6 +1013,19 @@ impl<'src> Classifier<'src> {
10101013 }
10111014 }
10121015
1016+ fn new_macro_span (
1017+ & mut self ,
1018+ text : & ' src str ,
1019+ sink : & mut dyn FnMut ( Span , Highlight < ' src > ) ,
1020+ before : u32 ,
1021+ file_span : Span ,
1022+ ) {
1023+ self . in_macro = true ;
1024+ let span = new_span ( before, text, file_span) ;
1025+ sink ( DUMMY_SP , Highlight :: EnterSpan { class : Class :: Macro ( span) } ) ;
1026+ sink ( span, Highlight :: Token { text, class : None } ) ;
1027+ }
1028+
10131029 /// Single step of highlighting. This will classify `token`, but maybe also a couple of
10141030 /// following ones as well.
10151031 ///
@@ -1216,16 +1232,49 @@ impl<'src> Classifier<'src> {
12161232 LiteralKind :: Float { .. } | LiteralKind :: Int { .. } => Class :: Number ,
12171233 } ,
12181234 TokenKind :: GuardedStrPrefix => return no_highlight ( sink) ,
1219- TokenKind :: Ident | TokenKind :: RawIdent
1220- if let Some ( ( TokenKind :: Bang , _) ) = self . peek_non_trivia ( ) =>
1221- {
1235+ TokenKind :: RawIdent if let Some ( ( TokenKind :: Bang , _) ) = self . peek_non_trivia ( ) => {
12221236 self . in_macro = true ;
12231237 let span = new_span ( before, text, file_span) ;
12241238 sink ( DUMMY_SP , Highlight :: EnterSpan { class : Class :: Macro ( span) } ) ;
12251239 sink ( span, Highlight :: Token { text, class : None } ) ;
12261240 return ;
12271241 }
1228- TokenKind :: Ident => self . classify_ident ( before, text) ,
1242+ // Macro non-terminals (meta vars) take precedence.
1243+ TokenKind :: Ident if self . in_macro_nonterminal => {
1244+ self . in_macro_nonterminal = false ;
1245+ Class :: MacroNonTerminal
1246+ }
1247+ TokenKind :: Ident => {
1248+ let file_span = self . file_span ;
1249+ let span = || new_span ( before, text, file_span) ;
1250+
1251+ match text {
1252+ "ref" | "mut" => Class :: RefKeyWord ,
1253+ "false" | "true" => Class :: Bool ,
1254+ "self" | "Self" => Class :: Self_ ( span ( ) ) ,
1255+ "Option" | "Result" => Class :: PreludeTy ( span ( ) ) ,
1256+ "Some" | "None" | "Ok" | "Err" => Class :: PreludeVal ( span ( ) ) ,
1257+ _ if self . is_weak_keyword ( text) || is_keyword ( Symbol :: intern ( text) ) => {
1258+ // So if it's not a keyword which can be followed by a value (like `if` or
1259+ // `return`) and the next non-whitespace token is a `!`, then we consider
1260+ // it's a macro.
1261+ if !KEYWORDS_FOLLOWABLE_BY_VALUE . contains ( & text)
1262+ && matches ! ( self . peek_non_trivia( ) , Some ( ( TokenKind :: Bang , _) ) )
1263+ {
1264+ self . new_macro_span ( text, sink, before, file_span) ;
1265+ return ;
1266+ }
1267+ Class :: KeyWord
1268+ }
1269+ // If it's not a keyword and the next non whitespace token is a `!`, then
1270+ // we consider it's a macro.
1271+ _ if matches ! ( self . peek_non_trivia( ) , Some ( ( TokenKind :: Bang , _) ) ) => {
1272+ self . new_macro_span ( text, sink, before, file_span) ;
1273+ return ;
1274+ }
1275+ _ => Class :: Ident ( span ( ) ) ,
1276+ }
1277+ }
12291278 TokenKind :: RawIdent | TokenKind :: UnknownPrefix | TokenKind :: InvalidIdent => {
12301279 Class :: Ident ( new_span ( before, text, file_span) )
12311280 }
@@ -1246,27 +1295,6 @@ impl<'src> Classifier<'src> {
12461295 }
12471296 }
12481297
1249- fn classify_ident ( & mut self , before : u32 , text : & ' src str ) -> Class {
1250- // Macro non-terminals (meta vars) take precedence.
1251- if self . in_macro_nonterminal {
1252- self . in_macro_nonterminal = false ;
1253- return Class :: MacroNonTerminal ;
1254- }
1255-
1256- let file_span = self . file_span ;
1257- let span = || new_span ( before, text, file_span) ;
1258-
1259- match text {
1260- "ref" | "mut" => Class :: RefKeyWord ,
1261- "false" | "true" => Class :: Bool ,
1262- "self" | "Self" => Class :: Self_ ( span ( ) ) ,
1263- "Option" | "Result" => Class :: PreludeTy ( span ( ) ) ,
1264- "Some" | "None" | "Ok" | "Err" => Class :: PreludeVal ( span ( ) ) ,
1265- _ if self . is_weak_keyword ( text) || is_keyword ( Symbol :: intern ( text) ) => Class :: KeyWord ,
1266- _ => Class :: Ident ( span ( ) ) ,
1267- }
1268- }
1269-
12701298 fn is_weak_keyword ( & mut self , text : & str ) -> bool {
12711299 // NOTE: `yeet` (`do yeet $expr`), `catch` (`do catch $block`), `default` (specialization),
12721300 // `contract_{ensures,requires}`, `builtin` (builtin_syntax) & `reuse` (fn_delegation) are
0 commit comments