From: Arthur Cohen <arthur.co...@embecosm.com>

gcc/rust/ChangeLog:

        * expand/rust-macro-expand.cc (MacroExpander::expand_decl_macro): Call 
into
        TokenTreeDesugar.
        * expand/rust-token-tree-desugar.cc: New file.
        * expand/rust-token-tree-desugar.h: New file.
        * Make-lang.in: Compile them.

gcc/testsuite/ChangeLog:

        * rust/compile/macros/mbe/macro-issue3709-1.rs: New test.
        * rust/compile/macros/mbe/macro-issue3709-2.rs: New test.
---
 gcc/rust/Make-lang.in                         |  1 +
 gcc/rust/expand/rust-macro-expand.cc          |  6 +-
 gcc/rust/expand/rust-token-tree-desugar.cc    | 72 +++++++++++++++++
 gcc/rust/expand/rust-token-tree-desugar.h     | 55 +++++++++++++
 .../compile/macros/mbe/macro-issue3693.rs     | 10 +++
 .../compile/macros/mbe/macro-issue3709-1.rs   | 10 +++
 .../compile/macros/mbe/macro-issue3709-2.rs   | 81 +++++++++++++++++++
 7 files changed, 234 insertions(+), 1 deletion(-)
 create mode 100644 gcc/rust/expand/rust-token-tree-desugar.cc
 create mode 100644 gcc/rust/expand/rust-token-tree-desugar.h
 create mode 100644 gcc/testsuite/rust/compile/macros/mbe/macro-issue3693.rs
 create mode 100644 gcc/testsuite/rust/compile/macros/mbe/macro-issue3709-1.rs
 create mode 100644 gcc/testsuite/rust/compile/macros/mbe/macro-issue3709-2.rs

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 4028b47fa87..835e113aee2 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -115,6 +115,7 @@ GRS_OBJS = \
     rust/rust-macro-builtins-format-args.o \
     rust/rust-macro-builtins-location.o \
     rust/rust-macro-builtins-include.o \
+    rust/rust-token-tree-desugar.o \
        rust/rust-fmt.o \
     rust/rust-hir.o \
     rust/rust-hir-map.o \
diff --git a/gcc/rust/expand/rust-macro-expand.cc 
b/gcc/rust/expand/rust-macro-expand.cc
index 6e62a083ae7..673b8fb20fa 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -28,6 +28,7 @@
 #include "rust-cfg-strip.h"
 #include "rust-early-name-resolver.h"
 #include "rust-proc-macro.h"
