Branch data Line data Source code
1 : : // Copyright (C) 2020-2024 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-fmt.h"
20 : : #include "rust-macro-builtins.h"
21 : : #include "rust-macro-builtins-helpers.h"
22 : :
23 : : namespace Rust {
24 : :
25 : : /* Expand builtin macro compile_error!("error"), which forces a compile error
26 : : during the compile time. */
27 : : tl::optional<AST::Fragment>
28 : 5 : MacroBuiltin::compile_error_handler (location_t invoc_locus,
29 : : AST::MacroInvocData &invoc)
30 : : {
31 : 5 : auto lit_expr
32 : : = parse_single_string_literal (BuiltinMacro::CompileError,
33 : : invoc.get_delim_tok_tree (), invoc_locus,
34 : 5 : invoc.get_expander ());
35 : 5 : if (lit_expr == nullptr)
36 : 6 : return AST::Fragment::create_error ();
37 : :
38 : 2 : rust_assert (lit_expr->is_literal ());
39 : :
40 : 2 : std::string error_string = lit_expr->as_string ();
41 : 2 : rust_error_at (invoc_locus, "%s", error_string.c_str ());
42 : :
43 : 4 : return AST::Fragment::create_error ();
44 : 5 : }
45 : :
46 : : /* Expand builtin macro concat!(), which joins all the literal parameters
47 : : into a string with no delimiter. */
48 : :
49 : : // This is a weird one. We want to do something where, if something cannot be
50 : : // expanded yet (i.e. macro invocation?) we return the whole MacroInvocation
51 : : // node again but expanded as much as possible.
52 : : // Is that possible? How do we do that?
53 : : //
54 : : // Let's take a few examples:
55 : : //
56 : : // 1. concat!(1, 2, true);
57 : : // 2. concat!(a!(), 2, true);
58 : : // 3. concat!(concat!(1, false), 2, true);
59 : : // 4. concat!(concat!(1, a!()), 2, true);
60 : : //
61 : : // 1. We simply want to return the new fragment: "12true"
62 : : // 2. We want to return `concat!(a_expanded, 2, true)` as a fragment
63 : : // 3. We want to return `concat!(1, false, 2, true)`
64 : : // 4. We want to return `concat!(concat!(1, a_expanded), 2, true);
65 : : //
66 : : // How do we do that?
67 : : //
68 : : // For each (un)expanded fragment: we check if it is expanded fully
69 : : //
70 : : // 1. What is expanded fully?
71 : : // 2. How to check?
72 : : //
73 : : // If it is expanded fully and not a literal, then we error out.
74 : : // Otherwise we simply emplace it back and keep going.
75 : : //
76 : : // In the second case, we must mark that this concat invocation still has some
77 : : // expansion to do: This allows us to return a `MacroInvocation { ... }` as an
78 : : // AST fragment, instead of a completed string.
79 : : //
80 : : // This means that we must change all the `try_expand_many_*` APIs and so on to
81 : : // return some sort of index or way to signify that we might want to reuse some
82 : : // bits and pieces of the original token tree.
83 : : //
84 : : // Now, before that: How do we resolve the names used in a builtin macro
85 : : // invocation?
86 : : // Do we split the two passes of parsing the token tree and then expanding it?
87 : : // Can we do that easily?
88 : : tl::optional<AST::Fragment>
89 : 56 : MacroBuiltin::concat_handler (location_t invoc_locus,
90 : : AST::MacroInvocData &invoc)
91 : : {
92 : 56 : auto invoc_token_tree = invoc.get_delim_tok_tree ();
93 : 56 : MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
94 : 56 : Parser<MacroInvocLexer> parser (lex);
95 : :
96 : 56 : auto str = std::string ();
97 : 56 : bool has_error = false;
98 : :
99 : 56 : auto last_token_id = macro_end_token (invoc_token_tree, parser);
100 : :
101 : 56 : auto start = lex.get_offs ();
102 : : /* NOTE: concat! could accept no argument, so we don't have any checks here */
103 : 56 : auto expanded_expr = try_expand_many_expr (parser, last_token_id,
104 : 56 : invoc.get_expander (), has_error);
105 : 56 : auto end = lex.get_offs ();
106 : :
107 : 56 : auto tokens = lex.get_token_slice (start, end);
108 : :
109 : 56 : auto pending_invocations = check_for_eager_invocations (expanded_expr);
110 : 56 : if (!pending_invocations.empty ())
111 : 6 : return make_eager_builtin_invocation (BuiltinMacro::Concat, invoc_locus,
112 : 3 : invoc.get_delim_tok_tree (),
113 : 3 : std::move (pending_invocations));
114 : :
115 : 178 : for (auto &expr : expanded_expr)
116 : : {
117 : 125 : if (!expr->is_literal ()
118 : 125 : && expr->get_ast_kind () != AST::Kind::MACRO_INVOCATION)
119 : : {
120 : 2 : has_error = true;
121 : 2 : rust_error_at (expr->get_locus (), "expected a literal");
122 : : // diagnostics copied from rustc
123 : 2 : rust_inform (expr->get_locus (),
124 : : "only literals (like %<\"foo\"%>, %<42%> and "
125 : : "%<3.14%>) can be passed to %<concat!()%>");
126 : 2 : continue;
127 : : }
128 : 123 : auto *literal = static_cast<AST::LiteralExpr *> (expr.get ());
129 : 123 : if (literal->get_lit_type () == AST::Literal::BYTE
130 : 123 : || literal->get_lit_type () == AST::Literal::BYTE_STRING)
131 : : {
132 : 0 : has_error = true;
133 : 0 : rust_error_at (expr->get_locus (),
134 : : "cannot concatenate a byte string literal");
135 : 0 : continue;
136 : : }
137 : 123 : str += literal->as_string ();
138 : : }
139 : :
140 : 53 : parser.skip_token (last_token_id);
141 : :
142 : 53 : if (has_error)
143 : 8 : return AST::Fragment::create_error ();
144 : :
145 : 49 : auto node = AST::SingleASTNode (make_string (invoc_locus, str));
146 : 49 : auto str_tok = make_token (Token::make_string (invoc_locus, std::move (str)));
147 : :
148 : 147 : return AST::Fragment ({node}, std::move (str_tok));
149 : 112 : }
150 : :
151 : : /* Expand builtin macro env!(), which inspects an environment variable at
152 : : compile time. */
153 : : tl::optional<AST::Fragment>
154 : 32 : MacroBuiltin::env_handler (location_t invoc_locus, AST::MacroInvocData &invoc)
155 : : {
156 : 32 : auto invoc_token_tree = invoc.get_delim_tok_tree ();
157 : 32 : MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
158 : 32 : Parser<MacroInvocLexer> parser (lex);
159 : :
160 : 32 : auto last_token_id = macro_end_token (invoc_token_tree, parser);
161 : 32 : std::unique_ptr<AST::LiteralExpr> error_expr = nullptr;
162 : 32 : std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
163 : 32 : bool has_error = false;
164 : :
165 : 32 : auto start = lex.get_offs ();
166 : 32 : auto expanded_expr = try_expand_many_expr (parser, last_token_id,
167 : 32 : invoc.get_expander (), has_error);
168 : 32 : auto end = lex.get_offs ();
169 : :
170 : 32 : auto tokens = lex.get_token_slice (start, end);
171 : :
172 : 32 : if (has_error)
173 : 4 : return AST::Fragment::create_error ();
174 : :
175 : 30 : auto pending = check_for_eager_invocations (expanded_expr);
176 : 30 : if (!pending.empty ())
177 : 6 : return make_eager_builtin_invocation (BuiltinMacro::Env, invoc_locus,
178 : : invoc_token_tree,
179 : 3 : std::move (pending));
180 : :
181 : 27 : if (expanded_expr.size () < 1 || expanded_expr.size () > 2)
182 : : {
183 : 2 : rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
184 : 4 : return AST::Fragment::create_error ();
185 : : }
186 : 25 : if (expanded_expr.size () > 0)
187 : : {
188 : 50 : if (!(lit_expr
189 : 25 : = try_extract_string_literal_from_fragment (invoc_locus,
190 : 50 : expanded_expr[0])))
191 : : {
192 : 6 : return AST::Fragment::create_error ();
193 : : }
194 : : }
195 : 22 : if (expanded_expr.size () > 1)
196 : : {
197 : 5 : if (!(error_expr
198 : 5 : = try_extract_string_literal_from_fragment (invoc_locus,
199 : 10 : expanded_expr[1])))
200 : : {
201 : 2 : return AST::Fragment::create_error ();
202 : : }
203 : : }
204 : :
205 : 21 : parser.skip_token (last_token_id);
206 : :
207 : 21 : auto env_value = getenv (lit_expr->as_string ().c_str ());
208 : :
209 : 21 : if (env_value == nullptr)
210 : : {
211 : 7 : if (error_expr == nullptr)
212 : 3 : rust_error_at (invoc_locus, "environment variable %qs not defined",
213 : 6 : lit_expr->as_string ().c_str ());
214 : : else
215 : 4 : rust_error_at (invoc_locus, "%s", error_expr->as_string ().c_str ());
216 : 14 : return AST::Fragment::create_error ();
217 : : }
218 : :
219 : 14 : auto node = AST::SingleASTNode (make_string (invoc_locus, env_value));
220 : 14 : auto tok
221 : 28 : = make_token (Token::make_string (invoc_locus, std::move (env_value)));
222 : :
223 : 42 : return AST::Fragment ({node}, std::move (tok));
224 : 64 : }
225 : :
226 : : tl::optional<AST::Fragment>
227 : 47 : MacroBuiltin::cfg_handler (location_t invoc_locus, AST::MacroInvocData &invoc)
228 : : {
229 : : // only parse if not already parsed
230 : 47 : if (!invoc.is_parsed ())
231 : : {
232 : 47 : std::unique_ptr<AST::AttrInputMetaItemContainer> converted_input (
233 : 47 : invoc.get_delim_tok_tree ().parse_to_meta_item ());
234 : :
235 : 47 : if (converted_input == nullptr)
236 : : {
237 : 0 : rust_debug ("DEBUG: failed to parse macro to meta item");
238 : : // TODO: do something now? is this an actual error?
239 : : }
240 : : else
241 : : {
242 : 47 : std::vector<std::unique_ptr<AST::MetaItemInner>> meta_items (
243 : 47 : std::move (converted_input->get_items ()));
244 : 47 : invoc.set_meta_item_output (std::move (meta_items));
245 : 47 : }
246 : 47 : }
247 : :
248 : : /* TODO: assuming that cfg! macros can only have one meta item inner, like cfg
249 : : * attributes */
250 : 47 : if (invoc.get_meta_items ().size () != 1)
251 : 0 : return AST::Fragment::create_error ();
252 : :
253 : 47 : bool result = invoc.get_meta_items ()[0]->check_cfg_predicate (
254 : 47 : Session::get_instance ());
255 : 47 : auto literal_exp = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
256 : : new AST::LiteralExpr (result ? "true" : "false", AST::Literal::BOOL,
257 : 115 : PrimitiveCoreType::CORETYPE_BOOL, {}, invoc_locus)));
258 : 47 : auto tok = make_token (
259 : 68 : Token::make (result ? TRUE_LITERAL : FALSE_LITERAL, invoc_locus));
260 : :
261 : 141 : return AST::Fragment ({literal_exp}, std::move (tok));
262 : 47 : }
263 : :
264 : : tl::optional<AST::Fragment>
265 : 8 : MacroBuiltin::stringify_handler (location_t invoc_locus,
266 : : AST::MacroInvocData &invoc)
267 : : {
268 : 8 : std::string content;
269 : 8 : auto invoc_token_tree = invoc.get_delim_tok_tree ();
270 : 8 : auto tokens = invoc_token_tree.to_token_stream ();
271 : :
272 : : // Tokens stream includes the first and last delimiter
273 : : // which we need to skip.
274 : 51 : for (auto token = tokens.cbegin () + 1; token < tokens.cend () - 1; token++)
275 : : {
276 : : // Rust stringify format has no garantees but the reference compiler
277 : : // removes spaces before some tokens depending on the lexer's behavior,
278 : : // let's mimick some of those behaviors.
279 : 43 : auto token_id = (*token)->get_id ();
280 : 43 : if (token_id != RIGHT_PAREN && token_id != EXCLAM
281 : 43 : && token != tokens.cbegin () + 1)
282 : : {
283 : 19 : content.push_back (' ');
284 : : }
285 : 43 : content += (*token)->as_string ();
286 : : }
287 : :
288 : 8 : auto node = AST::SingleASTNode (make_string (invoc_locus, content));
289 : 8 : auto token
290 : 8 : = make_token (Token::make_string (invoc_locus, std::move (content)));
291 : 24 : return AST::Fragment ({node}, std::move (token));
292 : 8 : }
293 : :
294 : : } // namespace Rust
|