LCOV - code coverage report
Current view: top level - gcc/rust/expand - rust-macro-expand.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 87.6 % 589 516
Test Date: 2026-07-11 15:47:05 Functions: 92.7 % 41 38
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2020-2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : #include "rust-macro-expand.h"
      20              : #include "optional.h"
      21              : #include "rust-ast-fragment.h"
      22              : #include "rust-macro-builtins.h"
      23              : #include "rust-macro-substitute-ctx.h"
      24              : #include "rust-ast-full.h"
      25              : #include "rust-ast-visitor.h"
      26              : #include "rust-diagnostics.h"
      27              : #include "rust-macro.h"
      28              : #include "rust-parse.h"
      29              : #include "rust-cfg-strip.h"
      30              : #include "rust-proc-macro.h"
      31              : #include "rust-token-tree-desugar.h"
      32              : 
      33              : namespace Rust {
      34              : 
      35              : AST::Fragment
      36         2451 : MacroExpander::expand_decl_macro (location_t invoc_locus,
      37              :                                   AST::MacroInvocData &invoc,
      38              :                                   AST::MacroRulesDefinition &rules_def,
      39              :                                   AST::InvocKind semicolon)
      40              : {
      41              :   // ensure that both invocation and rules are in a valid state
      42         2451 :   rust_assert (!invoc.is_marked_for_strip ());
      43         2451 :   rust_assert (!rules_def.is_marked_for_strip ());
      44         2451 :   rust_assert (rules_def.get_macro_rules ().size () > 0);
      45              : 
      46              :   /* probably something here about parsing invoc and rules def token trees to
      47              :    * token stream. if not, how would parser handle the captures of exprs and
      48              :    * stuff? on the other hand, token trees may be kind of useful in rules def
      49              :    * as creating a point where recursion can occur (like having
      50              :    * "compare_macro_match" and then it calling itself when it finds
      51              :    * delimiters)
      52              :    */
      53              : 
      54              :   /* find matching rule to invoc token tree, based on macro rule's matcher. if
      55              :    * none exist, error.
      56              :    * - specifically, check each matcher in order. if one fails to match, move
      57              :    * onto next. */
      58              :   /* TODO: does doing this require parsing expressions and whatever in the
      59              :    * invoc? if so, might as well save the results if referenced using $ or
      60              :    * whatever. If not, do another pass saving them. Except this is probably
      61              :    * useless as different rules could have different starting points for exprs
      62              :    * or whatever. Decision trees could avoid this, but they have their own
      63              :    * issues. */
      64              :   /* TODO: will need to modify the parser so that it can essentially "catch"
      65              :    * errors - maybe "try_parse_expr" or whatever methods. */
      66              :   // this technically creates a back-tracking parser - this will be the
      67              :   // implementation style
      68              : 
      69              :   /* then, after results are saved, generate the macro output from the
      70              :    * transcriber token tree. if i understand this correctly, the macro
      71              :    * invocation gets replaced by the transcriber tokens, except with
      72              :    * substitutions made (e.g. for $i variables) */
      73              : 
      74              :   /* TODO: it is probably better to modify AST::Token to store a pointer to a
      75              :    * Lexer::Token (rather than being converted) - i.e. not so much have
      76              :    * AST::Token as a Token but rather a TokenContainer (as it is another type
      77              :    * of TokenTree). This will prevent re-conversion of Tokens between each
      78              :    * type all the time, while still allowing the heterogenous storage of token
      79              :    * trees.
      80              :    */
      81              : 
      82         2451 :   AST::DelimTokenTree &invoc_token_tree_sugar = invoc.get_delim_tok_tree ();
      83              : 
      84              :   // We must first desugar doc comments into proper attributes
      85         2451 :   auto invoc_token_tree = AST::TokenTreeDesugar ().go (invoc_token_tree_sugar);
      86              : 
      87              :   // find matching arm
      88         2451 :   AST::MacroRule *matched_rule = nullptr;
      89         2451 :   std::map<std::string, std::unique_ptr<MatchedFragmentContainer>>
      90         2451 :     matched_fragments;
      91         3847 :   for (auto &rule : rules_def.get_rules ())
      92              :     {
      93         3839 :       sub_stack.push ();
      94         3839 :       bool did_match_rule = try_match_rule (rule, invoc_token_tree);
      95         7678 :       matched_fragments = sub_stack.pop ();
      96              : 
      97         3839 :       if (did_match_rule)
      98              :         {
      99              :           //  // Debugging
     100              :           //  for (auto &kv : matched_fragments)
     101              :           //    rust_debug ("[fragment]: %s (%ld - %s)", kv.first.c_str (),
     102              :           //            kv.second.get_fragments ().size (),
     103              :           //            kv.second.get_kind ()
     104              :           //                == MatchedFragmentContainer::Kind::Repetition
     105              :           //              ? "repetition"
     106              :           //              : "metavar");
     107              : 
     108              :           matched_rule = &rule;
     109              :           break;
     110              :         }
     111              :     }
     112              : 
     113         2451 :   if (matched_rule == nullptr)
     114              :     {
     115            8 :       if (!had_duplicate_error)
     116              :         {
     117            7 :           rich_location r (line_table, invoc_locus);
     118            7 :           r.add_range (rules_def.get_locus ());
     119            7 :           rust_error_at (r, "Failed to match any rule within macro");
     120            7 :         }
     121            8 :       had_duplicate_error = false;
     122            8 :       return AST::Fragment::create_error ();
     123              :     }
     124              : 
     125         2443 :   std::map<std::string, MatchedFragmentContainer *> matched_fragments_ptr;
     126              : 
     127         6479 :   for (auto &ent : matched_fragments)
     128         4036 :     matched_fragments_ptr.emplace (ent.first, ent.second.get ());
     129              : 
     130         2443 :   return transcribe_rule (rules_def, *matched_rule, invoc_token_tree,
     131         2443 :                           matched_fragments_ptr, semicolon, peek_context ());
     132         4894 : }
     133              : 
     134              : void
     135           46 : MacroExpander::expand_eager_invocations (AST::MacroInvocation &invoc)
     136              : {
     137           46 :   if (invoc.get_pending_eager_invocations ().empty ())
     138            0 :     return;
     139              : 
     140              :   // We have to basically create a new delimited token tree which contains the
     141              :   // result of one step of expansion. In the case of builtin macros called with
     142              :   // other macro invocations, such as `concat!("h", 'a', a!())`, we need to
     143              :   // expand `a!()` before expanding the concat macro.
     144              :   // This will, ideally, give us a new token tree containing the various
     145              :   // existing tokens + the result of the expansion of a!().
     146              :   // To do this, we "parse" the given token tree to find anything that "looks
     147              :   // like a macro invocation". Then, we get the corresponding macro invocation
     148              :   // from the `pending_eager_invocations` vector and expand it.
     149              :   // Because the `pending_eager_invocations` vector is created in the same order
     150              :   // that the DelimTokenTree is parsed, we know that the first macro invocation
     151              :   // within the DelimTokenTree corresponds to the first element in
     152              :   // `pending_eager_invocations`. The idea is thus to:
     153              :   // 1. Find a macro invocation in the token tree, noting the index of the start
     154              :   //    token and of the end token
     155              :   // 2. Get its associated invocation in `pending_eager_invocations`
     156              :   // 3. Expand that element
     157              :   // 4. Get the token tree associated with that AST fragment
     158              :   // 5. Replace the original tokens corresponding to the invocation with the new
     159              :   //    tokens from the fragment
     160              :   // pseudo-code:
     161              :   //
     162              :   // i = 0;
     163              :   // for tok in dtt:
     164              :   //   if tok is identifier && tok->next() is !:
     165              :   //     start = index(tok);
     166              :   //     l_delim = tok->next()->next();
     167              :   //     tok = skip_until_r_delim();
     168              :   //     end = index(tok);
     169              :   //
     170              :   //     new_tt = expand_eager_invoc(eagers[i++]);
     171              :   //     old_tt[start..end] = new_tt;
     172              : 
     173           46 :   auto dtt = invoc.get_invoc_data ().get_delim_tok_tree ();
     174           46 :   auto stream = dtt.to_token_stream ();
     175           46 :   std::vector<std::unique_ptr<AST::TokenTree>> new_stream;
     176           46 :   size_t current_pending = 0;
     177              : 
     178              :   // we need to create a clone of the delimited token tree as the lexer
     179              :   // expects ownership of the tokens
     180           46 :   std::vector<const_TokenPtr> dtt_clone;
     181          409 :   for (auto &tok : stream)
     182          726 :     dtt_clone.emplace_back (tok->get_tok_ptr ());
     183              : 
     184           46 :   MacroInvocLexer lex (std::move (dtt_clone));
     185           46 :   Parser<MacroInvocLexer> parser (lex);
     186              : 
     187              :   // we want to build a substitution map - basically, associating a `start` and
     188              :   // `end` index for each of the pending macro invocations
     189           46 :   std::map<std::pair<size_t, size_t>, AST::MacroInvocation *> substitution_map;
     190              : 
     191           46 :   auto &pending = invoc.get_pending_eager_invocations ();
     192              : 
     193          409 :   for (size_t i = 0; i < stream.size (); i++)
     194              :     {
     195              :       // FIXME: Can't these offsets be figure out when we actually parse the
     196              :       // pending_eager_invocation in the first place?
     197          363 :       auto invocation = parser.parse_macro_invocation ({});
     198              : 
     199              :       // if we've managed to parse a macro invocation, we look at the current
     200              :       // offset and store them in the substitution map. Otherwise, we skip one
     201              :       // token and try parsing again
     202          363 :       if (invocation)
     203           52 :         substitution_map.insert ({{i, parser.get_token_source ().get_offs ()},
     204           52 :                                   pending[current_pending++].get ()});
     205              :       else
     206          311 :         parser.skip_token (stream[i]->get_id ());
     207          363 :     }
     208              : 
     209           46 :   auto pending_it = pending.begin ();
     210           46 :   size_t current_idx = 0;
     211           98 :   for (auto kv : substitution_map)
     212              :     {
     213           52 :       AST::MacroInvocation *to_expand = kv.second;
     214           52 :       expand_invoc (*to_expand, AST::InvocKind::Expr);
     215              : 
     216           52 :       auto fragment = take_expanded_fragment ();
     217              : 
     218           52 :       if (fragment.is_error ())
     219              :         {
     220              :           // skip expansion of this macro
     221              :           // leave current_idx as-is, and continue
     222            2 :           pending_it++;
     223            2 :           continue;
     224              :         }
     225              : 
     226           50 :       auto &new_tokens = fragment.get_tokens ();
     227              : 
     228           50 :       auto start = kv.first.first;
     229           50 :       auto end = kv.first.second;
     230              : 
     231              :       // We're now going to re-add the tokens to the invocation's token tree.
     232              :       // 1. Basically, what we want to do is insert all tokens up until the
     233              :       //    beginning of the macro invocation (start).
     234              :       // 2. Then, we'll insert all of the tokens resulting from the macro
     235              :       //    expansion: These are in `new_tokens`.
     236              :       // 3. Finally, we'll do that again from
     237              :       //    the end of macro and go back to 1.
     238              : 
     239          106 :       for (size_t i = current_idx; i < start; i++)
     240           56 :         new_stream.emplace_back (stream[i]->clone_token ());
     241              : 
     242          100 :       for (auto &tok : new_tokens)
     243           50 :         new_stream.emplace_back (tok->clone_token ());
     244              : 
     245           50 :       current_idx = end;
     246           50 :       pending_it = pending.erase (pending_it);
     247           52 :     }
     248              : 
     249              :   // Once all of that is done, we copy the last remaining tokens from the
     250              :   // original stream
     251          137 :   for (size_t i = current_idx; i < stream.size (); i++)
     252           91 :     new_stream.emplace_back (stream[i]->clone_token ());
     253              : 
     254           46 :   auto new_dtt
     255           46 :     = AST::DelimTokenTree (dtt.get_delim_type (), std::move (new_stream));
     256              : 
     257           46 :   invoc.get_invoc_data ().set_delim_tok_tree (new_dtt);
     258           46 : }
     259              : 
     260              : void
     261         2864 : MacroExpander::expand_invoc (AST::MacroInvocation &invoc,
     262              :                              AST::InvocKind semicolon)
     263              : {
     264         2864 :   if (depth_exceeds_recursion_limit ())
     265              :     {
     266            0 :       rust_error_at (invoc.get_locus (), "reached recursion limit");
     267           46 :       return;
     268              :     }
     269              : 
     270         2864 :   if (invoc.get_kind () == AST::MacroInvocation::InvocKind::Builtin)
     271              :     {
     272              :       // Eager expansions are always expressions
     273           46 :       push_context (ContextType::EXPR);
     274           46 :       expand_eager_invocations (invoc);
     275           46 :       pop_context ();
     276              : 
     277              :       // if we have pending eager invocations still, don't expand
     278           46 :       if (!invoc.get_pending_eager_invocations ().empty ())
     279              :         {
     280            1 :           set_expanded_fragment (AST::Fragment::create_error ());
     281            1 :           return;
     282              :         }
     283              :     }
     284              : 
     285         2863 :   AST::MacroInvocData &invoc_data = invoc.get_invoc_data ();
     286              : 
     287              :   // ??
     288              :   // switch on type of macro:
     289              :   //  - '!' syntax macro (inner switch)
     290              :   //      - procedural macro - "A token-based function-like macro"
     291              :   //      - 'macro_rules' (by example/pattern-match) macro? or not? "an
     292              :   // AST-based function-like macro"
     293              :   //      - else is unreachable
     294              :   //  - attribute syntax macro (inner switch)
     295              :   //  - procedural macro attribute syntax - "A token-based attribute
     296              :   // macro"
     297              :   //      - legacy macro attribute syntax? - "an AST-based attribute macro"
     298              :   //      - non-macro attribute: mark known
     299              :   //      - else is unreachable
     300              :   //  - derive macro (inner switch)
     301              :   //      - derive or legacy derive - "token-based" vs "AST-based"
     302              :   //      - else is unreachable
     303              :   //  - derive container macro - unreachable
     304              : 
     305         2863 :   auto fragment = AST::Fragment::create_error ();
     306         2863 :   invoc_data.set_expander (this);
     307              : 
     308              :   // lookup the rules
     309         2863 :   auto rules_def = mappings.lookup_macro_invocation (invoc);
     310              : 
     311              :   // We special case the `offset_of!()` macro if the flag is here and manually
     312              :   // resolve to the builtin transcriber we have specified
     313         2863 :   auto assume_builtin_offset_of
     314         2863 :     = flag_assume_builtin_offset_of
     315           36 :       && (invoc.get_invoc_data ().get_path ().as_string () == "offset_of")
     316         2881 :       && !rules_def;
     317              : 
     318              :   // TODO: This is *massive hack* which should be removed as we progress to
     319              :   // Rust 1.71 when offset_of gets added to core
     320         2863 :   if (assume_builtin_offset_of)
     321              :     {
     322           18 :       fragment = MacroBuiltin::offset_of_handler (invoc.get_locus (),
     323              :                                                   invoc_data, semicolon)
     324           54 :                    .value_or (AST::Fragment::create_empty ());
     325              : 
     326           18 :       set_expanded_fragment (std::move (fragment));
     327              : 
     328           18 :       return;
     329              :     }
     330              : 
     331              :   // If there's no rule associated with the invocation, we can simply return
     332              :   // early. The early name resolver will have already emitted an error.
     333         2845 :   if (!rules_def)
     334              :     {
     335              :       // error fragment
     336           27 :       set_expanded_fragment (std::move (fragment));
     337           27 :       return;
     338              :     }
     339              : 
     340         2818 :   auto rdef = rules_def.value ();
     341              : 
     342              :   // We store the last expanded invocation and macro definition for error
     343              :   // reporting in case the recursion limit is reached
     344         2818 :   last_invoc = *invoc.clone_macro_invocation_impl ();
     345         2818 :   last_def = *rdef;
     346              : 
     347         2818 :   if (rdef->is_builtin ())
     348          367 :     fragment = rdef
     349          367 :                  ->get_builtin_transcriber () (invoc.get_locus (), invoc_data,
     350              :                                                semicolon)
     351         1101 :                  .value_or (AST::Fragment::create_empty ());
     352              :   else
     353         2451 :     fragment
     354         4902 :       = expand_decl_macro (invoc.get_locus (), invoc_data, *rdef, semicolon);
     355              : 
     356         2818 :   set_expanded_fragment (std::move (fragment));
     357         2863 : }
     358              : 
     359              : void
     360            0 : MacroExpander::expand_crate ()
     361              : {
     362              :   /* fill macro/decorator map from init list? not sure where init list comes
     363              :    * from? */
     364              : 
     365              :   // TODO: does cfg apply for inner attributes? research.
     366              :   // the apparent answer (from playground test) is yes
     367              : 
     368            0 :   push_context (ContextType::ITEM);
     369              : 
     370              :   // expand attributes recursively and strip items if required
     371              :   //  AttrVisitor attr_visitor (*this);
     372            0 :   auto &items = crate.items;
     373            0 :   for (auto it = items.begin (); it != items.end ();)
     374              :     {
     375            0 :       auto &item = *it;
     376              : 
     377            0 :       auto fragment = take_expanded_fragment ();
     378            0 :       if (fragment.should_expand ())
     379              :         {
     380              :           // Remove the current expanded invocation
     381            0 :           it = items.erase (it);
     382            0 :           for (auto &node : fragment.get_nodes ())
     383              :             {
     384            0 :               it = items.insert (it, node.take_item ());
     385            0 :               it++;
     386              :             }
     387              :         }
     388            0 :       else if (item->is_marked_for_strip ())
     389            0 :         it = items.erase (it);
     390              :       else
     391            0 :         it++;
     392            0 :     }
     393              : 
     394            0 :   pop_context ();
     395              : 
     396              :   // TODO: should recursive attribute and macro expansion be done in the same
     397              :   // transversal? Or in separate ones like currently?
     398              : 
     399              :   // expand module tree recursively
     400              : 
     401              :   // post-process
     402              : 
     403              :   // extract exported macros?
     404            0 : }
     405              : 
     406              : bool
     407         6799 : MacroExpander::depth_exceeds_recursion_limit () const
     408              : {
     409         6799 :   return expansion_depth >= cfg.recursion_limit;
     410              : }
     411              : 
     412              : bool
     413         3839 : MacroExpander::try_match_rule (AST::MacroRule &match_rule,
     414              :                                AST::DelimTokenTree &invoc_token_tree)
     415              : {
     416         3839 :   MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
     417         3839 :   Parser<MacroInvocLexer> parser (lex);
     418              : 
     419         3839 :   AST::MacroMatcher &matcher = match_rule.get_matcher ();
     420              : 
     421         3839 :   expansion_depth++;
     422         3839 :   if (!match_matcher (parser, matcher, false, false))
     423              :     {
     424         1396 :       expansion_depth--;
     425         1396 :       return false;
     426              :     }
     427         2443 :   expansion_depth--;
     428              : 
     429         2443 :   bool used_all_input_tokens = parser.skip_token (END_OF_FILE);
     430         2443 :   return used_all_input_tokens;
     431         3839 : }
     432              : 
     433              : bool
     434         8168 : MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
     435              :                                AST::MacroMatchFragment &fragment)
     436              : {
     437         8168 :   switch (fragment.get_frag_spec ().get_kind ())
     438              :     {
     439         1183 :     case AST::MacroFragSpec::EXPR:
     440         2365 :       parser.parse_expr ();
     441         1183 :       break;
     442              : 
     443            2 :     case AST::MacroFragSpec::BLOCK:
     444            4 :       parser.parse_block_expr ();
     445            2 :       break;
     446              : 
     447          480 :     case AST::MacroFragSpec::IDENT:
     448          480 :       parser.parse_identifier_or_keyword_token ();
     449          480 :       break;
     450              : 
     451         3474 :     case AST::MacroFragSpec::LITERAL:
     452         6945 :       std::ignore = parser.parse_literal_expr ();
     453         3474 :       break;
     454              : 
     455            1 :     case AST::MacroFragSpec::ITEM:
     456            1 :       parser.parse_item (false);
     457            1 :       break;
     458              : 
     459         2380 :     case AST::MacroFragSpec::TY:
     460         2380 :       parser.parse_type ();
     461         2380 :       break;
     462              : 
     463           17 :     case AST::MacroFragSpec::PAT:
     464           17 :       parser.parse_pattern ();
     465           17 :       break;
     466              : 
     467            0 :     case AST::MacroFragSpec::PATH:
     468            0 :       parser.parse_path_in_expression ();
     469            0 :       break;
     470              : 
     471            0 :     case AST::MacroFragSpec::VIS:
     472            0 :       parser.parse_visibility ();
     473            0 :       break;
     474              : 
     475          303 :     case AST::MacroFragSpec::STMT:
     476          303 :       {
     477          303 :         auto restrictions = ParseRestrictions ();
     478          303 :         restrictions.consume_semi = false;
     479          303 :         parser.parse_stmt (restrictions);
     480          303 :         break;
     481              :       }
     482              : 
     483            4 :     case AST::MacroFragSpec::LIFETIME:
     484            4 :       parser.parse_lifetime_params ();
     485            4 :       break;
     486              : 
     487              :       // is meta attributes?
     488           46 :     case AST::MacroFragSpec::META:
     489           46 :       parser.parse_attribute_body ();
     490           46 :       break;
     491              : 
     492          278 :     case AST::MacroFragSpec::TT:
     493          278 :       parser.parse_token_tree ();
     494          278 :       break;
     495              : 
     496              :       // i guess we just ignore invalid and just error out
     497              :     case AST::MacroFragSpec::INVALID:
     498              :       return false;
     499              :     }
     500              : 
     501              :   // it matches if the parser did not produce errors trying to parse that type
     502              :   // of item
     503         8168 :   return !parser.has_errors ();
     504              : }
     505              : 
     506              : bool
     507         3935 : MacroExpander::match_matcher (Parser<MacroInvocLexer> &parser,
     508              :                               AST::MacroMatcher &matcher, bool in_repetition,
     509              :                               bool match_delim)
     510              : {
     511         3935 :   if (depth_exceeds_recursion_limit ())
     512              :     {
     513            0 :       rust_error_at (matcher.get_match_locus (), "reached recursion limit");
     514            0 :       return false;
     515              :     }
     516              : 
     517         3935 :   auto delimiter = parser.peek_current_token ();
     518              : 
     519         7866 :   auto check_delim = [&matcher, match_delim] (AST::DelimType delim) {
     520           92 :     return !match_delim || matcher.get_delim_type () == delim;
     521         3935 :   };
     522              : 
     523              :   // this is used so we can check that we delimit the stream correctly.
     524         3935 :   switch (delimiter->get_id ())
     525              :     {
     526         3628 :     case LEFT_PAREN:
     527         3628 :       {
     528         3628 :         if (!check_delim (AST::DelimType::PARENS))
     529              :           return false;
     530              :       }
     531              :       break;
     532              : 
     533           46 :     case LEFT_SQUARE:
     534           46 :       {
     535           46 :         if (!check_delim (AST::DelimType::SQUARE))
     536              :           return false;
     537              :       }
     538              :       break;
     539              : 
     540          257 :     case LEFT_CURLY:
     541          257 :       {
     542         4192 :         if (!check_delim (AST::DelimType::CURLY))
     543              :           return false;
     544              :       }
     545              :       break;
     546              :     default:
     547              :       return false;
     548              :     }
     549         3930 :   parser.skip_token ();
     550              : 
     551         3930 :   const MacroInvocLexer &source = parser.get_token_source ();
     552              : 
     553         7860 :   std::unordered_map<std::string, location_t> duplicate_check;
     554              : 
     555        10475 :   for (auto &match : matcher.get_matches ())
     556              :     {
     557         6590 :       size_t offs_begin = source.get_offs ();
     558              : 
     559         6590 :       switch (match->get_macro_match_type ())
     560              :         {
     561         3745 :         case AST::MacroMatch::MacroMatchType::Fragment:
     562         3745 :           {
     563         3745 :             AST::MacroMatchFragment *fragment
     564         3745 :               = static_cast<AST::MacroMatchFragment *> (match.get ());
     565         3745 :             if (!match_fragment (parser, *fragment))
     566            3 :               return false;
     567              : 
     568         7486 :             auto duplicate_result = duplicate_check.insert (
     569        11229 :               std::make_pair (fragment->get_ident ().as_string (),
     570         3743 :                               fragment->get_ident ().get_locus ()));
     571              : 
     572         3743 :             if (!duplicate_result.second)
     573              :               {
     574              :                 // TODO: add range labels?
     575            1 :                 rich_location r (line_table,
     576            1 :                                  fragment->get_ident ().get_locus ());
     577            1 :                 r.add_range (duplicate_result.first->second);
     578            1 :                 rust_error_at (r, "duplicate matcher binding");
     579            1 :                 had_duplicate_error = true;
     580            1 :                 return false;
     581            1 :               }
     582              : 
     583              :             // matched fragment get the offset in the token stream
     584         3742 :             size_t offs_end = source.get_offs ();
     585         7484 :             sub_stack.insert_metavar (
     586         7484 :               MatchedFragment (fragment->get_ident ().as_string (), offs_begin,
     587        11226 :                                offs_end));
     588              :           }
     589         3742 :           break;
     590              : 
     591         1070 :         case AST::MacroMatch::MacroMatchType::Tok:
     592         1070 :           {
     593         1070 :             AST::Token *tok = static_cast<AST::Token *> (match.get ());
     594         1070 :             if (!match_token (parser, *tok))
     595              :               return false;
     596              :           }
     597              :           break;
     598              : 
     599         1712 :         case AST::MacroMatch::MacroMatchType::Repetition:
     600         1712 :           {
     601         1712 :             AST::MacroMatchRepetition *rep
     602         1712 :               = static_cast<AST::MacroMatchRepetition *> (match.get ());
     603         1712 :             if (!match_repetition (parser, *rep))
     604              :               return false;
     605              :           }
     606              :           break;
     607              : 
     608           63 :         case AST::MacroMatch::MacroMatchType::Matcher:
     609           63 :           {
     610           63 :             AST::MacroMatcher *m
     611           63 :               = static_cast<AST::MacroMatcher *> (match.get ());
     612           63 :             expansion_depth++;
     613           63 :             if (!match_matcher (parser, *m, in_repetition))
     614              :               {
     615            3 :                 expansion_depth--;
     616            3 :                 return false;
     617              :               }
     618           60 :             expansion_depth--;
     619              :           }
     620           60 :           break;
     621              :         }
     622              :     }
     623              : 
     624         3885 :   switch (delimiter->get_id ())
     625              :     {
     626         3583 :     case LEFT_PAREN:
     627         3583 :       {
     628         3583 :         if (!parser.skip_token (RIGHT_PAREN))
     629              :           return false;
     630              :       }
     631              :       break;
     632              : 
     633           46 :     case LEFT_SQUARE:
     634           46 :       {
     635           46 :         if (!parser.skip_token (RIGHT_SQUARE))
     636              :           return false;
     637              :       }
     638              :       break;
     639              : 
     640          256 :     case LEFT_CURLY:
     641          256 :       {
     642          256 :         if (!parser.skip_token (RIGHT_CURLY))
     643              :           return false;
     644              :       }
     645              :       break;
     646            0 :     default:
     647            0 :       rust_unreachable ();
     648              :     }
     649              : 
     650              :   return true;
     651         3935 : }
     652              : 
     653              : bool
     654         2696 : MacroExpander::match_token (Parser<MacroInvocLexer> &parser, AST::Token &token)
     655              : {
     656         5392 :   return parser.skip_token (token.get_tok_ptr ());
     657              : }
     658              : 
     659              : bool
     660         1726 : MacroExpander::match_n_matches (Parser<MacroInvocLexer> &parser,
     661              :                                 AST::MacroMatchRepetition &rep,
     662              :                                 size_t &match_amount, size_t lo_bound,
     663              :                                 size_t hi_bound)
     664              : {
     665         1726 :   match_amount = 0;
     666         1726 :   auto &matches = rep.get_matches ();
     667              : 
     668         1726 :   const MacroInvocLexer &source = parser.get_token_source ();
     669        10152 :   while (true)
     670              :     {
     671              :       // If the current token is a closing macro delimiter, break away.
     672              :       // TODO: Is this correct?
     673         5939 :       auto t_id = parser.peek_current_token ()->get_id ();
     674         5939 :       if (t_id == RIGHT_PAREN || t_id == RIGHT_SQUARE || t_id == RIGHT_CURLY)
     675              :         break;
     676              : 
     677              :       // Skip parsing a separator on the first match, otherwise consume it.
     678              :       // If it isn't present, this is an error
     679         4239 :       if (rep.has_sep () && match_amount > 0)
     680          357 :         if (!match_token (parser, *rep.get_sep ()))
     681              :           break;
     682              : 
     683         4224 :       sub_stack.push ();
     684         4224 :       bool valid_current_match = false;
     685         9963 :       for (auto &match : matches)
     686              :         {
     687         5739 :           size_t offs_begin = source.get_offs ();
     688         5739 :           switch (match->get_macro_match_type ())
     689              :             {
     690         4423 :             case AST::MacroMatch::MacroMatchType::Fragment:
     691         4423 :               {
     692         4423 :                 AST::MacroMatchFragment *fragment
     693         4423 :                   = static_cast<AST::MacroMatchFragment *> (match.get ());
     694         4423 :                 valid_current_match = match_fragment (parser, *fragment);
     695              : 
     696              :                 // matched fragment get the offset in the token stream
     697         4423 :                 size_t offs_end = source.get_offs ();
     698              : 
     699         4423 :                 if (valid_current_match)
     700         8834 :                   sub_stack.insert_metavar (
     701         8834 :                     MatchedFragment (fragment->get_ident ().as_string (),
     702        13251 :                                      offs_begin, offs_end));
     703              :               }
     704              :               break;
     705              : 
     706         1269 :             case AST::MacroMatch::MacroMatchType::Tok:
     707         1269 :               {
     708         1269 :                 AST::Token *tok = static_cast<AST::Token *> (match.get ());
     709         1269 :                 valid_current_match = match_token (parser, *tok);
     710              :               }
     711         1269 :               break;
     712              : 
     713           14 :             case AST::MacroMatch::MacroMatchType::Repetition:
     714           14 :               {
     715           14 :                 AST::MacroMatchRepetition *rep
     716           14 :                   = static_cast<AST::MacroMatchRepetition *> (match.get ());
     717           14 :                 valid_current_match = match_repetition (parser, *rep);
     718              :               }
     719           14 :               break;
     720              : 
     721           33 :             case AST::MacroMatch::MacroMatchType::Matcher:
     722           33 :               {
     723           33 :                 AST::MacroMatcher *m
     724           33 :                   = static_cast<AST::MacroMatcher *> (match.get ());
     725           33 :                 valid_current_match = match_matcher (parser, *m, true);
     726              :               }
     727           33 :               break;
     728              :             }
     729              :         }
     730         4224 :       auto old_stack = sub_stack.pop ();
     731              : 
     732              :       // If we've encountered an error once, stop trying to match more
     733              :       // repetitions
     734         4224 :       if (!valid_current_match)
     735              :         break;
     736              : 
     737              :       // nest metavars into repetitions
     738         8655 :       for (auto &ent : old_stack)
     739         8882 :         sub_stack.append_fragment (ent.first, std::move (ent.second));
     740              : 
     741         4214 :       match_amount++;
     742              : 
     743              :       // Break early if we notice there's too many expressions already
     744         4214 :       if (hi_bound && match_amount > hi_bound)
     745              :         break;
     746         4224 :     }
     747              : 
     748              :   // Check if the amount of matches we got is valid: Is it more than the lower
     749              :   // bound and less than the higher bound?
     750         1726 :   bool did_meet_lo_bound = match_amount >= lo_bound;
     751         1726 :   bool did_meet_hi_bound = hi_bound ? match_amount <= hi_bound : true;
     752              : 
     753              :   // If the end-result is valid, then we can clear the parse errors: Since
     754              :   // repetitions are parsed eagerly, it is okay to fail in some cases
     755         3451 :   auto res = did_meet_lo_bound && did_meet_hi_bound;
     756         1725 :   if (res)
     757         1721 :     parser.clear_errors ();
     758              : 
     759         1726 :   return res;
     760              : }
     761              : 
     762              : /*
     763              :  * Helper function for defining unmatched repetition metavars
     764              :  */
     765              : void
     766         2922 : MacroExpander::match_repetition_skipped_metavars (AST::MacroMatch &match)
     767              : {
     768              :   // We have to handle zero fragments differently: They will not have been
     769              :   // "matched" but they are still valid and should be inserted as a special
     770              :   // case. So we go through the stack map, and for every fragment which doesn't
     771              :   // exist, insert a zero-matched fragment.
     772         2922 :   switch (match.get_macro_match_type ())
     773              :     {
     774         1770 :     case AST::MacroMatch::MacroMatchType::Fragment:
     775         1770 :       match_repetition_skipped_metavars (
     776              :         static_cast<AST::MacroMatchFragment &> (match));
     777         1770 :       break;
     778           12 :     case AST::MacroMatch::MacroMatchType::Repetition:
     779           12 :       match_repetition_skipped_metavars (
     780              :         static_cast<AST::MacroMatchRepetition &> (match));
     781           12 :       break;
     782           13 :     case AST::MacroMatch::MacroMatchType::Matcher:
     783           13 :       match_repetition_skipped_metavars (
     784              :         static_cast<AST::MacroMatcher &> (match));
     785           13 :       break;
     786              :     case AST::MacroMatch::MacroMatchType::Tok:
     787              :       break;
     788              :     }
     789         2922 : }
     790              : 
     791              : void
     792         1770 : MacroExpander::match_repetition_skipped_metavars (
     793              :   AST::MacroMatchFragment &fragment)
     794              : {
     795         1770 :   auto &stack_map = sub_stack.peek ();
     796         1770 :   auto it = stack_map.find (fragment.get_ident ().as_string ());
     797              : 
     798         1770 :   if (it == stack_map.end ())
     799          114 :     sub_stack.insert_matches (fragment.get_ident ().as_string (),
     800          114 :                               MatchedFragmentContainer::zero ());
     801         1770 : }
     802              : 
     803              : void
     804         1738 : MacroExpander::match_repetition_skipped_metavars (
     805              :   AST::MacroMatchRepetition &rep)
     806              : {
     807         4654 :   for (auto &match : rep.get_matches ())
     808         2916 :     match_repetition_skipped_metavars (*match);
     809         1738 : }
     810              : 
     811              : void
     812           13 : MacroExpander::match_repetition_skipped_metavars (AST::MacroMatcher &rep)
     813              : {
     814           19 :   for (auto &match : rep.get_matches ())
     815            6 :     match_repetition_skipped_metavars (*match);
     816           13 : }
     817              : 
     818              : bool
     819         1726 : MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser,
     820              :                                  AST::MacroMatchRepetition &rep)
     821              : {
     822         1726 :   size_t match_amount = 0;
     823         1726 :   bool res = false;
     824              : 
     825         1726 :   std::string lo_str;
     826         1726 :   std::string hi_str;
     827         1726 :   switch (rep.get_op ())
     828              :     {
     829         1565 :     case AST::MacroMatchRepetition::MacroRepOp::ANY:
     830         1565 :       lo_str = "0";
     831         1565 :       hi_str = "+inf";
     832         1565 :       res = match_n_matches (parser, rep, match_amount);
     833         1565 :       break;
     834          113 :     case AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE:
     835          113 :       lo_str = "1";
     836          113 :       hi_str = "+inf";
     837          113 :       res = match_n_matches (parser, rep, match_amount, 1);
     838          113 :       break;
     839           48 :     case AST::MacroMatchRepetition::MacroRepOp::ZERO_OR_ONE:
     840           48 :       lo_str = "0";
     841           48 :       hi_str = "1";
     842           48 :       res = match_n_matches (parser, rep, match_amount, 0, 1);
     843           48 :       break;
     844            0 :     default:
     845            0 :       rust_unreachable ();
     846              :     }
     847              : 
     848         1731 :   rust_debug_loc (rep.get_match_locus (), "%s matched %lu times",
     849              :                   res ? "successfully" : "unsuccessfully",
     850              :                   (unsigned long) match_amount);
     851              : 
     852         1726 :   match_repetition_skipped_metavars (rep);
     853              : 
     854         1726 :   return res;
     855         1726 : }
     856              : 
     857              : /**
     858              :  * Helper function to refactor calling a parsing function 0 or more times
     859              :  */
     860              : static AST::Fragment
     861          738 : parse_many (Parser<MacroInvocLexer> &parser, TokenId delimiter,
     862              :             std::function<AST::SingleASTNode ()> parse_fn)
     863              : {
     864          738 :   auto &lexer = parser.get_token_source ();
     865          738 :   auto start = lexer.get_offs ();
     866              : 
     867          738 :   std::vector<AST::SingleASTNode> nodes;
     868         3137 :   while (true)
     869              :     {
     870         7750 :       if (parser.peek_current_token ()->get_id () == delimiter)
     871              :         break;
     872              : 
     873         3141 :       auto node = parse_fn ();
     874         3141 :       if (node.is_error ())
     875              :         {
     876            9 :           for (auto err : parser.get_errors ())
     877            5 :             err.emit ();
     878              : 
     879            4 :           return AST::Fragment::create_error ();
     880              :         }
     881              : 
     882         3137 :       nodes.emplace_back (std::move (node));
     883         3141 :     }
     884          734 :   auto end = lexer.get_offs ();
     885              : 
     886          734 :   return AST::Fragment (std::move (nodes), lexer.get_token_slice (start, end));
     887          738 : }
     888              : 
     889              : /**
     890              :  * Transcribe 0 or more items from a macro invocation
     891              :  *
     892              :  * @param parser Parser to extract items from
     893              :  * @param delimiter Id of the token on which parsing should stop
     894              :  */
     895              : static AST::Fragment
     896          337 : transcribe_many_items (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
     897              : {
     898          337 :   return parse_many (parser, delimiter, [&parser] () {
     899         2590 :     auto item = parser.parse_item (true);
     900         2590 :     if (!item)
     901            1 :       return AST::SingleASTNode (std::unique_ptr<AST::Item> (nullptr));
     902         2589 :     return AST::SingleASTNode (std::move (item.value ()));
     903          337 :   });
     904              : }
     905              : 
     906              : /**
     907              :  * Transcribe 0 or more external items from a macro invocation
     908              :  *
     909              :  * @param parser Parser to extract items from
     910              :  * @param delimiter Id of the token on which parsing should stop
     911              :  */
     912              : static AST::Fragment
     913            2 : transcribe_many_ext (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
     914              : {
     915            2 :   return parse_many (parser, delimiter, [&parser] () {
     916            3 :     auto item = parser.parse_external_item ();
     917            3 :     return AST::SingleASTNode (std::move (item));
     918            5 :   });
     919              : }
     920              : 
     921              : /**
     922              :  * Transcribe 0 or more trait items from a macro invocation
     923              :  *
     924              :  * @param parser Parser to extract items from
     925              :  * @param delimiter Id of the token on which parsing should stop
     926              :  */
     927              : static AST::Fragment
     928            1 : transcribe_many_trait_items (Parser<MacroInvocLexer> &parser,
     929              :                              TokenId &delimiter)
     930              : {
     931            1 :   return parse_many (parser, delimiter, [&parser] () {
     932            2 :     auto item = parser.parse_trait_item ();
     933            2 :     return AST::SingleASTNode (std::move (item));
     934            3 :   });
     935              : }
     936              : 
     937              : /**
     938              :  * Transcribe 0 or more impl items from a macro invocation
     939              :  *
     940              :  * @param parser Parser to extract items from
     941              :  * @param delimiter Id of the token on which parsing should stop
     942              :  */
     943              : static AST::Fragment
     944            1 : transcribe_many_impl_items (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
     945              : {
     946            1 :   return parse_many (parser, delimiter, [&parser] () {
     947            2 :     auto item = parser.parse_inherent_impl_item ();
     948            2 :     return AST::SingleASTNode (std::move (item));
     949            3 :   });
     950              : }
     951              : 
     952              : /**
     953              :  * Transcribe 0 or more trait impl items from a macro invocation
     954              :  *
     955              :  * @param parser Parser to extract items from
     956              :  * @param delimiter Id of the token on which parsing should stop
     957              :  */
     958              : static AST::Fragment
     959           31 : transcribe_many_trait_impl_items (Parser<MacroInvocLexer> &parser,
     960              :                                   TokenId &delimiter)
     961              : {
     962           31 :   return parse_many (parser, delimiter, [&parser] () {
     963          103 :     auto item = parser.parse_trait_impl_item ();
     964          103 :     return AST::SingleASTNode (std::move (item));
     965          134 :   });
     966              : }
     967              : 
     968              : /**
     969              :  * Transcribe 0 or more statements from a macro invocation
     970              :  *
     971              :  * @param parser Parser to extract statements from
     972              :  * @param delimiter Id of the token on which parsing should stop
     973              :  */
     974              : static AST::Fragment
     975          366 : transcribe_many_stmts (Parser<MacroInvocLexer> &parser, TokenId delimiter,
     976              :                        bool semicolon)
     977              : {
     978          366 :   auto restrictions = ParseRestrictions ();
     979          366 :   restrictions.allow_close_after_expr_stmt = true;
     980              : 
     981          366 :   return parse_many (parser, delimiter,
     982          366 :                      [&parser, restrictions, delimiter, semicolon] () {
     983          441 :                        auto stmt = parser.parse_stmt (restrictions);
     984          433 :                        if (semicolon && stmt
     985         1304 :                            && parser.peek_current_token ()->get_id ()
     986          430 :                                 == delimiter)
     987          336 :                          stmt->add_semicolon ();
     988              : 
     989          441 :                        return AST::SingleASTNode (std::move (stmt));
     990          807 :                      });
     991              : }
     992              : 
     993              : /**
     994              :  * Transcribe one expression from a macro invocation
     995              :  *
     996              :  * @param parser Parser to extract statements from
     997              :  */
     998              : static AST::Fragment
     999         1672 : transcribe_expression (Parser<MacroInvocLexer> &parser)
    1000              : {
    1001         1672 :   auto &lexer = parser.get_token_source ();
    1002         1672 :   auto start = lexer.get_offs ();
    1003              : 
    1004         1672 :   auto attrs = parser.parse_outer_attributes ();
    1005         1672 :   auto expr = parser.parse_expr (std::move (attrs));
    1006         1677 :   for (auto error : parser.get_errors ())
    1007            5 :     error.emit ();
    1008         1672 :   if (!expr)
    1009            1 :     return AST::Fragment::create_error ();
    1010              : 
    1011              :   // FIXME: make this an error for some edititons
    1012         3342 :   if (parser.peek_current_token ()->get_id () == SEMICOLON)
    1013              :     {
    1014            1 :       rust_warning_at (
    1015            1 :         parser.peek_current_token ()->get_locus (), 0,
    1016              :         "trailing semicolon in macro used in expression context");
    1017            1 :       parser.skip_token ();
    1018              :     }
    1019              : 
    1020         1671 :   auto end = lexer.get_offs ();
    1021              : 
    1022         3342 :   return AST::Fragment ({std::move (expr.value ())},
    1023         6684 :                         lexer.get_token_slice (start, end));
    1024         1672 : }
    1025              : 
    1026              : /**
    1027              :  * Transcribe one type from a macro invocation
    1028              :  *
    1029              :  * @param parser Parser to extract statements from
    1030              :  */
    1031              : static AST::Fragment
    1032           29 : transcribe_type (Parser<MacroInvocLexer> &parser)
    1033              : {
    1034           29 :   auto &lexer = parser.get_token_source ();
    1035           29 :   auto start = lexer.get_offs ();
    1036              : 
    1037           29 :   auto type = parser.parse_type (true);
    1038           29 :   for (auto err : parser.get_errors ())
    1039            0 :     err.emit ();
    1040           29 :   if (!type)
    1041            0 :     return AST::Fragment::create_error ();
    1042              : 
    1043           29 :   auto end = lexer.get_offs ();
    1044              : 
    1045           58 :   return AST::Fragment ({std::move (type)}, lexer.get_token_slice (start, end));
    1046           29 : }
    1047              : 
    1048              : /**
    1049              :  * Transcribe one pattern from a macro invocation
    1050              :  *
    1051              :  * @param parser Parser to extract statements from
    1052              :  */
    1053              : static AST::Fragment
    1054            4 : transcribe_pattern (Parser<MacroInvocLexer> &parser)
    1055              : {
    1056            4 :   auto &lexer = parser.get_token_source ();
    1057            4 :   auto start = lexer.get_offs ();
    1058              : 
    1059            4 :   auto pattern = parser.parse_pattern ();
    1060            8 :   for (auto err : parser.get_errors ())
    1061            4 :     err.emit ();
    1062              : 
    1063            4 :   if (!pattern)
    1064            2 :     return AST::Fragment::create_error ();
    1065              : 
    1066            2 :   auto end = lexer.get_offs ();
    1067              : 
    1068            4 :   return AST::Fragment ({std::move (pattern)},
    1069            6 :                         lexer.get_token_slice (start, end));
    1070            4 : }
    1071              : 
    1072              : static AST::Fragment
    1073         2443 : transcribe_context (MacroExpander::ContextType ctx,
    1074              :                     Parser<MacroInvocLexer> &parser, bool semicolon,
    1075              :                     AST::DelimType delimiter, TokenId last_token_id)
    1076              : {
    1077              :   // The flow-chart in order to choose a parsing function is as follows:
    1078              :   //
    1079              :   // [switch special context]
    1080              :   //     -- Item --> parser.parse_item();
    1081              :   //     -- Trait --> parser.parse_trait_item();
    1082              :   //     -- Impl --> parser.parse_impl_item();
    1083              :   //     -- Extern --> parser.parse_extern_item();
    1084              :   //     -- Pattern --> parser.parse_pattern();
    1085              :   //     -- None --> [has semicolon?]
    1086              :   //                 -- Yes --> parser.parse_stmt();
    1087              :   //                 -- No --> [switch invocation.delimiter()]
    1088              :   //                             -- { } --> parser.parse_stmt();
    1089              :   //                             -- _ --> parser.parse_expr(); // once!
    1090              : 
    1091              :   // If there is a semicolon OR we are expanding a MacroInvocationSemi, then
    1092              :   // we can parse multiple items. Otherwise, parse *one* expression
    1093              : 
    1094         2443 :   switch (ctx)
    1095              :     {
    1096          337 :     case MacroExpander::ContextType::ITEM:
    1097          337 :       return transcribe_many_items (parser, last_token_id);
    1098            1 :       break;
    1099            1 :     case MacroExpander::ContextType::TRAIT:
    1100            1 :       return transcribe_many_trait_items (parser, last_token_id);
    1101            1 :       break;
    1102            1 :     case MacroExpander::ContextType::IMPL:
    1103            1 :       return transcribe_many_impl_items (parser, last_token_id);
    1104           31 :       break;
    1105           31 :     case MacroExpander::ContextType::TRAIT_IMPL:
    1106           31 :       return transcribe_many_trait_impl_items (parser, last_token_id);
    1107            2 :       break;
    1108            2 :     case MacroExpander::ContextType::EXTERN:
    1109            2 :       return transcribe_many_ext (parser, last_token_id);
    1110           29 :       break;
    1111           29 :     case MacroExpander::ContextType::TYPE:
    1112           29 :       return transcribe_type (parser);
    1113            4 :     case MacroExpander::ContextType::PATTERN:
    1114            4 :       return transcribe_pattern (parser);
    1115          366 :       break;
    1116          366 :     case MacroExpander::ContextType::STMT:
    1117          366 :       return transcribe_many_stmts (parser, last_token_id, semicolon);
    1118         1672 :     case MacroExpander::ContextType::EXPR:
    1119         1672 :       return transcribe_expression (parser);
    1120            0 :     default:
    1121            0 :       rust_unreachable ();
    1122              :     }
    1123              : }
    1124              : 
    1125              : static std::string
    1126         2443 : tokens_to_str (std::vector<std::unique_ptr<AST::Token>> &tokens)
    1127              : {
    1128         2443 :   std::string str;
    1129         2443 :   if (!tokens.empty ())
    1130              :     {
    1131         4886 :       str += tokens[0]->as_string ();
    1132       133443 :       for (size_t i = 1; i < tokens.size (); i++)
    1133       262000 :         str += " " + tokens[i]->as_string ();
    1134              :     }
    1135              : 
    1136         2443 :   return str;
    1137              : }
    1138              : 
    1139              : AST::Fragment
    1140         2443 : MacroExpander::transcribe_rule (
    1141              :   AST::MacroRulesDefinition &definition, AST::MacroRule &match_rule,
    1142              :   AST::DelimTokenTree &invoc_token_tree,
    1143              :   std::map<std::string, MatchedFragmentContainer *> &matched_fragments,
    1144              :   AST::InvocKind invoc_kind, ContextType ctx)
    1145              : {
    1146         2443 :   bool semicolon = invoc_kind == AST::InvocKind::Semicoloned;
    1147              : 
    1148              :   // we can manipulate the token tree to substitute the dollar identifiers so
    1149              :   // that when we call parse its already substituted for us
    1150         2443 :   AST::MacroTranscriber &transcriber = match_rule.get_transcriber ();
    1151         2443 :   AST::DelimTokenTree &transcribe_tree = transcriber.get_token_tree ();
    1152              : 
    1153         2443 :   auto invoc_stream = invoc_token_tree.to_token_stream ();
    1154         2443 :   auto macro_rule_tokens = transcribe_tree.to_token_stream ();
    1155              : 
    1156         2443 :   auto substitute_context
    1157              :     = SubstituteCtx (invoc_stream, macro_rule_tokens, matched_fragments,
    1158         2443 :                      definition, invoc_token_tree.get_locus ());
    1159         2443 :   std::vector<std::unique_ptr<AST::Token>> substituted_tokens
    1160         2443 :     = substitute_context.substitute_tokens ();
    1161              : 
    1162         2443 :   rust_debug ("substituted tokens: %s",
    1163              :               tokens_to_str (substituted_tokens).c_str ());
    1164              : 
    1165              :   // parse it to an Fragment
    1166         2443 :   MacroInvocLexer lex (std::move (substituted_tokens));
    1167         2443 :   Parser<MacroInvocLexer> parser (lex);
    1168              : 
    1169         2443 :   auto last_token_id = TokenId::RIGHT_CURLY;
    1170              : 
    1171              :   // this is used so we can check that we delimit the stream correctly.
    1172         2443 :   switch (transcribe_tree.get_delim_type ())
    1173              :     {
    1174          133 :     case AST::DelimType::PARENS:
    1175          133 :       last_token_id = TokenId::RIGHT_PAREN;
    1176          133 :       rust_assert (parser.skip_token (LEFT_PAREN));
    1177              :       break;
    1178              : 
    1179         2310 :     case AST::DelimType::CURLY:
    1180         2310 :       rust_assert (parser.skip_token (LEFT_CURLY));
    1181              :       break;
    1182              : 
    1183            0 :     case AST::DelimType::SQUARE:
    1184            0 :       last_token_id = TokenId::RIGHT_SQUARE;
    1185            0 :       rust_assert (parser.skip_token (LEFT_SQUARE));
    1186              :       break;
    1187              :     }
    1188              : 
    1189              :   // see https://github.com/Rust-GCC/gccrs/issues/22
    1190              :   // TL;DR:
    1191              :   //   - Treat all macro invocations with parentheses, (), or square brackets,
    1192              :   //   [], as expressions.
    1193              :   //   - If the macro invocation has curly brackets, {}, it may be parsed as a
    1194              :   //   statement depending on the context.
    1195              :   //   - If the macro invocation has a semicolon at the end, it must be parsed
    1196              :   //   as a statement (either via ExpressionStatement or
    1197              :   //   MacroInvocationWithSemi)
    1198              : 
    1199         2443 :   auto fragment
    1200              :     = transcribe_context (ctx, parser, semicolon,
    1201         2443 :                           invoc_token_tree.get_delim_type (), last_token_id);
    1202              : 
    1203              :   // emit any errors
    1204         2443 :   if (parser.has_errors ())
    1205           12 :     return AST::Fragment::create_error ();
    1206              : 
    1207              :   // are all the tokens used?
    1208         2431 :   bool did_delimit = parser.skip_token (last_token_id);
    1209              : 
    1210         2431 :   bool reached_end_of_stream = did_delimit && parser.skip_token (END_OF_FILE);
    1211            0 :   if (!reached_end_of_stream)
    1212              :     {
    1213              :       // FIXME: rustc has some cases it accepts this with a warning due to
    1214              :       // backwards compatibility.
    1215            0 :       const_TokenPtr current_token = parser.peek_current_token ();
    1216            0 :       rust_error_at (current_token->get_locus (),
    1217              :                      "tokens here and after are unparsed");
    1218            0 :     }
    1219              : 
    1220         2431 :   return fragment;
    1221         2443 : }
    1222              : 
    1223              : AST::Fragment
    1224            0 : MacroExpander::parse_proc_macro_output (ProcMacro::TokenStream ts)
    1225              : {
    1226            0 :   MacroInvocLexer lex (convert (ts));
    1227            0 :   Parser<MacroInvocLexer> parser (lex);
    1228              : 
    1229            0 :   std::vector<AST::SingleASTNode> nodes;
    1230            0 :   switch (peek_context ())
    1231              :     {
    1232              :     case ContextType::ITEM:
    1233            0 :       while (lex.peek_token ()->get_id () != END_OF_FILE)
    1234              :         {
    1235            0 :           auto result = parser.parse_item (false);
    1236            0 :           if (!result)
    1237              :             break;
    1238            0 :           nodes.emplace_back (std::move (result.value ()));
    1239            0 :         }
    1240              :       break;
    1241              :     case ContextType::STMT:
    1242            0 :       while (lex.peek_token ()->get_id () != END_OF_FILE)
    1243              :         {
    1244            0 :           auto result = parser.parse_stmt ();
    1245            0 :           if (result == nullptr)
    1246              :             break;
    1247            0 :           nodes.emplace_back (std::move (result));
    1248            0 :         }
    1249              :       break;
    1250            0 :     case ContextType::TRAIT:
    1251            0 :     case ContextType::IMPL:
    1252            0 :     case ContextType::TRAIT_IMPL:
    1253            0 :     case ContextType::EXTERN:
    1254            0 :     case ContextType::TYPE:
    1255            0 :     case ContextType::EXPR:
    1256            0 :     default:
    1257            0 :       rust_unreachable ();
    1258              :     }
    1259              : 
    1260            0 :   if (parser.has_errors ())
    1261            0 :     return AST::Fragment::create_error ();
    1262              :   else
    1263            0 :     return {nodes, std::vector<std::unique_ptr<AST::Token>> ()};
    1264            0 : }
    1265              : 
    1266              : MatchedFragment &
    1267        11372 : MatchedFragmentContainer::get_single_fragment ()
    1268              : {
    1269        11372 :   rust_assert (is_single_fragment ());
    1270              : 
    1271        11372 :   return static_cast<MatchedFragmentContainerMetaVar &> (*this).get_fragment ();
    1272              : }
    1273              : 
    1274              : std::vector<std::unique_ptr<MatchedFragmentContainer>> &
    1275         8864 : MatchedFragmentContainer::get_fragments ()
    1276              : {
    1277         8864 :   rust_assert (!is_single_fragment ());
    1278              : 
    1279         8864 :   return static_cast<MatchedFragmentContainerRepetition &> (*this)
    1280         8864 :     .get_fragments ();
    1281              : }
    1282              : 
    1283              : void
    1284            0 : MatchedFragmentContainer::add_fragment (MatchedFragment fragment)
    1285              : {
    1286            0 :   rust_assert (!is_single_fragment ());
    1287              : 
    1288            0 :   return static_cast<MatchedFragmentContainerRepetition &> (*this)
    1289            0 :     .add_fragment (fragment);
    1290              : }
    1291              : 
    1292              : void
    1293         4441 : MatchedFragmentContainer::add_fragment (
    1294              :   std::unique_ptr<MatchedFragmentContainer> fragment)
    1295              : {
    1296         4441 :   rust_assert (!is_single_fragment ());
    1297              : 
    1298         4441 :   return static_cast<MatchedFragmentContainerRepetition &> (*this)
    1299         4441 :     .add_fragment (std::move (fragment));
    1300              : }
    1301              : 
    1302              : std::unique_ptr<MatchedFragmentContainer>
    1303           57 : MatchedFragmentContainer::zero ()
    1304              : {
    1305           57 :   return std::unique_ptr<MatchedFragmentContainer> (
    1306           57 :     new MatchedFragmentContainerRepetition ());
    1307              : }
    1308              : 
    1309              : std::unique_ptr<MatchedFragmentContainer>
    1310         8159 : MatchedFragmentContainer::metavar (MatchedFragment fragment)
    1311              : {
    1312         8159 :   return std::unique_ptr<MatchedFragmentContainer> (
    1313         8159 :     new MatchedFragmentContainerMetaVar (fragment));
    1314              : }
    1315              : 
    1316              : } // 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.