LCOV - code coverage report
Current view: top level - gcc/rust/expand - rust-macro-builtins-helpers.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 92.2 % 129 119
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 11 11
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2020-2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : #include "rust-macro-builtins-helpers.h"
      20              : 
      21              : namespace Rust {
      22              : 
      23              : std::string
      24           51 : make_macro_path_str (BuiltinMacro kind)
      25              : {
      26           51 :   auto str = MacroBuiltin::builtins.lookup (kind);
      27           51 :   rust_assert (str.has_value ());
      28              : 
      29           51 :   return str.value ();
      30              : }
      31              : 
      32              : std::vector<std::unique_ptr<AST::MacroInvocation>>
      33          112 : check_for_eager_invocations (
      34              :   std::vector<std::unique_ptr<AST::Expr>> &expressions)
      35              : {
      36          112 :   std::vector<std::unique_ptr<AST::MacroInvocation>> pending;
      37              : 
      38          315 :   for (auto &expr : expressions)
      39          203 :     if (expr->get_expr_kind () == AST::Expr::Kind::MacroInvocation)
      40           34 :       pending.emplace_back (std::unique_ptr<AST::MacroInvocation> (
      41           34 :         static_cast<AST::MacroInvocation *> (expr->clone_expr ().release ())));
      42              : 
      43          112 :   return pending;
      44              : }
      45              : 
      46              : //
      47              : // Shorthand function for creating unique_ptr tokens
      48              : //
      49              : std::unique_ptr<AST::Token>
      50         3713 : make_token (const TokenPtr tok)
      51              : {
      52         3713 :   return std::unique_ptr<AST::Token> (new AST::Token (tok));
      53              : }
      54              : 
      55              : std::unique_ptr<AST::Expr>
      56          104 : make_string (location_t locus, std::string value)
      57              : {
      58          104 :   return std::unique_ptr<AST::Expr> (
      59              :     new AST::LiteralExpr (value, AST::Literal::STRING,
      60          208 :                           PrimitiveCoreType::CORETYPE_STR, {}, locus));
      61              : }
      62              : 
      63              : // TODO: Is this correct?
      64              : AST::Fragment
      65           14 : make_eager_builtin_invocation (
      66              :   BuiltinMacro kind, location_t locus, AST::DelimTokenTree arguments,
      67              :   std::vector<std::unique_ptr<AST::MacroInvocation>> &&pending_invocations)
      68              : {
      69           14 :   auto path_str = make_macro_path_str (kind);
      70              : 
      71           14 :   auto token_stream = arguments.to_token_stream ();
      72           28 :   std::unique_ptr<AST::Expr> node = AST::MacroInvocation::Builtin (
      73              :     kind,
      74           14 :     AST::MacroInvocData (AST::SimplePath (
      75           56 :                            {AST::SimplePathSegment (path_str, locus)}),
      76           42 :                          std::move (arguments)),
      77           14 :     {}, locus, std::move (pending_invocations));
      78              : 
      79           28 :   return AST::Fragment ({AST::SingleASTNode (std::move (node))},
      80           42 :                         std::move (token_stream));
      81           14 : }
      82              : 
      83              : /* Match the end token of a macro given the start delimiter of the macro */
      84              : TokenId
      85          282 : macro_end_token (AST::DelimTokenTree &invoc_token_tree,
      86              :                  Parser<MacroInvocLexer> &parser)
      87              : {
      88          282 :   auto last_token_id = TokenId::RIGHT_CURLY;
      89          282 :   switch (invoc_token_tree.get_delim_type ())
      90              :     {
      91          282 :     case AST::DelimType::PARENS:
      92          282 :       last_token_id = TokenId::RIGHT_PAREN;
      93          282 :       rust_assert (parser.skip_token (LEFT_PAREN));
      94              :       break;
      95              : 
      96            0 :     case AST::DelimType::CURLY:
      97            0 :       rust_assert (parser.skip_token (LEFT_CURLY));
      98              :       break;
      99              : 
     100            0 :     case AST::DelimType::SQUARE:
     101            0 :       last_token_id = TokenId::RIGHT_SQUARE;
     102            0 :       rust_assert (parser.skip_token (LEFT_SQUARE));
     103              :       break;
     104              :     }
     105              : 
     106          282 :   return last_token_id;
     107              : }
     108              : 
     109              : // Expand and then extract a string literal from the macro
     110              : std::unique_ptr<AST::LiteralExpr>
     111           47 : try_extract_string_literal_from_fragment (const location_t &parent_locus,
     112              :                                           std::unique_ptr<AST::Expr> &node)
     113              : {
     114           47 :   if (!node || !node->is_literal ()
     115           93 :       || static_cast<AST::LiteralExpr &> (*node).get_lit_type ()
     116              :            != AST::Literal::STRING)
     117              :     {
     118            5 :       rust_error_at (parent_locus, "argument must be a string literal");
     119            5 :       if (node)
     120            5 :         rust_inform (node->get_locus (), "expanded from here");
     121            5 :       return nullptr;
     122              :     }
     123           42 :   return std::unique_ptr<AST::LiteralExpr> (
     124           42 :     static_cast<AST::LiteralExpr *> (node->clone_expr ().release ()));
     125              : }
     126              : 
     127              : std::vector<std::unique_ptr<AST::Expr>>
     128          114 : try_expand_many_expr (Parser<MacroInvocLexer> &parser,
     129              :                       const TokenId last_token_id, MacroExpander *expander,
     130              :                       bool &has_error)
     131              : {
     132          114 :   auto restrictions = Rust::ParseRestrictions ();
     133              :   // stop parsing when encountered a braces/brackets
     134          114 :   restrictions.expr_can_be_null = true;
     135              :   // we can't use std::optional, so...
     136          114 :   auto result = std::vector<std::unique_ptr<AST::Expr>> ();
     137          114 :   auto empty_expr = std::vector<std::unique_ptr<AST::Expr>> ();
     138              : 
     139          114 :   auto first_token = parser.peek_current_token ()->get_id ();
     140          114 :   if (first_token == COMMA)
     141              :     {
     142            2 :       rust_error_at (parser.peek_current_token ()->get_locus (),
     143              :                      "expected expression, found %<,%>");
     144            2 :       has_error = true;
     145            2 :       return empty_expr;
     146              :     }
     147              : 
     148          630 :   while (parser.peek_current_token ()->get_id () != last_token_id
     149         1040 :          && parser.peek_current_token ()->get_id () != END_OF_FILE)
     150              :     {
     151          205 :       auto expr = parser.parse_expr (AST::AttrVec (), restrictions);
     152              :       // something must be so wrong that the expression could not be parsed
     153          205 :       rust_assert (expr);
     154          205 :       result.push_back (std::move (expr.value ()));
     155              : 
     156          205 :       auto next_token = parser.peek_current_token ();
     157          205 :       if (!parser.skip_token (COMMA) && next_token->get_id () != last_token_id)
     158              :         {
     159            2 :           rust_error_at (next_token->get_locus (), "expected token: %<,%>");
     160              :           // TODO: is this recoverable? to avoid crashing the parser in the next
     161              :           // fragment we have to exit early here
     162            2 :           has_error = true;
     163            2 :           return empty_expr;
     164              :         }
     165          410 :     }
     166              : 
     167          110 :   return result;
     168          114 : }
     169              : 
     170              : // Parse a single string literal from the given delimited token tree,
     171              : // and return the LiteralExpr for it. Allow for an optional trailing comma,
     172              : // but otherwise enforce that these are the only tokens.
     173              : // FIXME(Arthur): This function needs a rework - it should not emit errors, it
     174              : // should probably be smaller
     175              : std::unique_ptr<AST::Expr>
     176          105 : parse_single_string_literal (BuiltinMacro kind,
     177              :                              AST::DelimTokenTree &invoc_token_tree,
     178              :                              location_t invoc_locus, MacroExpander *expander,
     179              :                              bool is_semicoloned)
     180              : {
     181          105 :   MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
     182          105 :   Parser<MacroInvocLexer> parser (lex);
     183              : 
     184          105 :   auto last_token_id = macro_end_token (invoc_token_tree, parser);
     185              : 
     186          105 :   std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
     187          105 :   std::unique_ptr<AST::MacroInvocation> macro_invoc = nullptr;
     188              : 
     189          210 :   if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
     190              :     {
     191          124 :       lit_expr = parser.parse_literal_expr ().value ();
     192           62 :       parser.maybe_skip_token (COMMA);
     193          124 :       if (parser.peek_current_token ()->get_id () != last_token_id)
     194              :         {
     195            3 :           lit_expr = nullptr;
     196            3 :           rust_error_at (invoc_locus, "macro takes 1 argument");
     197              :         }
     198              :     }
     199           86 :   else if (parser.peek_current_token ()->get_id () == last_token_id)
     200            3 :     rust_error_at (invoc_locus, "macro takes 1 argument");
     201              :   else
     202              :     {
     203           40 :       macro_invoc = parser.parse_macro_invocation (AST::AttrVec ());
     204              : 
     205           40 :       parser.maybe_skip_token (COMMA);
     206           80 :       if (parser.peek_current_token ()->get_id () != last_token_id)
     207              :         {
     208            0 :           lit_expr = nullptr;
     209            0 :           rust_error_at (invoc_locus, "macro takes 1 argument");
     210              :         }
     211              : 
     212           40 :       if (macro_invoc != nullptr)
     213              :         {
     214           37 :           auto path_str = make_macro_path_str (kind);
     215              : 
     216           37 :           auto pending_invocations
     217           37 :             = std::vector<std::unique_ptr<AST::MacroInvocation>> ();
     218              : 
     219           37 :           pending_invocations.push_back (std::move (macro_invoc));
     220              : 
     221           74 :           return AST::MacroInvocation::Builtin (
     222              :             kind,
     223           37 :             AST::MacroInvocData (AST::SimplePath ({AST::SimplePathSegment (
     224          148 :                                    path_str, invoc_locus)}),
     225          111 :                                  std::move (invoc_token_tree)),
     226           37 :             {}, invoc_locus, std::move (pending_invocations), is_semicoloned);
     227           37 :         }
     228              :       else
     229              :         {
     230            3 :           rust_error_at (invoc_locus, "argument must be a string literal or a "
     231              :                                       "macro which expands to a string");
     232              :         }
     233              :     }
     234              : 
     235           68 :   parser.skip_token (last_token_id);
     236              : 
     237           68 :   return std::unique_ptr<AST::Expr> (std::move (lit_expr));
     238          210 : }
     239              : 
     240              : /* Treat PATH as a path relative to the source file currently being
     241              :    compiled, and return the absolute path for it.  */
     242              : std::string
     243           57 : source_relative_path (std::string path, location_t locus)
     244              : {
     245           57 :   std::string compile_fname = LOCATION_FILE (locus);
     246              : 
     247           57 :   auto dir_separator_pos = compile_fname.rfind (file_separator);
     248              : 
     249              :   /* If there is no file_separator in the path, use current dir ('.').  */
     250           57 :   std::string dirname;
     251           57 :   if (dir_separator_pos == std::string::npos)
     252            0 :     dirname = std::string (".") + file_separator;
     253              :   else
     254          114 :     dirname = compile_fname.substr (0, dir_separator_pos) + file_separator;
     255              : 
     256           57 :   return dirname + path;
     257           57 : }
     258              : 
     259              : /* Read the full contents of the file FILENAME and return them in a vector.
     260              :    FIXME: platform specific.  */
     261              : tl::optional<std::vector<uint8_t>>
     262           52 : load_file_bytes (location_t invoc_locus, const char *filename)
     263              : {
     264           52 :   RAIIFile file_wrap (filename);
     265           52 :   if (file_wrap.get_raw () == nullptr)
     266              :     {
     267            4 :       rust_error_at (invoc_locus, "cannot open filename %s: %m", filename);
     268            4 :       return tl::nullopt;
     269              :     }
     270              : 
     271           48 :   FILE *f = file_wrap.get_raw ();
     272           48 :   fseek (f, 0L, SEEK_END);
     273           48 :   long fsize = ftell (f);
     274           48 :   fseek (f, 0L, SEEK_SET);
     275              : 
     276           48 :   std::vector<uint8_t> buf (fsize);
     277              : 
     278           48 :   if (fsize > 0 && fread (&buf[0], fsize, 1, f) != 1)
     279              :     {
     280            0 :       rust_error_at (invoc_locus, "error reading file %s: %m", filename);
     281            0 :       return std::vector<uint8_t> ();
     282              :     }
     283              : 
     284           48 :   return buf;
     285           48 : }
     286              : } // namespace Rust
        

Generated by: LCOV version 2.4-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.