about summary refs log tree commit diff
path: root/compiler/rustc_parse/src/parser/attr_wrapper.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_parse/src/parser/attr_wrapper.rs')
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs45
1 files changed, 35 insertions, 10 deletions
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 7512f46988c..36a0fda6458 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -98,21 +98,46 @@ impl<'a> Parser<'a> {
         }
         impl CreateTokenStream for LazyTokenStreamImpl {
             fn create_token_stream(&self) -> TokenStream {
-                // The token produced by the final call to `next` or `next_desugared`
-                // was not actually consumed by the callback. The combination
-                // of chaining the initial token and using `take` produces the desired
-                // result - we produce an empty `TokenStream` if no calls were made,
-                // and omit the final token otherwise.
+                if self.num_calls == 0 {
+                    return TokenStream::new(vec![]);
+                }
+
                 let mut cursor_snapshot = self.cursor_snapshot.clone();
-                let tokens = std::iter::once(self.start_token.clone())
-                    .chain((0..self.num_calls).map(|_| {
-                        if self.desugar_doc_comments {
+                // Don't skip `None` delimiters, since we want to pass them to
+                // proc macros. Normally, we'll end up capturing `TokenKind::Interpolated`,
+                // which gets converted to a `None`-delimited group when we invoke
+                // a proc-macro. However, it's possible to already have a `None`-delimited
+                // group in the stream (such as when parsing the output of a proc-macro,
+                // or in certain unusual cases with cross-crate `macro_rules!` macros).
+                cursor_snapshot.skip_none_delims = false;
+
+                // The token produced by the final call to `next` or `next_desugared`
+                // was not actually consumed by the callback.
+                let num_calls = self.num_calls - 1;
+                let mut i = 0;
+                let tokens =
+                    std::iter::once(self.start_token.clone()).chain(std::iter::from_fn(|| {
+                        if i >= num_calls {
+                            return None;
+                        }
+
+                        let token = if self.desugar_doc_comments {
                             cursor_snapshot.next_desugared()
                         } else {
                             cursor_snapshot.next()
+                        };
+
+                        // When the `LazyTokenStreamImpl` was original produced, we did *not*
+                        // include `NoDelim` tokens in `num_calls`, since they are normally ignored
+                        // by the parser. Therefore, we only increment our counter for other types of tokens.
+                        if !matches!(
+                            token.0.kind,
+                            token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim)
+                        ) {
+                            i += 1;
                         }
-                    }))
-                    .take(self.num_calls);
+                        Some(token)
+                    }));
 
                 make_token_stream(tokens, self.append_unglued_token.clone())
             }