Line data Source code
1 : // Copyright (C) 2020-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-fmt.h"
20 : #include "rust-ast-builder.h"
21 : #include "rust-macro-builtins.h"
22 : #include "rust-macro-builtins-helpers.h"
23 : #include "rust-session-manager.h"
24 : #include "rust-stmt.h"
25 :
26 : namespace Rust {
27 :
28 : /* Expand builtin macro compile_error!("error"), which forces a compile error
29 : during the compile time. */
30 : tl::optional<AST::Fragment>
31 5 : MacroBuiltin::compile_error_handler (location_t invoc_locus,
32 : AST::MacroInvocData &invoc,
33 : AST::InvocKind semicolon)
34 : {
35 5 : auto lit_expr
36 : = parse_single_string_literal (BuiltinMacro::CompileError,
37 : invoc.get_delim_tok_tree (), invoc_locus,
38 5 : invoc.get_expander ());
39 5 : if (lit_expr == nullptr)
40 6 : return AST::Fragment::create_error ();
41 :
42 2 : rust_assert (lit_expr->is_literal ());
43 :
44 2 : std::string error_string = lit_expr->as_string ();
45 2 : rust_error_at (invoc_locus, "%s", error_string.c_str ());
46 :
47 4 : return AST::Fragment::create_error ();
48 5 : }
49 :
50 : /* Expand builtin macro concat!(), which joins all the literal parameters
51 : into a string with no delimiter. */
52 :
53 : // This is a weird one. We want to do something where, if something cannot be
54 : // expanded yet (i.e. macro invocation?) we return the whole MacroInvocation
55 : // node again but expanded as much as possible.
56 : // Is that possible? How do we do that?
57 : //
58 : // Let's take a few examples:
59 : //
60 : // 1. concat!(1, 2, true);
61 : // 2. concat!(a!(), 2, true);
62 : // 3. concat!(concat!(1, false), 2, true);
63 : // 4. concat!(concat!(1, a!()), 2, true);
64 : //
65 : // 1. We simply want to return the new fragment: "12true"
66 : // 2. We want to return `concat!(a_expanded, 2, true)` as a fragment
67 : // 3. We want to return `concat!(1, false, 2, true)`
68 : // 4. We want to return `concat!(concat!(1, a_expanded), 2, true);
69 : //
70 : // How do we do that?
71 : //
72 : // For each (un)expanded fragment: we check if it is expanded fully
73 : //
74 : // 1. What is expanded fully?
75 : // 2. How to check?
76 : //
77 : // If it is expanded fully and not a literal, then we error out.
78 : // Otherwise we simply emplace it back and keep going.
79 : //
80 : // In the second case, we must mark that this concat invocation still has some
81 : // expansion to do: This allows us to return a `MacroInvocation { ... }` as an
82 : // AST fragment, instead of a completed string.
83 : //
84 : // This means that we must change all the `try_expand_many_*` APIs and so on to
85 : // return some sort of index or way to signify that we might want to reuse some
86 : // bits and pieces of the original token tree.
87 : //
88 : // Now, before that: How do we resolve the names used in a builtin macro
89 : // invocation?
90 : // Do we split the two passes of parsing the token tree and then expanding it?
91 : // Can we do that easily?
92 : tl::optional<AST::Fragment>
93 660 : MacroBuiltin::concat_handler (location_t invoc_locus,
94 : AST::MacroInvocData &invoc,
95 : AST::InvocKind semicolon)
96 : {
97 660 : auto invoc_token_tree = invoc.get_delim_tok_tree ();
98 660 : MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
99 660 : Parser<MacroInvocLexer> parser (lex);
100 :
101 660 : auto str = std::string ();
102 660 : bool has_error = false;
103 :
104 660 : auto last_token_id = macro_end_token (invoc_token_tree, parser);
105 :
106 660 : auto start = lex.get_offs ();
107 : /* NOTE: concat! could accept no argument, so we don't have any checks here */
108 660 : auto expanded_expr = try_expand_many_expr (parser, last_token_id,
109 660 : invoc.get_expander (), has_error);
110 660 : auto end = lex.get_offs ();
111 :
112 660 : auto tokens = lex.get_token_slice (start, end);
113 :
114 660 : auto pending_invocations = check_for_eager_invocations (expanded_expr);
115 660 : if (!pending_invocations.empty ())
116 608 : return make_eager_builtin_invocation (BuiltinMacro::Concat, invoc_locus,
117 304 : invoc.get_delim_tok_tree (),
118 304 : std::move (pending_invocations));
119 :
120 2305 : for (auto &expr : expanded_expr)
121 : {
122 1949 : if (!expr->is_literal ()
123 1949 : && expr->get_expr_kind () != AST::Expr::Kind::MacroInvocation)
124 : {
125 2 : has_error = true;
126 2 : rust_error_at (expr->get_locus (), "expected a literal");
127 : // diagnostics copied from rustc
128 2 : rust_inform (expr->get_locus (),
129 : "only literals (like %<\"foo\"%>, %<42%> and "
130 : "%<3.14%>) can be passed to %<concat!()%>");
131 2 : continue;
132 : }
133 1947 : auto *literal = static_cast<AST::LiteralExpr *> (expr.get ());
134 1947 : if (literal->get_lit_type () == AST::Literal::BYTE
135 1947 : || literal->get_lit_type () == AST::Literal::BYTE_STRING)
136 : {
137 0 : has_error = true;
138 0 : rust_error_at (expr->get_locus (),
139 : "cannot concatenate a byte string literal");
140 0 : continue;
141 : }
142 3894 : str += literal->as_string ();
143 : }
144 :
145 356 : parser.skip_token (last_token_id);
146 :
147 356 : if (has_error)
148 8 : return AST::Fragment::create_error ();
149 :
150 704 : auto node = AST::SingleASTNode (make_string (invoc_locus, str));
151 704 : auto str_tok = make_token (Token::make_string (invoc_locus, std::move (str)));
152 :
153 1056 : return AST::Fragment ({node}, std::move (str_tok));
154 660 : }
155 :
156 : /* Expand builtin macro env!(), which inspects an environment variable at
157 : compile time. */
158 : tl::optional<AST::Fragment>
159 32 : MacroBuiltin::env_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
160 : AST::InvocKind semicolon)
161 : {
162 32 : auto invoc_token_tree = invoc.get_delim_tok_tree ();
163 32 : MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
164 32 : Parser<MacroInvocLexer> parser (lex);
165 :
166 32 : auto last_token_id = macro_end_token (invoc_token_tree, parser);
167 32 : std::unique_ptr<AST::LiteralExpr> error_expr = nullptr;
168 32 : std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
169 32 : bool has_error = false;
170 :
171 32 : auto start = lex.get_offs ();
172 32 : auto expanded_expr = try_expand_many_expr (parser, last_token_id,
173 32 : invoc.get_expander (), has_error);
174 32 : auto end = lex.get_offs ();
175 :
176 32 : auto tokens = lex.get_token_slice (start, end);
177 :
178 32 : if (has_error)
179 4 : return AST::Fragment::create_error ();
180 :
181 30 : auto pending = check_for_eager_invocations (expanded_expr);
182 30 : if (!pending.empty ())
183 6 : return make_eager_builtin_invocation (BuiltinMacro::Env, invoc_locus,
184 : invoc_token_tree,
185 3 : std::move (pending));
186 :
187 27 : if (expanded_expr.size () < 1 || expanded_expr.size () > 2)
188 : {
189 2 : rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
190 4 : return AST::Fragment::create_error ();
191 : }
192 25 : if (expanded_expr.size () > 0)
193 : {
194 50 : if (!(lit_expr
195 25 : = try_extract_string_literal_from_fragment (invoc_locus,
196 50 : expanded_expr[0])))
197 : {
198 6 : return AST::Fragment::create_error ();
199 : }
200 : }
201 22 : if (expanded_expr.size () > 1)
202 : {
203 5 : if (!(error_expr
204 5 : = try_extract_string_literal_from_fragment (invoc_locus,
205 10 : expanded_expr[1])))
206 : {
207 2 : return AST::Fragment::create_error ();
208 : }
209 : }
210 :
211 21 : parser.skip_token (last_token_id);
212 :
213 21 : auto env_value = getenv (lit_expr->as_string ().c_str ());
214 :
215 21 : if (env_value == nullptr)
216 : {
217 7 : if (error_expr == nullptr)
218 3 : rust_error_at (invoc_locus, "environment variable %qs not defined",
219 6 : lit_expr->as_string ().c_str ());
220 : else
221 4 : rust_error_at (invoc_locus, "%s", error_expr->as_string ().c_str ());
222 14 : return AST::Fragment::create_error ();
223 : }
224 :
225 14 : auto node = AST::SingleASTNode (make_string (invoc_locus, env_value));
226 14 : auto tok
227 28 : = make_token (Token::make_string (invoc_locus, std::move (env_value)));
228 :
229 42 : return AST::Fragment ({node}, std::move (tok));
230 32 : }
231 :
232 : /* Expand builtin macro option_env!(), which inspects an environment variable at
233 : compile time. */
234 : tl::optional<AST::Fragment>
235 25 : MacroBuiltin::option_env_handler (location_t invoc_locus,
236 : AST::MacroInvocData &invoc,
237 : AST::InvocKind semicolon)
238 : {
239 25 : auto invoc_token_tree = invoc.get_delim_tok_tree ();
240 25 : MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
241 25 : Parser<MacroInvocLexer> parser (lex);
242 :
243 25 : auto last_token_id = macro_end_token (invoc_token_tree, parser);
244 25 : std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
245 25 : bool has_error = false;
246 :
247 25 : auto start = lex.get_offs ();
248 25 : auto expanded_expr = try_expand_many_expr (parser, last_token_id,
249 25 : invoc.get_expander (), has_error);
250 25 : auto end = lex.get_offs ();
251 :
252 25 : auto tokens = lex.get_token_slice (start, end);
253 :
254 25 : if (has_error)
255 0 : return AST::Fragment::create_error ();
256 :
257 25 : auto pending = check_for_eager_invocations (expanded_expr);
258 25 : if (!pending.empty ())
259 14 : return make_eager_builtin_invocation (BuiltinMacro::OptionEnv, invoc_locus,
260 : invoc_token_tree,
261 7 : std::move (pending));
262 :
263 18 : if (expanded_expr.size () != 1)
264 : {
265 1 : rust_error_at (invoc_locus, "%<option_env!%> takes 1 argument");
266 2 : return AST::Fragment::create_error ();
267 : }
268 :
269 17 : if (expanded_expr.size () > 0)
270 34 : if (!(lit_expr
271 17 : = try_extract_string_literal_from_fragment (invoc_locus,
272 34 : expanded_expr[0])))
273 2 : return AST::Fragment::create_error ();
274 :
275 16 : parser.skip_token (last_token_id);
276 :
277 16 : auto env_value = getenv (lit_expr->as_string ().c_str ());
278 16 : AST::Builder b (invoc_locus, AST::Builder::Source::Any);
279 :
280 16 : if (env_value == nullptr)
281 : {
282 1 : auto none_expr = std::unique_ptr<AST::Expr> (
283 : new AST::PathInExpression (LangItem::Kind::OPTION_NONE, {},
284 1 : invoc_locus));
285 :
286 1 : auto node = AST::SingleASTNode (std::move (none_expr));
287 1 : std::vector<AST::SingleASTNode> nodes;
288 1 : nodes.push_back (node);
289 :
290 2 : return AST::Fragment (nodes, std::vector<std::unique_ptr<AST::Token>> ());
291 1 : }
292 15 : std::vector<std::unique_ptr<AST::Expr>> args;
293 15 : args.push_back (b.literal_string (env_value));
294 :
295 15 : std::unique_ptr<AST::Expr> some_expr
296 15 : = b.call (std::unique_ptr<AST::Expr> (
297 : new AST::PathInExpression (LangItem::Kind::OPTION_SOME, {},
298 30 : invoc_locus)),
299 15 : std::move (args));
300 :
301 15 : auto node = AST::SingleASTNode (std::move (some_expr));
302 :
303 15 : std::vector<AST::SingleASTNode> nodes;
304 15 : nodes.push_back (node);
305 :
306 30 : return AST::Fragment (nodes, std::vector<std::unique_ptr<AST::Token>> ());
307 25 : }
308 :
309 : tl::optional<AST::Fragment>
310 132 : MacroBuiltin::cfg_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
311 : AST::InvocKind semicolon)
312 : {
313 : // only parse if not already parsed
314 132 : if (!invoc.is_parsed ())
315 : {
316 132 : std::unique_ptr<AST::AttrInputMetaItemContainer> converted_input (
317 132 : invoc.get_delim_tok_tree ().parse_to_meta_item ());
318 :
319 132 : if (converted_input == nullptr)
320 : {
321 0 : rust_debug ("DEBUG: failed to parse macro to meta item");
322 : // TODO: do something now? is this an actual error?
323 : }
324 : else
325 : {
326 132 : std::vector<std::unique_ptr<AST::MetaItemInner>> meta_items (
327 132 : std::move (converted_input->get_items ()));
328 132 : invoc.set_meta_item_output (std::move (meta_items));
329 132 : }
330 132 : }
331 :
332 : /* TODO: assuming that cfg! macros can only have one meta item inner, like cfg
333 : * attributes */
334 132 : if (invoc.get_meta_items ().size () != 1)
335 0 : return AST::Fragment::create_error ();
336 :
337 132 : bool result = invoc.get_meta_items ()[0]->check_cfg_predicate (
338 132 : Session::get_instance ());
339 132 : auto literal_exp = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
340 132 : new AST::LiteralExpr (result ? "true" : "false", AST::Literal::BOOL,
341 396 : PrimitiveCoreType::CORETYPE_BOOL, {}, invoc_locus)));
342 132 : auto tok = make_token (
343 237 : Token::make (result ? TRUE_LITERAL : FALSE_LITERAL, invoc_locus));
344 :
345 396 : return AST::Fragment ({literal_exp}, std::move (tok));
346 132 : }
347 :
348 : tl::optional<AST::Fragment>
349 29 : MacroBuiltin::cfg_select_handler (location_t invoc_locus,
350 : AST::MacroInvocData &invoc,
351 : AST::InvocKind semicolon)
352 : {
353 29 : auto invoc_token_tree = invoc.get_delim_tok_tree ();
354 29 : MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
355 :
356 29 : Parser<MacroInvocLexer> parser (lex);
357 :
358 29 : if (!parser.skip_token (LEFT_CURLY))
359 : {
360 0 : rust_error_at (invoc_locus, "expected %<(%> in %<cfg_select!%>");
361 0 : return AST::Fragment::create_error ();
362 : }
363 :
364 29 : std::vector<AST::SingleASTNode> matched_body_nodes;
365 29 : std::vector<std::unique_ptr<AST::Token>> matched_body_tokens;
366 29 : bool has_match = false;
367 :
368 29 : while (lex.peek_token ()->get_id () != RIGHT_CURLY
369 204 : && lex.peek_token ()->get_id () != END_OF_FILE)
370 : {
371 102 : if (lex.peek_token ()->get_id () == UNDERSCORE)
372 : {
373 : // wildcard predicate
374 22 : lex.skip_token (); // consume '_'
375 22 : has_match = true;
376 : }
377 : else
378 : {
379 29 : size_t pred_start = lex.get_offs ();
380 :
381 : // parse the predicate (until =>)
382 2212 : while (lex.peek_token ()->get_id () != MATCH_ARROW)
383 : {
384 2154 : if (lex.peek_token ()->get_id () == END_OF_FILE)
385 : {
386 0 : rust_error_at (invoc_locus,
387 : "unterminated %<cfg_select!%>arm");
388 0 : return AST::Fragment::create_error ();
389 : }
390 1077 : lex.skip_token ();
391 : }
392 :
393 29 : size_t pred_end = lex.get_offs ();
394 :
395 29 : std::vector<const_TokenPtr> synth;
396 29 : synth.emplace_back (Token::make (LEFT_PAREN, invoc_locus));
397 29 : auto pred_tokens = lex.get_token_slice (pred_start, pred_end);
398 1106 : for (auto &t : pred_tokens)
399 2154 : synth.emplace_back (t->get_tok_ptr ());
400 29 : synth.emplace_back (Token::make (RIGHT_PAREN, invoc_locus));
401 :
402 29 : AST::AttributeParser attr_parser (std::move (synth));
403 29 : auto items = attr_parser.parse_meta_item_seq ();
404 29 : if (items.size () != 1)
405 : {
406 0 : rust_error_at (invoc_locus,
407 : " %<cfg_select!%> arm predicate must "
408 : "be a single cfg expression");
409 0 : return AST::Fragment::create_error ();
410 : }
411 :
412 29 : bool result
413 29 : = items[0]->check_cfg_predicate (Session::get_instance ());
414 29 : if (result)
415 : has_match = true;
416 29 : }
417 :
418 51 : if (!parser.skip_token (MATCH_ARROW))
419 : {
420 0 : rust_error_at (lex.peek_token ()->get_locus (),
421 : "expected %<=>%> in %<cfg_select!%> arm");
422 0 : return AST::Fragment::create_error ();
423 : }
424 :
425 : // parse the body (after =>)
426 : // always parse the body regardless of whether has_match is set, so lex
427 : // will be at the next predicate in the next loop
428 51 : size_t body_start = lex.get_offs ();
429 51 : auto block_res = parser.parse_block_expr ();
430 51 : if (has_match)
431 : {
432 29 : size_t body_end = lex.get_offs ();
433 29 : if (!block_res)
434 : {
435 0 : rust_error_at (lex.peek_token ()->get_locus (),
436 : "failed to parse %<cfg_select!%> arm body");
437 0 : return AST::Fragment::create_error ();
438 : }
439 :
440 29 : auto block = std::move (*block_res);
441 44 : for (auto &stmt : block->get_statements ())
442 : {
443 15 : if (stmt->get_stmt_kind () == AST::Stmt::Kind::Item)
444 : {
445 15 : AST::Stmt *raw = stmt.release ();
446 15 : matched_body_nodes.emplace_back (std::unique_ptr<AST::Item> (
447 15 : static_cast<AST::Item *> (raw)));
448 : }
449 : else
450 : {
451 0 : matched_body_nodes.emplace_back (std::move (stmt));
452 : }
453 : }
454 29 : if (block->has_tail_expr ())
455 : {
456 14 : auto tail = block->take_tail_expr ();
457 14 : matched_body_nodes.emplace_back (AST::SingleASTNode (
458 28 : std::make_unique<AST::ExprStmt> (std::move (tail), invoc_locus,
459 14 : false)));
460 14 : }
461 :
462 29 : matched_body_tokens = lex.get_token_slice (body_start, body_end);
463 29 : break;
464 29 : }
465 :
466 22 : parser.maybe_skip_token (COMMA);
467 51 : }
468 :
469 29 : if (!has_match)
470 : {
471 0 : rust_error_at (
472 : invoc_locus,
473 : "no %<cfg_select!%> arm matched and no %<_%> arm was provided");
474 0 : return AST::Fragment::create_error ();
475 : }
476 :
477 29 : return AST::Fragment (std::move (matched_body_nodes),
478 58 : std::move (matched_body_tokens));
479 29 : }
480 :
481 : tl::optional<AST::Fragment>
482 696 : MacroBuiltin::stringify_handler (location_t invoc_locus,
483 : AST::MacroInvocData &invoc,
484 : AST::InvocKind semicolon)
485 : {
486 696 : std::string content;
487 696 : auto invoc_token_tree = invoc.get_delim_tok_tree ();
488 696 : auto tokens = invoc_token_tree.to_token_stream ();
489 :
490 : // Tokens stream includes the first and last delimiter
491 : // which we need to skip.
492 1433 : for (auto token = tokens.cbegin () + 1; token < tokens.cend () - 1; token++)
493 : {
494 : // Rust stringify format has no garantees but the reference compiler
495 : // removes spaces before some tokens depending on the lexer's behavior,
496 : // let's mimick some of those behaviors.
497 737 : auto token_id = (*token)->get_id ();
498 737 : if (token_id != RIGHT_PAREN && token_id != EXCLAM
499 737 : && token != tokens.cbegin () + 1)
500 : {
501 25 : content.push_back (' ');
502 : }
503 1474 : content += (*token)->as_string ();
504 : }
505 :
506 1392 : auto node = AST::SingleASTNode (make_string (invoc_locus, content));
507 696 : auto token
508 1392 : = make_token (Token::make_string (invoc_locus, std::move (content)));
509 2088 : return AST::Fragment ({node}, std::move (token));
510 696 : }
511 :
512 : } // namespace Rust
|