]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/rust/expand/rust-macro-builtins.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / expand / rust-macro-builtins.cc
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-macro-builtins.h"
20 #include "rust-ast.h"
21 #include "rust-diagnostics.h"
22 #include "rust-expr.h"
23 #include "rust-session-manager.h"
24 #include "rust-macro-invoc-lexer.h"
25 #include "rust-lex.h"
26 #include "rust-parse.h"
27 #include "rust-early-name-resolver.h"
28 #include "rust-attribute-visitor.h"
29
30 namespace Rust {
31 namespace {
32
33 /**
34 * Shorthand function for creating unique_ptr tokens
35 */
36 static std::unique_ptr<AST::Token>
37 make_token (const TokenPtr tok)
38 {
39 return std::unique_ptr<AST::Token> (new AST::Token (tok));
40 }
41
42 std::unique_ptr<AST::Expr>
43 make_string (Location locus, std::string value)
44 {
45 return std::unique_ptr<AST::Expr> (
46 new AST::LiteralExpr (value, AST::Literal::STRING,
47 PrimitiveCoreType::CORETYPE_STR, {}, locus));
48 }
49
50 // TODO: Is this correct?
51 static AST::Fragment
52 make_eager_builtin_invocation (
53 AST::BuiltinMacro kind, Location locus, AST::DelimTokenTree arguments,
54 std::vector<std::unique_ptr<AST::MacroInvocation>> &&pending_invocations)
55 {
56 std::string path_str;
57
58 switch (kind)
59 {
60 // TODO: Should this be a table lookup?
61 case AST::BuiltinMacro::Assert:
62 path_str = "assert";
63 break;
64 case AST::BuiltinMacro::File:
65 path_str = "file";
66 break;
67 case AST::BuiltinMacro::Line:
68 path_str = "line";
69 break;
70 case AST::BuiltinMacro::Column:
71 path_str = "column";
72 break;
73 case AST::BuiltinMacro::IncludeBytes:
74 path_str = "include_bytes";
75 break;
76 case AST::BuiltinMacro::IncludeStr:
77 path_str = "include_str";
78 break;
79 case AST::BuiltinMacro::CompileError:
80 path_str = "compile_error";
81 break;
82 case AST::BuiltinMacro::Concat:
83 path_str = "concat";
84 break;
85 case AST::BuiltinMacro::Env:
86 path_str = "env";
87 break;
88 case AST::BuiltinMacro::Cfg:
89 path_str = "cfg";
90 break;
91 case AST::BuiltinMacro::Include:
92 path_str = "include";
93 break;
94 }
95
96 std::unique_ptr<AST::Expr> node = AST::MacroInvocation::Builtin (
97 kind,
98 AST::MacroInvocData (AST::SimplePath (
99 {AST::SimplePathSegment (path_str, locus)}),
100 std::move (arguments)),
101 {}, locus, std::move (pending_invocations));
102
103 return AST::Fragment ({AST::SingleASTNode (std::move (node))},
104 arguments.to_token_stream ());
105 }
106
107 /* Match the end token of a macro given the start delimiter of the macro */
108 static inline TokenId
109 macro_end_token (AST::DelimTokenTree &invoc_token_tree,
110 Parser<MacroInvocLexer> &parser)
111 {
112 auto last_token_id = TokenId::RIGHT_CURLY;
113 switch (invoc_token_tree.get_delim_type ())
114 {
115 case AST::DelimType::PARENS:
116 last_token_id = TokenId::RIGHT_PAREN;
117 rust_assert (parser.skip_token (LEFT_PAREN));
118 break;
119
120 case AST::DelimType::CURLY:
121 rust_assert (parser.skip_token (LEFT_CURLY));
122 break;
123
124 case AST::DelimType::SQUARE:
125 last_token_id = TokenId::RIGHT_SQUARE;
126 rust_assert (parser.skip_token (LEFT_SQUARE));
127 break;
128 }
129
130 return last_token_id;
131 }
132
133 /* Expand and then extract a string literal from the macro */
134 static std::unique_ptr<AST::LiteralExpr>
135 try_extract_string_literal_from_fragment (const Location &parent_locus,
136 std::unique_ptr<AST::Expr> &node)
137 {
138 auto maybe_lit = static_cast<AST::LiteralExpr *> (node.get ());
139 if (!node || !node->is_literal ()
140 || maybe_lit->get_lit_type () != AST::Literal::STRING)
141 {
142 rust_error_at (parent_locus, "argument must be a string literal");
143 if (node)
144 rust_inform (node->get_locus (), "expanded from here");
145 return nullptr;
146 }
147 return std::unique_ptr<AST::LiteralExpr> (
148 static_cast<AST::LiteralExpr *> (node->clone_expr ().release ()));
149 }
150
151 static std::vector<std::unique_ptr<AST::Expr>>
152 try_expand_many_expr (Parser<MacroInvocLexer> &parser,
153 const TokenId last_token_id, MacroExpander *expander,
154 bool &has_error)
155 {
156 auto restrictions = Rust::ParseRestrictions ();
157 // stop parsing when encountered a braces/brackets
158 restrictions.expr_can_be_null = true;
159 // we can't use std::optional, so...
160 auto result = std::vector<std::unique_ptr<AST::Expr>> ();
161 auto empty_expr = std::vector<std::unique_ptr<AST::Expr>> ();
162
163 auto first_token = parser.peek_current_token ()->get_id ();
164 if (first_token == COMMA)
165 {
166 rust_error_at (parser.peek_current_token ()->get_locus (),
167 "expected expression, found %<,%>");
168 has_error = true;
169 return empty_expr;
170 }
171
172 while (parser.peek_current_token ()->get_id () != last_token_id
173 && parser.peek_current_token ()->get_id () != END_OF_FILE)
174 {
175 auto expr = parser.parse_expr (AST::AttrVec (), restrictions);
176 // something must be so wrong that the expression could not be parsed
177 rust_assert (expr);
178 result.push_back (std::move (expr));
179
180 auto next_token = parser.peek_current_token ();
181 if (!parser.skip_token (COMMA) && next_token->get_id () != last_token_id)
182 {
183 rust_error_at (next_token->get_locus (), "expected token: %<,%>");
184 // TODO: is this recoverable? to avoid crashing the parser in the next
185 // fragment we have to exit early here
186 has_error = true;
187 return empty_expr;
188 }
189 }
190
191 return result;
192 }
193
194 /* Parse a single string literal from the given delimited token tree,
195 and return the LiteralExpr for it. Allow for an optional trailing comma,
196 but otherwise enforce that these are the only tokens. */
197
198 std::unique_ptr<AST::LiteralExpr>
199 parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
200 Location invoc_locus, MacroExpander *expander)
201 {
202 MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
203 Parser<MacroInvocLexer> parser (lex);
204
205 auto last_token_id = macro_end_token (invoc_token_tree, parser);
206
207 std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
208
209 if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
210 {
211 lit_expr = parser.parse_literal_expr ();
212 parser.maybe_skip_token (COMMA);
213 if (parser.peek_current_token ()->get_id () != last_token_id)
214 {
215 lit_expr = nullptr;
216 rust_error_at (invoc_locus, "macro takes 1 argument");
217 }
218 }
219 else if (parser.peek_current_token ()->get_id () == last_token_id)
220 rust_error_at (invoc_locus, "macro takes 1 argument");
221 else
222 rust_error_at (invoc_locus, "argument must be a string literal");
223
224 parser.skip_token (last_token_id);
225
226 return lit_expr;
227 }
228
229 /* Treat PATH as a path relative to the source file currently being
230 compiled, and return the absolute path for it. */
231
232 std::string
233 source_relative_path (std::string path, Location locus)
234 {
235 std::string compile_fname
236 = Session::get_instance ().linemap->location_file (locus);
237
238 auto dir_separator_pos = compile_fname.rfind (file_separator);
239
240 /* If there is no file_separator in the path, use current dir ('.'). */
241 std::string dirname;
242 if (dir_separator_pos == std::string::npos)
243 dirname = std::string (".") + file_separator;
244 else
245 dirname = compile_fname.substr (0, dir_separator_pos) + file_separator;
246
247 return dirname + path;
248 }
249
250 /* Read the full contents of the file FILENAME and return them in a vector.
251 FIXME: platform specific. */
252
253 std::vector<uint8_t>
254 load_file_bytes (const char *filename)
255 {
256 RAIIFile file_wrap (filename);
257 if (file_wrap.get_raw () == nullptr)
258 {
259 rust_error_at (Location (), "cannot open filename %s: %m", filename);
260 return std::vector<uint8_t> ();
261 }
262
263 FILE *f = file_wrap.get_raw ();
264 fseek (f, 0L, SEEK_END);
265 long fsize = ftell (f);
266 fseek (f, 0L, SEEK_SET);
267
268 std::vector<uint8_t> buf (fsize);
269
270 if (fread (&buf[0], fsize, 1, f) != 1)
271 {
272 rust_error_at (Location (), "error reading file %s: %m", filename);
273 return std::vector<uint8_t> ();
274 }
275
276 return buf;
277 }
278 } // namespace
279
280 AST::Fragment
281 MacroBuiltin::assert_handler (Location, AST::MacroInvocData &)
282 {
283 rust_debug ("assert!() called");
284
285 return AST::Fragment::create_error ();
286 }
287
288 AST::Fragment
289 MacroBuiltin::file_handler (Location invoc_locus, AST::MacroInvocData &)
290 {
291 auto current_file
292 = Session::get_instance ().linemap->location_file (invoc_locus);
293 auto file_str = AST::SingleASTNode (make_string (invoc_locus, current_file));
294 auto str_token
295 = make_token (Token::make_string (invoc_locus, std::move (current_file)));
296
297 return AST::Fragment ({file_str}, std::move (str_token));
298 }
299
300 AST::Fragment
301 MacroBuiltin::column_handler (Location invoc_locus, AST::MacroInvocData &)
302 {
303 auto current_column
304 = Session::get_instance ().linemap->location_to_column (invoc_locus);
305
306 auto column_tok = make_token (
307 Token::make_int (invoc_locus, std::to_string (current_column)));
308 auto column_no = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
309 new AST::LiteralExpr (std::to_string (current_column), AST::Literal::INT,
310 PrimitiveCoreType::CORETYPE_U32, {}, invoc_locus)));
311
312 return AST::Fragment ({column_no}, std::move (column_tok));
313 }
314
315 /* Expand builtin macro include_bytes!("filename"), which includes the contents
316 of the given file as reference to a byte array. Yields an expression of type
317 &'static [u8; N]. */
318
319 AST::Fragment
320 MacroBuiltin::include_bytes_handler (Location invoc_locus,
321 AST::MacroInvocData &invoc)
322 {
323 /* Get target filename from the macro invocation, which is treated as a path
324 relative to the include!-ing file (currently being compiled). */
325 auto lit_expr
326 = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus,
327 invoc.get_expander ());
328 if (lit_expr == nullptr)
329 return AST::Fragment::create_error ();
330
331 std::string target_filename
332 = source_relative_path (lit_expr->as_string (), invoc_locus);
333
334 std::vector<uint8_t> bytes = load_file_bytes (target_filename.c_str ());
335
336 /* Is there a more efficient way to do this? */
337 std::vector<std::unique_ptr<AST::Expr>> elts;
338
339 // We create the tokens for a borrow expression of a byte array, so
340 // & [ <byte0>, <byte1>, ... ]
341 std::vector<std::unique_ptr<AST::Token>> toks;
342 toks.emplace_back (make_token (Token::make (AMP, invoc_locus)));
343 toks.emplace_back (make_token (Token::make (LEFT_SQUARE, invoc_locus)));
344
345 for (uint8_t b : bytes)
346 {
347 elts.emplace_back (
348 new AST::LiteralExpr (std::string (1, (char) b), AST::Literal::BYTE,
349 PrimitiveCoreType::CORETYPE_U8,
350 {} /* outer_attrs */, invoc_locus));
351 toks.emplace_back (make_token (Token::make_byte_char (invoc_locus, b)));
352 toks.emplace_back (make_token (Token::make (COMMA, invoc_locus)));
353 }
354
355 toks.emplace_back (make_token (Token::make (RIGHT_SQUARE, invoc_locus)));
356
357 auto elems = std::unique_ptr<AST::ArrayElems> (
358 new AST::ArrayElemsValues (std::move (elts), invoc_locus));
359
360 auto array = std::unique_ptr<AST::Expr> (
361 new AST::ArrayExpr (std::move (elems), {}, {}, invoc_locus));
362
363 auto borrow = std::unique_ptr<AST::Expr> (
364 new AST::BorrowExpr (std::move (array), false, false, {}, invoc_locus));
365
366 auto node = AST::SingleASTNode (std::move (borrow));
367
368 return AST::Fragment ({node}, std::move (toks));
369 } // namespace Rust
370
371 /* Expand builtin macro include_str!("filename"), which includes the contents
372 of the given file as a string. The file must be UTF-8 encoded. Yields an
373 expression of type &'static str. */
374
375 AST::Fragment
376 MacroBuiltin::include_str_handler (Location invoc_locus,
377 AST::MacroInvocData &invoc)
378 {
379 /* Get target filename from the macro invocation, which is treated as a path
380 relative to the include!-ing file (currently being compiled). */
381 auto lit_expr
382 = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus,
383 invoc.get_expander ());
384 if (lit_expr == nullptr)
385 return AST::Fragment::create_error ();
386
387 std::string target_filename
388 = source_relative_path (lit_expr->as_string (), invoc_locus);
389
390 std::vector<uint8_t> bytes = load_file_bytes (target_filename.c_str ());
391
392 /* FIXME: reuse lexer */
393 int expect_single = 0;
394 for (uint8_t b : bytes)
395 {
396 if (expect_single)
397 {
398 if ((b & 0xC0) != 0x80)
399 /* character was truncated, exit with expect_single != 0 */
400 break;
401 expect_single--;
402 }
403 else if (b & 0x80)
404 {
405 if (b >= 0xF8)
406 {
407 /* more than 4 leading 1s */
408 expect_single = 1;
409 break;
410 }
411 else if (b >= 0xF0)
412 {
413 /* 4 leading 1s */
414 expect_single = 3;
415 }
416 else if (b >= 0xE0)
417 {
418 /* 3 leading 1s */
419 expect_single = 2;
420 }
421 else if (b >= 0xC0)
422 {
423 /* 2 leading 1s */
424 expect_single = 1;
425 }
426 else
427 {
428 /* only 1 leading 1 */
429 expect_single = 1;
430 break;
431 }
432 }
433 }
434
435 std::string str;
436 if (expect_single)
437 rust_error_at (invoc_locus, "%s was not a valid utf-8 file",
438 target_filename.c_str ());
439 else
440 str = std::string ((const char *) &bytes[0], bytes.size ());
441
442 auto node = AST::SingleASTNode (make_string (invoc_locus, str));
443 auto str_tok = make_token (Token::make_string (invoc_locus, std::move (str)));
444
445 // FIXME: Do not return an empty token vector here
446 return AST::Fragment ({node}, std::move (str_tok));
447 }
448
449 /* Expand builtin macro compile_error!("error"), which forces a compile error
450 during the compile time. */
451 AST::Fragment
452 MacroBuiltin::compile_error_handler (Location invoc_locus,
453 AST::MacroInvocData &invoc)
454 {
455 auto lit_expr
456 = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus,
457 invoc.get_expander ());
458 if (lit_expr == nullptr)
459 return AST::Fragment::create_error ();
460
461 std::string error_string = lit_expr->as_string ();
462 rust_error_at (invoc_locus, "%s", error_string.c_str ());
463
464 return AST::Fragment::create_error ();
465 }
466
467 static std::vector<std::unique_ptr<AST::MacroInvocation>>
468 check_for_eager_invocations (
469 std::vector<std::unique_ptr<AST::Expr>> &expressions)
470 {
471 std::vector<std::unique_ptr<AST::MacroInvocation>> pending;
472
473 for (auto &expr : expressions)
474 if (expr->get_ast_kind () == AST::Kind::MACRO_INVOCATION)
475 pending.emplace_back (std::unique_ptr<AST::MacroInvocation> (
476 static_cast<AST::MacroInvocation *> (expr->clone_expr ().release ())));
477
478 return pending;
479 }
480
481 /* Expand builtin macro concat!(), which joins all the literal parameters
482 into a string with no delimiter. */
483
484 // This is a weird one. We want to do something where, if something cannot be
485 // expanded yet (i.e. macro invocation?) we return the whole MacroInvocation
486 // node again but expanded as much as possible.
487 // Is that possible? How do we do that?
488 //
489 // Let's take a few examples:
490 //
491 // 1. concat!(1, 2, true);
492 // 2. concat!(a!(), 2, true);
493 // 3. concat!(concat!(1, false), 2, true);
494 // 4. concat!(concat!(1, a!()), 2, true);
495 //
496 // 1. We simply want to return the new fragment: "12true"
497 // 2. We want to return `concat!(a_expanded, 2, true)` as a fragment
498 // 3. We want to return `concat!(1, false, 2, true)`
499 // 4. We want to return `concat!(concat!(1, a_expanded), 2, true);
500 //
501 // How do we do that?
502 //
503 // For each (un)expanded fragment: we check if it is expanded fully
504 //
505 // 1. What is expanded fully?
506 // 2. How to check?
507 //
508 // If it is expanded fully and not a literal, then we error out.
509 // Otherwise we simply emplace it back and keep going.
510 //
511 // In the second case, we must mark that this concat invocation still has some
512 // expansion to do: This allows us to return a `MacroInvocation { ... }` as an
513 // AST fragment, instead of a completed string.
514 //
515 // This means that we must change all the `try_expand_many_*` APIs and so on to
516 // return some sort of index or way to signify that we might want to reuse some
517 // bits and pieces of the original token tree.
518 //
519 // Now, before that: How do we resolve the names used in a builtin macro
520 // invocation?
521 // Do we split the two passes of parsing the token tree and then expanding it?
522 // Can we do that easily?
523 AST::Fragment
524 MacroBuiltin::concat_handler (Location invoc_locus, AST::MacroInvocData &invoc)
525 {
526 auto invoc_token_tree = invoc.get_delim_tok_tree ();
527 MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
528 Parser<MacroInvocLexer> parser (lex);
529
530 auto str = std::string ();
531 bool has_error = false;
532
533 auto last_token_id = macro_end_token (invoc_token_tree, parser);
534
535 auto start = lex.get_offs ();
536 /* NOTE: concat! could accept no argument, so we don't have any checks here */
537 auto expanded_expr = try_expand_many_expr (parser, last_token_id,
538 invoc.get_expander (), has_error);
539 auto end = lex.get_offs ();
540
541 auto tokens = lex.get_token_slice (start, end);
542
543 auto pending_invocations = check_for_eager_invocations (expanded_expr);
544 if (!pending_invocations.empty ())
545 return make_eager_builtin_invocation (AST::BuiltinMacro::Concat,
546 invoc_locus,
547 invoc.get_delim_tok_tree (),
548 std::move (pending_invocations));
549
550 for (auto &expr : expanded_expr)
551 {
552 if (!expr->is_literal ()
553 && expr->get_ast_kind () != AST::MACRO_INVOCATION)
554 {
555 has_error = true;
556 rust_error_at (expr->get_locus (), "expected a literal");
557 // diagnostics copied from rustc
558 rust_inform (expr->get_locus (),
559 "only literals (like %<\"foo\"%>, %<42%> and "
560 "%<3.14%>) can be passed to %<concat!()%>");
561 continue;
562 }
563 auto *literal = static_cast<AST::LiteralExpr *> (expr.get ());
564 if (literal->get_lit_type () == AST::Literal::BYTE
565 || literal->get_lit_type () == AST::Literal::BYTE_STRING)
566 {
567 has_error = true;
568 rust_error_at (expr->get_locus (),
569 "cannot concatenate a byte string literal");
570 continue;
571 }
572 str += literal->as_string ();
573 }
574
575 parser.skip_token (last_token_id);
576
577 if (has_error)
578 return AST::Fragment::create_error ();
579
580 auto node = AST::SingleASTNode (make_string (invoc_locus, str));
581 auto str_tok = make_token (Token::make_string (invoc_locus, std::move (str)));
582
583 return AST::Fragment ({node}, std::move (str_tok));
584 }
585
586 /* Expand builtin macro env!(), which inspects an environment variable at
587 compile time. */
588 AST::Fragment
589 MacroBuiltin::env_handler (Location invoc_locus, AST::MacroInvocData &invoc)
590 {
591 auto invoc_token_tree = invoc.get_delim_tok_tree ();
592 MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
593 Parser<MacroInvocLexer> parser (lex);
594
595 auto last_token_id = macro_end_token (invoc_token_tree, parser);
596 std::unique_ptr<AST::LiteralExpr> error_expr = nullptr;
597 std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
598 bool has_error = false;
599
600 auto start = lex.get_offs ();
601 auto expanded_expr = try_expand_many_expr (parser, last_token_id,
602 invoc.get_expander (), has_error);
603 auto end = lex.get_offs ();
604
605 auto tokens = lex.get_token_slice (start, end);
606
607 if (has_error)
608 return AST::Fragment::create_error ();
609
610 auto pending = check_for_eager_invocations (expanded_expr);
611 if (!pending.empty ())
612 return make_eager_builtin_invocation (AST::BuiltinMacro::Env, invoc_locus,
613 invoc_token_tree,
614 std::move (pending));
615
616 if (expanded_expr.size () < 1 || expanded_expr.size () > 2)
617 {
618 rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
619 return AST::Fragment::create_error ();
620 }
621 if (expanded_expr.size () > 0)
622 {
623 if (!(lit_expr
624 = try_extract_string_literal_from_fragment (invoc_locus,
625 expanded_expr[0])))
626 {
627 return AST::Fragment::create_error ();
628 }
629 }
630 if (expanded_expr.size () > 1)
631 {
632 if (!(error_expr
633 = try_extract_string_literal_from_fragment (invoc_locus,
634 expanded_expr[1])))
635 {
636 return AST::Fragment::create_error ();
637 }
638 }
639
640 parser.skip_token (last_token_id);
641
642 auto env_value = getenv (lit_expr->as_string ().c_str ());
643
644 if (env_value == nullptr)
645 {
646 if (error_expr == nullptr)
647 rust_error_at (invoc_locus, "environment variable %qs not defined",
648 lit_expr->as_string ().c_str ());
649 else
650 rust_error_at (invoc_locus, "%s", error_expr->as_string ().c_str ());
651 return AST::Fragment::create_error ();
652 }
653
654 auto node = AST::SingleASTNode (make_string (invoc_locus, env_value));
655 auto tok
656 = make_token (Token::make_string (invoc_locus, std::move (env_value)));
657
658 // FIXME: Do not return an empty token vector here
659 return AST::Fragment ({node}, std::move (tok));
660 }
661
662 AST::Fragment
663 MacroBuiltin::cfg_handler (Location invoc_locus, AST::MacroInvocData &invoc)
664 {
665 // only parse if not already parsed
666 if (!invoc.is_parsed ())
667 {
668 std::unique_ptr<AST::AttrInputMetaItemContainer> converted_input (
669 invoc.get_delim_tok_tree ().parse_to_meta_item ());
670
671 if (converted_input == nullptr)
672 {
673 rust_debug ("DEBUG: failed to parse macro to meta item");
674 // TODO: do something now? is this an actual error?
675 }
676 else
677 {
678 std::vector<std::unique_ptr<AST::MetaItemInner>> meta_items (
679 std::move (converted_input->get_items ()));
680 invoc.set_meta_item_output (std::move (meta_items));
681 }
682 }
683
684 /* TODO: assuming that cfg! macros can only have one meta item inner, like cfg
685 * attributes */
686 if (invoc.get_meta_items ().size () != 1)
687 return AST::Fragment::create_error ();
688
689 bool result = invoc.get_meta_items ()[0]->check_cfg_predicate (
690 Session::get_instance ());
691 auto literal_exp = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
692 new AST::LiteralExpr (result ? "true" : "false", AST::Literal::BOOL,
693 PrimitiveCoreType::CORETYPE_BOOL, {}, invoc_locus)));
694 auto tok = make_token (
695 Token::make (result ? TRUE_LITERAL : FALSE_LITERAL, invoc_locus));
696
697 // FIXME: Do not return an empty token vector here
698 return AST::Fragment ({literal_exp}, std::move (tok));
699 }
700
701 /* Expand builtin macro include!(), which includes a source file at the current
702 scope compile time. */
703
704 AST::Fragment
705 MacroBuiltin::include_handler (Location invoc_locus, AST::MacroInvocData &invoc)
706 {
707 /* Get target filename from the macro invocation, which is treated as a path
708 relative to the include!-ing file (currently being compiled). */
709 auto lit_expr
710 = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus,
711 invoc.get_expander ());
712 if (lit_expr == nullptr)
713 return AST::Fragment::create_error ();
714
715 std::string filename
716 = source_relative_path (lit_expr->as_string (), invoc_locus);
717 auto target_filename
718 = Rust::Session::get_instance ().include_extra_file (std::move (filename));
719
720 RAIIFile target_file (target_filename);
721 Linemap *linemap = Session::get_instance ().linemap;
722
723 if (!target_file.ok ())
724 {
725 rust_error_at (lit_expr->get_locus (),
726 "cannot open included file %qs: %m", target_filename);
727 return AST::Fragment::create_error ();
728 }
729
730 rust_debug ("Attempting to parse included file %s", target_filename);
731
732 Lexer lex (target_filename, std::move (target_file), linemap);
733 Parser<Lexer> parser (lex);
734
735 auto parsed_items = parser.parse_items ();
736 bool has_error = !parser.get_errors ().empty ();
737
738 for (const auto &error : parser.get_errors ())
739 error.emit ();
740
741 if (has_error)
742 {
743 // inform the user that the errors above are from a included file
744 rust_inform (invoc_locus, "included from here");
745 return AST::Fragment::create_error ();
746 }
747
748 std::vector<AST::SingleASTNode> nodes{};
749 for (auto &item : parsed_items)
750 {
751 AST::SingleASTNode node (std::move (item));
752 nodes.push_back (node);
753 }
754
755 // FIXME: This returns an empty vector of tokens and works fine, but is that
756 // the expected behavior? `include` macros are a bit harder to reason about
757 // since they include tokens. Furthermore, our lexer has no easy way to return
758 // a slice of tokens like the MacroInvocLexer. So it gets even harder to
759 // extrac tokens from here. For now, let's keep it that way and see if it
760 // eventually breaks, but I don't expect it to cause many issues since the
761 // list of tokens is only used when a macro invocation mixes eager
762 // macro invocations and already expanded tokens. Think
763 // `concat!(a!(), 15, b!())`. We need to be able to expand a!(), expand b!(),
764 // and then insert the `15` token in between. In the case of `include!()`, we
765 // only have one argument. So it's either going to be a macro invocation or a
766 // string literal.
767 return AST::Fragment (nodes, std::vector<std::unique_ptr<AST::Token>> ());
768 }
769
770 AST::Fragment
771 MacroBuiltin::line_handler (Location invoc_locus, AST::MacroInvocData &)
772 {
773 auto current_line
774 = Session::get_instance ().linemap->location_to_line (invoc_locus);
775
776 auto line_no = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
777 new AST::LiteralExpr (std::to_string (current_line), AST::Literal::INT,
778 PrimitiveCoreType::CORETYPE_U32, {}, invoc_locus)));
779 auto tok
780 = make_token (Token::make_int (invoc_locus, std::to_string (current_line)));
781
782 // FIXME: Do not return an empty token vector here
783 return AST::Fragment ({line_no}, std::move (tok));
784 }
785
786 } // namespace Rust