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