diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index f70b350de283b..1c162a79c4c44 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -1246,7 +1246,7 @@ impl<'src> Classifier<'src> {
LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number,
},
TokenKind::GuardedStrPrefix => return no_highlight(sink),
- TokenKind::RawIdent if let Some((TokenKind::Bang, _)) = self.peek_non_trivia() => {
+ TokenKind::RawIdent if self.check_if_macro_call("") => {
self.new_macro_span(text, sink, before, file_span);
return;
}
@@ -1268,9 +1268,7 @@ impl<'src> Classifier<'src> {
// So if it's not a keyword which can be followed by a value (like `if` or
// `return`) and the next non-whitespace token is a `!`, then we consider
// it's a macro.
- if !NON_MACRO_KEYWORDS.contains(&text)
- && matches!(self.peek_non_trivia(), Some((TokenKind::Bang, _)))
- {
+ if !NON_MACRO_KEYWORDS.contains(&text) && self.check_if_macro_call(text) {
self.new_macro_span(text, sink, before, file_span);
return;
}
@@ -1278,7 +1276,7 @@ impl<'src> Classifier<'src> {
}
// If it's not a keyword and the next non whitespace token is a `!`, then
// we consider it's a macro.
- _ if matches!(self.peek_non_trivia(), Some((TokenKind::Bang, _))) => {
+ _ if self.check_if_macro_call(text) => {
self.new_macro_span(text, sink, before, file_span);
return;
}
@@ -1339,6 +1337,37 @@ impl<'src> Classifier<'src> {
self.tokens.stop_peeking();
None
}
+
+ fn check_if_macro_call(&mut self, ident: &str) -> bool {
+ let mut has_bang = false;
+ let is_macro_rule_ident = ident == "macro_rules";
+
+ while let Some((kind, _)) = self.tokens.peek_next() {
+ if let TokenKind::Whitespace
+ | TokenKind::LineComment { doc_style: None }
+ | TokenKind::BlockComment { doc_style: None, .. } = kind
+ {
+ continue;
+ }
+ if !has_bang {
+ if kind != TokenKind::Bang {
+ break;
+ }
+ has_bang = true;
+ continue;
+ }
+ self.tokens.stop_peeking();
+ if is_macro_rule_ident {
+ return matches!(kind, TokenKind::Ident | TokenKind::RawIdent);
+ }
+ return matches!(
+ kind,
+ TokenKind::OpenParen | TokenKind::OpenBracket | TokenKind::OpenBrace
+ );
+ }
+ self.tokens.stop_peeking();
+ false
+ }
}
fn is_keyword(symbol: Symbol) -> bool {
diff --git a/tests/rustdoc-html/source-code-pages/macro-call-2.rs b/tests/rustdoc-html/source-code-pages/macro-call-2.rs
new file mode 100644
index 0000000000000..d9c3df57c6021
--- /dev/null
+++ b/tests/rustdoc-html/source-code-pages/macro-call-2.rs
@@ -0,0 +1,18 @@
+// This is yet another test to ensure that only macro calls are considered as such
+// by the rustdoc highlighter, in particular when named `macro_rules`.
+// This is a regression test for .
+
+#![crate_name = "foo"]
+
+//@ has src/foo/macro-call-2.rs.html
+//@ count - '//code/span[@class="macro"]' 2
+//@ has - '//code/span[@class="macro"]' 'macro_rules!'
+//@ has - '//code/span[@class="macro"]' 'r#macro_rules!'
+
+macro_rules! r#macro_rules {
+ () => {
+ fn main() {}
+ }
+}
+
+r#macro_rules!();
diff --git a/tests/rustdoc-html/source-code-pages/macro-call.rs b/tests/rustdoc-html/source-code-pages/macro-call.rs
new file mode 100644
index 0000000000000..df2d22aa9b60a
--- /dev/null
+++ b/tests/rustdoc-html/source-code-pages/macro-call.rs
@@ -0,0 +1,29 @@
+// This is yet another test to ensure that only macro calls are considered as such
+// by the rustdoc highlighter.
+// This is a regression test for .
+
+#![crate_name = "foo"]
+
+//@ has src/foo/macro-call.rs.html
+//@ count - '//code/span[@class="macro"]' 2
+//@ has - '//code/span[@class="macro"]' 'panic!'
+//@ has - '//code/span[@class="macro"]' 'macro_rules!'
+
+pub struct Layout;
+
+impl Layout {
+ pub fn new() {}
+}
+
+pub fn bar() {
+ let layout = Layout::new::();
+ if layout != Layout::new::() {
+ panic!();
+ }
+ let macro_rules = 3;
+ if macro_rules != 3 {}
+}
+
+macro_rules! blob {
+ () => {}
+}