]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Eagerly expand format_args macro
authorOwen Avery <powerboat9.gamer@gmail.com>
Fri, 22 Aug 2025 02:22:36 +0000 (22:22 -0400)
committerArthur Cohen <arthur.cohen@embecosm.com>
Thu, 30 Oct 2025 19:58:44 +0000 (20:58 +0100)
This allows format_args!(some_macro!(...), ...) to compile.

gcc/rust/ChangeLog:

* expand/rust-macro-builtins-format-args.cc
(format_args_parse_arguments): Split format expression parsing
into...
(format_args_parse_expr): ...a new function here, while handling
eager expansion.
(MacroBuiltin::format_args_handler): Use format_args_parse_expr.

gcc/testsuite/ChangeLog:

* rust/compile/format_args_concat.rs: New test.

Signed-off-by: Owen Avery <powerboat9.gamer@gmail.com>
gcc/rust/expand/rust-macro-builtins-format-args.cc
gcc/testsuite/rust/compile/format_args_concat.rs [new file with mode: 0644]

index b20c849532063f9e93b895a322c2462b4f5e7e48..eb2a1cc6a724ed6d578af77f285fca8ae41a2eb8 100644 (file)
@@ -37,39 +37,42 @@ struct FormatArgsParseError
   } kind;
 };
 
-static tl::expected<FormatArgsInput, FormatArgsParseError>
-format_args_parse_arguments (AST::MacroInvocData &invoc)
+static inline tl::expected<std::string, AST::Fragment>
+format_args_parse_expr (location_t invoc_locus, AST::MacroInvocData &invoc,
+                       Parser<MacroInvocLexer> &parser,
+                       BuiltinMacro macro_kind)
 {
-  MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
-  Parser<MacroInvocLexer> parser (lex);
-
-  // TODO: check if EOF - return that format_args!() requires at least one
-  // argument
-
-  auto args = AST::FormatArguments ();
-  auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
-  std::unique_ptr<AST::Expr> format_expr = nullptr;
+  std::unique_ptr<AST::Expr> format_expr = parser.parse_expr ();
+  rust_assert (format_expr);
 
-  // TODO: Handle the case where we're not parsing a string literal (macro
-  // invocation for e.g.)
-  switch (parser.peek_current_token ()->get_id ())
+  if (format_expr->get_expr_kind () == AST::Expr::Kind::MacroInvocation)
     {
-    case STRING_LITERAL:
-    case RAW_STRING_LITERAL:
-      format_expr = parser.parse_literal_expr ();
-    default:
-      // do nothing
-      ;
+      std::vector<std::unique_ptr<AST::MacroInvocation>> pending;
+      pending.emplace_back (
+       static_cast<AST::MacroInvocation *> (format_expr.release ()));
+      return tl::unexpected<AST::Fragment> (
+       make_eager_builtin_invocation (macro_kind, invoc_locus,
+                                      invoc.get_delim_tok_tree (),
+                                      std::move (pending)));
     }
 
-  rust_assert (format_expr);
-
   // TODO(Arthur): Clean this up - if we haven't parsed a string literal but a
   // macro invocation, what do we do here? return a tl::unexpected?
-  auto format_str = static_cast<AST::LiteralExpr &> (*format_expr)
-                     .get_literal ()
-                     .as_string ();
+  rust_assert (format_expr->is_literal ());
+  return static_cast<AST::LiteralExpr &> (*format_expr)
+    .get_literal ()
+    .as_string ();
+}
+
+static inline tl::expected<AST::FormatArguments, FormatArgsParseError>
+format_args_parse_arguments (AST::MacroInvocData &invoc,
+                            Parser<MacroInvocLexer> &parser,
+                            TokenId last_token_id)
+{
+  // TODO: check if EOF - return that format_args!() requires at least one
+  // argument
 
+  auto args = AST::FormatArguments ();
   // TODO: Allow implicit captures ONLY if the the first arg is a string literal
   // and not a macro invocation
 
@@ -126,7 +129,7 @@ format_args_parse_arguments (AST::MacroInvocData &invoc)
       // we need to skip commas, don't we?
     }
 
-  return FormatArgsInput{std::move (format_str), std::move (args)};
+  return args;
 }
 
 tl::optional<AST::Fragment>
@@ -135,9 +138,24 @@ MacroBuiltin::format_args_handler (location_t invoc_locus,
                                   AST::InvocKind semicolon,
                                   AST::FormatArgs::Newline nl)
 {
-  auto input = format_args_parse_arguments (invoc);
+  MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
+  Parser<MacroInvocLexer> parser (lex);
+
+  auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
+
+  auto format_str = format_args_parse_expr (invoc_locus, invoc, parser,
+                                           nl == AST::FormatArgs::Newline::Yes
+                                             ? BuiltinMacro::FormatArgsNl
+                                             : BuiltinMacro::FormatArgs);
+
+  if (!format_str)
+    {
+      return std::move (format_str.error ());
+    }
+
+  auto args = format_args_parse_arguments (invoc, parser, last_token_id);
 
-  if (!input)
+  if (!args)
     {
       rust_error_at (invoc_locus,
                     "could not parse arguments to %<format_args!()%>");
@@ -173,7 +191,7 @@ MacroBuiltin::format_args_handler (location_t invoc_locus,
 
   bool append_newline = nl == AST::FormatArgs::Newline::Yes;
 
-  auto fmt_str = std::move (input->format_str);
+  auto fmt_str = std::move (format_str.value ());
   if (append_newline)
     fmt_str += '\n';
 
@@ -189,7 +207,7 @@ MacroBuiltin::format_args_handler (location_t invoc_locus,
   // for creating the `template`
 
   auto fmt_args_node = AST::FormatArgs (invoc_locus, std::move (pieces),
-                                       std::move (input->args));
+                                       std::move (args.value ()));
 
   auto expanded
     = Fmt::expand_format_args (fmt_args_node,
diff --git a/gcc/testsuite/rust/compile/format_args_concat.rs b/gcc/testsuite/rust/compile/format_args_concat.rs
new file mode 100644 (file)
index 0000000..b180667
--- /dev/null
@@ -0,0 +1,51 @@
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    () => {};
+}
+
+#[rustc_builtin_macro]
+macro_rules! concat {
+    () => {};
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+pub mod core {
+    pub mod fmt {
+        pub struct Formatter;
+        pub struct Result;
+
+        pub struct Arguments<'a>;
+
+        impl<'a> Arguments<'a> {
+            pub fn new_v1(_: &'a [&'static str], _: &'a [ArgumentV1<'a>]) -> Arguments<'a> {
+                Arguments
+            }
+        }
+
+        pub struct ArgumentV1<'a>;
+
+        impl<'a> ArgumentV1<'a> {
+            pub fn new<'b, T>(_: &'b T, _: fn(&T, &mut Formatter) -> Result) -> ArgumentV1 {
+                ArgumentV1
+            }
+        }
+
+        pub trait Display {
+            fn fmt(&self, _: &mut Formatter) -> Result;
+        }
+
+        impl Display for i32 {
+            fn fmt(&self, _: &mut Formatter) -> Result {
+                Result
+            }
+        }
+    }
+}
+
+fn main() {
+    let _formatted = format_args!(concat!("hello ", "{}"), 15);
+}