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