// Copyright (C) 2024-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
// .
#include "rust-expand-format-args.h"
#include "rust-ast-fragment.h"
#include "rust-ast.h"
#include "rust-builtin-ast-nodes.h"
#include "rust-ast-builder.h"
#include "rust-diagnostics.h"
#include "rust-expr.h"
#include "rust-fmt.h"
#include "rust-path.h"
#include "rust-system.h"
#include "rust-token.h"
namespace Rust {
namespace Fmt {
static std::unique_ptr
format_arg (const AST::Builder &builder, std::unique_ptr &&to_format,
const std::string &trait)
{
auto formatter_fn = std::unique_ptr (new AST::PathInExpression (
builder.path_in_expression ({"core", "fmt", trait, "fmt"})));
auto path = std::unique_ptr (new AST::PathInExpression (
builder.path_in_expression ({"core", "fmt", "ArgumentV1", "new"})));
auto args = std::vector> ();
args.emplace_back (std::move (to_format));
args.emplace_back (std::move (formatter_fn));
return builder.call (std::move (path), std::move (args));
}
const std::string &
get_trait_name (ffi::FormatSpec format_specifier)
{
static const std::unordered_map spec_map = {
{"", "Display"}, {"?", "Debug"}, {"e", "LowerExp"},
{"E", "UpperExp"}, {"o", "Octal"}, {"p", "Pointer"},
{"b", "Binary"}, {"x", "LowerHex"}, {"X", "UpperHex"},
};
auto it = spec_map.find (format_specifier.ty.to_string ());
if (it == spec_map.end ())
rust_unreachable ();
return it->second;
}
tl::optional
expand_format_args (AST::FormatArgs &fmt,
std::vector> &&tokens)
{
auto loc = fmt.get_locus ();
auto builder = AST::Builder (loc);
auto &arguments = fmt.get_arguments ();
auto static_pieces = std::vector> ();
auto args
= std::vector, ffi::FormatSpec>> ();
for (const auto &node : fmt.get_template ().get_pieces ())
{
switch (node.tag)
{
case ffi::Piece::Tag::String:
static_pieces.emplace_back (
builder.literal_string (node.string._0.to_string ()));
break;
case ffi::Piece::Tag::NextArgument: {
auto next_argument = node.next_argument._0;
switch (node.next_argument._0.position.tag)
{
case ffi::Position::Tag::ArgumentImplicitlyIs: {
auto idx = next_argument.position.argument_implicitly_is._0;
auto trait = next_argument.format;
auto arg = arguments.at (idx);
// FIXME(Arthur): This API sucks
rust_assert (arg.get_kind ().kind
== AST::FormatArgumentKind::Kind::Normal);
args.push_back ({arg.get_expr ().clone_expr (), trait});
}
break;
case ffi::Position::Tag::ArgumentIs:
case ffi::Position::Tag::ArgumentNamed:
rust_sorry_at (loc, "unhandled argument position specifier");
break;
}
}
break;
}
}
auto args_array = std::vector> ();
for (auto &&arg : args)
args_array.emplace_back (format_arg (builder,
builder.ref (std::move (arg.first)),
get_trait_name (arg.second)));
auto pieces = builder.ref (builder.array (std::move (static_pieces)));
auto args_slice = builder.ref (builder.array (std::move (args_array)));
auto final_path = make_unique (
builder.path_in_expression ({"core", "fmt", "Arguments", "new_v1"}));
auto final_args = std::vector> ();
final_args.emplace_back (std::move (pieces));
final_args.emplace_back (std::move (args_slice));
auto final_call
= builder.call (std::move (final_path), std::move (final_args));
auto node = AST::SingleASTNode (std::move (final_call));
return AST::Fragment ({node}, std::move (tokens));
}
} // namespace Fmt
} // namespace Rust