gccrs: expansion: Desugar doc comments into attributes before expansion

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.
This commit is contained in:
Arthur Cohen 2025-04-08 16:20:18 +02:00
parent cb23182fa2
commit 9710cf3e34
7 changed files with 234 additions and 1 deletions

View file

@ -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 \

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,10 @@
macro_rules! generate_pattern_iterators {
{
$(#[$forward_iterator_attribute:meta])*
} => {
}
}
generate_pattern_iterators! {
/// Created with the method [`split`].
}

View file

@ -0,0 +1,10 @@
macro_rules! doc_comment {
(#[ $attr: meta ]) => {
#[$attr]
struct Generated; // { dg-warning "never constructed" }
};
}
doc_comment! {
/// This is a generated struct
}

View file

@ -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| -> () {
};
}