diff --git a/gcc/rust/expand/rust-macro-builtins.cc b/gcc/rust/expand/rust-macro-builtins.cc
index f103759acdd..9e6716c5975 100644
--- a/gcc/rust/expand/rust-macro-builtins.cc
+++ b/gcc/rust/expand/rust-macro-builtins.cc
@@ -17,6 +17,8 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "libproc_macro_internal/tokenstream.h"
+#include "rust-ast-full-decls.h"
+#include "rust-builtin-ast-nodes.h"
 #include "rust-token-converter.h"
 #include "rust-system.h"
 #include "rust-macro-builtins.h"
@@ -78,6 +80,14 @@ const BiMap<std::string, BuiltinMacro> MacroBuiltin::builtins = {{
 
 }};
 
+AST::MacroTranscriberFunc
+format_args_maker (AST::FormatArgs::Newline nl)
+{
+  return [nl] (location_t loc, AST::MacroInvocData &invoc) {
+    return MacroBuiltin::format_args_handler (loc, invoc, nl);
+  };
+}
+
 std::unordered_map<std::string, AST::MacroTranscriberFunc>
   MacroBuiltin::builtin_transcribers = {
     {"assert", MacroBuiltin::assert_handler},
@@ -92,10 +102,10 @@ std::unordered_map<std::string, AST::MacroTranscriberFunc>
     {"env", MacroBuiltin::env_handler},
     {"cfg", MacroBuiltin::cfg_handler},
     {"include", MacroBuiltin::include_handler},
-    {"format_args", MacroBuiltin::format_args_handler},
+    {"format_args", format_args_maker (AST::FormatArgs::Newline::No)},
+    {"format_args_nl", format_args_maker (AST::FormatArgs::Newline::Yes)},
     /* Unimplemented macro builtins */
     {"option_env", MacroBuiltin::sorry},
-    {"format_args_nl", MacroBuiltin::sorry},
     {"concat_idents", MacroBuiltin::sorry},
     {"module_path", MacroBuiltin::sorry},
     {"asm", MacroBuiltin::sorry},
@@ -286,6 +296,8 @@ try_expand_many_expr (Parser<MacroInvocLexer> &parser,
    and return the LiteralExpr for it. Allow for an optional trailing comma,
    but otherwise enforce that these are the only tokens.  */
 
+// FIXME(Arthur): This function needs a rework - it should not emit errors, it
+// should probably be smaller
 std::unique_ptr<AST::Expr>
 parse_single_string_literal (BuiltinMacro kind,
 			     AST::DelimTokenTree &invoc_token_tree,
@@ -946,17 +958,31 @@ MacroBuiltin::stringify_handler (location_t invoc_locus,
 
 tl::optional<AST::Fragment>
 MacroBuiltin::format_args_handler (location_t invoc_locus,
-				   AST::MacroInvocData &invoc)
+				   AST::MacroInvocData &invoc,
+				   AST::FormatArgs::Newline nl)
 {
+  // Remove the delimiters from the macro invocation:
+  // the invoc data for `format_args!(fmt, arg1, arg2)` is `(fmt, arg1, arg2)`,
+  // so we pop the front and back to remove the parentheses (or curly brackets,
+  // or brackets)
   auto tokens = invoc.get_delim_tok_tree ().to_token_stream ();
   tokens.erase (tokens.begin ());
   tokens.pop_back ();
 
-  std::stringstream stream;
-  for (const auto &tok : tokens)
-    stream << tok->as_string () << ' ';
+  auto append_newline = nl == AST::FormatArgs::Newline::Yes ? true : false;
+  auto fmt_arg
+    = parse_single_string_literal (append_newline ? BuiltinMacro::FormatArgsNl
+						  : BuiltinMacro::FormatArgs,
+				   invoc.get_delim_tok_tree (), invoc_locus,
+				   invoc.get_expander ());
 
-  rust_debug ("[ARTHU]: `%s`", stream.str ().c_str ());
+  if (!fmt_arg->is_literal ())
+    {
+      rust_sorry_at (
+	invoc_locus,
+	"cannot yet use eager macro invocations as format strings");
+      return AST::Fragment::create_empty ();
+    }
 
   // FIXME: We need to handle this
   // // if it is not a literal, it's an eager macro invocation - return it
@@ -967,8 +993,37 @@ MacroBuiltin::format_args_handler (location_t invoc_locus,
   // 	    token_tree.to_token_stream ());
   //   }
 
+  auto fmt_str = static_cast<AST::LiteralExpr &> (*fmt_arg.get ());
+
+  // Switch on the format string to know if the string is raw or cooked
+  switch (fmt_str.get_lit_type ())
+    {
+    // case AST::Literal::RAW_STRING:
+    case AST::Literal::STRING:
+      break;
+    case AST::Literal::CHAR:
+    case AST::Literal::BYTE:
+    case AST::Literal::BYTE_STRING:
+    case AST::Literal::INT:
+    case AST::Literal::FLOAT:
+    case AST::Literal::BOOL:
+    case AST::Literal::ERROR:
+      rust_unreachable ();
+    }
+
+  std::stringstream stream;
+  for (const auto &tok : tokens)
+    stream << tok->as_string () << ' ';
+
+  rust_debug ("[ARTHUR]: `%s`", stream.str ().c_str ());
+
   auto pieces = Fmt::Pieces::collect (stream.str ());
 
+  // TODO:
+  // do the transformation into an AST::FormatArgs node
+  // return that
+  // expand it during lowering
+
   return AST::Fragment::create_empty ();
 }
 
diff --git a/gcc/rust/expand/rust-macro-builtins.h b/gcc/rust/expand/rust-macro-builtins.h
index 1d6b30b5933..62961561716 100644
--- a/gcc/rust/expand/rust-macro-builtins.h
+++ b/gcc/rust/expand/rust-macro-builtins.h
@@ -20,6 +20,7 @@
 #define RUST_MACRO_BUILTINS_H
 
 #include "rust-ast.h"
+#include "rust-builtin-ast-nodes.h"
 #include "rust-ast-fragment.h"
 #include "rust-location.h"
 #include "bi-map.h"
@@ -158,7 +159,8 @@ public:
 						   AST::MacroInvocData &invoc);
 
   static tl::optional<AST::Fragment>
-  format_args_handler (location_t invoc_locus, AST::MacroInvocData &invoc);
+  format_args_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
+		       AST::FormatArgs::Newline nl);
 
   static tl::optional<AST::Fragment> sorry (location_t invoc_locus,
 					    AST::MacroInvocData &invoc);
