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: 2026-02-28 14:20:25 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            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         1308 : extract_module_path (const AST::AttrVec &inner_attrs,
      27              :                      const AST::AttrVec &outer_attrs, const std::string &name)
      28              : {
      29         1308 :   AST::Attribute path_attr = AST::Attribute::create_empty ();
      30         1407 :   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         1308 :   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         1414 :   for (const auto &attr : outer_attrs)
      50              :     {
      51          169 :       if (attr.get_path ().as_string () == Values::Attributes::PATH)
      52              :         {
      53           63 :           path_attr = attr;
      54           63 :           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         1308 :   if (path_attr.is_empty ())
      61         1244 :     return name;
      62              : 
      63              :   // Here, we found a path attribute, but it has no associated string. This is
      64              :   // invalid
      65           64 :   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           57 :   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           57 :   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           57 :   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           43 :   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           43 :   return path;
     103          100 : }
     104              : 
     105              : template <typename T>
     106              : static bool
     107          189 : contains (std::vector<T> &vec, T elm)
     108              : {
     109          198 :   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           22 :   if (values.empty ())
     131              :     return nullptr;
     132              : 
     133           22 :   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          218 : peculiar_fragment_match_compatible (const AST::MacroMatchFragment &last_match,
     163              :                                     const AST::MacroMatch &match)
     164              : {
     165          218 :   static std::unordered_map<AST::MacroFragSpec::Kind, std::vector<TokenId>>
     166              :     follow_set
     167              :     = {{AST::MacroFragSpec::EXPR, {MATCH_ARROW, COMMA, SEMICOLON}},
     168          138 :        {AST::MacroFragSpec::STMT, {MATCH_ARROW, COMMA, SEMICOLON}},
     169          138 :        {AST::MacroFragSpec::PAT, {MATCH_ARROW, COMMA, EQUAL, PIPE, IF, IN}},
     170          138 :        {AST::MacroFragSpec::PATH,
     171              :         {MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
     172              :          RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
     173          138 :        {AST::MacroFragSpec::TY,
     174              :         {MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
     175              :          RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
     176          138 :        {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         1184 :          YIELD}}};
     240              : 
     241          218 :   location_t error_locus = match.get_match_locus ();
     242          218 :   std::string kind_str = "fragment";
     243          218 :   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          218 :   switch (match.get_macro_match_type ())
     252              :     {
     253          189 :     case AST::MacroMatch::Tok:
     254          189 :       {
     255          189 :         auto tok = static_cast<const AST::Token *> (&match);
     256          378 :         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           22 :     case AST::MacroMatch::Repetition:
     265           22 :       {
     266           22 :         auto repetition
     267              :           = static_cast<const AST::MacroMatchRepetition *> (&match);
     268           22 :         auto &matches = repetition->get_matches ();
     269           22 :         auto first_frag = get_front_ptr (matches);
     270           22 :         if (first_frag)
     271           22 :           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          583 : is_match_compatible (const AST::MacroMatch &last_match,
     328              :                      const AST::MacroMatch &match)
     329              : {
     330          605 :   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          605 :   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          250 :     case AST::MacroMatch::Fragment:
     345          250 :       {
     346          250 :         auto fragment
     347              :           = static_cast<const AST::MacroMatchFragment *> (&last_match);
     348          250 :         if (fragment->get_frag_spec ().has_follow_set_restrictions ())
     349          196 :           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.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.