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: 2025-06-21 16:26:05 Functions: 100.0 % 11 11
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

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