]> git.lizzy.rs Git - rust.git/commitdiff
Improve doc comment desugaring.
authorNicholas Nethercote <n.nethercote@gmail.com>
Tue, 31 Jan 2023 23:53:00 +0000 (10:53 +1100)
committerNicholas Nethercote <n.nethercote@gmail.com>
Thu, 2 Feb 2023 23:06:52 +0000 (10:06 +1100)
Sometimes the parser needs to desugar a doc comment into `#[doc =
r"foo"]`. Currently it does this in a hacky way: by pushing a "fake" new
frame (one without a delimiter) onto the `TokenCursor` stack.

This commit changes things so that the token stream itself is modified
in place. The nice thing about this is that it means
`TokenCursorFrame::delim_sp` is now only `None` for the outermost frame.

compiler/rustc_ast/src/tokenstream.rs
compiler/rustc_parse/src/parser/mod.rs

index fabd43a1618a4387fa8af8680fe8a52f17af90d1..dd01fc8ffc506db7b26bd63a1cf94af6286e3709 100644 (file)
@@ -614,6 +614,15 @@ pub fn next_ref(&mut self) -> Option<&TokenTree> {
     pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
         self.stream.0.get(self.index + n)
     }
+
+    // Replace the previously obtained token tree with `tts`, and rewind to
+    // just before them.
+    pub fn replace_prev_and_rewind(&mut self, tts: Vec<TokenTree>) {
+        assert!(self.index > 0);
+        self.index -= 1;
+        let stream = Lrc::make_mut(&mut self.stream.0);
+        stream.splice(self.index..self.index + 1, tts);
+    }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
index ffb23b50a160de4101fb021faff9b6af54166b4c..0499a56a09db46afd3be2d864170511e51c0aad5 100644 (file)
@@ -224,7 +224,7 @@ fn drop(&mut self) {
 #[derive(Clone)]
 struct TokenCursor {
     // The current (innermost) frame. `frame` and `stack` could be combined,
-    // but it's faster to have them separately to access `frame` directly
+    // but it's faster to keep them separate and access `frame` directly
     // rather than via something like `stack.last().unwrap()` or
     // `stack[stack.len() - 1]`.
     frame: TokenCursorFrame,
@@ -259,6 +259,7 @@ struct TokenCursor {
 
 #[derive(Clone)]
 struct TokenCursorFrame {
+    // This is `None` only for the outermost frame.
     delim_sp: Option<(Delimiter, DelimSpan)>,
     tree_cursor: tokenstream::Cursor,
 }
@@ -285,7 +286,9 @@ fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
                 match tree {
                     &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) {
                         (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => {
-                            return self.desugar(attr_style, data, span);
+                            let desugared = self.desugar(attr_style, data, span);
+                            self.frame.tree_cursor.replace_prev_and_rewind(desugared);
+                            // Continue to get the first token of the desugared doc comment.
                         }
                         _ => return (token.clone(), spacing),
                     },
@@ -300,19 +303,22 @@ fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
                     }
                 };
             } else if let Some(frame) = self.stack.pop() {
-                if let Some((delim, span)) = self.frame.delim_sp && delim != Delimiter::Invisible {
-                    self.frame = frame;
+                // We have exhausted this frame. Move back to its parent frame.
+                let (delim, span) = self.frame.delim_sp.unwrap();
+                self.frame = frame;
+                if delim != Delimiter::Invisible {
                     return (Token::new(token::CloseDelim(delim), span.close), Spacing::Alone);
                 }
-                self.frame = frame;
                 // No close delimiter to return; continue on to the next iteration.
             } else {
+                // We have exhausted the outermost frame.
                 return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone);
             }
         }
     }
 
-    fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> (Token, Spacing) {
+    // Desugar a doc comment into something like `#[doc = r"foo"]`.
+    fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> Vec<TokenTree> {
         // Searches for the occurrences of `"#*` and returns the minimum number of `#`s
         // required to wrap the text. E.g.
         // - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0)
@@ -346,27 +352,15 @@ fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> (Token
             .collect::<TokenStream>(),
         );
 
-        self.stack.push(mem::replace(
-            &mut self.frame,
-            TokenCursorFrame::new(
-                None,
-                if attr_style == AttrStyle::Inner {
-                    [
-                        TokenTree::token_alone(token::Pound, span),
-                        TokenTree::token_alone(token::Not, span),
-                        body,
-                    ]
-                    .into_iter()
-                    .collect::<TokenStream>()
-                } else {
-                    [TokenTree::token_alone(token::Pound, span), body]
-                        .into_iter()
-                        .collect::<TokenStream>()
-                },
-            ),
-        ));
-
-        self.next(/* desugar_doc_comments */ false)
+        if attr_style == AttrStyle::Inner {
+            vec![
+                TokenTree::token_alone(token::Pound, span),
+                TokenTree::token_alone(token::Not, span),
+                body,
+            ]
+        } else {
+            vec![TokenTree::token_alone(token::Pound, span), body]
+        }
     }
 }