LCOV - code coverage report
Current view: top level - gcc/rust/expand - rust-macro-builtins.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 91.1 % 393 358
Test Date: 2024-04-13 14:00:49 Functions: 92.3 % 26 24
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             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
        

Generated by: LCOV version 2.1-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.