LCOV - code coverage report
Current view: top level - gcc/rust/parse - rust-parse.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 94.9 % 118 112
Test Date: 2025-09-20 13:40:47 Functions: 50.0 % 8 4
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* This file is part of GCC.
       2                 :             : 
       3                 :             : GCC is free software; you can redistribute it and/or modify
       4                 :             : it under the terms of the GNU General Public License as published by
       5                 :             : the Free Software Foundation; either version 3, or (at your option)
       6                 :             : any later version.
       7                 :             : 
       8                 :             : GCC is distributed in the hope that it will be useful,
       9                 :             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      10                 :             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      11                 :             : GNU General Public License for more details.
      12                 :             : 
      13                 :             : You should have received a copy of the GNU General Public License
      14                 :             : along with GCC; see the file COPYING3.  If not see
      15                 :             : <http://www.gnu.org/licenses/>. */
      16                 :             : 
      17                 :             : #include "rust-parse.h"
      18                 :             : #include "rust-linemap.h"
      19                 :             : #include "rust-diagnostics.h"
      20                 :             : #include "rust-token.h"
      21                 :             : #include "rust-attribute-values.h"
      22                 :             : 
      23                 :             : namespace Rust {
      24                 :             : 
      25                 :             : std::string
      26                 :        1279 : extract_module_path (const AST::AttrVec &inner_attrs,
      27                 :             :                      const AST::AttrVec &outer_attrs, const std::string &name)
      28                 :             : {
      29                 :        1279 :   AST::Attribute path_attr = AST::Attribute::create_empty ();
      30                 :        1378 :   for (const auto &attr : inner_attrs)
      31                 :             :     {
      32                 :         101 :       if (attr.get_path ().as_string () == Values::Attributes::PATH)
      33                 :             :         {
      34                 :           2 :           path_attr = attr;
      35                 :           2 :           break;
      36                 :             :         }
      37                 :             :     }
      38                 :             : 
      39                 :             :   // Here, we found a path attribute, but it has no associated string. This is
      40                 :             :   // invalid
      41                 :        1279 :   if (!path_attr.is_empty () && !path_attr.has_attr_input ())
      42                 :             :     {
      43                 :           0 :       rust_error_at (
      44                 :             :         path_attr.get_locus (),
      45                 :             :         "path attributes must contain a filename: %<#[path = \"file\"]%>");
      46                 :           0 :       return name;
      47                 :             :     }
      48                 :             : 
      49                 :        1383 :   for (const auto &attr : outer_attrs)
      50                 :             :     {
      51                 :         166 :       if (attr.get_path ().as_string () == Values::Attributes::PATH)
      52                 :             :         {
      53                 :          62 :           path_attr = attr;
      54                 :          62 :           break;
      55                 :             :         }
      56                 :             :     }
      57                 :             : 
      58                 :             :   // We didn't find a path attribute. This is not an error, there simply isn't
      59                 :             :   // one present
      60                 :        1279 :   if (path_attr.is_empty ())
      61                 :        1216 :     return name;
      62                 :             : 
      63                 :             :   // Here, we found a path attribute, but it has no associated string. This is
      64                 :             :   // invalid
      65                 :          63 :   if (!path_attr.has_attr_input ())
      66                 :             :     {
      67                 :           7 :       rust_error_at (
      68                 :             :         path_attr.get_locus (),
      69                 :             :         "path attributes must contain a filename: %<#[path = \"file\"]%>");
      70                 :           7 :       return name;
      71                 :             :     }
      72                 :             : 
      73                 :          56 :   auto path_value = path_attr.get_attr_input ().as_string ();
      74                 :             : 
      75                 :             :   // At this point, the 'path' is of the following format: '= "<file.rs>"'
      76                 :             :   // We need to remove the equal sign and only keep the actual filename.
      77                 :             :   // In order to do this, we can simply go through the string until we find
      78                 :             :   // a character that is not an equal sign or whitespace
      79                 :          56 :   auto filename_begin = path_value.find_first_not_of ("=\t ");
      80                 :             : 
      81                 :             :   // If the path consists of only whitespace, then we have an error
      82                 :          56 :   if (filename_begin == std::string::npos)
      83                 :             :     {
      84                 :          14 :       rust_error_at (
      85                 :             :         path_attr.get_locus (),
      86                 :             :         "path attributes must contain a filename: %<#[path = \"file\"]%>");
      87                 :          14 :       return name;
      88                 :             :     }
      89                 :             : 
      90                 :          42 :   auto path = path_value.substr (filename_begin);
      91                 :             : 
      92                 :             :   // On windows, the path might mix '/' and '\' separators. Replace the
      93                 :             :   // UNIX-like separators by MSDOS separators to make sure the path will resolve
      94                 :             :   // properly.
      95                 :             :   //
      96                 :             :   // Source: rustc compiler
      97                 :             :   // (https://github.com/rust-lang/rust/blob/9863bf51a52b8e61bcad312f81b5193d53099f9f/compiler/rustc_expand/src/module.rs#L174)
      98                 :             : #if defined(HAVE_DOS_BASED_FILE_SYSTEM)
      99                 :             :   std::replace (path.begin (), path.end (), '/', '\\');
     100                 :             : #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
     101                 :             : 
     102                 :          42 :   return path;
     103                 :          98 : }
     104                 :             : 
     105                 :             : template <typename T>
     106                 :             : static bool
     107                 :         166 : contains (std::vector<T> &vec, T elm)
     108                 :             : {
     109                 :         175 :   return std::find (vec.begin (), vec.end (), elm) != vec.end ();
     110                 :             : }
     111                 :             : 
     112                 :             : /**
     113                 :             :  * Avoid UB by calling .front() and .back() on empty containers...
     114                 :             :  */
     115                 :             : 
     116                 :             : template <typename T>
     117                 :             : static const T *
     118                 :          22 : get_back_ptr (const std::vector<std::unique_ptr<T>> &values)
     119                 :             : {
     120                 :          22 :   if (values.empty ())
     121                 :             :     return nullptr;
     122                 :             : 
     123                 :          22 :   return values.back ().get ();
     124                 :             : }
     125                 :             : 
     126                 :             : template <typename T>
     127                 :             : static const T *
     128                 :           0 : get_front_ptr (const std::vector<std::unique_ptr<T>> &values)
     129                 :             : {
     130                 :          15 :   if (values.empty ())
     131                 :             :     return nullptr;
     132                 :             : 
     133                 :          15 :   return values.front ().get ();
     134                 :             : }
     135                 :             : 
     136                 :             : static bool
     137                 :           4 : peculiar_fragment_match_compatible_fragment (
     138                 :             :   const AST::MacroFragSpec &last_spec, const AST::MacroFragSpec &spec,
     139                 :             :   location_t match_locus)
     140                 :             : {
     141                 :           4 :   static std::unordered_map<AST::MacroFragSpec::Kind,
     142                 :             :                             std::vector<AST::MacroFragSpec::Kind>>
     143                 :             :     fragment_follow_set
     144                 :             :     = {{AST::MacroFragSpec::PATH, {AST::MacroFragSpec::BLOCK}},
     145                 :           4 :        {AST::MacroFragSpec::TY, {AST::MacroFragSpec::BLOCK}},
     146                 :           4 :        {AST::MacroFragSpec::VIS,
     147                 :             :         {AST::MacroFragSpec::IDENT, AST::MacroFragSpec::TY,
     148                 :          20 :          AST::MacroFragSpec::PATH}}};
     149                 :             : 
     150                 :           4 :   auto is_valid
     151                 :           4 :     = contains (fragment_follow_set[last_spec.get_kind ()], spec.get_kind ());
     152                 :             : 
     153                 :           4 :   if (!is_valid)
     154                 :           1 :     rust_error_at (match_locus,
     155                 :             :                    "fragment specifier %qs is not allowed after %qs fragments",
     156                 :           2 :                    spec.as_string ().c_str (), last_spec.as_string ().c_str ());
     157                 :             : 
     158                 :           4 :   return is_valid;
     159                 :             : }
     160                 :             : 
     161                 :             : static bool
     162                 :         188 : peculiar_fragment_match_compatible (const AST::MacroMatchFragment &last_match,
     163                 :             :                                     const AST::MacroMatch &match)
     164                 :             : {
     165                 :         188 :   static std::unordered_map<AST::MacroFragSpec::Kind, std::vector<TokenId>>
     166                 :             :     follow_set
     167                 :             :     = {{AST::MacroFragSpec::EXPR, {MATCH_ARROW, COMMA, SEMICOLON}},
     168                 :         129 :        {AST::MacroFragSpec::STMT, {MATCH_ARROW, COMMA, SEMICOLON}},
     169                 :         129 :        {AST::MacroFragSpec::PAT, {MATCH_ARROW, COMMA, EQUAL, PIPE, IF, IN}},
     170                 :         129 :        {AST::MacroFragSpec::PATH,
     171                 :             :         {MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
     172                 :             :          RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
     173                 :         129 :        {AST::MacroFragSpec::TY,
     174                 :             :         {MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
     175                 :             :          RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
     176                 :         129 :        {AST::MacroFragSpec::VIS,
     177                 :             :         {COMMA,
     178                 :             :          IDENTIFIER,
     179                 :             :          LEFT_PAREN,
     180                 :             :          LEFT_SQUARE,
     181                 :             :          EXCLAM,
     182                 :             :          ASTERISK,
     183                 :             :          AMP,
     184                 :             :          LOGICAL_AND,
     185                 :             :          QUESTION_MARK,
     186                 :             :          LIFETIME,
     187                 :             :          LEFT_ANGLE,
     188                 :             :          LEFT_SHIFT,
     189                 :             :          UNDERSCORE,
     190                 :             :          ABSTRACT,
     191                 :             :          AS,
     192                 :             :          ASYNC,
     193                 :             :          AUTO,
     194                 :             :          BECOME,
     195                 :             :          BOX,
     196                 :             :          BREAK,
     197                 :             :          CONST,
     198                 :             :          CONTINUE,
     199                 :             :          CRATE,
     200                 :             :          DO,
     201                 :             :          DYN,
     202                 :             :          ELSE,
     203                 :             :          ENUM_KW,
     204                 :             :          EXTERN_KW,
     205                 :             :          FALSE_LITERAL,
     206                 :             :          FINAL_KW,
     207                 :             :          FN_KW,
     208                 :             :          FOR,
     209                 :             :          IF,
     210                 :             :          IMPL,
     211                 :             :          IN,
     212                 :             :          LET,
     213                 :             :          LOOP,
     214                 :             :          MACRO,
     215                 :             :          MATCH_KW,
     216                 :             :          MOD,
     217                 :             :          MOVE,
     218                 :             :          MUT,
     219                 :             :          OVERRIDE_KW,
     220                 :             :          PUB,
     221                 :             :          REF,
     222                 :             :          RETURN_KW,
     223                 :             :          SELF_ALIAS,
     224                 :             :          SELF,
     225                 :             :          STATIC_KW,
     226                 :             :          STRUCT_KW,
     227                 :             :          SUPER,
     228                 :             :          TRAIT,
     229                 :             :          TRUE_LITERAL,
     230                 :             :          TRY,
     231                 :             :          TYPE,
     232                 :             :          TYPEOF,
     233                 :             :          UNSAFE,
     234                 :             :          UNSIZED,
     235                 :             :          USE,
     236                 :             :          VIRTUAL,
     237                 :             :          WHERE,
     238                 :             :          WHILE,
     239                 :        1091 :          YIELD}}};
     240                 :             : 
     241                 :         188 :   location_t error_locus = match.get_match_locus ();
     242                 :         188 :   std::string kind_str = "fragment";
     243                 :         188 :   auto &allowed_toks = follow_set[last_match.get_frag_spec ().get_kind ()];
     244                 :             : 
     245                 :             :   // There are two behaviors to handle here: If the follow-up match is a token,
     246                 :             :   // we want to check if it is allowed.
     247                 :             :   // If it is a fragment, repetition or matcher then we know that it will be
     248                 :             :   // an error.
     249                 :             :   // For repetitions and matchers we want to extract a proper location to report
     250                 :             :   // the error.
     251                 :         188 :   switch (match.get_macro_match_type ())
     252                 :             :     {
     253                 :         166 :     case AST::MacroMatch::Tok:
     254                 :         166 :       {
     255                 :         166 :         auto tok = static_cast<const AST::Token *> (&match);
     256                 :         332 :         if (contains (allowed_toks, tok->get_id ()))
     257                 :             :           return true;
     258                 :           8 :         kind_str = "token `"
     259                 :          16 :                    + std::string (get_token_description (tok->get_id ())) + "`";
     260                 :           8 :         error_locus = tok->get_match_locus ();
     261                 :           8 :         break;
     262                 :             :       }
     263                 :             :       break;
     264                 :          15 :     case AST::MacroMatch::Repetition:
     265                 :          15 :       {
     266                 :          15 :         auto repetition
     267                 :             :           = static_cast<const AST::MacroMatchRepetition *> (&match);
     268                 :          15 :         auto &matches = repetition->get_matches ();
     269                 :          15 :         auto first_frag = get_front_ptr (matches);
     270                 :          15 :         if (first_frag)
     271                 :          15 :           return peculiar_fragment_match_compatible (last_match, *first_frag);
     272                 :             :         break;
     273                 :             :       }
     274                 :           1 :     case AST::MacroMatch::Matcher:
     275                 :           1 :       {
     276                 :           1 :         auto matcher = static_cast<const AST::MacroMatcher *> (&match);
     277                 :           1 :         auto first_token = matcher->get_delim_type ();
     278                 :           1 :         TokenId delim_id;
     279                 :           1 :         switch (first_token)
     280                 :             :           {
     281                 :             :           case AST::PARENS:
     282                 :             :             delim_id = LEFT_PAREN;
     283                 :             :             break;
     284                 :             :           case AST::SQUARE:
     285                 :             :             delim_id = LEFT_SQUARE;
     286                 :             :             break;
     287                 :             :           case AST::CURLY:
     288                 :             :             delim_id = LEFT_CURLY;
     289                 :             :             break;
     290                 :           0 :           default:
     291                 :           0 :             rust_unreachable ();
     292                 :           1 :             break;
     293                 :             :           }
     294                 :           2 :         if (contains (allowed_toks, delim_id))
     295                 :             :           return true;
     296                 :           2 :         kind_str = "token `" + std::string (get_token_description (delim_id))
     297                 :           1 :                    + "` at start of matcher";
     298                 :           1 :         error_locus = matcher->get_match_locus ();
     299                 :           1 :         break;
     300                 :             :       }
     301                 :           6 :     case AST::MacroMatch::Fragment:
     302                 :           6 :       {
     303                 :           6 :         auto last_spec = last_match.get_frag_spec ();
     304                 :           6 :         auto fragment = static_cast<const AST::MacroMatchFragment *> (&match);
     305                 :           6 :         if (last_spec.has_follow_set_fragment_restrictions ())
     306                 :           4 :           return peculiar_fragment_match_compatible_fragment (
     307                 :           8 :             last_spec, fragment->get_frag_spec (), match.get_match_locus ());
     308                 :             :       }
     309                 :           2 :       break;
     310                 :             :     }
     311                 :             : 
     312                 :          11 :   rust_error_at (error_locus, "%s is not allowed after %qs fragment",
     313                 :             :                  kind_str.c_str (),
     314                 :          11 :                  last_match.get_frag_spec ().as_string ().c_str ());
     315                 :          11 :   auto allowed_toks_str
     316                 :          22 :     = "`" + std::string (get_token_description (allowed_toks[0])) + "`";
     317                 :          93 :   for (size_t i = 1; i < allowed_toks.size (); i++)
     318                 :          82 :     allowed_toks_str
     319                 :         246 :       += ", `" + std::string (get_token_description (allowed_toks[i])) + "`";
     320                 :             : 
     321                 :          11 :   rust_inform (error_locus, "allowed tokens are %s", allowed_toks_str.c_str ());
     322                 :             : 
     323                 :          11 :   return false;
     324                 :          11 : }
     325                 :             : 
     326                 :             : bool
     327                 :         542 : is_match_compatible (const AST::MacroMatch &last_match,
     328                 :             :                      const AST::MacroMatch &match)
     329                 :             : {
     330                 :         564 :   const AST::MacroMatch *new_last = nullptr;
     331                 :             : 
     332                 :             :   // We want to "extract" the concerning matches. In cases such as matchers and
     333                 :             :   // repetitions, we actually store multiple matchers, but are only concerned
     334                 :             :   // about the follow-set ambiguities of certain elements.
     335                 :             :   // There are some cases where we can short-circuit the algorithm: There will
     336                 :             :   // never be restrictions on token literals, or on certain fragments which do
     337                 :             :   // not have a set of follow-restrictions.
     338                 :             : 
     339                 :         564 :   switch (last_match.get_macro_match_type ())
     340                 :             :     {
     341                 :             :     // This is our main stop condition: When we are finally looking at the
     342                 :             :     // last match (or its actual last component), and it is a fragment, it
     343                 :             :     // may contain some follow up restrictions.
     344                 :         226 :     case AST::MacroMatch::Fragment:
     345                 :         226 :       {
     346                 :         226 :         auto fragment
     347                 :             :           = static_cast<const AST::MacroMatchFragment *> (&last_match);
     348                 :         226 :         if (fragment->get_frag_spec ().has_follow_set_restrictions ())
     349                 :         173 :           return peculiar_fragment_match_compatible (*fragment, match);
     350                 :             :         else
     351                 :             :           return true;
     352                 :             :       }
     353                 :          22 :     case AST::MacroMatch::Repetition:
     354                 :          22 :       {
     355                 :             :         // A repetition on the left hand side means we want to make sure the
     356                 :             :         // last match of the repetition is compatible with the new match
     357                 :          22 :         auto repetition
     358                 :             :           = static_cast<const AST::MacroMatchRepetition *> (&last_match);
     359                 :          22 :         new_last = get_back_ptr (repetition->get_matches ());
     360                 :             :         // If there are no matches in the matcher, then it can be followed by
     361                 :             :         // anything
     362                 :          22 :         if (!new_last)
     363                 :             :           return true;
     364                 :             :         break;
     365                 :             :       }
     366                 :             :     case AST::MacroMatch::Matcher:
     367                 :             :     case AST::MacroMatch::Tok:
     368                 :             :       return true;
     369                 :             :     }
     370                 :             : 
     371                 :           0 :   rust_assert (new_last);
     372                 :             : 
     373                 :             :   // We check recursively until we find a terminating condition
     374                 :             :   // FIXME: Does expansion depth/limit matter here?
     375                 :             :   return is_match_compatible (*new_last, match);
     376                 :             : }
     377                 :             : } // 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.