1 // Copyright (C) 2020-2023 Free Software Foundation, Inc.
3 // This file is part of GCC.
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
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
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/>.
19 #include "rust-macro-builtins.h"
21 #include "rust-diagnostics.h"
22 #include "rust-expr.h"
23 #include "rust-session-manager.h"
24 #include "rust-macro-invoc-lexer.h"
26 #include "rust-parse.h"
27 #include "rust-early-name-resolver.h"
28 #include "rust-attribute-visitor.h"
34 * Shorthand function for creating unique_ptr tokens
36 static std::unique_ptr
<AST::Token
>
37 make_token (const TokenPtr tok
)
39 return std::unique_ptr
<AST::Token
> (new AST::Token (tok
));
42 std::unique_ptr
<AST::Expr
>
43 make_string (Location locus
, std::string value
)
45 return std::unique_ptr
<AST::Expr
> (
46 new AST::LiteralExpr (value
, AST::Literal::STRING
,
47 PrimitiveCoreType::CORETYPE_STR
, {}, locus
));
50 // TODO: Is this correct?
52 make_eager_builtin_invocation (
53 AST::BuiltinMacro kind
, Location locus
, AST::DelimTokenTree arguments
,
54 std::vector
<std::unique_ptr
<AST::MacroInvocation
>> &&pending_invocations
)
60 // TODO: Should this be a table lookup?
61 case AST::BuiltinMacro::Assert
:
64 case AST::BuiltinMacro::File
:
67 case AST::BuiltinMacro::Line
:
70 case AST::BuiltinMacro::Column
:
73 case AST::BuiltinMacro::IncludeBytes
:
74 path_str
= "include_bytes";
76 case AST::BuiltinMacro::IncludeStr
:
77 path_str
= "include_str";
79 case AST::BuiltinMacro::CompileError
:
80 path_str
= "compile_error";
82 case AST::BuiltinMacro::Concat
:
85 case AST::BuiltinMacro::Env
:
88 case AST::BuiltinMacro::Cfg
:
91 case AST::BuiltinMacro::Include
:
96 std::unique_ptr
<AST::Expr
> node
= AST::MacroInvocation::Builtin (
98 AST::MacroInvocData (AST::SimplePath (
99 {AST::SimplePathSegment (path_str
, locus
)}),
100 std::move (arguments
)),
101 {}, locus
, std::move (pending_invocations
));
103 return AST::Fragment ({AST::SingleASTNode (std::move (node
))},
104 arguments
.to_token_stream ());
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
)
112 auto last_token_id
= TokenId::RIGHT_CURLY
;
113 switch (invoc_token_tree
.get_delim_type ())
115 case AST::DelimType::PARENS
:
116 last_token_id
= TokenId::RIGHT_PAREN
;
117 rust_assert (parser
.skip_token (LEFT_PAREN
));
120 case AST::DelimType::CURLY
:
121 rust_assert (parser
.skip_token (LEFT_CURLY
));
124 case AST::DelimType::SQUARE
:
125 last_token_id
= TokenId::RIGHT_SQUARE
;
126 rust_assert (parser
.skip_token (LEFT_SQUARE
));
130 return last_token_id
;
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
)
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
)
142 rust_error_at (parent_locus
, "argument must be a string literal");
144 rust_inform (node
->get_locus (), "expanded from here");
147 return std::unique_ptr
<AST::LiteralExpr
> (
148 static_cast<AST::LiteralExpr
*> (node
->clone_expr ().release ()));
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
,
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
>> ();
163 auto first_token
= parser
.peek_current_token ()->get_id ();
164 if (first_token
== COMMA
)
166 rust_error_at (parser
.peek_current_token ()->get_locus (),
167 "expected expression, found %<,%>");
172 while (parser
.peek_current_token ()->get_id () != last_token_id
173 && parser
.peek_current_token ()->get_id () != END_OF_FILE
)
175 auto expr
= parser
.parse_expr (AST::AttrVec (), restrictions
);
176 // something must be so wrong that the expression could not be parsed
178 result
.push_back (std::move (expr
));
180 auto next_token
= parser
.peek_current_token ();
181 if (!parser
.skip_token (COMMA
) && next_token
->get_id () != last_token_id
)
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
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. */
198 std::unique_ptr
<AST::LiteralExpr
>
199 parse_single_string_literal (AST::DelimTokenTree
&invoc_token_tree
,
200 Location invoc_locus
, MacroExpander
*expander
)
202 MacroInvocLexer
lex (invoc_token_tree
.to_token_stream ());
203 Parser
<MacroInvocLexer
> parser (lex
);
205 auto last_token_id
= macro_end_token (invoc_token_tree
, parser
);
207 std::unique_ptr
<AST::LiteralExpr
> lit_expr
= nullptr;
209 if (parser
.peek_current_token ()->get_id () == STRING_LITERAL
)
211 lit_expr
= parser
.parse_literal_expr ();
212 parser
.maybe_skip_token (COMMA
);
213 if (parser
.peek_current_token ()->get_id () != last_token_id
)
216 rust_error_at (invoc_locus
, "macro takes 1 argument");
219 else if (parser
.peek_current_token ()->get_id () == last_token_id
)
220 rust_error_at (invoc_locus
, "macro takes 1 argument");
222 rust_error_at (invoc_locus
, "argument must be a string literal");
224 parser
.skip_token (last_token_id
);
229 /* Treat PATH as a path relative to the source file currently being
230 compiled, and return the absolute path for it. */
233 source_relative_path (std::string path
, Location locus
)
235 std::string compile_fname
236 = Session::get_instance ().linemap
->location_file (locus
);
238 auto dir_separator_pos
= compile_fname
.rfind (file_separator
);
240 /* If there is no file_separator in the path, use current dir ('.'). */
242 if (dir_separator_pos
== std::string::npos
)
243 dirname
= std::string (".") + file_separator
;
245 dirname
= compile_fname
.substr (0, dir_separator_pos
) + file_separator
;
247 return dirname
+ path
;
250 /* Read the full contents of the file FILENAME and return them in a vector.
251 FIXME: platform specific. */
254 load_file_bytes (const char *filename
)
256 RAIIFile
file_wrap (filename
);
257 if (file_wrap
.get_raw () == nullptr)
259 rust_error_at (Location (), "cannot open filename %s: %m", filename
);
260 return std::vector
<uint8_t> ();
263 FILE *f
= file_wrap
.get_raw ();
264 fseek (f
, 0L, SEEK_END
);
265 long fsize
= ftell (f
);
266 fseek (f
, 0L, SEEK_SET
);
268 std::vector
<uint8_t> buf (fsize
);
270 if (fread (&buf
[0], fsize
, 1, f
) != 1)
272 rust_error_at (Location (), "error reading file %s: %m", filename
);
273 return std::vector
<uint8_t> ();
281 MacroBuiltin::assert_handler (Location
, AST::MacroInvocData
&)
283 rust_debug ("assert!() called");
285 return AST::Fragment::create_error ();
289 MacroBuiltin::file_handler (Location invoc_locus
, AST::MacroInvocData
&)
292 = Session::get_instance ().linemap
->location_file (invoc_locus
);
293 auto file_str
= AST::SingleASTNode (make_string (invoc_locus
, current_file
));
295 = make_token (Token::make_string (invoc_locus
, std::move (current_file
)));
297 return AST::Fragment ({file_str
}, std::move (str_token
));
301 MacroBuiltin::column_handler (Location invoc_locus
, AST::MacroInvocData
&)
304 = Session::get_instance ().linemap
->location_to_column (invoc_locus
);
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
)));
312 return AST::Fragment ({column_no
}, std::move (column_tok
));
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
320 MacroBuiltin::include_bytes_handler (Location invoc_locus
,
321 AST::MacroInvocData
&invoc
)
323 /* Get target filename from the macro invocation, which is treated as a path
324 relative to the include!-ing file (currently being compiled). */
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 ();
331 std::string target_filename
332 = source_relative_path (lit_expr
->as_string (), invoc_locus
);
334 std::vector
<uint8_t> bytes
= load_file_bytes (target_filename
.c_str ());
336 /* Is there a more efficient way to do this? */
337 std::vector
<std::unique_ptr
<AST::Expr
>> elts
;
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
)));
345 for (uint8_t b
: bytes
)
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
)));
355 toks
.emplace_back (make_token (Token::make (RIGHT_SQUARE
, invoc_locus
)));
357 auto elems
= std::unique_ptr
<AST::ArrayElems
> (
358 new AST::ArrayElemsValues (std::move (elts
), invoc_locus
));
360 auto array
= std::unique_ptr
<AST::Expr
> (
361 new AST::ArrayExpr (std::move (elems
), {}, {}, invoc_locus
));
363 auto borrow
= std::unique_ptr
<AST::Expr
> (
364 new AST::BorrowExpr (std::move (array
), false, false, {}, invoc_locus
));
366 auto node
= AST::SingleASTNode (std::move (borrow
));
368 return AST::Fragment ({node
}, std::move (toks
));
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. */
376 MacroBuiltin::include_str_handler (Location invoc_locus
,
377 AST::MacroInvocData
&invoc
)
379 /* Get target filename from the macro invocation, which is treated as a path
380 relative to the include!-ing file (currently being compiled). */
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 ();
387 std::string target_filename
388 = source_relative_path (lit_expr
->as_string (), invoc_locus
);
390 std::vector
<uint8_t> bytes
= load_file_bytes (target_filename
.c_str ());
392 /* FIXME: reuse lexer */
393 int expect_single
= 0;
394 for (uint8_t b
: bytes
)
398 if ((b
& 0xC0) != 0x80)
399 /* character was truncated, exit with expect_single != 0 */
407 /* more than 4 leading 1s */
428 /* only 1 leading 1 */
437 rust_error_at (invoc_locus
, "%s was not a valid utf-8 file",
438 target_filename
.c_str ());
440 str
= std::string ((const char *) &bytes
[0], bytes
.size ());
442 auto node
= AST::SingleASTNode (make_string (invoc_locus
, str
));
443 auto str_tok
= make_token (Token::make_string (invoc_locus
, std::move (str
)));
445 // FIXME: Do not return an empty token vector here
446 return AST::Fragment ({node
}, std::move (str_tok
));
449 /* Expand builtin macro compile_error!("error"), which forces a compile error
450 during the compile time. */
452 MacroBuiltin::compile_error_handler (Location invoc_locus
,
453 AST::MacroInvocData
&invoc
)
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 ();
461 std::string error_string
= lit_expr
->as_string ();
462 rust_error_at (invoc_locus
, "%s", error_string
.c_str ());
464 return AST::Fragment::create_error ();
467 static std::vector
<std::unique_ptr
<AST::MacroInvocation
>>
468 check_for_eager_invocations (
469 std::vector
<std::unique_ptr
<AST::Expr
>> &expressions
)
471 std::vector
<std::unique_ptr
<AST::MacroInvocation
>> pending
;
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 ())));
481 /* Expand builtin macro concat!(), which joins all the literal parameters
482 into a string with no delimiter. */
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?
489 // Let's take a few examples:
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);
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);
501 // How do we do that?
503 // For each (un)expanded fragment: we check if it is expanded fully
505 // 1. What is expanded fully?
508 // If it is expanded fully and not a literal, then we error out.
509 // Otherwise we simply emplace it back and keep going.
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.
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.
519 // Now, before that: How do we resolve the names used in a builtin macro
521 // Do we split the two passes of parsing the token tree and then expanding it?
522 // Can we do that easily?
524 MacroBuiltin::concat_handler (Location invoc_locus
, AST::MacroInvocData
&invoc
)
526 auto invoc_token_tree
= invoc
.get_delim_tok_tree ();
527 MacroInvocLexer
lex (invoc_token_tree
.to_token_stream ());
528 Parser
<MacroInvocLexer
> parser (lex
);
530 auto str
= std::string ();
531 bool has_error
= false;
533 auto last_token_id
= macro_end_token (invoc_token_tree
, parser
);
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 ();
541 auto tokens
= lex
.get_token_slice (start
, end
);
543 auto pending_invocations
= check_for_eager_invocations (expanded_expr
);
544 if (!pending_invocations
.empty ())
545 return make_eager_builtin_invocation (AST::BuiltinMacro::Concat
,
547 invoc
.get_delim_tok_tree (),
548 std::move (pending_invocations
));
550 for (auto &expr
: expanded_expr
)
552 if (!expr
->is_literal ()
553 && expr
->get_ast_kind () != AST::MACRO_INVOCATION
)
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!()%>");
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
)
568 rust_error_at (expr
->get_locus (),
569 "cannot concatenate a byte string literal");
572 str
+= literal
->as_string ();
575 parser
.skip_token (last_token_id
);
578 return AST::Fragment::create_error ();
580 auto node
= AST::SingleASTNode (make_string (invoc_locus
, str
));
581 auto str_tok
= make_token (Token::make_string (invoc_locus
, std::move (str
)));
583 return AST::Fragment ({node
}, std::move (str_tok
));
586 /* Expand builtin macro env!(), which inspects an environment variable at
589 MacroBuiltin::env_handler (Location invoc_locus
, AST::MacroInvocData
&invoc
)
591 auto invoc_token_tree
= invoc
.get_delim_tok_tree ();
592 MacroInvocLexer
lex (invoc_token_tree
.to_token_stream ());
593 Parser
<MacroInvocLexer
> parser (lex
);
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;
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 ();
605 auto tokens
= lex
.get_token_slice (start
, end
);
608 return AST::Fragment::create_error ();
610 auto pending
= check_for_eager_invocations (expanded_expr
);
611 if (!pending
.empty ())
612 return make_eager_builtin_invocation (AST::BuiltinMacro::Env
, invoc_locus
,
614 std::move (pending
));
616 if (expanded_expr
.size () < 1 || expanded_expr
.size () > 2)
618 rust_error_at (invoc_locus
, "env! takes 1 or 2 arguments");
619 return AST::Fragment::create_error ();
621 if (expanded_expr
.size () > 0)
624 = try_extract_string_literal_from_fragment (invoc_locus
,
627 return AST::Fragment::create_error ();
630 if (expanded_expr
.size () > 1)
633 = try_extract_string_literal_from_fragment (invoc_locus
,
636 return AST::Fragment::create_error ();
640 parser
.skip_token (last_token_id
);
642 auto env_value
= getenv (lit_expr
->as_string ().c_str ());
644 if (env_value
== nullptr)
646 if (error_expr
== nullptr)
647 rust_error_at (invoc_locus
, "environment variable %qs not defined",
648 lit_expr
->as_string ().c_str ());
650 rust_error_at (invoc_locus
, "%s", error_expr
->as_string ().c_str ());
651 return AST::Fragment::create_error ();
654 auto node
= AST::SingleASTNode (make_string (invoc_locus
, env_value
));
656 = make_token (Token::make_string (invoc_locus
, std::move (env_value
)));
658 // FIXME: Do not return an empty token vector here
659 return AST::Fragment ({node
}, std::move (tok
));
663 MacroBuiltin::cfg_handler (Location invoc_locus
, AST::MacroInvocData
&invoc
)
665 // only parse if not already parsed
666 if (!invoc
.is_parsed ())
668 std::unique_ptr
<AST::AttrInputMetaItemContainer
> converted_input (
669 invoc
.get_delim_tok_tree ().parse_to_meta_item ());
671 if (converted_input
== nullptr)
673 rust_debug ("DEBUG: failed to parse macro to meta item");
674 // TODO: do something now? is this an actual error?
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
));
684 /* TODO: assuming that cfg! macros can only have one meta item inner, like cfg
686 if (invoc
.get_meta_items ().size () != 1)
687 return AST::Fragment::create_error ();
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
));
697 // FIXME: Do not return an empty token vector here
698 return AST::Fragment ({literal_exp
}, std::move (tok
));
701 /* Expand builtin macro include!(), which includes a source file at the current
702 scope compile time. */
705 MacroBuiltin::include_handler (Location invoc_locus
, AST::MacroInvocData
&invoc
)
707 /* Get target filename from the macro invocation, which is treated as a path
708 relative to the include!-ing file (currently being compiled). */
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 ();
716 = source_relative_path (lit_expr
->as_string (), invoc_locus
);
718 = Rust::Session::get_instance ().include_extra_file (std::move (filename
));
720 RAIIFile
target_file (target_filename
);
721 Linemap
*linemap
= Session::get_instance ().linemap
;
723 if (!target_file
.ok ())
725 rust_error_at (lit_expr
->get_locus (),
726 "cannot open included file %qs: %m", target_filename
);
727 return AST::Fragment::create_error ();
730 rust_debug ("Attempting to parse included file %s", target_filename
);
732 Lexer
lex (target_filename
, std::move (target_file
), linemap
);
733 Parser
<Lexer
> parser (lex
);
735 auto parsed_items
= parser
.parse_items ();
736 bool has_error
= !parser
.get_errors ().empty ();
738 for (const auto &error
: parser
.get_errors ())
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 ();
748 std::vector
<AST::SingleASTNode
> nodes
{};
749 for (auto &item
: parsed_items
)
751 AST::SingleASTNode
node (std::move (item
));
752 nodes
.push_back (node
);
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
767 return AST::Fragment (nodes
, std::vector
<std::unique_ptr
<AST::Token
>> ());
771 MacroBuiltin::line_handler (Location invoc_locus
, AST::MacroInvocData
&)
774 = Session::get_instance ().linemap
->location_to_line (invoc_locus
);
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
)));
780 = make_token (Token::make_int (invoc_locus
, std::to_string (current_line
)));
782 // FIXME: Do not return an empty token vector here
783 return AST::Fragment ({line_no
}, std::move (tok
));