@@ -798,6 +798,9 @@ fn get_real_ident_class(text: &str, allow_path_keywords: bool) -> Option<Class>
798798 } )
799799}
800800
801+ /// Used to know if a keyword followed by a `!` should never be treated as a macro.
802+ const KEYWORDS_FOLLOWABLE_BY_VALUE : & [ & str ] = & [ "if" , "while" , "match" , "break" , "return" ] ;
803+
801804/// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
802805/// just the next item by using `peek_next`. The `peek` method always returns the next item after
803806/// the current one whereas `peek_next` will return the next item after the last one peeked.
@@ -1017,6 +1020,19 @@ impl<'src> Classifier<'src> {
10171020 }
10181021 }
10191022
1023+ fn new_macro_span (
1024+ & mut self ,
1025+ text : & ' src str ,
1026+ sink : & mut dyn FnMut ( Span , Highlight < ' src > ) ,
1027+ before : u32 ,
1028+ file_span : Span ,
1029+ ) {
1030+ self . in_macro = true ;
1031+ let span = new_span ( before, text, file_span) ;
1032+ sink ( DUMMY_SP , Highlight :: EnterSpan { class : Class :: Macro ( span) } ) ;
1033+ sink ( span, Highlight :: Token { text, class : None } ) ;
1034+ }
1035+
10201036 /// Single step of highlighting. This will classify `token`, but maybe also a couple of
10211037 /// following ones as well.
10221038 ///
@@ -1223,17 +1239,18 @@ impl<'src> Classifier<'src> {
12231239 LiteralKind :: Float { .. } | LiteralKind :: Int { .. } => Class :: Number ,
12241240 } ,
12251241 TokenKind :: GuardedStrPrefix => return no_highlight ( sink) ,
1226- TokenKind :: Ident | TokenKind :: RawIdent
1227- if self . peek_non_whitespace ( ) == Some ( TokenKind :: Bang ) =>
1228- {
1229- self . in_macro = true ;
1230- let span = new_span ( before, text, file_span) ;
1231- sink ( DUMMY_SP , Highlight :: EnterSpan { class : Class :: Macro ( span) } ) ;
1232- sink ( span, Highlight :: Token { text, class : None } ) ;
1242+ TokenKind :: RawIdent if self . peek_non_whitespace ( ) == Some ( TokenKind :: Bang ) => {
1243+ self . new_macro_span ( text, sink, before, file_span) ;
12331244 return ;
12341245 }
12351246 TokenKind :: Ident => {
12361247 match get_real_ident_class ( text, false ) {
1248+ // If it's not a keyword and the next non whitespace token is a `!`, then
1249+ // we consider it's a macro.
1250+ None if self . peek_non_whitespace ( ) == Some ( TokenKind :: Bang ) => {
1251+ self . new_macro_span ( text, sink, before, file_span) ;
1252+ return ;
1253+ }
12371254 None => match text {
12381255 "Option" | "Result" => Class :: PreludeTy ( new_span ( before, text, file_span) ) ,
12391256 "Some" | "None" | "Ok" | "Err" => {
@@ -1249,7 +1266,18 @@ impl<'src> Classifier<'src> {
12491266 "self" | "Self" => Class :: Self_ ( new_span ( before, text, file_span) ) ,
12501267 _ => Class :: Ident ( new_span ( before, text, file_span) ) ,
12511268 } ,
1252- Some ( c) => c,
1269+ Some ( c) => {
1270+ // So if it's not a keyword which can be followed by a value (like `if` or
1271+ // `return`) and the next non-whitespace token is a `!`, then we consider
1272+ // it's a macro.
1273+ if !KEYWORDS_FOLLOWABLE_BY_VALUE . contains ( & text)
1274+ && self . peek_non_whitespace ( ) == Some ( TokenKind :: Bang )
1275+ {
1276+ self . new_macro_span ( text, sink, before, file_span) ;
1277+ return ;
1278+ }
1279+ c
1280+ }
12531281 }
12541282 }
12551283 TokenKind :: RawIdent | TokenKind :: UnknownPrefix | TokenKind :: InvalidIdent => {
0 commit comments