diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 89a40b0db9662..239bcb3d69b99 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -172,6 +172,21 @@ impl<'a> Classifier<'a> {
}
}
+ /// Gets the next token out of the lexer, emitting fatal errors if lexing fails.
+ fn try_next_token(&mut self) -> io::Result {
+ match self.lexer.try_next_token() {
+ Ok(tas) => Ok(tas),
+ Err(_) => {
+ self.lexer.emit_fatal_errors();
+ self.lexer.sess.span_diagnostic
+ .struct_warn("Backing out of syntax highlighting")
+ .note("You probably did not intend to render this as a rust code-block")
+ .emit();
+ Err(io::Error::new(io::ErrorKind::Other, ""))
+ }
+ }
+ }
+
/// Exhausts the `lexer` writing the output into `out`.
///
/// The general structure for this method is to iterate over each token,
@@ -183,18 +198,7 @@ impl<'a> Classifier<'a> {
out: &mut W)
-> io::Result<()> {
loop {
- let next = match self.lexer.try_next_token() {
- Ok(tas) => tas,
- Err(_) => {
- self.lexer.emit_fatal_errors();
- self.lexer.sess.span_diagnostic
- .struct_warn("Backing out of syntax highlighting")
- .note("You probably did not intend to render this as a rust code-block")
- .emit();
- return Err(io::Error::new(io::ErrorKind::Other, ""));
- }
- };
-
+ let next = self.try_next_token()?;
if next.tok == token::Eof {
break;
}
@@ -255,13 +259,37 @@ impl<'a> Classifier<'a> {
}
}
- // This is the start of an attribute. We're going to want to
+ // This might be the start of an attribute. We're going to want to
// continue highlighting it as an attribute until the ending ']' is
// seen, so skip out early. Down below we terminate the attribute
// span when we see the ']'.
token::Pound => {
- self.in_attribute = true;
- out.enter_span(Class::Attribute)?;
+ // We can't be sure that our # begins an attribute (it could
+ // just be appearing in a macro) until we read either `#![` or
+ // `#[` from the input stream.
+ //
+ // We don't want to start highlighting as an attribute until
+ // we're confident there is going to be a ] coming up, as
+ // otherwise # tokens in macros highlight the rest of the input
+ // as an attribute.
+
+ // Case 1: #![inner_attribute]
+ if self.lexer.peek().tok == token::Not {
+ self.try_next_token()?; // NOTE: consumes `!` token!
+ if self.lexer.peek().tok == token::OpenDelim(token::Bracket) {
+ self.in_attribute = true;
+ out.enter_span(Class::Attribute)?;
+ }
+ out.string("#", Class::None, None)?;
+ out.string("!", Class::None, None)?;
+ return Ok(());
+ }
+
+ // Case 2: #[outer_attribute]
+ if self.lexer.peek().tok == token::OpenDelim(token::Bracket) {
+ self.in_attribute = true;
+ out.enter_span(Class::Attribute)?;
+ }
out.string("#", Class::None, None)?;
return Ok(());
}
diff --git a/src/test/rustdoc/issue-41783.rs b/src/test/rustdoc/issue-41783.rs
index 3933b8bcbb8fd..991cf4cf2b375 100644
--- a/src/test/rustdoc/issue-41783.rs
+++ b/src/test/rustdoc/issue-41783.rs
@@ -12,8 +12,10 @@
// @!has - 'space'
// @!has - 'comment'
// @has - '# single'
-// @has - '## double'
-// @has - '### triple'
+// @has - '## double'
+// @has - '### triple'
+// @has - '#[outer]'
+// @has - '#![inner]'
/// ```no_run
/// # # space
@@ -21,5 +23,7 @@
/// ## single
/// ### double
/// #### triple
+/// ##[outer]
+/// ##![inner]
/// ```
pub struct Foo;