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