Line data Source code
1 : // Copyright (C) 2024-2026 Free Software Foundation, Inc.
2 :
3 : // This file is part of GCC.
4 :
5 : // GCC is free software; you can redistribute it and/or modify it under
6 : // the terms of the GNU General Public License as published by the Free
7 : // Software Foundation; either version 3, or (at your option) any later
8 : // version.
9 :
10 : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : // for more details.
14 :
15 : // You should have received a copy of the GNU General Public License
16 : // along with GCC; see the file COPYING3. If not see
17 : // <http://www.gnu.org/licenses/>.
18 :
19 : #include "rust-expand-format-args.h"
20 : #include "rust-ast-fragment.h"
21 : #include "rust-ast.h"
22 : #include "rust-builtin-ast-nodes.h"
23 : #include "rust-ast-builder.h"
24 : #include "rust-diagnostics.h"
25 : #include "rust-expr.h"
26 : #include "rust-fmt.h"
27 : #include "rust-path.h"
28 : #include "rust-system.h"
29 : #include "rust-token.h"
30 :
31 : namespace Rust {
32 : namespace Fmt {
33 :
34 : static std::unique_ptr<AST::Expr>
35 119 : format_arg (const AST::Builder &builder, std::unique_ptr<AST::Expr> &&to_format,
36 : const std::string &trait)
37 : {
38 119 : auto formatter_fn = std::unique_ptr<AST::Expr> (
39 833 : new AST::PathInExpression (builder.path_in_expression (
40 238 : {builder.get_path_start (), "fmt", trait, "fmt"})));
41 :
42 119 : auto path = std::unique_ptr<AST::Expr> (
43 714 : new AST::PathInExpression (builder.path_in_expression (
44 238 : {builder.get_path_start (), "fmt", "ArgumentV1", "new"})));
45 :
46 119 : auto args = std::vector<std::unique_ptr<AST::Expr>> ();
47 119 : args.emplace_back (std::move (to_format));
48 119 : args.emplace_back (std::move (formatter_fn));
49 :
50 119 : return builder.call (std::move (path), std::move (args));
51 119 : }
52 :
53 : const std::string &
54 119 : get_trait_name (ffi::FormatSpec format_specifier)
55 : {
56 119 : static const std::unordered_map<std::string, std::string> spec_map = {
57 : {"", "Display"}, {"?", "Debug"}, {"e", "LowerExp"},
58 : {"E", "UpperExp"}, {"o", "Octal"}, {"p", "Pointer"},
59 : {"b", "Binary"}, {"x", "LowerHex"}, {"X", "UpperHex"},
60 155 : };
61 :
62 119 : auto it = spec_map.find (format_specifier.ty.to_string ());
63 :
64 119 : if (it == spec_map.end ())
65 0 : rust_unreachable ();
66 :
67 119 : return it->second;
68 : }
69 :
70 : tl::optional<AST::Fragment>
71 76 : expand_format_args (AST::FormatArgs &fmt,
72 : std::vector<std::unique_ptr<AST::Token>> &&tokens)
73 : {
74 76 : auto loc = fmt.get_locus ();
75 : // FIXME: This needs to be aware of the crate in which we are expanding it.
76 : // Expanding format_args!() within core needs to use paths that start with
77 : // `crate::`, not `core::`
78 76 : auto builder = AST::Builder (loc, AST::Builder::Source::InCore);
79 76 : auto &arguments = fmt.get_arguments ();
80 :
81 76 : auto static_pieces = std::vector<std::unique_ptr<AST::Expr>> ();
82 76 : auto args
83 : = std::vector<std::pair<std::unique_ptr<AST::Expr>, ffi::FormatSpec>> ();
84 :
85 346 : for (const auto &node : fmt.get_template ().get_pieces ())
86 : {
87 270 : switch (node.tag)
88 : {
89 151 : case ffi::Piece::Tag::String:
90 151 : static_pieces.emplace_back (
91 302 : builder.literal_string (node.string._0.to_string ()));
92 151 : break;
93 119 : case ffi::Piece::Tag::NextArgument:
94 119 : {
95 119 : auto next_argument = node.next_argument._0;
96 119 : switch (node.next_argument._0.position.tag)
97 : {
98 119 : case ffi::Position::Tag::ArgumentImplicitlyIs:
99 119 : {
100 119 : auto idx = next_argument.position.argument_implicitly_is._0;
101 119 : auto trait = next_argument.format;
102 119 : auto arg = arguments.at (idx);
103 :
104 : // FIXME(Arthur): This API sucks
105 119 : rust_assert (arg.get_kind ().kind
106 : == AST::FormatArgumentKind::Kind::Normal);
107 :
108 119 : args.push_back ({arg.get_expr ().clone_expr (), trait});
109 119 : }
110 119 : break;
111 0 : case ffi::Position::Tag::ArgumentIs:
112 0 : case ffi::Position::Tag::ArgumentNamed:
113 0 : rust_sorry_at (loc, "unhandled argument position specifier");
114 0 : break;
115 : }
116 119 : }
117 119 : break;
118 : }
119 : }
120 :
121 76 : auto args_array = std::vector<std::unique_ptr<AST::Expr>> ();
122 195 : for (auto &&arg : args)
123 238 : args_array.emplace_back (format_arg (builder,
124 238 : builder.ref (std::move (arg.first)),
125 238 : get_trait_name (arg.second)));
126 :
127 76 : auto pieces = builder.ref (builder.array (std::move (static_pieces)));
128 76 : auto args_slice = builder.ref (builder.array (std::move (args_array)));
129 :
130 76 : auto final_path
131 456 : = std::make_unique<AST::PathInExpression> (builder.path_in_expression (
132 76 : {builder.get_path_start (), "fmt", "Arguments", "new_v1"}));
133 76 : auto final_args = std::vector<std::unique_ptr<AST::Expr>> ();
134 76 : final_args.emplace_back (std::move (pieces));
135 76 : final_args.emplace_back (std::move (args_slice));
136 :
137 76 : auto final_call
138 76 : = builder.call (std::move (final_path), std::move (final_args));
139 :
140 76 : auto node = AST::SingleASTNode (std::move (final_call));
141 :
142 228 : return AST::Fragment ({node}, std::move (tokens));
143 76 : }
144 :
145 : } // namespace Fmt
146 : } // namespace Rust
|