about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCamille GILLOT <gillot.camille@gmail.com>2022-08-08 00:05:20 +0200
committerCamille GILLOT <gillot.camille@gmail.com>2022-09-13 19:46:01 +0200
commit8823634db8c4ee65b4a3d47ccb1d81891e33d0de (patch)
tree5ca7c1ad571ba288dcf120ffa2546a5be2d7e99a
parent5338f5f1d4ad9c99706e5cb691f8d5bbac341262 (diff)
downloadrust-8823634db8c4ee65b4a3d47ccb1d81891e33d0de.tar.gz
rust-8823634db8c4ee65b4a3d47ccb1d81891e33d0de.zip
Manually cleanup token stream when macro expansion aborts.
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs45
-rw-r--r--src/test/ui/macros/syntax-error-recovery.rs17
-rw-r--r--src/test/ui/macros/syntax-error-recovery.stderr24
3 files changed, 71 insertions, 15 deletions
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 5fdafd187c6..34021c40ba9 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -442,23 +442,38 @@ fn make_token_stream(
         }
         token_and_spacing = iter.next();
     }
-    let mut final_buf = stack.pop().expect("Missing final buf!");
-    if break_last_token {
-        let last_token = final_buf.inner.pop().unwrap();
-        if let AttrTokenTree::Token(last_token, spacing) = last_token {
-            let unglued_first = last_token.kind.break_two_token_op().unwrap().0;
-
-            // An 'unglued' token is always two ASCII characters
-            let mut first_span = last_token.span.shrink_to_lo();
-            first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1));
-
-            final_buf
+    while let Some(FrameData { open_delim_sp, mut inner }) = stack.pop() {
+        // A former macro expansion could give us malformed tokens.
+        // In that case, manually close all open delimitors so downstream users
+        // don't ICE on them.
+        if let Some((delim, open_sp)) = open_delim_sp {
+            let dspan = DelimSpan::from_pair(open_sp, rustc_span::DUMMY_SP);
+            let stream = AttrTokenStream::new(inner);
+            let delimited = AttrTokenTree::Delimited(dspan, delim, stream);
+            stack
+                .last_mut()
+                .unwrap_or_else(|| panic!("Bottom token frame is missing for recovered token"))
                 .inner
-                .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing));
+                .push(delimited);
         } else {
-            panic!("Unexpected last token {:?}", last_token)
+            if break_last_token {
+                let last_token = inner.pop().unwrap();
+                if let AttrTokenTree::Token(last_token, spacing) = last_token {
+                    let unglued_first = last_token.kind.break_two_token_op().unwrap().0;
+
+                    // An 'unglued' token is always two ASCII characters
+                    let mut first_span = last_token.span.shrink_to_lo();
+                    first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1));
+
+                    inner
+                        .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing));
+                } else {
+                    panic!("Unexpected last token {:?}", last_token)
+                }
+            }
+            assert!(stack.is_empty(), "Stack should be empty: stack={:?}", stack);
+            return AttrTokenStream::new(inner);
         }
     }
-    assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack);
-    AttrTokenStream::new(final_buf.inner)
+    panic!("Missing final buf!")
 }
diff --git a/src/test/ui/macros/syntax-error-recovery.rs b/src/test/ui/macros/syntax-error-recovery.rs
new file mode 100644
index 00000000000..2b2eec04705
--- /dev/null
+++ b/src/test/ui/macros/syntax-error-recovery.rs
@@ -0,0 +1,17 @@
+macro_rules! values {
+    ($($token:ident($value:literal) $(as $inner:ty)? => $attr:meta,)*) => {
+        #[derive(Debug)]
+        pub enum TokenKind {
+            $(
+                #[$attr]
+                $token $($inner)? = $value,
+            )*
+        }
+    };
+}
+//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)`
+//~| ERROR macro expansion ignores token `(String)` and any following
+
+values!(STRING(1) as (String) => cfg(test),);
+
+fn main() {}
diff --git a/src/test/ui/macros/syntax-error-recovery.stderr b/src/test/ui/macros/syntax-error-recovery.stderr
new file mode 100644
index 00000000000..1d9ce110d7b
--- /dev/null
+++ b/src/test/ui/macros/syntax-error-recovery.stderr
@@ -0,0 +1,24 @@
+error: expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)`
+  --> $DIR/syntax-error-recovery.rs:7:26
+   |
+LL |                 $token $($inner)? = $value,
+   |                          ^^^^^^ expected one of `(`, `,`, `=`, `{`, or `}`
+...
+LL | values!(STRING(1) as (String) => cfg(test),);
+   | -------------------------------------------- in this macro invocation
+   |
+   = note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: macro expansion ignores token `(String)` and any following
+  --> $DIR/syntax-error-recovery.rs:7:26
+   |
+LL |                 $token $($inner)? = $value,
+   |                          ^^^^^^
+...
+LL | values!(STRING(1) as (String) => cfg(test),);
+   | -------------------------------------------- caused by the macro expansion here
+   |
+   = note: the usage of `values!` is likely invalid in item context
+
+error: aborting due to 2 previous errors
+