@@ -172,6 +172,21 @@ impl<'a> Classifier<'a> {
172
172
}
173
173
}
174
174
175
+ /// Gets the next token out of the lexer, emitting fatal errors if lexing fails.
176
+ fn try_next_token ( & mut self ) -> io:: Result < TokenAndSpan > {
177
+ match self . lexer . try_next_token ( ) {
178
+ Ok ( tas) => Ok ( tas) ,
179
+ Err ( _) => {
180
+ self . lexer . emit_fatal_errors ( ) ;
181
+ self . lexer . sess . span_diagnostic
182
+ . struct_warn ( "Backing out of syntax highlighting" )
183
+ . note ( "You probably did not intend to render this as a rust code-block" )
184
+ . emit ( ) ;
185
+ Err ( io:: Error :: new ( io:: ErrorKind :: Other , "" ) )
186
+ }
187
+ }
188
+ }
189
+
175
190
/// Exhausts the `lexer` writing the output into `out`.
176
191
///
177
192
/// The general structure for this method is to iterate over each token,
@@ -183,18 +198,7 @@ impl<'a> Classifier<'a> {
183
198
out : & mut W )
184
199
-> io:: Result < ( ) > {
185
200
loop {
186
- let next = match self . lexer . try_next_token ( ) {
187
- Ok ( tas) => tas,
188
- Err ( _) => {
189
- self . lexer . emit_fatal_errors ( ) ;
190
- self . lexer . sess . span_diagnostic
191
- . struct_warn ( "Backing out of syntax highlighting" )
192
- . note ( "You probably did not intend to render this as a rust code-block" )
193
- . emit ( ) ;
194
- return Err ( io:: Error :: new ( io:: ErrorKind :: Other , "" ) ) ;
195
- }
196
- } ;
197
-
201
+ let next = self . try_next_token ( ) ?;
198
202
if next. tok == token:: Eof {
199
203
break ;
200
204
}
@@ -255,13 +259,37 @@ impl<'a> Classifier<'a> {
255
259
}
256
260
}
257
261
258
- // This is the start of an attribute. We're going to want to
262
+ // This might be the start of an attribute. We're going to want to
259
263
// continue highlighting it as an attribute until the ending ']' is
260
264
// seen, so skip out early. Down below we terminate the attribute
261
265
// span when we see the ']'.
262
266
token:: Pound => {
263
- self . in_attribute = true ;
264
- out. enter_span ( Class :: Attribute ) ?;
267
+ // We can't be sure that our # begins an attribute (it could
268
+ // just be appearing in a macro) until we read either `#![` or
269
+ // `#[` from the input stream.
270
+ //
271
+ // We don't want to start highlighting as an attribute until
272
+ // we're confident there is going to be a ] coming up, as
273
+ // otherwise # tokens in macros highlight the rest of the input
274
+ // as an attribute.
275
+
276
+ // Case 1: #![inner_attribute]
277
+ if self . lexer . peek ( ) . tok == token:: Not {
278
+ self . try_next_token ( ) ?; // NOTE: consumes `!` token!
279
+ if self . lexer . peek ( ) . tok == token:: OpenDelim ( token:: Bracket ) {
280
+ self . in_attribute = true ;
281
+ out. enter_span ( Class :: Attribute ) ?;
282
+ }
283
+ out. string ( "#" , Class :: None , None ) ?;
284
+ out. string ( "!" , Class :: None , None ) ?;
285
+ return Ok ( ( ) ) ;
286
+ }
287
+
288
+ // Case 2: #[outer_attribute]
289
+ if self . lexer . peek ( ) . tok == token:: OpenDelim ( token:: Bracket ) {
290
+ self . in_attribute = true ;
291
+ out. enter_span ( Class :: Attribute ) ?;
292
+ }
265
293
out. string ( "#" , Class :: None , None ) ?;
266
294
return Ok ( ( ) ) ;
267
295
}
0 commit comments