+#include "rust-token-tree-desugar.h"
 
 namespace Rust {
 
@@ -78,7 +79,10 @@ MacroExpander::expand_decl_macro (location_t invoc_locus,
    * trees.
    */
 
-  AST::DelimTokenTree &invoc_token_tree = invoc.get_delim_tok_tree ();
+  AST::DelimTokenTree &invoc_token_tree_sugar = invoc.get_delim_tok_tree ();
+
+  // We must first desugar doc comments into proper attributes
+  auto invoc_token_tree = AST::TokenTreeDesugar ().go (invoc_token_tree_sugar);
 
   // find matching arm
   AST::MacroRule *matched_rule = nullptr;
diff --git a/gcc/rust/expand/rust-token-tree-desugar.cc 
b/gcc/rust/expand/rust-token-tree-desugar.cc
new file mode 100644
index 00000000000..3b471805924
--- /dev/null
+++ b/gcc/rust/expand/rust-token-tree-desugar.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-token-tree-desugar.h"
+#include "rust-ast.h"
+#include "rust-token.h"
+
+namespace Rust {
+namespace AST {
+
+DelimTokenTree
+TokenTreeDesugar::go (DelimTokenTree &tts)
+{
+  tts.accept_vis (*this);
+
+  return DelimTokenTree (tts.get_delim_type (), std::move (desugared),
+                        tts.get_locus ());
+}
+
+void
+TokenTreeDesugar::append (TokenPtr &&new_token)
+{
+  desugared.emplace_back (std::make_unique<Token> (std::move (new_token)));
+}
+
+void
+TokenTreeDesugar::append (std::unique_ptr<TokenTree> &&new_token)
+{
+  desugared.emplace_back (std::move (new_token));
+}
+
+void
+TokenTreeDesugar::visit (Token &tts)
+{
+  if (tts.get_id () == TokenId::OUTER_DOC_COMMENT
+      || tts.get_id () == TokenId::INNER_DOC_COMMENT)
+    {
+      append (Rust::Token::make (TokenId::HASH, tts.get_locus ()));
+
+      if (tts.get_id () == TokenId::INNER_DOC_COMMENT)
+       append (Rust::Token::make (EXCLAM, tts.get_locus ()));
+
+      append (Rust::Token::make (TokenId::LEFT_SQUARE, tts.get_locus ()));
+      append (Rust::Token::make_identifier (tts.get_locus (), "doc"));
+      append (Rust::Token::make (TokenId::EQUAL, tts.get_locus ()));
+      append (Rust::Token::make_string (tts.get_locus (),
+                                       std::string (tts.get_str ())));
+      append (Rust::Token::make (TokenId::RIGHT_SQUARE, tts.get_locus ()));
+    }
+  else
+    {
+      append (tts.clone_token ());
+    }
+}
+
+}; // namespace AST
+}; // namespace Rust
diff --git a/gcc/rust/expand/rust-token-tree-desugar.h 
b/gcc/rust/expand/rust-token-tree-desugar.h
new file mode 100644
index 00000000000..ccba53b1102
--- /dev/null
+++ b/gcc/rust/expand/rust-token-tree-desugar.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_TOKEN_TREE_DESUGAR_H
+#define RUST_TOKEN_TREE_DESUGAR_H
+
+#include "rust-ast-visitor.h"
+#include "rust-system.h"
+#include "rust-ast.h"
+
+namespace Rust {
+namespace AST {
+
+/**
+ * Desugar a given token-tree before parsing it for a macro invocation. At the
+ * moment, the sole purpose of this desugar is to transform doc-comments into
+ * their attribute form (/// comment -> #[doc = "comment"])
+ */
+class TokenTreeDesugar : public DefaultASTVisitor
+{
+public:
+  TokenTreeDesugar () : desugared (std::vector<std::unique_ptr<TokenTree>> ())
+  {}
+
+  DelimTokenTree go (DelimTokenTree &tts);
+
+private:
+  std::vector<std::unique_ptr<TokenTree>> desugared;
+  void append (TokenPtr &&new_token);
+  void append (std::unique_ptr<TokenTree> &&new_token);
+
+  using DefaultASTVisitor::visit;
+
+  virtual void visit (Token &tts) override;
+};
+
+}; // namespace AST
+}; // namespace Rust
+
+#endif //! RUST_TOKEN_TREE_DESUGAR_H
diff --git a/gcc/testsuite/rust/compile/macros/mbe/macro-issue3693.rs 
b/gcc/testsuite/rust/compile/macros/mbe/macro-issue3693.rs
new file mode 100644
index 00000000000..e990c8bc355
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macros/mbe/macro-issue3693.rs
@@ -0,0 +1,10 @@
+macro_rules! generate_pattern_iterators {
+    {
+        $(#[$forward_iterator_attribute:meta])*
+    } => {
+    }
+}
+
+generate_pattern_iterators! {
+    /// Created with the method [`split`].
+}
diff --git a/gcc/testsuite/rust/compile/macros/mbe/macro-issue3709-1.rs 
b/gcc/testsuite/rust/compile/macros/mbe/macro-issue3709-1.rs
new file mode 100644
index 00000000000..6fc3a319dd4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macros/mbe/macro-issue3709-1.rs
@@ -0,0 +1,10 @@
+macro_rules! doc_comment {
+    (#[ $attr: meta ]) => {
+        #[$attr]
+        struct Generated; // { dg-warning "never constructed" }
+    };
+}
+
+doc_comment! {
+    /// This is a generated struct
+}
diff --git a/gcc/testsuite/rust/compile/macros/mbe/macro-issue3709-2.rs 
b/gcc/testsuite/rust/compile/macros/mbe/macro-issue3709-2.rs
new file mode 100644
index 00000000000..cfc8ab460b7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macros/mbe/macro-issue3709-2.rs
@@ -0,0 +1,81 @@
+// { dg-additional-options "-frust-name-resolution-2.0 
-frust-compile-until=lowering" }
+
+macro_rules! impl_fn_for_zst {
+    ($(
+        $( #[$attr: meta] )*
+        struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn =
+            |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty
+            $body: block;
+    )+) => {
+        $(
+            $( #[$attr] )*
+            struct $Name;
+
+            impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name {
+                #[inline]
+                extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, 
)*)) -> $ReturnTy {
+                    $body
+                }
+            }
+
+            impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name {
+                #[inline]
+                extern "rust-call" fn call_mut(
+                    &mut self,
+                    ($( $arg, )*): ($( $ArgTy, )*)
+                ) -> $ReturnTy {
+                    Fn::call(&*self, ($( $arg, )*))
+                }
+            }
+
+            impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name {
+                type Output = $ReturnTy;
+
+                #[inline]
+                extern "rust-call" fn call_once(self, ($( $arg, )*): ($( 
$ArgTy, )*)) -> $ReturnTy {
+                    Fn::call(&self, ($( $arg, )*))
+                }
+            }
+        )+
+    }
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "copy"]
+trait Copy {}
+
+#[lang = "fn"]
+pub trait Fn<Args>: FnMut<Args> {
+    /// Performs the call operation.
+    #[unstable(feature = "fn_traits", issue = "29625")]
+    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
+}
+
+#[lang = "fn_mut"]
+#[must_use = "closures are lazy and do nothing unless called"]
+pub trait FnMut<Args>: FnOnce<Args> {
+    /// Performs the call operation.
+    #[unstable(feature = "fn_traits", issue = "29625")]
+    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
+}
+
+#[lang = "fn_once"]
+pub trait FnOnce<Args> {
+    /// The returned type after the call operator is used.
+    #[lang = "fn_once_output"]
+    #[stable(feature = "fn_once_output", since = "1.12.0")]
+    type Output;
+
+    /// Performs the call operation.
+    #[unstable(feature = "fn_traits", issue = "29625")]
+    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+}
+
+impl_fn_for_zst! {
+    /// Documentation for the zst
+    #[derive(Copy)]
+    struct LinesAnyMap impl<'a> Fn = |line: &'a str| -> () {
+    };
+}
-- 
2.49.0

Reply via email to