about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Layzell <michael@thelayzells.com>2017-08-16 20:08:27 -0400
committerMichael Layzell <michael@thelayzells.com>2017-08-16 20:08:27 -0400
commite83c8085a0260b7280210adebbb1db852d17eee9 (patch)
tree994c7d977dd3a0381565640f99214dd1977613f1
parenta7e0d3a81f224649c8fcfc8ac3cb93f1e1107bea (diff)
downloadrust-e83c8085a0260b7280210adebbb1db852d17eee9.tar.gz
rust-e83c8085a0260b7280210adebbb1db852d17eee9.zip
Don't highlight # which does not start an attribute in rustdoc
-rw-r--r--src/librustdoc/html/highlight.rs58
1 files changed, 43 insertions, 15 deletions
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 89a40b0db96..239bcb3d69b 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<TokenAndSpan> {
+        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(());
             }