LCOV - code coverage report
Current view: top level - gcc/rust/parse - rust-parse-impl.hxx (source / functions) Coverage Total Hit
Test: gcc.info Lines: 75.5 % 3186 2407
Test Date: 2026-04-20 14:57:17 Functions: 73.3 % 210 154
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              : /* Template implementation for Rust::Parser. Previously in rust-parse.cc (before
      20              :  * Parser was template). Separated from rust-parse.h for readability. */
      21              : 
      22              : /* DO NOT INCLUDE ANYWHERE - this is automatically included
      23              :  *   by rust-parse-impl-*.cc
      24              :  * This is also the reason why there are no include guards. */
      25              : 
      26              : #include "expected.h"
      27              : #include "rust-ast.h"
      28              : #include "rust-common.h"
      29              : #include "rust-expr.h"
      30              : #include "rust-item.h"
      31              : #include "rust-common.h"
      32              : #include "rust-parse.h"
      33              : #include "rust-token.h"
      34              : #define INCLUDE_ALGORITHM
      35              : #include "rust-diagnostics.h"
      36              : #include "rust-dir-owner.h"
      37              : #include "rust-keyword-values.h"
      38              : #include "rust-edition.h"
      39              : #include "rust-parse-error.h"
      40              : 
      41              : #include "optional.h"
      42              : 
      43              : namespace Rust {
      44              : 
      45              : /* HACK-y special handling for skipping a right angle token at the end of
      46              :  * generic arguments.
      47              :  * Currently, this replaces the "current token" with one that is identical
      48              :  * except has the leading '>' removed (e.g. '>>' becomes '>'). This is bad
      49              :  * for several reasons - it modifies the token stream to something that
      50              :  * actually doesn't make syntactic sense, it may not worked if the token
      51              :  * has already been skipped, etc. It was done because it would not
      52              :  * actually require inserting new items into the token stream (which I
      53              :  * thought would take more work to not mess up) and because I wasn't sure
      54              :  * if the "already seen right angle" flag in the parser would work
      55              :  * correctly.
      56              :  * Those two other approaches listed are in my opinion actually better
      57              :  * long-term - insertion is probably best as it reflects syntactically
      58              :  * what occurs. On the other hand, I need to do a code audit to make sure
      59              :  * that insertion doesn't mess anything up. So that's a FIXME. */
      60              : template <typename ManagedTokenSource>
      61              : bool
      62         8045 : Parser<ManagedTokenSource>::skip_generics_right_angle ()
      63              : {
      64              :   /* OK, new great idea. Have a lexer method called
      65              :    * "split_current_token(TokenType newLeft, TokenType newRight)", which is
      66              :    * called here with whatever arguments are appropriate. That lexer method
      67              :    * handles "replacing" the current token with the "newLeft" and "inserting"
      68              :    * the next token with the "newRight" (and creating a location, etc. for it)
      69              :    */
      70              : 
      71              :   /* HACK: special handling for right shift '>>', greater or equal '>=', and
      72              :    * right shift assig */
      73              :   // '>>='
      74         8045 :   const_TokenPtr tok = lexer.peek_token ();
      75         8045 :   switch (tok->get_id ())
      76              :     {
      77         7797 :     case RIGHT_ANGLE:
      78              :       // this is good - skip token
      79         7797 :       lexer.skip_token ();
      80         7797 :       return true;
      81          247 :     case RIGHT_SHIFT:
      82              :       {
      83              :         // new implementation that should be better
      84          247 :         lexer.split_current_token (RIGHT_ANGLE, RIGHT_ANGLE);
      85          247 :         lexer.skip_token ();
      86          247 :         return true;
      87              :       }
      88            0 :     case GREATER_OR_EQUAL:
      89              :       {
      90              :         // new implementation that should be better
      91            0 :         lexer.split_current_token (RIGHT_ANGLE, EQUAL);
      92            0 :         lexer.skip_token ();
      93            0 :         return true;
      94              :       }
      95            0 :     case RIGHT_SHIFT_EQ:
      96              :       {
      97              :         // new implementation that should be better
      98            0 :         lexer.split_current_token (RIGHT_ANGLE, GREATER_OR_EQUAL);
      99            0 :         lexer.skip_token ();
     100            0 :         return true;
     101              :       }
     102            1 :     default:
     103            1 :       add_error (Error (tok->get_locus (),
     104              :                         "expected %<>%> at end of generic argument - found %qs",
     105              :                         tok->get_token_description ()));
     106            1 :       return false;
     107              :     }
     108         8045 : }
     109              : 
     110              : /* Gets left binding power for specified token.
     111              :  * Not suitable for use at the moment or possibly ever because binding power
     112              :  * cannot be purely determined from operator token with Rust grammar - e.g.
     113              :  * method call and field access have
     114              :  * different left binding powers but the same operator token. */
     115              : template <typename ManagedTokenSource>
     116              : int
     117       106993 : Parser<ManagedTokenSource>::left_binding_power (const_TokenPtr token)
     118              : {
     119              :   // HACK: called with "peek_token()", so lookahead is "peek_token(1)"
     120       106993 :   switch (token->get_id ())
     121              :     {
     122              :       /* TODO: issue here - distinguish between method calls and field access
     123              :        * somehow? Also would have to distinguish between paths and function
     124              :        * calls (:: operator), maybe more stuff. */
     125              :       /* Current plan for tackling LBP - don't do it based on token, use
     126              :        * lookahead. Or alternatively, only use Pratt parsing for OperatorExpr
     127              :        * and handle other expressions without it. rustc only considers
     128              :        * arithmetic, logical/relational, 'as',
     129              :        * '?=', ranges, colons, and assignment to have operator precedence and
     130              :        * associativity rules applicable. It then has
     131              :        * a separate "ExprPrecedence" that also includes binary operators. */
     132              : 
     133              :       // TODO: handle operator overloading - have a function replace the
     134              :       // operator?
     135              : 
     136              :       /*case DOT:
     137              :           return LBP_DOT;*/
     138              : 
     139            0 :     case SCOPE_RESOLUTION:
     140            0 :       rust_debug (
     141              :         "possible error - looked up LBP of scope resolution operator. should "
     142              :         "be handled elsewhere.");
     143            0 :       return LBP_PATH;
     144              : 
     145              :     /* Resolved by lookahead HACK that should work with current code. If next
     146              :      * token is identifier and token after that isn't parenthesised expression
     147              :      * list, it is a field reference. */
     148         8837 :     case DOT:
     149        17674 :       if (lexer.peek_token (1)->get_id () == IDENTIFIER
     150        16772 :           && lexer.peek_token (2)->get_id () != LEFT_PAREN)
     151              :         {
     152              :           return LBP_FIELD_EXPR;
     153              :         }
     154              :       return LBP_METHOD_CALL;
     155              : 
     156              :     case LEFT_PAREN:
     157              :       return LBP_FUNCTION_CALL;
     158              : 
     159              :     case LEFT_SQUARE:
     160              :       return LBP_ARRAY_REF;
     161              : 
     162              :     // postfix question mark (i.e. error propagation expression)
     163            1 :     case QUESTION_MARK:
     164            1 :       return LBP_QUESTION_MARK;
     165              : 
     166         5291 :     case AS:
     167         5291 :       return LBP_AS;
     168              : 
     169              :     case ASTERISK:
     170              :       return LBP_MUL;
     171              :     case DIV:
     172              :       return LBP_DIV;
     173              :     case PERCENT:
     174              :       return LBP_MOD;
     175              : 
     176              :     case PLUS:
     177              :       return LBP_PLUS;
     178              :     case MINUS:
     179              :       return LBP_MINUS;
     180              : 
     181              :     case LEFT_SHIFT:
     182              :       return LBP_L_SHIFT;
     183              :     case RIGHT_SHIFT:
     184              :       return LBP_R_SHIFT;
     185              : 
     186              :     // binary & operator
     187           52 :     case AMP:
     188           52 :       return LBP_AMP;
     189              : 
     190              :     // binary ^ operator
     191           77 :     case CARET:
     192           77 :       return LBP_CARET;
     193              : 
     194              :     // binary | operator
     195           27 :     case PIPE:
     196           27 :       return LBP_PIPE;
     197              : 
     198              :     case EQUAL_EQUAL:
     199              :       return LBP_EQUAL;
     200              :     case NOT_EQUAL:
     201              :       return LBP_NOT_EQUAL;
     202              :     case RIGHT_ANGLE:
     203              :       return LBP_GREATER_THAN;
     204              :     case GREATER_OR_EQUAL:
     205              :       return LBP_GREATER_EQUAL;
     206              :     case LEFT_ANGLE:
     207              :       return LBP_SMALLER_THAN;
     208              :     case LESS_OR_EQUAL:
     209              :       return LBP_SMALLER_EQUAL;
     210              : 
     211          659 :     case LOGICAL_AND:
     212          659 :       return LBP_LOGICAL_AND;
     213              : 
     214           91 :     case OR:
     215           91 :       return LBP_LOGICAL_OR;
     216              : 
     217              :     case DOT_DOT:
     218              :       return LBP_DOT_DOT;
     219              : 
     220              :     case DOT_DOT_EQ:
     221              :       return LBP_DOT_DOT_EQ;
     222              : 
     223              :     case EQUAL:
     224              :       return LBP_ASSIG;
     225              :     case PLUS_EQ:
     226              :       return LBP_PLUS_ASSIG;
     227              :     case MINUS_EQ:
     228              :       return LBP_MINUS_ASSIG;
     229              :     case ASTERISK_EQ:
     230              :       return LBP_MULT_ASSIG;
     231              :     case DIV_EQ:
     232              :       return LBP_DIV_ASSIG;
     233              :     case PERCENT_EQ:
     234              :       return LBP_MOD_ASSIG;
     235              :     case AMP_EQ:
     236              :       return LBP_AMP_ASSIG;
     237              :     case PIPE_EQ:
     238              :       return LBP_PIPE_ASSIG;
     239              :     case CARET_EQ:
     240              :       return LBP_CARET_ASSIG;
     241              :     case LEFT_SHIFT_EQ:
     242              :       return LBP_L_SHIFT_ASSIG;
     243              :     case RIGHT_SHIFT_EQ:
     244              :       return LBP_R_SHIFT_ASSIG;
     245              : 
     246              :     /* HACK: float literal due to lexer misidentifying a dot then an integer as
     247              :      * a float */
     248              :     case FLOAT_LITERAL:
     249              :       return LBP_FIELD_EXPR;
     250              :       // field expr is same as tuple expr in precedence, i imagine
     251              :       // TODO: is this needed anymore? lexer shouldn't do that anymore
     252              : 
     253              :     // anything that can't appear in an infix position is given lowest priority
     254        77881 :     default:
     255        77881 :       return LBP_LOWEST;
     256              :     }
     257              : }
     258              : 
     259              : // Returns true when current token is EOF.
     260              : template <typename ManagedTokenSource>
     261              : bool
     262            0 : Parser<ManagedTokenSource>::done_end_of_file ()
     263              : {
     264            0 :   return lexer.peek_token ()->get_id () == END_OF_FILE;
     265              : }
     266              : 
     267              : // Parses a sequence of items within a module or the implicit top-level module
     268              : // in a crate
     269              : template <typename ManagedTokenSource>
     270              : tl::expected<std::vector<std::unique_ptr<AST::Item>>, Parse::Error::Items>
     271         4893 : Parser<ManagedTokenSource>::parse_items ()
     272              : {
     273         4893 :   std::vector<std::unique_ptr<AST::Item>> items;
     274              : 
     275         4893 :   const_TokenPtr t = lexer.peek_token ();
     276        24014 :   while (t->get_id () != END_OF_FILE)
     277              :     {
     278        19121 :       auto item = parse_item (false);
     279        19121 :       if (!item)
     280           82 :         return Parse::Error::Items::make_malformed (std::move (items));
     281              : 
     282        19039 :       items.push_back (std::move (item.value ()));
     283              : 
     284        19039 :       t = lexer.peek_token ();
     285              :     }
     286              : 
     287              :     // GCC 5->7 bug doesn't threat lvalue as an rvalue for the overload
     288              : #if __GNUC__ <= 7
     289              :   return std::move (items);
     290              : #else
     291         4811 :   return items;
     292              : #endif
     293         4893 : }
     294              : 
     295              : // Parses a crate (compilation unit) - entry point
     296              : template <typename ManagedTokenSource>
     297              : std::unique_ptr<AST::Crate>
     298         4840 : Parser<ManagedTokenSource>::parse_crate ()
     299              : {
     300              :   // parse inner attributes
     301         4840 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
     302              : 
     303              :   // parse items
     304         4840 :   auto items
     305         4840 :     = parse_items ().value_or (std::vector<std::unique_ptr<AST::Item>>{});
     306              : 
     307              :   // emit all errors
     308         5015 :   for (const auto &error : error_table)
     309          175 :     error.emit ();
     310              : 
     311              :   return std::unique_ptr<AST::Crate> (
     312         4840 :     new AST::Crate (std::move (items), std::move (inner_attrs)));
     313         4840 : }
     314              : 
     315              : // Parses an identifier/keyword as a Token
     316              : template <typename ManagedTokenSource>
     317              : tl::expected<std::unique_ptr<AST::Token>, Parse::Error::Node>
     318          498 : Parser<ManagedTokenSource>::parse_identifier_or_keyword_token ()
     319              : {
     320          498 :   const_TokenPtr t = lexer.peek_token ();
     321              : 
     322          498 :   if (t->get_id () == IDENTIFIER || token_id_is_keyword (t->get_id ()))
     323              :     {
     324          496 :       lexer.skip_token ();
     325          496 :       return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
     326              :     }
     327              :   else
     328              :     {
     329            2 :       add_error (Error (t->get_locus (), "expected keyword or identifier"));
     330            2 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     331              :     }
     332          498 : }
     333              : 
     334              : template <typename ManagedTokenSource>
     335              : bool
     336         1511 : Parser<ManagedTokenSource>::is_macro_rules_def (const_TokenPtr t)
     337              : {
     338         1511 :   auto macro_name = lexer.peek_token (2)->get_id ();
     339              : 
     340         1511 :   bool allowed_macro_name = (macro_name == IDENTIFIER || macro_name == TRY);
     341              : 
     342         1511 :   return t->get_str () == Values::WeakKeywords::MACRO_RULES
     343         2456 :          && lexer.peek_token (1)->get_id () == EXCLAM && allowed_macro_name;
     344              : }
     345              : 
     346              : // Parses a single item
     347              : template <typename ManagedTokenSource>
     348              : tl::expected<std::unique_ptr<AST::Item>, Parse::Error::Item>
     349        24562 : Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
     350              : {
     351              :   // has a "called_from_statement" parameter for better error message handling
     352              : 
     353              :   // TODO: GCC 5 does not handle implicit return type correctly so we're forced
     354              :   // to specify it almost every time until the baseline GCC gets bumped.
     355              :   // Since this type is quite long and the code is dense we use an alias.
     356              :   //
     357              :   // When support for GCC 5 stops: remove this alias as well as the explicit
     358              :   // ctor calls.
     359              :   using RType = tl::expected<std::unique_ptr<AST::Item>, Parse::Error::Item>;
     360              : 
     361              :   // parse outer attributes for item
     362        24562 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
     363        24562 :   const_TokenPtr t = lexer.peek_token ();
     364              : 
     365        24562 :   switch (t->get_id ())
     366              :     {
     367            7 :     case END_OF_FILE:
     368              :       // not necessarily an error, unless we just read outer
     369              :       // attributes which needs to be attached
     370            7 :       if (!outer_attrs.empty ())
     371              :         {
     372            0 :           Rust::AST::Attribute attr = outer_attrs.back ();
     373            0 :           Error error (attr.get_locus (),
     374              :                        "expected item after outer attribute or doc comment");
     375            0 :           add_error (std::move (error));
     376            0 :         }
     377              :       return Parse::Error::Item::make_end_of_file ();
     378              : 
     379        23193 :     case ASYNC:
     380              :     case PUB:
     381              :     case MOD:
     382              :     case EXTERN_KW:
     383              :     case USE:
     384              :     case FN_KW:
     385              :     case TYPE:
     386              :     case STRUCT_KW:
     387              :     case ENUM_KW:
     388              :     case CONST:
     389              :     case STATIC_KW:
     390              :     case AUTO:
     391              :     case TRAIT:
     392              :     case IMPL:
     393              :     case MACRO:
     394              :     /* TODO: implement union keyword but not really because of
     395              :      * context-dependence crappy hack way to parse a union written below to
     396              :      * separate it from the good code. */
     397              :     // case UNION:
     398              :     case UNSAFE: // maybe - unsafe traits are a thing
     399              :       // if any of these (should be all possible VisItem prefixes), parse a
     400              :       // VisItem
     401              :       {
     402        23193 :         auto vis_item = parse_vis_item (std::move (outer_attrs));
     403        23193 :         if (!vis_item)
     404              :           return Parse::Error::Item::make_malformed ();
     405        23133 :         return RType{std::move (vis_item)};
     406        23193 :       }
     407            3 :     case SUPER:
     408              :     case SELF:
     409              :     case CRATE:
     410              :     case DOLLAR_SIGN:
     411              :       // almost certainly macro invocation semi
     412              :       {
     413            3 :         auto macro_invoc_semi
     414            3 :           = parse_macro_invocation_semi (std::move (outer_attrs));
     415            3 :         if (!macro_invoc_semi)
     416              :           return Parse::Error::Item::make_malformed ();
     417            3 :         return RType{std::move (macro_invoc_semi)};
     418            3 :       }
     419              :     // crappy hack to do union "keyword"
     420         1359 :     case IDENTIFIER:
     421              :       // TODO: ensure std::string and literal comparison works
     422         2659 :       if (t->get_str () == Values::WeakKeywords::UNION
     423         1426 :           && lexer.peek_token (1)->get_id () == IDENTIFIER)
     424              :         {
     425           67 :           auto vis_item = parse_vis_item (std::move (outer_attrs));
     426           67 :           if (!vis_item)
     427              :             return Parse::Error::Item::make_malformed ();
     428           67 :           return RType{std::move (vis_item)};
     429              :           // or should this go straight to parsing union?
     430           67 :         }
     431         2525 :       else if (t->get_str () == Values::WeakKeywords::DEFAULT
     432         1294 :                && lexer.peek_token (1)->get_id () != EXCLAM)
     433              :         {
     434              :           // parse normal functions with `default` qualifier
     435              :           // they will be rejected in ASTValidation pass
     436            1 :           return parse_vis_item (std::move (outer_attrs));
     437              :         }
     438         2582 :       else if (is_macro_rules_def (t))
     439              :         {
     440              :           // macro_rules! macro item
     441          941 :           auto macro_rule_def = parse_macro_rules_def (std::move (outer_attrs));
     442          941 :           if (!macro_rule_def)
     443              :             return Parse::Error::Item::make_malformed ();
     444          928 :           return RType{std::move (macro_rule_def)};
     445          941 :         }
     446          700 :       else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
     447          699 :                || lexer.peek_token (1)->get_id () == EXCLAM)
     448              :         {
     449              :           /* path (probably) or macro invocation, so probably a macro invocation
     450              :            * semi */
     451          347 :           auto macro_invocation_semi
     452          347 :             = parse_macro_invocation_semi (std::move (outer_attrs));
     453          347 :           if (!macro_invocation_semi)
     454              :             return Parse::Error::Item::make_malformed ();
     455          346 :           return RType{std::move (macro_invocation_semi)};
     456          347 :         }
     457              :       gcc_fallthrough ();
     458              :     default:
     459              :       // otherwise unrecognised
     460            6 :       add_error (Error (t->get_locus (),
     461              :                         "unrecognised token %qs for start of %s",
     462              :                         t->get_token_description (),
     463              :                         called_from_statement ? "statement" : "item"));
     464              : 
     465              :       // skip somewhere?
     466              :       return Parse::Error::Item::make_malformed ();
     467              :       break;
     468              :     }
     469        24562 : }
     470              : 
     471              : // Parses a VisItem (item that can have non-default visibility).
     472              : template <typename ManagedTokenSource>
     473              : std::unique_ptr<AST::VisItem>
     474        23812 : Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs)
     475              : {
     476              :   // parse visibility, which may or may not exist
     477        23812 :   auto vis_res = parse_visibility ();
     478        23812 :   if (!vis_res)
     479            0 :     return nullptr;
     480        23812 :   auto vis = vis_res.value ();
     481              : 
     482              :   // select VisItem to create depending on keyword
     483        23812 :   const_TokenPtr t = lexer.peek_token ();
     484              : 
     485        23812 :   switch (t->get_id ())
     486              :     {
     487         1372 :     case MOD:
     488         1372 :       return parse_module (std::move (vis), std::move (outer_attrs));
     489         1658 :     case EXTERN_KW:
     490              :       // lookahead to resolve syntactical production
     491         1658 :       t = lexer.peek_token (1);
     492              : 
     493         1658 :       switch (t->get_id ())
     494              :         {
     495           27 :         case CRATE:
     496           27 :           return parse_extern_crate (std::move (vis), std::move (outer_attrs));
     497            0 :         case FN_KW: // extern function
     498            0 :           return parse_function (std::move (vis), std::move (outer_attrs));
     499            0 :         case LEFT_CURLY: // extern block
     500            0 :           return parse_extern_block (std::move (vis), std::move (outer_attrs));
     501         1631 :         case STRING_LITERAL: // for specifying extern ABI
     502              :           // could be extern block or extern function, so more lookahead
     503         1631 :           t = lexer.peek_token (2);
     504              : 
     505         1631 :           switch (t->get_id ())
     506              :             {
     507            4 :             case FN_KW:
     508            4 :               return parse_function (std::move (vis), std::move (outer_attrs));
     509         1627 :             case LEFT_CURLY:
     510         1627 :               return parse_extern_block (std::move (vis),
     511         1627 :                                          std::move (outer_attrs));
     512            0 :             default:
     513            0 :               add_error (
     514            0 :                 Error (t->get_locus (),
     515              :                        "unexpected token %qs in some sort of extern production",
     516              :                        t->get_token_description ()));
     517              : 
     518            0 :               lexer.skip_token (2); // TODO: is this right thing to do?
     519            0 :               return nullptr;
     520              :             }
     521            0 :         default:
     522            0 :           add_error (
     523            0 :             Error (t->get_locus (),
     524              :                    "unexpected token %qs in some sort of extern production",
     525              :                    t->get_token_description ()));
     526              : 
     527            0 :           lexer.skip_token (1); // TODO: is this right thing to do?
     528            0 :           return nullptr;
     529              :         }
     530          685 :     case USE:
     531          685 :       return parse_use_decl (std::move (vis), std::move (outer_attrs));
     532         6546 :     case FN_KW:
     533         6546 :       return parse_function (std::move (vis), std::move (outer_attrs));
     534           62 :     case TYPE:
     535           62 :       return parse_type_alias (std::move (vis), std::move (outer_attrs));
     536         2704 :     case STRUCT_KW:
     537         2704 :       return parse_struct (std::move (vis), std::move (outer_attrs));
     538          558 :     case ENUM_KW:
     539          558 :       return parse_enum (std::move (vis), std::move (outer_attrs));
     540              :     // TODO: implement union keyword but not really because of
     541              :     // context-dependence case UNION: crappy hack to do union "keyword"
     542          112 :     case IDENTIFIER:
     543          224 :       if (t->get_str () == Values::WeakKeywords::UNION
     544          223 :           && lexer.peek_token (1)->get_id () == IDENTIFIER)
     545              :         {
     546          111 :           return parse_union (std::move (vis), std::move (outer_attrs));
     547              :           // or should item switch go straight to parsing union?
     548              :         }
     549            1 :       else if (t->get_str () == Values::WeakKeywords::DEFAULT)
     550              :         {
     551              :           // parse normal functions with `default` qualifier they will be
     552              :           // rejected in ASTValidation pass
     553            1 :           return parse_function (std::move (vis), std::move (outer_attrs));
     554              :         }
     555              :       break;
     556          565 :     case CONST:
     557              :       // lookahead to resolve syntactical production
     558          565 :       t = lexer.peek_token (1);
     559              : 
     560          565 :       switch (t->get_id ())
     561              :         {
     562          479 :         case IDENTIFIER:
     563              :         case UNDERSCORE:
     564          479 :           return parse_const_item (std::move (vis), std::move (outer_attrs));
     565            1 :         case ASYNC:
     566            1 :           return parse_async_item (std::move (vis), std::move (outer_attrs));
     567           85 :         case UNSAFE:
     568              :         case EXTERN_KW:
     569              :         case FN_KW:
     570           85 :           return parse_function (std::move (vis), std::move (outer_attrs));
     571            0 :         default:
     572            0 :           add_error (
     573            0 :             Error (t->get_locus (),
     574              :                    "unexpected token %qs in some sort of const production",
     575              :                    t->get_token_description ()));
     576              : 
     577            0 :           lexer.skip_token (1); // TODO: is this right thing to do?
     578            0 :           return nullptr;
     579              :         }
     580              :     // for async functions
     581            6 :     case ASYNC:
     582            6 :       return parse_async_item (std::move (vis), std::move (outer_attrs));
     583              : 
     584           68 :     case STATIC_KW:
     585           68 :       return parse_static_item (std::move (vis), std::move (outer_attrs));
     586         3874 :     case AUTO:
     587              :     case TRAIT:
     588         3874 :       return parse_trait (std::move (vis), std::move (outer_attrs));
     589         5236 :     case IMPL:
     590         5236 :       return parse_impl (std::move (vis), std::move (outer_attrs));
     591          320 :     case UNSAFE: // unsafe traits, unsafe functions, unsafe impls (trait impls),
     592              :       // lookahead to resolve syntactical production
     593          320 :       t = lexer.peek_token (1);
     594              : 
     595          320 :       switch (t->get_id ())
     596              :         {
     597           56 :         case AUTO:
     598              :         case TRAIT:
     599           56 :           return parse_trait (std::move (vis), std::move (outer_attrs));
     600          195 :         case EXTERN_KW:
     601              :         case FN_KW:
     602          195 :           return parse_function (std::move (vis), std::move (outer_attrs));
     603           67 :         case IMPL:
     604           67 :           return parse_impl (std::move (vis), std::move (outer_attrs));
     605            1 :         case MOD:
     606            1 :           return parse_module (std::move (vis), std::move (outer_attrs));
     607            1 :         default:
     608            1 :           add_error (
     609            1 :             Error (t->get_locus (),
     610              :                    "unexpected token %qs in some sort of unsafe production",
     611              :                    t->get_token_description ()));
     612              : 
     613            1 :           lexer.skip_token (1); // TODO: is this right thing to do?
     614            1 :           return nullptr;
     615              :         }
     616           45 :     case MACRO:
     617           45 :       return parse_decl_macro_def (std::move (vis), std::move (outer_attrs));
     618              :     default:
     619              :       // otherwise vis item clearly doesn't exist, which is not an error
     620              :       // has a catch-all post-switch return to allow other breaks to occur
     621              :       break;
     622              :     }
     623            1 :   return nullptr;
     624        23812 : }
     625              : 
     626              : template <typename ManagedTokenSource>
     627              : std::unique_ptr<AST::Function>
     628            8 : Parser<ManagedTokenSource>::parse_async_item (AST::Visibility vis,
     629              :                                               AST::AttrVec outer_attrs)
     630              : {
     631            8 :   auto offset = (lexer.peek_token ()->get_id () == CONST) ? 1 : 0;
     632            8 :   const_TokenPtr t = lexer.peek_token (offset);
     633              : 
     634            8 :   if (get_rust_edition () == Edition::E2015)
     635              :     {
     636            1 :       add_error (Error (t->get_locus (), ErrorCode::E0670,
     637              :                         "%<async fn%> is not permitted in Rust 2015"));
     638            1 :       add_error (
     639            2 :         Error::Hint (t->get_locus (),
     640              :                      "to use %<async fn%>, switch to Rust 2018 or later"));
     641              :     }
     642              : 
     643            8 :   t = lexer.peek_token (offset + 1);
     644              : 
     645            8 :   switch (t->get_id ())
     646              :     {
     647            8 :     case UNSAFE:
     648              :     case FN_KW:
     649            8 :       return parse_function (std::move (vis), std::move (outer_attrs));
     650              : 
     651            0 :     default:
     652            0 :       add_error (
     653            0 :         Error (t->get_locus (), "expected item, found keyword %<async%>"));
     654              : 
     655            0 :       lexer.skip_token (1);
     656            0 :       return nullptr;
     657              :     }
     658            8 : }
     659              : 
     660              : // Parses a macro rules definition syntax extension whatever thing.
     661              : template <typename ManagedTokenSource>
     662              : std::unique_ptr<AST::MacroRulesDefinition>
     663          950 : Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs)
     664              : {
     665              :   // ensure that first token is identifier saying "macro_rules"
     666          950 :   const_TokenPtr t = lexer.peek_token ();
     667          950 :   if (t->get_id () != IDENTIFIER
     668          950 :       || t->get_str () != Values::WeakKeywords::MACRO_RULES)
     669              :     {
     670            0 :       Error error (
     671              :         t->get_locus (),
     672              :         "macro rules definition does not start with %<macro_rules%>");
     673            0 :       add_error (std::move (error));
     674              : 
     675              :       // skip after somewhere?
     676            0 :       return nullptr;
     677            0 :     }
     678          950 :   lexer.skip_token ();
     679          950 :   location_t macro_locus = t->get_locus ();
     680              : 
     681          950 :   if (!skip_token (EXCLAM))
     682              :     {
     683              :       // skip after somewhere?
     684            0 :       return nullptr;
     685              :     }
     686              : 
     687              :   // parse macro name
     688          950 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
     689          950 :   if (ident_tok == nullptr)
     690              :     {
     691            1 :       return nullptr;
     692              :     }
     693          949 :   Identifier rule_name{ident_tok};
     694              : 
     695              :   // DEBUG
     696          949 :   rust_debug ("in macro rules def, about to parse parens.");
     697              : 
     698              :   // save delim type to ensure it is reused later
     699          949 :   AST::DelimType delim_type = AST::PARENS;
     700              : 
     701              :   // Map tokens to DelimType
     702          949 :   t = lexer.peek_token ();
     703          949 :   switch (t->get_id ())
     704              :     {
     705              :     case LEFT_PAREN:
     706              :       delim_type = AST::PARENS;
     707              :       break;
     708            0 :     case LEFT_SQUARE:
     709            0 :       delim_type = AST::SQUARE;
     710            0 :       break;
     711          947 :     case LEFT_CURLY:
     712          947 :       delim_type = AST::CURLY;
     713          947 :       break;
     714            0 :     default:
     715            0 :       add_error (Error (t->get_locus (),
     716              :                         "unexpected token %qs - expecting delimiters (for a "
     717              :                         "macro rules definition)",
     718              :                         t->get_token_description ()));
     719              : 
     720            0 :       return nullptr;
     721              :     }
     722          949 :   lexer.skip_token ();
     723              : 
     724              :   // parse actual macro rules
     725          949 :   std::vector<AST::MacroRule> macro_rules;
     726              : 
     727              :   // must be at least one macro rule, so parse it
     728          949 :   AST::MacroRule initial_rule = parse_macro_rule ();
     729          949 :   if (initial_rule.is_error ())
     730              :     {
     731           12 :       Error error (lexer.peek_token ()->get_locus (),
     732              :                    "required first macro rule in macro rules definition "
     733              :                    "could not be parsed");
     734           12 :       add_error (std::move (error));
     735              : 
     736              :       // skip after somewhere?
     737           12 :       return nullptr;
     738           12 :     }
     739          937 :   macro_rules.push_back (std::move (initial_rule));
     740              : 
     741              :   // DEBUG
     742          937 :   rust_debug ("successfully pushed back initial macro rule");
     743              : 
     744          937 :   t = lexer.peek_token ();
     745              :   // parse macro rules
     746         1065 :   while (t->get_id () == SEMICOLON)
     747              :     {
     748              :       // skip semicolon
     749          640 :       lexer.skip_token ();
     750              : 
     751              :       // don't parse if end of macro rules
     752         1280 :       if (Parse::Utils::token_id_matches_delims (lexer.peek_token ()->get_id (),
     753              :                                                  delim_type))
     754              :         {
     755              :           // DEBUG
     756          512 :           rust_debug (
     757              :             "broke out of parsing macro rules loop due to finding delim");
     758              : 
     759          512 :           break;
     760              :         }
     761              : 
     762              :       // try to parse next rule
     763          128 :       AST::MacroRule rule = parse_macro_rule ();
     764          128 :       if (rule.is_error ())
     765              :         {
     766            0 :           Error error (lexer.peek_token ()->get_locus (),
     767              :                        "failed to parse macro rule in macro rules definition");
     768            0 :           add_error (std::move (error));
     769              : 
     770            0 :           return nullptr;
     771            0 :         }
     772              : 
     773          128 :       macro_rules.push_back (std::move (rule));
     774              : 
     775              :       // DEBUG
     776          128 :       rust_debug ("successfully pushed back another macro rule");
     777              : 
     778          128 :       t = lexer.peek_token ();
     779              :     }
     780              : 
     781              :   // parse end delimiters
     782          937 :   t = lexer.peek_token ();
     783          937 :   if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
     784              :     {
     785              :       // tokens match opening delimiter, so skip.
     786          937 :       lexer.skip_token ();
     787              : 
     788          937 :       if (delim_type != AST::CURLY)
     789              :         {
     790              :           // skip semicolon at end of non-curly macro definitions
     791            2 :           if (!skip_token (SEMICOLON))
     792              :             {
     793              :               // as this is the end, allow recovery (probably) - may change
     794              :               return std::unique_ptr<AST::MacroRulesDefinition> (
     795            0 :                 AST::MacroRulesDefinition::mbe (
     796              :                   std::move (rule_name), delim_type, std::move (macro_rules),
     797            0 :                   std::move (outer_attrs), macro_locus));
     798              :             }
     799              :         }
     800              : 
     801              :       return std::unique_ptr<AST::MacroRulesDefinition> (
     802         1874 :         AST::MacroRulesDefinition::mbe (std::move (rule_name), delim_type,
     803              :                                         std::move (macro_rules),
     804          937 :                                         std::move (outer_attrs), macro_locus));
     805              :     }
     806              :   else
     807              :     {
     808              :       // tokens don't match opening delimiters, so produce error
     809            0 :       Error error (t->get_locus (),
     810              :                    "unexpected token %qs - expecting closing delimiter %qs "
     811              :                    "(for a macro rules definition)",
     812              :                    t->get_token_description (),
     813              :                    (delim_type == AST::PARENS
     814              :                       ? ")"
     815              :                       : (delim_type == AST::SQUARE ? "]" : "}")));
     816            0 :       add_error (std::move (error));
     817              : 
     818              :       /* return empty macro definiton despite possibly parsing mostly valid one
     819              :        * - TODO is this a good idea? */
     820            0 :       return nullptr;
     821            0 :     }
     822         2848 : }
     823              : 
     824              : // Parses a declarative macro 2.0 definition.
     825              : template <typename ManagedTokenSource>
     826              : std::unique_ptr<AST::MacroRulesDefinition>
     827           45 : Parser<ManagedTokenSource>::parse_decl_macro_def (AST::Visibility vis,
     828              :                                                   AST::AttrVec outer_attrs)
     829              : {
     830              :   // ensure that first token is identifier saying "macro"
     831           45 :   const_TokenPtr t = lexer.peek_token ();
     832           45 :   if (t->get_id () != MACRO)
     833              :     {
     834            0 :       Error error (
     835              :         t->get_locus (),
     836              :         "declarative macro definition does not start with %<macro%>");
     837            0 :       add_error (std::move (error));
     838              : 
     839              :       // skip after somewhere?
     840            0 :       return nullptr;
     841            0 :     }
     842           45 :   lexer.skip_token ();
     843           45 :   location_t macro_locus = t->get_locus ();
     844              : 
     845              :   // parse macro name
     846           45 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
     847           45 :   if (ident_tok == nullptr)
     848              :     {
     849            0 :       return nullptr;
     850              :     }
     851           45 :   Identifier rule_name{ident_tok};
     852              : 
     853           45 :   t = lexer.peek_token ();
     854           45 :   if (t->get_id () == LEFT_PAREN)
     855              :     {
     856              :       // single definiton of macro rule
     857              :       // e.g. `macro foo($e:expr) {}`
     858              : 
     859              :       // parse macro matcher
     860           20 :       location_t locus = lexer.peek_token ()->get_locus ();
     861           20 :       AST::MacroMatcher matcher = parse_macro_matcher ();
     862           20 :       if (matcher.is_error ())
     863            0 :         return nullptr;
     864              : 
     865              :       // check delimiter of macro matcher
     866           20 :       if (matcher.get_delim_type () != AST::DelimType::PARENS)
     867              :         {
     868            0 :           Error error (locus, "only parenthesis can be used for a macro "
     869              :                               "matcher in declarative macro definition");
     870            0 :           add_error (std::move (error));
     871            0 :           return nullptr;
     872            0 :         }
     873              : 
     874           20 :       location_t transcriber_loc = lexer.peek_token ()->get_locus ();
     875           20 :       auto delim_tok_tree = parse_delim_token_tree ();
     876           20 :       if (!delim_tok_tree)
     877            0 :         return nullptr;
     878              : 
     879           20 :       AST::MacroTranscriber transcriber (delim_tok_tree.value (),
     880              :                                          transcriber_loc);
     881              : 
     882           20 :       if (transcriber.get_token_tree ().get_delim_type ()
     883              :           != AST::DelimType::CURLY)
     884              :         {
     885            1 :           Error error (transcriber_loc,
     886              :                        "only braces can be used for a macro transcriber "
     887              :                        "in declarative macro definition");
     888            1 :           add_error (std::move (error));
     889            1 :           return nullptr;
     890            1 :         }
     891              : 
     892           19 :       std::vector<AST::MacroRule> macro_rules;
     893           19 :       macro_rules.emplace_back (std::move (matcher), std::move (transcriber),
     894              :                                 locus);
     895              : 
     896              :       return std::unique_ptr<AST::MacroRulesDefinition> (
     897           38 :         AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
     898              :                                                macro_rules,
     899              :                                                std::move (outer_attrs),
     900           19 :                                                macro_locus, vis));
     901           59 :     }
     902           25 :   else if (t->get_id () == LEFT_CURLY)
     903              :     {
     904              :       // multiple definitions of macro rule separated by comma
     905              :       // e.g. `macro foo { () => {}, ($e:expr) => {}, }`
     906              : 
     907              :       // parse left curly
     908           25 :       const_TokenPtr left_curly = expect_token (LEFT_CURLY);
     909           25 :       if (left_curly == nullptr)
     910              :         {
     911            0 :           return nullptr;
     912              :         }
     913              : 
     914              :       // parse actual macro rules
     915           25 :       std::vector<AST::MacroRule> macro_rules;
     916              : 
     917              :       // must be at least one macro rule, so parse it
     918           25 :       AST::MacroRule initial_rule = parse_macro_rule ();
     919           25 :       if (initial_rule.is_error ())
     920              :         {
     921            1 :           Error error (
     922            1 :             lexer.peek_token ()->get_locus (),
     923              :             "required first macro rule in declarative macro definition "
     924              :             "could not be parsed");
     925            1 :           add_error (std::move (error));
     926              : 
     927              :           // skip after somewhere?
     928            1 :           return nullptr;
     929            1 :         }
     930           24 :       macro_rules.push_back (std::move (initial_rule));
     931              : 
     932           24 :       t = lexer.peek_token ();
     933              :       // parse macro rules
     934           40 :       while (t->get_id () == COMMA)
     935              :         {
     936              :           // skip comma
     937           32 :           lexer.skip_token ();
     938              : 
     939              :           // don't parse if end of macro rules
     940           32 :           if (Parse::Utils::token_id_matches_delims (
     941           64 :                 lexer.peek_token ()->get_id (), AST::CURLY))
     942              :             {
     943              :               break;
     944              :             }
     945              : 
     946              :           // try to parse next rule
     947           16 :           AST::MacroRule rule = parse_macro_rule ();
     948           16 :           if (rule.is_error ())
     949              :             {
     950            0 :               Error error (
     951            0 :                 lexer.peek_token ()->get_locus (),
     952              :                 "failed to parse macro rule in declarative macro definition");
     953            0 :               add_error (std::move (error));
     954              : 
     955            0 :               return nullptr;
     956            0 :             }
     957              : 
     958           16 :           macro_rules.push_back (std::move (rule));
     959              : 
     960           16 :           t = lexer.peek_token ();
     961              :         }
     962              : 
     963              :       // parse right curly
     964           24 :       const_TokenPtr right_curly = expect_token (RIGHT_CURLY);
     965           24 :       if (right_curly == nullptr)
     966              :         {
     967            0 :           return nullptr;
     968              :         }
     969              : 
     970              :       return std::unique_ptr<AST::MacroRulesDefinition> (
     971           48 :         AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
     972              :                                                std::move (macro_rules),
     973              :                                                std::move (outer_attrs),
     974           24 :                                                macro_locus, vis));
     975           50 :     }
     976              :   else
     977              :     {
     978            0 :       add_error (Error (t->get_locus (),
     979              :                         "unexpected token %qs - expecting delimiters "
     980              :                         "(for a declarative macro definiton)",
     981              :                         t->get_token_description ()));
     982            0 :       return nullptr;
     983              :     }
     984           90 : }
     985              : 
     986              : /* Parses a visibility syntactical production (i.e. creating a non-default
     987              :  * visibility) */
     988              : template <typename ManagedTokenSource>
     989              : tl::expected<AST::Visibility, Parse::Error::Visibility>
     990        42848 : Parser<ManagedTokenSource>::parse_visibility ()
     991              : {
     992              :   // check for no visibility
     993        85696 :   if (lexer.peek_token ()->get_id () != PUB)
     994              :     {
     995        34080 :       return AST::Visibility::create_private ();
     996              :     }
     997              : 
     998         8768 :   auto vis_loc = lexer.peek_token ()->get_locus ();
     999         8768 :   lexer.skip_token ();
    1000              : 
    1001              :   // create simple pub visibility if
    1002              :   // - found no parentheses
    1003              :   // - found unit type `()`
    1004        17536 :   if (lexer.peek_token ()->get_id () != LEFT_PAREN
    1005         8835 :       || lexer.peek_token (1)->get_id () == RIGHT_PAREN)
    1006              :     {
    1007         8702 :       return AST::Visibility::create_public (vis_loc);
    1008              :       // or whatever
    1009              :     }
    1010              : 
    1011           66 :   lexer.skip_token ();
    1012              : 
    1013           66 :   const_TokenPtr t = lexer.peek_token ();
    1014           66 :   auto path_loc = t->get_locus ();
    1015              : 
    1016           66 :   switch (t->get_id ())
    1017              :     {
    1018           53 :     case CRATE:
    1019           53 :       lexer.skip_token ();
    1020              : 
    1021           53 :       skip_token (RIGHT_PAREN);
    1022              : 
    1023           53 :       return AST::Visibility::create_crate (path_loc, vis_loc);
    1024            0 :     case SELF:
    1025            0 :       lexer.skip_token ();
    1026              : 
    1027            0 :       skip_token (RIGHT_PAREN);
    1028              : 
    1029            0 :       return AST::Visibility::create_self (path_loc, vis_loc);
    1030            1 :     case SUPER:
    1031            1 :       lexer.skip_token ();
    1032              : 
    1033            1 :       skip_token (RIGHT_PAREN);
    1034              : 
    1035            1 :       return AST::Visibility::create_super (path_loc, vis_loc);
    1036           12 :     case IN:
    1037              :       {
    1038           12 :         lexer.skip_token ();
    1039              : 
    1040              :         // parse the "in" path as well
    1041           12 :         auto path = parse_simple_path ();
    1042           12 :         if (!path)
    1043              :           {
    1044            0 :             Error error (lexer.peek_token ()->get_locus (),
    1045              :                          "missing path in pub(in path) visibility");
    1046            0 :             add_error (std::move (error));
    1047              : 
    1048              :             // skip after somewhere?
    1049            0 :             return Parse::Error::Visibility::make_missing_path ();
    1050            0 :           }
    1051              : 
    1052           12 :         skip_token (RIGHT_PAREN);
    1053              : 
    1054           24 :         return AST::Visibility::create_in_path (std::move (path.value ()),
    1055           12 :                                                 vis_loc);
    1056           12 :       }
    1057            0 :     default:
    1058            0 :       add_error (Error (t->get_locus (), "unexpected token %qs in visibility",
    1059              :                         t->get_token_description ()));
    1060              : 
    1061            0 :       lexer.skip_token ();
    1062              :       return Parse::Error::Visibility::make_malformed ();
    1063              :     }
    1064           66 : }
    1065              : 
    1066              : // Parses a module - either a bodied module or a module defined in another file.
    1067              : template <typename ManagedTokenSource>
    1068              : std::unique_ptr<AST::Module>
    1069         1373 : Parser<ManagedTokenSource>::parse_module (AST::Visibility vis,
    1070              :                                           AST::AttrVec outer_attrs)
    1071              : {
    1072         1373 :   location_t locus = lexer.peek_token ()->get_locus ();
    1073              : 
    1074         1373 :   Unsafety safety = Unsafety::Normal;
    1075         2746 :   if (lexer.peek_token ()->get_id () == UNSAFE)
    1076              :     {
    1077            1 :       safety = Unsafety::Unsafe;
    1078            1 :       skip_token (UNSAFE);
    1079              :     }
    1080              : 
    1081         1373 :   skip_token (MOD);
    1082              : 
    1083         1373 :   const_TokenPtr module_name = expect_token (IDENTIFIER);
    1084         1373 :   if (module_name == nullptr)
    1085              :     {
    1086            0 :       return nullptr;
    1087              :     }
    1088         1373 :   Identifier name{module_name};
    1089              : 
    1090         1373 :   const_TokenPtr t = lexer.peek_token ();
    1091              : 
    1092         1373 :   switch (t->get_id ())
    1093              :     {
    1094          138 :     case SEMICOLON:
    1095          138 :       lexer.skip_token ();
    1096              : 
    1097              :       // Construct an external module
    1098              :       return std::unique_ptr<AST::Module> (
    1099          414 :         new AST::Module (std::move (name), std::move (vis),
    1100              :                          std::move (outer_attrs), locus, safety,
    1101          414 :                          lexer.get_filename (), inline_module_stack));
    1102         1235 :     case LEFT_CURLY:
    1103              :       {
    1104         1235 :         lexer.skip_token ();
    1105              : 
    1106              :         // parse inner attributes
    1107         1235 :         AST::AttrVec inner_attrs = parse_inner_attributes ();
    1108              : 
    1109         1235 :         std::string default_path = name.as_string ();
    1110              : 
    1111         1235 :         if (inline_module_stack.empty ())
    1112              :           {
    1113          706 :             std::string filename = lexer.get_filename ();
    1114          706 :             auto slash_idx = filename.rfind (file_separator);
    1115          706 :             if (slash_idx == std::string::npos)
    1116              :               slash_idx = 0;
    1117              :             else
    1118          706 :               slash_idx++;
    1119          706 :             filename = filename.substr (slash_idx);
    1120              : 
    1121          706 :             std::string subdir;
    1122          706 :             if (get_file_subdir (filename, subdir))
    1123          706 :               default_path = subdir + file_separator + name.as_string ();
    1124          706 :           }
    1125              : 
    1126         1235 :         std::string module_path_name
    1127              :           = extract_module_path (inner_attrs, outer_attrs, default_path);
    1128         1235 :         InlineModuleStackScope scope (*this, std::move (module_path_name));
    1129              : 
    1130              :         // parse items
    1131         1235 :         std::vector<std::unique_ptr<AST::Item>> items;
    1132         1235 :         const_TokenPtr tok = lexer.peek_token ();
    1133         4085 :         while (tok->get_id () != RIGHT_CURLY)
    1134              :           {
    1135         2850 :             auto item = parse_item (false);
    1136         2850 :             if (!item)
    1137              :               {
    1138            1 :                 Error error (tok->get_locus (),
    1139              :                              "failed to parse item in module");
    1140            1 :                 add_error (std::move (error));
    1141              : 
    1142            1 :                 return nullptr;
    1143            1 :               }
    1144              : 
    1145         2849 :             items.push_back (std::move (item.value ()));
    1146              : 
    1147         2849 :             tok = lexer.peek_token ();
    1148              :           }
    1149              : 
    1150         1234 :         if (!skip_token (RIGHT_CURLY))
    1151              :           {
    1152              :             // skip somewhere?
    1153            0 :             return nullptr;
    1154              :           }
    1155              : 
    1156              :         return std::unique_ptr<AST::Module> (
    1157         1234 :           new AST::Module (std::move (name), locus, std::move (items),
    1158              :                            std::move (vis), safety, std::move (inner_attrs),
    1159         1234 :                            std::move (outer_attrs))); // module name?
    1160         1235 :       }
    1161            0 :     default:
    1162            0 :       add_error (
    1163            0 :         Error (t->get_locus (),
    1164              :                "unexpected token %qs in module declaration/definition item",
    1165              :                t->get_token_description ()));
    1166              : 
    1167            0 :       lexer.skip_token ();
    1168            0 :       return nullptr;
    1169              :     }
    1170         1373 : }
    1171              : 
    1172              : // Parses an extern crate declaration (dependency on external crate)
    1173              : template <typename ManagedTokenSource>
    1174              : std::unique_ptr<AST::ExternCrate>
    1175           27 : Parser<ManagedTokenSource>::parse_extern_crate (AST::Visibility vis,
    1176              :                                                 AST::AttrVec outer_attrs)
    1177              : {
    1178           27 :   location_t locus = lexer.peek_token ()->get_locus ();
    1179           27 :   if (!skip_token (EXTERN_KW))
    1180              :     {
    1181            0 :       skip_after_semicolon ();
    1182            0 :       return nullptr;
    1183              :     }
    1184              : 
    1185           27 :   if (!skip_token (CRATE))
    1186              :     {
    1187            0 :       skip_after_semicolon ();
    1188            0 :       return nullptr;
    1189              :     }
    1190              : 
    1191              :   /* parse crate reference name - this has its own syntactical rule in reference
    1192              :    * but seems to not be used elsewhere, so i'm putting it here */
    1193           27 :   const_TokenPtr crate_name_tok = lexer.peek_token ();
    1194           27 :   std::string crate_name;
    1195              : 
    1196           27 :   switch (crate_name_tok->get_id ())
    1197              :     {
    1198           27 :     case IDENTIFIER:
    1199           27 :       crate_name = crate_name_tok->get_str ();
    1200           27 :       lexer.skip_token ();
    1201              :       break;
    1202            0 :     case SELF:
    1203            0 :       crate_name = Values::Keywords::SELF;
    1204            0 :       lexer.skip_token ();
    1205              :       break;
    1206            0 :     default:
    1207            0 :       add_error (
    1208            0 :         Error (crate_name_tok->get_locus (),
    1209              :                "expecting crate name (identifier or %<self%>), found %qs",
    1210              :                crate_name_tok->get_token_description ()));
    1211              : 
    1212            0 :       skip_after_semicolon ();
    1213            0 :       return nullptr;
    1214              :     }
    1215              : 
    1216              :   // don't parse as clause if it doesn't exist
    1217           54 :   if (lexer.peek_token ()->get_id () == SEMICOLON)
    1218              :     {
    1219           27 :       lexer.skip_token ();
    1220              : 
    1221              :       return std::unique_ptr<AST::ExternCrate> (
    1222           27 :         new AST::ExternCrate (std::move (crate_name), std::move (vis),
    1223           27 :                               std::move (outer_attrs), locus));
    1224              :     }
    1225              : 
    1226              :   /* parse as clause - this also has its own syntactical rule in reference and
    1227              :    * also seems to not be used elsewhere, so including here again. */
    1228            0 :   if (!skip_token (AS))
    1229              :     {
    1230            0 :       skip_after_semicolon ();
    1231            0 :       return nullptr;
    1232              :     }
    1233              : 
    1234            0 :   const_TokenPtr as_name_tok = lexer.peek_token ();
    1235            0 :   std::string as_name;
    1236              : 
    1237            0 :   switch (as_name_tok->get_id ())
    1238              :     {
    1239            0 :     case IDENTIFIER:
    1240            0 :       as_name = as_name_tok->get_str ();
    1241            0 :       lexer.skip_token ();
    1242              :       break;
    1243            0 :     case UNDERSCORE:
    1244            0 :       as_name = Values::Keywords::UNDERSCORE;
    1245            0 :       lexer.skip_token ();
    1246              :       break;
    1247            0 :     default:
    1248            0 :       add_error (
    1249            0 :         Error (as_name_tok->get_locus (),
    1250              :                "expecting as clause name (identifier or %<_%>), found %qs",
    1251              :                as_name_tok->get_token_description ()));
    1252              : 
    1253            0 :       skip_after_semicolon ();
    1254            0 :       return nullptr;
    1255              :     }
    1256              : 
    1257            0 :   if (!skip_token (SEMICOLON))
    1258              :     {
    1259            0 :       skip_after_semicolon ();
    1260            0 :       return nullptr;
    1261              :     }
    1262              : 
    1263              :   return std::unique_ptr<AST::ExternCrate> (
    1264            0 :     new AST::ExternCrate (std::move (crate_name), std::move (vis),
    1265            0 :                           std::move (outer_attrs), locus, std::move (as_name)));
    1266           54 : }
    1267              : 
    1268              : // Parses a use declaration.
    1269              : template <typename ManagedTokenSource>
    1270              : std::unique_ptr<AST::UseDeclaration>
    1271          685 : Parser<ManagedTokenSource>::parse_use_decl (AST::Visibility vis,
    1272              :                                             AST::AttrVec outer_attrs)
    1273              : {
    1274          685 :   location_t locus = lexer.peek_token ()->get_locus ();
    1275          685 :   if (!skip_token (USE))
    1276              :     {
    1277            0 :       skip_after_semicolon ();
    1278            0 :       return nullptr;
    1279              :     }
    1280              : 
    1281              :   // parse use tree, which is required
    1282          685 :   std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
    1283          685 :   if (use_tree == nullptr)
    1284              :     {
    1285            1 :       Error error (lexer.peek_token ()->get_locus (),
    1286              :                    "could not parse use tree in use declaration");
    1287            1 :       add_error (std::move (error));
    1288              : 
    1289            1 :       skip_after_semicolon ();
    1290            1 :       return nullptr;
    1291            1 :     }
    1292              : 
    1293          684 :   if (!skip_token (SEMICOLON))
    1294              :     {
    1295            0 :       skip_after_semicolon ();
    1296            0 :       return nullptr;
    1297              :     }
    1298              : 
    1299              :   return std::unique_ptr<AST::UseDeclaration> (
    1300          684 :     new AST::UseDeclaration (std::move (use_tree), std::move (vis),
    1301          684 :                              std::move (outer_attrs), locus));
    1302          685 : }
    1303              : 
    1304              : // Parses a use tree (which can be recursive and is actually a base class).
    1305              : template <typename ManagedTokenSource>
    1306              : std::unique_ptr<AST::UseTree>
    1307         1270 : Parser<ManagedTokenSource>::parse_use_tree ()
    1308              : {
    1309              :   /* potential syntax definitions in attempt to get algorithm:
    1310              :    *  Glob:
    1311              :    *      <- SimplePath :: *
    1312              :    *      <- :: *
    1313              :    *      <- *
    1314              :    *  Nested tree thing:
    1315              :    *      <- SimplePath :: { COMPLICATED_INNER_TREE_THING }
    1316              :    *      <- :: COMPLICATED_INNER_TREE_THING }
    1317              :    *      <- { COMPLICATED_INNER_TREE_THING }
    1318              :    *  Rebind thing:
    1319              :    *      <- SimplePath as IDENTIFIER
    1320              :    *      <- SimplePath as _
    1321              :    *      <- SimplePath
    1322              :    */
    1323              : 
    1324              :   /* current plan of attack: try to parse SimplePath first - if fails, one of
    1325              :    * top two then try parse :: - if fails, one of top two. Next is deciding
    1326              :    * character for top two. */
    1327              : 
    1328              :   /* Thus, parsing smaller parts of use tree may require feeding into function
    1329              :    * via parameters (or could handle all in this single function because other
    1330              :    * use tree types aren't recognised as separate in the spec) */
    1331              : 
    1332              :   // TODO: I think this function is too complex, probably should split it
    1333              : 
    1334         1270 :   location_t locus = lexer.peek_token ()->get_locus ();
    1335              : 
    1336              :   // bool has_path = false;
    1337         1270 :   auto path = parse_simple_path ();
    1338              : 
    1339         1270 :   if (!path)
    1340              :     {
    1341              :       // has no path, so must be glob or nested tree UseTree type
    1342              : 
    1343            3 :       bool is_global = false;
    1344              : 
    1345              :       // check for global scope resolution operator
    1346            6 :       if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
    1347              :         {
    1348            1 :           lexer.skip_token ();
    1349            1 :           is_global = true;
    1350              :         }
    1351              : 
    1352            3 :       const_TokenPtr t = lexer.peek_token ();
    1353            3 :       switch (t->get_id ())
    1354              :         {
    1355            1 :         case ASTERISK:
    1356              :           // glob UseTree type
    1357            1 :           lexer.skip_token ();
    1358              : 
    1359            1 :           if (is_global)
    1360            1 :             return std::unique_ptr<AST::UseTreeGlob> (
    1361            2 :               new AST::UseTreeGlob (AST::UseTreeGlob::GLOBAL,
    1362            3 :                                     AST::SimplePath::create_empty (), locus));
    1363              :           else
    1364            0 :             return std::unique_ptr<AST::UseTreeGlob> (
    1365            0 :               new AST::UseTreeGlob (AST::UseTreeGlob::NO_PATH,
    1366            0 :                                     AST::SimplePath::create_empty (), locus));
    1367            2 :         case LEFT_CURLY:
    1368              :           {
    1369              :             // nested tree UseTree type
    1370            2 :             lexer.skip_token ();
    1371              : 
    1372            2 :             std::vector<std::unique_ptr<AST::UseTree>> use_trees;
    1373              : 
    1374            2 :             const_TokenPtr t = lexer.peek_token ();
    1375            5 :             while (t->get_id () != RIGHT_CURLY)
    1376              :               {
    1377            3 :                 std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
    1378            3 :                 if (use_tree == nullptr)
    1379              :                   {
    1380              :                     break;
    1381              :                   }
    1382              : 
    1383            3 :                 use_trees.push_back (std::move (use_tree));
    1384              : 
    1385            6 :                 if (lexer.peek_token ()->get_id () != COMMA)
    1386              :                   break;
    1387              : 
    1388            1 :                 lexer.skip_token ();
    1389            1 :                 t = lexer.peek_token ();
    1390              :               }
    1391              : 
    1392              :             // skip end curly delimiter
    1393            2 :             if (!skip_token (RIGHT_CURLY))
    1394              :               {
    1395              :                 // skip after somewhere?
    1396            0 :                 return nullptr;
    1397              :               }
    1398              : 
    1399            2 :             if (is_global)
    1400            0 :               return std::unique_ptr<AST::UseTreeList> (
    1401            0 :                 new AST::UseTreeList (AST::UseTreeList::GLOBAL,
    1402            0 :                                       AST::SimplePath::create_empty (),
    1403            0 :                                       std::move (use_trees), locus));
    1404              :             else
    1405            2 :               return std::unique_ptr<AST::UseTreeList> (
    1406            4 :                 new AST::UseTreeList (AST::UseTreeList::NO_PATH,
    1407            4 :                                       AST::SimplePath::create_empty (),
    1408            2 :                                       std::move (use_trees), locus));
    1409            2 :           }
    1410            0 :         case AS:
    1411              :           // this is not allowed
    1412            0 :           add_error (Error (
    1413              :             t->get_locus (),
    1414              :             "use declaration with rebind %<as%> requires a valid simple path - "
    1415              :             "none found"));
    1416              : 
    1417            0 :           skip_after_semicolon ();
    1418            0 :           return nullptr;
    1419            0 :         default:
    1420            0 :           add_error (Error (t->get_locus (),
    1421              :                             "unexpected token %qs in use tree with "
    1422              :                             "no valid simple path (i.e. list"
    1423              :                             " or glob use tree)",
    1424              :                             t->get_token_description ()));
    1425              : 
    1426            0 :           skip_after_semicolon ();
    1427            0 :           return nullptr;
    1428              :         }
    1429            3 :     }
    1430              :   else
    1431              :     {
    1432         1267 :       const_TokenPtr t = lexer.peek_token ();
    1433              : 
    1434         1267 :       switch (t->get_id ())
    1435              :         {
    1436            6 :         case AS:
    1437              :           {
    1438              :             // rebind UseTree type
    1439            6 :             lexer.skip_token ();
    1440              : 
    1441            6 :             const_TokenPtr t = lexer.peek_token ();
    1442            6 :             switch (t->get_id ())
    1443              :               {
    1444            4 :               case IDENTIFIER:
    1445              :                 // skip lexer token
    1446            4 :                 lexer.skip_token ();
    1447              : 
    1448            4 :                 return std::unique_ptr<AST::UseTreeRebind> (
    1449           12 :                   new AST::UseTreeRebind (AST::UseTreeRebind::IDENTIFIER,
    1450           12 :                                           std::move (path.value ()), locus, t));
    1451            2 :               case UNDERSCORE:
    1452              :                 // skip lexer token
    1453            2 :                 lexer.skip_token ();
    1454              : 
    1455            2 :                 return std::unique_ptr<AST::UseTreeRebind> (
    1456            6 :                   new AST::UseTreeRebind (AST::UseTreeRebind::WILDCARD,
    1457            2 :                                           std::move (path.value ()), locus,
    1458              :                                           {Values::Keywords::UNDERSCORE,
    1459            2 :                                            t->get_locus ()}));
    1460            0 :               default:
    1461            0 :                 add_error (Error (
    1462              :                   t->get_locus (),
    1463              :                   "unexpected token %qs in use tree with as clause - expected "
    1464              :                   "identifier or %<_%>",
    1465              :                   t->get_token_description ()));
    1466              : 
    1467            0 :                 skip_after_semicolon ();
    1468            0 :                 return nullptr;
    1469              :               }
    1470            6 :           }
    1471         1092 :         case SEMICOLON:
    1472              :           // rebind UseTree type without rebinding - path only
    1473              : 
    1474              :           // don't skip semicolon - handled in parse_use_tree
    1475              :           // lexer.skip_token();
    1476              :         case COMMA:
    1477              :         case RIGHT_CURLY:
    1478              :           // this may occur in recursive calls - assume it is ok and ignore it
    1479         1092 :           return std::unique_ptr<AST::UseTreeRebind> (
    1480         3276 :             new AST::UseTreeRebind (AST::UseTreeRebind::NONE,
    1481         1092 :                                     std::move (path.value ()), locus));
    1482              :         case SCOPE_RESOLUTION:
    1483              :           // keep going
    1484              :           break;
    1485            1 :         default:
    1486            1 :           add_error (Error (t->get_locus (),
    1487              :                             "unexpected token %qs in use tree with valid path",
    1488              :                             t->get_token_description ()));
    1489            1 :           return nullptr;
    1490              :         }
    1491              : 
    1492          168 :       skip_token ();
    1493          168 :       t = lexer.peek_token ();
    1494              : 
    1495          168 :       switch (t->get_id ())
    1496              :         {
    1497           14 :         case ASTERISK:
    1498              :           // glob UseTree type
    1499           14 :           lexer.skip_token ();
    1500              : 
    1501           14 :           return std::unique_ptr<AST::UseTreeGlob> (
    1502           42 :             new AST::UseTreeGlob (AST::UseTreeGlob::PATH_PREFIXED,
    1503           14 :                                   std::move (path.value ()), locus));
    1504          154 :         case LEFT_CURLY:
    1505              :           {
    1506              :             // nested tree UseTree type
    1507          154 :             lexer.skip_token ();
    1508              : 
    1509          154 :             std::vector<std::unique_ptr<AST::UseTree>> use_trees;
    1510              : 
    1511              :             // TODO: think of better control structure
    1512          154 :             const_TokenPtr t = lexer.peek_token ();
    1513          736 :             while (t->get_id () != RIGHT_CURLY)
    1514              :               {
    1515          582 :                 std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
    1516          582 :                 if (use_tree == nullptr)
    1517              :                   {
    1518              :                     break;
    1519              :                   }
    1520              : 
    1521          582 :                 use_trees.push_back (std::move (use_tree));
    1522              : 
    1523         1164 :                 if (lexer.peek_token ()->get_id () != COMMA)
    1524              :                   break;
    1525              : 
    1526          428 :                 lexer.skip_token ();
    1527          428 :                 t = lexer.peek_token ();
    1528              :               }
    1529              : 
    1530              :             // skip end curly delimiter
    1531          154 :             if (!skip_token (RIGHT_CURLY))
    1532              :               {
    1533              :                 // skip after somewhere?
    1534            0 :                 return nullptr;
    1535              :               }
    1536              : 
    1537          154 :             return std::unique_ptr<AST::UseTreeList> (
    1538          308 :               new AST::UseTreeList (AST::UseTreeList::PATH_PREFIXED,
    1539          154 :                                     std::move (path.value ()),
    1540          154 :                                     std::move (use_trees), locus));
    1541          154 :           }
    1542            0 :         default:
    1543            0 :           add_error (Error (t->get_locus (),
    1544              :                             "unexpected token %qs in use tree with valid path",
    1545              :                             t->get_token_description ()));
    1546              : 
    1547              :           // skip_after_semicolon();
    1548            0 :           return nullptr;
    1549              :         }
    1550         1267 :     }
    1551         1270 : }
    1552              : 
    1553              : // Parses a function (not a method).
    1554              : template <typename ManagedTokenSource>
    1555              : std::unique_ptr<AST::Function>
    1556        11925 : Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,
    1557              :                                             AST::AttrVec outer_attrs,
    1558              :                                             bool is_external)
    1559              : {
    1560        11925 :   location_t locus = lexer.peek_token ()->get_locus ();
    1561              :   // Get qualifiers for function if they exist
    1562        11925 :   auto qualifiers = parse_function_qualifiers ();
    1563        11925 :   if (!qualifiers)
    1564            1 :     return nullptr;
    1565              : 
    1566        11924 :   skip_token (FN_KW);
    1567              : 
    1568              :   // Save function name token
    1569        11924 :   const_TokenPtr function_name_tok = expect_token (IDENTIFIER);
    1570        11924 :   if (function_name_tok == nullptr)
    1571              :     {
    1572            0 :       skip_after_next_block ();
    1573            0 :       return nullptr;
    1574              :     }
    1575        11924 :   Identifier function_name{function_name_tok};
    1576              : 
    1577              :   // parse generic params - if exist
    1578        11924 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    1579              :     = parse_generic_params_in_angles ();
    1580              : 
    1581        11924 :   if (!skip_token (LEFT_PAREN))
    1582              :     {
    1583            0 :       Error error (lexer.peek_token ()->get_locus (),
    1584              :                    "function declaration missing opening parentheses before "
    1585              :                    "parameter list");
    1586            0 :       add_error (std::move (error));
    1587              : 
    1588            0 :       skip_after_next_block ();
    1589            0 :       return nullptr;
    1590            0 :     }
    1591              : 
    1592        11924 :   auto initial_param = parse_self_param ();
    1593              : 
    1594        11924 :   if (!initial_param.has_value ()
    1595        11924 :       && initial_param.error ().kind != Parse::Error::Self::Kind::NOT_SELF)
    1596            0 :     return nullptr;
    1597              : 
    1598        14175 :   if (initial_param.has_value () && lexer.peek_token ()->get_id () == COMMA)
    1599         1369 :     skip_token ();
    1600              : 
    1601              :   // parse function parameters (only if next token isn't right paren)
    1602        11924 :   std::vector<std::unique_ptr<AST::Param>> function_params;
    1603              : 
    1604        23848 :   if (lexer.peek_token ()->get_id () != RIGHT_PAREN)
    1605              :     function_params
    1606         5285 :       = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
    1607              : 
    1608        11924 :   if (initial_param.has_value ())
    1609         2251 :     function_params.insert (function_params.begin (),
    1610         2251 :                             std::move (*initial_param));
    1611              : 
    1612        11924 :   if (!skip_token (RIGHT_PAREN))
    1613              :     {
    1614            0 :       Error error (lexer.peek_token ()->get_locus (),
    1615              :                    "function declaration missing closing parentheses after "
    1616              :                    "parameter list");
    1617            0 :       add_error (std::move (error));
    1618              : 
    1619            0 :       skip_after_next_block ();
    1620            0 :       return nullptr;
    1621            0 :     }
    1622              : 
    1623              :   // parse function return type - if exists
    1624        11924 :   std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
    1625              : 
    1626              :   // parse where clause - if exists
    1627        11924 :   AST::WhereClause where_clause = parse_where_clause ();
    1628              : 
    1629        11924 :   tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
    1630        23848 :   if (lexer.peek_token ()->get_id () == SEMICOLON)
    1631         4219 :     lexer.skip_token ();
    1632              :   else
    1633              :     {
    1634         7705 :       auto block_expr = parse_block_expr ();
    1635         7705 :       if (!block_expr)
    1636           29 :         return nullptr;
    1637         7676 :       body = std::move (block_expr.value ());
    1638         7705 :     }
    1639              : 
    1640        31466 :   return std::unique_ptr<AST::Function> (new AST::Function (
    1641        11895 :     std::move (function_name), std::move (qualifiers.value ()),
    1642              :     std::move (generic_params), std::move (function_params),
    1643              :     std::move (return_type), std::move (where_clause), std::move (body),
    1644        11895 :     std::move (vis), std::move (outer_attrs), locus, is_external));
    1645        35772 : }
    1646              : 
    1647              : // Parses function or method qualifiers (i.e. const, unsafe, and extern).
    1648              : template <typename ManagedTokenSource>
    1649              : tl::expected<AST::FunctionQualifiers, Parse::Error::Node>
    1650        18691 : Parser<ManagedTokenSource>::parse_function_qualifiers ()
    1651              : {
    1652        18691 :   location_t locus = lexer.peek_token ()->get_locus ();
    1653              : 
    1654        18691 :   auto parsed = parse_function_qualifiers_raw (locus);
    1655        18691 :   if (!parsed)
    1656            3 :     return tl::unexpected<Parse::Error::Node> (parsed.error ());
    1657              : 
    1658        37376 :   return function_qualifiers_from_keywords (locus, std::move (parsed->first),
    1659        37376 :                                             std::move (parsed->second));
    1660        18691 : }
    1661              : 
    1662              : // Take the list of parsed function qualifiers and convert it to
    1663              : // the corrresponding flags to pass to the AST item constructor.
    1664              : //
    1665              : // This assumes ``keywords`` contains only those tokens that
    1666              : // map to qualifiers.
    1667              : template <typename ManagedTokenSource>
    1668              : tl::expected<AST::FunctionQualifiers, Parse::Error::Node>
    1669        18688 : Parser<ManagedTokenSource>::function_qualifiers_from_keywords (
    1670              :   location_t locus, const std::vector<TokenId> keywords, std::string abi)
    1671              : {
    1672        18688 :   Default default_status = Default::No;
    1673        18688 :   Async async_status = Async::No;
    1674        18688 :   Const const_status = Const::No;
    1675        18688 :   Unsafety unsafe_status = Unsafety::Normal;
    1676        18688 :   bool has_extern = false;
    1677              : 
    1678        20192 :   for (auto qualifier : keywords)
    1679              :     {
    1680         1504 :       switch (qualifier)
    1681              :         {
    1682           26 :         case IDENTIFIER:
    1683              :           // only "default" is valid in this context
    1684           26 :           default_status = Default::Yes;
    1685           26 :           continue;
    1686          864 :         case CONST:
    1687          864 :           const_status = Const::Yes;
    1688          864 :           continue;
    1689           11 :         case ASYNC:
    1690           11 :           async_status = Async::Yes;
    1691           11 :           continue;
    1692          501 :         case UNSAFE:
    1693          501 :           unsafe_status = Unsafety::Unsafe;
    1694          501 :           continue;
    1695          102 :         case EXTERN_KW:
    1696          102 :           has_extern = true;
    1697          102 :           continue;
    1698            0 :         default:
    1699              :           // non-qualifier token in input
    1700            0 :           rust_unreachable ();
    1701              :         }
    1702              :     }
    1703              : 
    1704        37376 :   return AST::FunctionQualifiers (locus, default_status, async_status,
    1705              :                                   const_status, unsafe_status, has_extern,
    1706        18688 :                                   std::move (abi));
    1707              : }
    1708              : 
    1709              : // this consumes as many function qualifier tokens while ensuring
    1710              : // uniqueness.
    1711              : template <typename ManagedTokenSource>
    1712              : tl::expected<std::pair<std::vector<TokenId>, std::string>, Parse::Error::Node>
    1713        18691 : Parser<ManagedTokenSource>::parse_function_qualifiers_raw (location_t locus)
    1714              : {
    1715        18691 :   std::vector<TokenId> found_order;
    1716        18691 :   std::string abi;
    1717              : 
    1718              :   // this will terminate on duplicates or the first non-qualifier token
    1719         1514 :   while (true)
    1720              :     {
    1721        20204 :       auto token = lexer.peek_token ();
    1722        20204 :       const TokenId token_id = token->get_id ();
    1723        20204 :       location_t locus = lexer.peek_token ()->get_locus ();
    1724              : 
    1725        20204 :       switch (token_id)
    1726              :         {
    1727           28 :         case IDENTIFIER:
    1728           28 :           if (token->get_str () != Values::WeakKeywords::DEFAULT)
    1729              :             {
    1730              :               // only "default" is valid in this context, so this must
    1731              :               // be a non-qualifier keyword
    1732            0 :               goto done;
    1733              :             }
    1734              :           // fallthrough
    1735              :         case CONST:
    1736              :         case ASYNC:
    1737              :         case UNSAFE:
    1738         1411 :           found_order.push_back (token_id);
    1739         1411 :           lexer.skip_token ();
    1740              :           break;
    1741          103 :         case EXTERN_KW:
    1742              :           {
    1743          103 :             found_order.push_back (token_id);
    1744          103 :             lexer.skip_token ();
    1745              : 
    1746              :             // detect optional abi name
    1747          103 :             const_TokenPtr next_tok = lexer.peek_token ();
    1748          103 :             if (next_tok->get_id () == STRING_LITERAL)
    1749              :               {
    1750          103 :                 abi = next_tok->get_str ();
    1751          103 :                 lexer.skip_token ();
    1752              :               }
    1753          103 :           }
    1754          103 :           break;
    1755        18690 :         default:
    1756              :           // non-qualifier keyword
    1757        18690 :           goto done;
    1758              :         }
    1759              : 
    1760         3028 :       if (std::count (found_order.cbegin (), found_order.cend (), token_id) > 1)
    1761              :         {
    1762              :           // qualifiers mustn't appear twice
    1763            1 :           Error error (locus, "encountered duplicate function qualifier %qs",
    1764              :                        token->get_token_description ());
    1765            1 :           add_error (std::move (error));
    1766              : 
    1767              :           return tl::unexpected<Parse::Error::Node> (
    1768            1 :             Parse::Error::Node::MALFORMED);
    1769            1 :         }
    1770              :     }
    1771        18690 : done:
    1772              : 
    1773        18690 :   if (!ensure_function_qualifier_order (locus, found_order))
    1774            2 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1775              : 
    1776        37376 :   return make_pair (found_order, abi);
    1777        18691 : }
    1778              : 
    1779              : // Validate the order of the list of function qualifiers; this assumes that
    1780              : // ``found_order`` consists only of function qualifier tokens.
    1781              : //
    1782              : // If the order is illegal, the generated error message gives both the wrong
    1783              : // order as found in the source and the correct order according to Rust syntax
    1784              : // rules.
    1785              : template <typename ManagedTokenSource>
    1786              : bool
    1787        18690 : Parser<ManagedTokenSource>::ensure_function_qualifier_order (
    1788              :   location_t locus, const std::vector<TokenId> &found_order)
    1789              : {
    1790              :   // Check in order of default, const, async, unsafe, extern
    1791         1511 :   auto token_priority = [] (const TokenId id) {
    1792         1511 :     switch (id)
    1793              :       {
    1794              :       case IDENTIFIER: // "default"; the only "weak" keyword considered here
    1795              :         return 1;
    1796          865 :       case CONST:
    1797          865 :         return 2;
    1798           12 :       case ASYNC:
    1799           12 :         return 3;
    1800          503 :       case UNSAFE:
    1801          503 :         return 4;
    1802          103 :       case EXTERN_KW:
    1803          103 :         return 5;
    1804            0 :       default:
    1805            0 :         rust_unreachable ();
    1806              :       };
    1807              :   };
    1808              : 
    1809        18690 :   size_t last_priority = 0;
    1810        20199 :   for (auto token_id : found_order)
    1811              :     {
    1812         1511 :       const size_t priority = token_priority (token_id);
    1813         1511 :       if (priority <= last_priority)
    1814              :         {
    1815            2 :           emit_function_qualifier_order_error_msg (locus, found_order);
    1816            2 :           return false;
    1817              :         }
    1818              : 
    1819         1509 :       last_priority = priority;
    1820              :     }
    1821              : 
    1822              :   return true;
    1823              : }
    1824              : 
    1825              : template <typename ManagedTokenSource>
    1826              : void
    1827            2 : Parser<ManagedTokenSource>::emit_function_qualifier_order_error_msg (
    1828              :   location_t locus, const std::vector<TokenId> &found_order)
    1829              : {
    1830            2 :   std::vector<TokenId> expected_order
    1831              :     = {IDENTIFIER, CONST, ASYNC, UNSAFE, EXTERN_KW};
    1832              : 
    1833              :   // we only keep the qualifiers actually used in the offending code
    1834            2 :   std::vector<TokenId>::iterator token_id = expected_order.begin ();
    1835           12 :   while (token_id != expected_order.end ())
    1836              :     {
    1837           20 :       if (std::find (found_order.cbegin (), found_order.cend (), *token_id)
    1838           20 :           == found_order.cend ())
    1839              :         {
    1840            3 :           token_id = expected_order.erase (token_id);
    1841              :         }
    1842              :       else
    1843              :         {
    1844            7 :           ++token_id;
    1845              :         }
    1846              :     }
    1847              : 
    1848            4 :   auto qualifiers_to_str = [] (const std::vector<TokenId> &token_ids) {
    1849            4 :     std::ostringstream ss;
    1850              : 
    1851           18 :     for (auto id : token_ids)
    1852              :       {
    1853           14 :         if (ss.tellp () != 0)
    1854           10 :           ss << ' ';
    1855              : 
    1856           14 :         if (id == IDENTIFIER)
    1857            4 :           ss << Values::WeakKeywords::DEFAULT;
    1858              :         else
    1859           10 :           ss << token_id_keyword_string (id);
    1860              :       }
    1861              : 
    1862            8 :     return ss.str ();
    1863            4 :   };
    1864              : 
    1865            2 :   const std::string found_qualifiers = qualifiers_to_str (found_order);
    1866            2 :   const std::string expected_qualifiers = qualifiers_to_str (expected_order);
    1867              : 
    1868              :   location_t error_locus
    1869            4 :     = make_location (locus, locus, lexer.peek_token ()->get_locus ());
    1870            2 :   Error error (error_locus,
    1871              :                "invalid order of function qualifiers; found %qs, expected %qs",
    1872              :                found_qualifiers.c_str (), expected_qualifiers.c_str ());
    1873            2 :   add_error (std::move (error));
    1874            2 : }
    1875              : 
    1876              : // Parses generic (lifetime or type) params inside angle brackets (optional).
    1877              : template <typename ManagedTokenSource>
    1878              : std::vector<std::unique_ptr<AST::GenericParam>>
    1879        32526 : Parser<ManagedTokenSource>::parse_generic_params_in_angles ()
    1880              : {
    1881        65052 :   if (lexer.peek_token ()->get_id () != LEFT_ANGLE)
    1882              :     {
    1883              :       // seems to be no generic params, so exit with empty vector
    1884        28151 :       return std::vector<std::unique_ptr<AST::GenericParam>> ();
    1885              :     }
    1886         4375 :   lexer.skip_token ();
    1887              : 
    1888              :   // DEBUG:
    1889         4375 :   rust_debug ("skipped left angle in generic param");
    1890              : 
    1891         4375 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    1892              :     = parse_generic_params (Parse::Utils::is_right_angle_tok);
    1893              : 
    1894              :   // DEBUG:
    1895         4375 :   rust_debug ("finished parsing actual generic params (i.e. inside angles)");
    1896              : 
    1897         4375 :   if (!skip_generics_right_angle ())
    1898              :     {
    1899              :       // DEBUG
    1900            1 :       rust_debug ("failed to skip generics right angle - returning empty "
    1901              :                   "generic params");
    1902              : 
    1903            1 :       return std::vector<std::unique_ptr<AST::GenericParam>> ();
    1904              :     }
    1905              : 
    1906         4374 :   return generic_params;
    1907         4375 : }
    1908              : 
    1909              : template <typename ManagedTokenSource>
    1910              : template <typename EndTokenPred>
    1911              : std::unique_ptr<AST::GenericParam>
    1912         4772 : Parser<ManagedTokenSource>::parse_generic_param (EndTokenPred is_end_token)
    1913              : {
    1914         4772 :   auto outer_attrs = parse_outer_attributes ();
    1915         4772 :   std::unique_ptr<AST::GenericParam> param;
    1916         4772 :   auto token = lexer.peek_token ();
    1917              : 
    1918         4772 :   switch (token->get_id ())
    1919              :     {
    1920          253 :     case LIFETIME:
    1921              :       {
    1922          253 :         auto lifetime = parse_lifetime (false);
    1923          253 :         if (!lifetime)
    1924              :           {
    1925            0 :             Error error (token->get_locus (),
    1926              :                          "failed to parse lifetime in generic parameter list");
    1927            0 :             add_error (std::move (error));
    1928              : 
    1929            0 :             return nullptr;
    1930            0 :           }
    1931              : 
    1932          253 :         std::vector<AST::Lifetime> lifetime_bounds;
    1933          506 :         if (lexer.peek_token ()->get_id () == COLON)
    1934              :           {
    1935            1 :             lexer.skip_token ();
    1936              :             // parse required bounds
    1937              :             lifetime_bounds
    1938            1 :               = parse_lifetime_bounds ([is_end_token] (TokenId id) {
    1939            1 :                   return is_end_token (id) || id == COMMA;
    1940              :                 });
    1941              :           }
    1942              : 
    1943          506 :         param = std::unique_ptr<AST::LifetimeParam> (new AST::LifetimeParam (
    1944          253 :           std::move (lifetime.value ()), std::move (lifetime_bounds),
    1945          253 :           std::move (outer_attrs), token->get_locus ()));
    1946              :         break;
    1947          506 :       }
    1948         4411 :     case IDENTIFIER:
    1949              :       {
    1950         4411 :         auto type_ident = token->get_str ();
    1951         4411 :         lexer.skip_token ();
    1952              : 
    1953         4411 :         std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
    1954         8822 :         if (lexer.peek_token ()->get_id () == COLON)
    1955              :           {
    1956          646 :             lexer.skip_token ();
    1957              : 
    1958              :             // parse optional type param bounds
    1959          646 :             type_param_bounds = parse_type_param_bounds ();
    1960              :           }
    1961              : 
    1962         4411 :         std::unique_ptr<AST::Type> type = nullptr;
    1963         8822 :         if (lexer.peek_token ()->get_id () == EQUAL)
    1964              :           {
    1965          362 :             lexer.skip_token ();
    1966              : 
    1967              :             // parse required type
    1968          362 :             type = parse_type ();
    1969          362 :             if (!type)
    1970              :               {
    1971            0 :                 Error error (
    1972            0 :                   lexer.peek_token ()->get_locus (),
    1973              :                   "failed to parse type in type param in generic params");
    1974            0 :                 add_error (std::move (error));
    1975              : 
    1976            0 :                 return nullptr;
    1977            0 :               }
    1978              :           }
    1979              : 
    1980         4411 :         param = std::unique_ptr<AST::TypeParam> (
    1981        13233 :           new AST::TypeParam (std::move (type_ident), token->get_locus (),
    1982              :                               std::move (type_param_bounds), std::move (type),
    1983         4411 :                               std::move (outer_attrs)));
    1984              :         break;
    1985         4411 :       }
    1986          107 :     case CONST:
    1987              :       {
    1988          107 :         lexer.skip_token ();
    1989              : 
    1990          107 :         auto name_token = expect_token (IDENTIFIER);
    1991              : 
    1992          214 :         if (!name_token || !expect_token (COLON))
    1993            1 :           return nullptr;
    1994              : 
    1995          106 :         auto type = parse_type ();
    1996          106 :         if (!type)
    1997            1 :           return nullptr;
    1998              : 
    1999              :         // optional default value
    2000          105 :         tl::optional<AST::GenericArg> default_expr = tl::nullopt;
    2001          210 :         if (lexer.peek_token ()->get_id () == EQUAL)
    2002              :           {
    2003           20 :             lexer.skip_token ();
    2004           20 :             auto tok = lexer.peek_token ();
    2005           39 :             default_expr = parse_generic_arg ();
    2006              : 
    2007           20 :             if (!default_expr)
    2008              :               {
    2009            1 :                 Error error (tok->get_locus (),
    2010              :                              "invalid token for start of default value for "
    2011              :                              "const generic parameter: expected %<block%>, "
    2012              :                              "%<identifier%> or %<literal%>, got %qs",
    2013              :                              token_id_to_str (tok->get_id ()));
    2014              : 
    2015            1 :                 add_error (std::move (error));
    2016            1 :                 return nullptr;
    2017            1 :               }
    2018              : 
    2019              :             // At this point, we *know* that we are parsing a const
    2020              :             // expression
    2021           19 :             if (default_expr.value ().get_kind ()
    2022              :                 == AST::GenericArg::Kind::Either)
    2023            1 :               default_expr = default_expr.value ().disambiguate_to_const ();
    2024           20 :           }
    2025              : 
    2026          104 :         param = std::unique_ptr<AST::ConstGenericParam> (
    2027          435 :           new AST::ConstGenericParam (name_token->get_str (), std::move (type),
    2028              :                                       default_expr, std::move (outer_attrs),
    2029          104 :                                       token->get_locus ()));
    2030              : 
    2031              :         break;
    2032          213 :       }
    2033            1 :     default:
    2034              :       // FIXME: Can we clean this last call with a method call?
    2035            1 :       Error error (token->get_locus (),
    2036              :                    "unexpected token when parsing generic parameters: %qs",
    2037            1 :                    token->as_string ().c_str ());
    2038            1 :       add_error (std::move (error));
    2039              : 
    2040            1 :       return nullptr;
    2041            1 :     }
    2042              : 
    2043         4768 :   return param;
    2044         4772 : }
    2045              : 
    2046              : /* Parse generic (lifetime or type) params NOT INSIDE ANGLE BRACKETS!!! Almost
    2047              :  * always parse_generic_params_in_angles is what is wanted. */
    2048              : template <typename ManagedTokenSource>
    2049              : template <typename EndTokenPred>
    2050              : std::vector<std::unique_ptr<AST::GenericParam>>
    2051         4375 : Parser<ManagedTokenSource>::parse_generic_params (EndTokenPred is_end_token)
    2052              : {
    2053         4375 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
    2054              : 
    2055              :   /* can't parse lifetime and type params separately due to lookahead issues
    2056              :    * thus, parse them all here */
    2057              : 
    2058              :   /* HACK: used to retain attribute data if a lifetime param is tentatively
    2059              :    * parsed but it turns out to be type param */
    2060         4375 :   AST::Attribute parsed_outer_attr = AST::Attribute::create_empty ();
    2061              : 
    2062              :   // Did we parse a generic type param yet
    2063         4375 :   auto type_seen = false;
    2064              :   // Did we parse a const param with a default value yet
    2065         4375 :   auto const_with_default_seen = false;
    2066              :   // Did the user write a lifetime parameter after a type one
    2067         4375 :   auto order_error = false;
    2068              :   // Did the user write a const param with a default value after a type one
    2069         4375 :   auto const_with_default_order_error = false;
    2070              : 
    2071              :   // parse lifetime params
    2072        23058 :   while (!is_end_token (lexer.peek_token ()->get_id ()))
    2073              :     {
    2074         4772 :       auto param = parse_generic_param (is_end_token);
    2075         4772 :       if (param)
    2076              :         {
    2077         4768 :           if (param->get_kind () == AST::GenericParam::Kind::Type)
    2078              :             {
    2079         4411 :               type_seen = true;
    2080         4411 :               if (const_with_default_seen)
    2081         4768 :                 const_with_default_order_error = true;
    2082              :             }
    2083          357 :           else if (param->get_kind () == AST::GenericParam::Kind::Lifetime
    2084          357 :                    && type_seen)
    2085              :             {
    2086            2 :               order_error = true;
    2087            2 :               if (const_with_default_seen)
    2088            0 :                 const_with_default_order_error = true;
    2089              :             }
    2090          355 :           else if (param->get_kind () == AST::GenericParam::Kind::Const)
    2091              :             {
    2092          104 :               type_seen = true;
    2093              :               AST::ConstGenericParam *const_param
    2094          104 :                 = static_cast<AST::ConstGenericParam *> (param.get ());
    2095          104 :               if (const_param->has_default_value ())
    2096              :                 const_with_default_seen = true;
    2097           85 :               else if (const_with_default_seen)
    2098         4768 :                 const_with_default_order_error = true;
    2099              :             }
    2100              : 
    2101         4768 :           generic_params.emplace_back (std::move (param));
    2102         4768 :           maybe_skip_token (COMMA);
    2103              :         }
    2104              :       else
    2105              :         break;
    2106              :     }
    2107              : 
    2108              :   // FIXME: Add reordering hint
    2109         4375 :   if (order_error)
    2110              :     {
    2111            2 :       Error error (generic_params.front ()->get_locus (),
    2112              :                    "invalid order for generic parameters: lifetime parameters "
    2113              :                    "must be declared prior to type and const parameters");
    2114            2 :       add_error (std::move (error));
    2115            2 :     }
    2116         4375 :   if (const_with_default_order_error)
    2117              :     {
    2118            1 :       Error error (generic_params.front ()->get_locus (),
    2119              :                    "invalid order for generic parameters: generic parameters "
    2120              :                    "with a default must be trailing");
    2121            1 :       add_error (std::move (error));
    2122            1 :     }
    2123              : 
    2124         4375 :   generic_params.shrink_to_fit ();
    2125         4375 :   return generic_params;
    2126         4375 : }
    2127              : 
    2128              : /* Parses lifetime generic parameters (pointers). Will also consume any
    2129              :  * trailing comma. No extra checks for end token. */
    2130              : template <typename ManagedTokenSource>
    2131              : std::vector<std::unique_ptr<AST::LifetimeParam>>
    2132            4 : Parser<ManagedTokenSource>::parse_lifetime_params ()
    2133              : {
    2134            4 :   std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params;
    2135              : 
    2136           12 :   while (lexer.peek_token ()->get_id () != END_OF_FILE)
    2137              :     {
    2138            4 :       auto lifetime_param = parse_lifetime_param ();
    2139              : 
    2140            4 :       if (!lifetime_param)
    2141              :         {
    2142              :           // can't treat as error as only way to get out with trailing comma
    2143              :           break;
    2144              :         }
    2145              : 
    2146            2 :       lifetime_params.emplace_back (
    2147            2 :         new AST::LifetimeParam (std::move (lifetime_param.value ())));
    2148              : 
    2149            4 :       if (lexer.peek_token ()->get_id () != COMMA)
    2150              :         break;
    2151              : 
    2152              :       // skip commas, including trailing commas
    2153            0 :       lexer.skip_token ();
    2154              :     }
    2155              : 
    2156            4 :   lifetime_params.shrink_to_fit ();
    2157              : 
    2158            4 :   return lifetime_params;
    2159              : }
    2160              : 
    2161              : /* Parses lifetime generic parameters (pointers). Will also consume any
    2162              :  * trailing comma. Has extra is_end_token predicate checking. */
    2163              : template <typename ManagedTokenSource>
    2164              : template <typename EndTokenPred>
    2165              : std::vector<std::unique_ptr<AST::LifetimeParam>>
    2166              : Parser<ManagedTokenSource>::parse_lifetime_params (EndTokenPred is_end_token)
    2167              : {
    2168              :   std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params;
    2169              : 
    2170              :   // if end_token is not specified, it defaults to EOF, so should work fine
    2171              :   while (!is_end_token (lexer.peek_token ()->get_id ()))
    2172              :     {
    2173              :       auto lifetime_param = parse_lifetime_param ();
    2174              : 
    2175              :       if (!lifetime_param)
    2176              :         {
    2177              :           /* TODO: is it worth throwing away all lifetime params just because
    2178              :            * one failed? */
    2179              :           Error error (lexer.peek_token ()->get_locus (),
    2180              :                        "failed to parse lifetime param in lifetime params");
    2181              :           add_error (std::move (error));
    2182              : 
    2183              :           return {};
    2184              :         }
    2185              : 
    2186              :       lifetime_params.emplace_back (
    2187              :         new AST::LifetimeParam (std::move (lifetime_param)));
    2188              : 
    2189              :       if (lexer.peek_token ()->get_id () != COMMA)
    2190              :         break;
    2191              : 
    2192              :       // skip commas, including trailing commas
    2193              :       lexer.skip_token ();
    2194              :     }
    2195              : 
    2196              :   lifetime_params.shrink_to_fit ();
    2197              : 
    2198              :   return lifetime_params;
    2199              : }
    2200              : 
    2201              : /* Parses lifetime generic parameters (objects). Will also consume any
    2202              :  * trailing comma. No extra checks for end token.
    2203              :  * TODO: is this best solution? implements most of the same algorithm.
    2204              :  * TODO: seems to be unused, remove? */
    2205              : template <typename ManagedTokenSource>
    2206              : std::vector<AST::LifetimeParam>
    2207            0 : Parser<ManagedTokenSource>::parse_lifetime_params_objs ()
    2208              : {
    2209            0 :   std::vector<AST::LifetimeParam> lifetime_params;
    2210              : 
    2211              :   // bad control structure as end token cannot be guaranteed
    2212            0 :   while (true)
    2213              :     {
    2214            0 :       auto lifetime_param = parse_lifetime_param ();
    2215              : 
    2216            0 :       if (!lifetime_param)
    2217              :         {
    2218              :           // not an error as only way to exit if trailing comma
    2219              :           break;
    2220              :         }
    2221              : 
    2222            0 :       lifetime_params.push_back (std::move (lifetime_param.value ()));
    2223              : 
    2224            0 :       if (lexer.peek_token ()->get_id () != COMMA)
    2225              :         break;
    2226              : 
    2227              :       // skip commas, including trailing commas
    2228            0 :       lexer.skip_token ();
    2229              :     }
    2230              : 
    2231            0 :   lifetime_params.shrink_to_fit ();
    2232              : 
    2233            0 :   return lifetime_params;
    2234              : }
    2235              : 
    2236              : /* Parses lifetime generic parameters (objects). Will also consume any
    2237              :  * trailing comma. Has extra is_end_token predicate checking.
    2238              :  * TODO: is this best solution? implements most of the same algorithm. */
    2239              : template <typename ManagedTokenSource>
    2240              : template <typename EndTokenPred>
    2241              : std::vector<AST::LifetimeParam>
    2242           26 : Parser<ManagedTokenSource>::parse_lifetime_params_objs (
    2243              :   EndTokenPred is_end_token)
    2244              : {
    2245           26 :   std::vector<AST::LifetimeParam> lifetime_params;
    2246              : 
    2247           78 :   while (!is_end_token (lexer.peek_token ()->get_id ()))
    2248              :     {
    2249           26 :       auto lifetime_param = parse_lifetime_param ();
    2250              : 
    2251           26 :       if (!lifetime_param)
    2252              :         {
    2253              :           /* TODO: is it worth throwing away all lifetime params just because
    2254              :            * one failed? */
    2255            0 :           Error error (lexer.peek_token ()->get_locus (),
    2256              :                        "failed to parse lifetime param in lifetime params");
    2257            0 :           add_error (std::move (error));
    2258              : 
    2259            0 :           return {};
    2260            0 :         }
    2261              : 
    2262           26 :       lifetime_params.push_back (std::move (lifetime_param.value ()));
    2263              : 
    2264           52 :       if (lexer.peek_token ()->get_id () != COMMA)
    2265              :         break;
    2266              : 
    2267              :       // skip commas, including trailing commas
    2268            0 :       lexer.skip_token ();
    2269              :     }
    2270              : 
    2271           26 :   lifetime_params.shrink_to_fit ();
    2272              : 
    2273           26 :   return lifetime_params;
    2274           26 : }
    2275              : 
    2276              : /* Parses a sequence of a certain grammar rule in object form (not pointer or
    2277              :  * smart pointer), delimited by commas and ending when 'is_end_token' is
    2278              :  * satisfied (templated). Will also consume any trailing comma.
    2279              :  * FIXME: this cannot be used due to member function pointer problems (i.e.
    2280              :  * parsing_function cannot be specified properly) */
    2281              : template <typename ManagedTokenSource>
    2282              : template <typename ParseFunction, typename EndTokenPred>
    2283              : auto
    2284              : Parser<ManagedTokenSource>::parse_non_ptr_sequence (
    2285              :   ParseFunction parsing_function, EndTokenPred is_end_token,
    2286              :   std::string error_msg) -> std::vector<decltype (parsing_function ())>
    2287              : {
    2288              :   std::vector<decltype (parsing_function ())> params;
    2289              : 
    2290              :   while (!is_end_token (lexer.peek_token ()->get_id ()))
    2291              :     {
    2292              :       auto param = parsing_function ();
    2293              : 
    2294              :       if (param.is_error ())
    2295              :         {
    2296              :           // TODO: is it worth throwing away all params just because one
    2297              :           // failed?
    2298              :           Error error (lexer.peek_token ()->get_locus (),
    2299              :                        std::move (error_msg));
    2300              :           add_error (std::move (error));
    2301              : 
    2302              :           return {};
    2303              :         }
    2304              : 
    2305              :       params.push_back (std::move (param));
    2306              : 
    2307              :       if (lexer.peek_token ()->get_id () != COMMA)
    2308              :         break;
    2309              : 
    2310              :       // skip commas, including trailing commas
    2311              :       lexer.skip_token ();
    2312              :     }
    2313              : 
    2314              :   params.shrink_to_fit ();
    2315              : 
    2316              :   return params;
    2317              : }
    2318              : 
    2319              : /* Parses a single lifetime generic parameter (not including comma). */
    2320              : template <typename ManagedTokenSource>
    2321              : tl::expected<AST::LifetimeParam, Parse::Error::LifetimeParam>
    2322           30 : Parser<ManagedTokenSource>::parse_lifetime_param ()
    2323              : {
    2324              :   // parse outer attributes, which are optional and may not exist
    2325           30 :   auto outer_attrs = parse_outer_attributes ();
    2326              : 
    2327              :   // save lifetime token - required
    2328           30 :   const_TokenPtr lifetime_tok = lexer.peek_token ();
    2329           30 :   if (lifetime_tok->get_id () != LIFETIME)
    2330              :     {
    2331              :       // if lifetime is missing, must not be a lifetime param, so return error
    2332              :       return Parse::Error::LifetimeParam::make_not_a_lifetime_param ();
    2333              :     }
    2334           28 :   lexer.skip_token ();
    2335           56 :   AST::Lifetime lifetime (AST::Lifetime::NAMED, lifetime_tok->get_str (),
    2336              :                           lifetime_tok->get_locus ());
    2337              : 
    2338              :   // parse lifetime bounds, if it exists
    2339           28 :   std::vector<AST::Lifetime> lifetime_bounds;
    2340           56 :   if (lexer.peek_token ()->get_id () == COLON)
    2341              :     {
    2342              :       // parse lifetime bounds
    2343            0 :       lifetime_bounds = parse_lifetime_bounds ();
    2344              :       // TODO: have end token passed in?
    2345              :     }
    2346              : 
    2347           56 :   return AST::LifetimeParam (std::move (lifetime), std::move (lifetime_bounds),
    2348              :                              std::move (outer_attrs),
    2349           28 :                              lifetime_tok->get_locus ());
    2350           58 : }
    2351              : 
    2352              : // Parses type generic parameters. Will also consume any trailing comma.
    2353              : template <typename ManagedTokenSource>
    2354              : std::vector<std::unique_ptr<AST::TypeParam>>
    2355            0 : Parser<ManagedTokenSource>::parse_type_params ()
    2356              : {
    2357            0 :   std::vector<std::unique_ptr<AST::TypeParam>> type_params;
    2358              : 
    2359              :   // infinite loop with break on failure as no info on ending token
    2360            0 :   while (true)
    2361              :     {
    2362            0 :       std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
    2363              : 
    2364            0 :       if (type_param == nullptr)
    2365              :         {
    2366              :           // break if fails to parse
    2367              :           break;
    2368              :         }
    2369              : 
    2370            0 :       type_params.push_back (std::move (type_param));
    2371              : 
    2372            0 :       if (lexer.peek_token ()->get_id () != COMMA)
    2373              :         break;
    2374              : 
    2375              :       // skip commas, including trailing commas
    2376            0 :       lexer.skip_token ();
    2377              :     }
    2378              : 
    2379            0 :   type_params.shrink_to_fit ();
    2380            0 :   return type_params;
    2381              : }
    2382              : 
    2383              : // Parses type generic parameters. Will also consume any trailing comma.
    2384              : template <typename ManagedTokenSource>
    2385              : template <typename EndTokenPred>
    2386              : std::vector<std::unique_ptr<AST::TypeParam>>
    2387              : Parser<ManagedTokenSource>::parse_type_params (EndTokenPred is_end_token)
    2388              : {
    2389              :   std::vector<std::unique_ptr<AST::TypeParam>> type_params;
    2390              : 
    2391              :   while (!is_end_token (lexer.peek_token ()->get_id ()))
    2392              :     {
    2393              :       std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
    2394              : 
    2395              :       if (type_param == nullptr)
    2396              :         {
    2397              :           Error error (lexer.peek_token ()->get_locus (),
    2398              :                        "failed to parse type param in type params");
    2399              :           add_error (std::move (error));
    2400              : 
    2401              :           return {};
    2402              :         }
    2403              : 
    2404              :       type_params.push_back (std::move (type_param));
    2405              : 
    2406              :       if (lexer.peek_token ()->get_id () != COMMA)
    2407              :         break;
    2408              : 
    2409              :       // skip commas, including trailing commas
    2410              :       lexer.skip_token ();
    2411              :     }
    2412              : 
    2413              :   type_params.shrink_to_fit ();
    2414              :   return type_params;
    2415              :   /* TODO: this shares most code with parse_lifetime_params - good place to
    2416              :    * use template (i.e. parse_non_ptr_sequence if doable) */
    2417              : }
    2418              : 
    2419              : /* Parses a single type (generic) parameter, not including commas. May change
    2420              :  * to return value. */
    2421              : template <typename ManagedTokenSource>
    2422              : std::unique_ptr<AST::TypeParam>
    2423            0 : Parser<ManagedTokenSource>::parse_type_param ()
    2424              : {
    2425              :   // parse outer attributes, which are optional and may not exist
    2426            0 :   auto outer_attrs = parse_outer_attributes ();
    2427              : 
    2428            0 :   const_TokenPtr identifier_tok = lexer.peek_token ();
    2429            0 :   if (identifier_tok->get_id () != IDENTIFIER)
    2430              :     {
    2431              :       // return null as type param can't exist without this required
    2432              :       // identifier
    2433            0 :       return nullptr;
    2434              :     }
    2435            0 :   Identifier ident{identifier_tok};
    2436            0 :   lexer.skip_token ();
    2437              : 
    2438              :   // parse type param bounds (if they exist)
    2439            0 :   std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
    2440            0 :   if (lexer.peek_token ()->get_id () == COLON)
    2441              :     {
    2442            0 :       lexer.skip_token ();
    2443              : 
    2444              :       // parse type param bounds, which may or may not exist
    2445            0 :       type_param_bounds = parse_type_param_bounds ();
    2446              :     }
    2447              : 
    2448              :   // parse type (if it exists)
    2449            0 :   std::unique_ptr<AST::Type> type = nullptr;
    2450            0 :   if (lexer.peek_token ()->get_id () == EQUAL)
    2451              :     {
    2452            0 :       lexer.skip_token ();
    2453              : 
    2454              :       // parse type (now required)
    2455            0 :       type = parse_type ();
    2456            0 :       if (type == nullptr)
    2457              :         {
    2458            0 :           Error error (lexer.peek_token ()->get_locus (),
    2459              :                        "failed to parse type in type param");
    2460            0 :           add_error (std::move (error));
    2461              : 
    2462            0 :           return nullptr;
    2463            0 :         }
    2464              :     }
    2465              : 
    2466              :   return std::unique_ptr<AST::TypeParam> (
    2467            0 :     new AST::TypeParam (std::move (ident), identifier_tok->get_locus (),
    2468              :                         std::move (type_param_bounds), std::move (type),
    2469            0 :                         std::move (outer_attrs)));
    2470            0 : }
    2471              : 
    2472              : /* Parses regular (i.e. non-generic) parameters in functions or methods. Also
    2473              :  * has end token handling. */
    2474              : template <typename ManagedTokenSource>
    2475              : template <typename EndTokenPred>
    2476              : std::vector<std::unique_ptr<AST::Param>>
    2477        10181 : Parser<ManagedTokenSource>::parse_function_params (EndTokenPred is_end_token)
    2478              : {
    2479        10181 :   std::vector<std::unique_ptr<AST::Param>> params;
    2480              : 
    2481        20362 :   if (is_end_token (lexer.peek_token ()->get_id ()))
    2482          827 :     return params;
    2483              : 
    2484         9354 :   auto initial_param = parse_function_param ();
    2485              : 
    2486              :   // Return empty parameter list if no parameter there
    2487         9354 :   if (initial_param == nullptr)
    2488              :     {
    2489              :       // TODO: is this an error?
    2490            0 :       return params;
    2491              :     }
    2492              : 
    2493         9354 :   params.push_back (std::move (initial_param));
    2494              : 
    2495              :   // maybe think of a better control structure here - do-while with an initial
    2496              :   // error state? basically, loop through parameter list until can't find any
    2497              :   // more params
    2498         9354 :   const_TokenPtr t = lexer.peek_token ();
    2499        11609 :   while (t->get_id () == COMMA)
    2500              :     {
    2501              :       // skip comma if applies
    2502         2255 :       lexer.skip_token ();
    2503              : 
    2504              :       // TODO: strictly speaking, shouldn't there be no trailing comma?
    2505         4510 :       if (is_end_token (lexer.peek_token ()->get_id ()))
    2506              :         break;
    2507              : 
    2508              :       // now, as right paren would break, function param is required
    2509         2255 :       auto param = parse_function_param ();
    2510         2255 :       if (param == nullptr)
    2511              :         {
    2512            0 :           Error error (lexer.peek_token ()->get_locus (),
    2513              :                        "failed to parse function param (in function params)");
    2514            0 :           add_error (std::move (error));
    2515              : 
    2516              :           // skip somewhere?
    2517            0 :           return std::vector<std::unique_ptr<AST::Param>> ();
    2518            0 :         }
    2519              : 
    2520         2255 :       params.push_back (std::move (param));
    2521              : 
    2522         2255 :       t = lexer.peek_token ();
    2523              :     }
    2524              : 
    2525         9354 :   params.shrink_to_fit ();
    2526         9354 :   return params;
    2527        10181 : }
    2528              : 
    2529              : /* Parses a single regular (i.e. non-generic) parameter in a function or
    2530              :  * method, i.e. the "name: type" bit. Also handles it not existing. */
    2531              : template <typename ManagedTokenSource>
    2532              : std::unique_ptr<AST::Param>
    2533        11609 : Parser<ManagedTokenSource>::parse_function_param ()
    2534              : {
    2535              :   // parse outer attributes if they exist
    2536        11609 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    2537              : 
    2538              :   // TODO: should saved location be at start of outer attributes or pattern?
    2539        11609 :   location_t locus = lexer.peek_token ()->get_locus ();
    2540              : 
    2541        23218 :   if (lexer.peek_token ()->get_id () == ELLIPSIS) // Unnamed variadic
    2542              :     {
    2543          827 :       lexer.skip_token (); // Skip ellipsis
    2544          827 :       return std::make_unique<AST::VariadicParam> (
    2545         1654 :         AST::VariadicParam (std::move (outer_attrs), locus));
    2546              :     }
    2547              : 
    2548        10782 :   std::unique_ptr<AST::Pattern> param_pattern = parse_pattern ();
    2549              : 
    2550              :   // create error function param if it doesn't exist
    2551        10782 :   if (param_pattern == nullptr)
    2552              :     {
    2553              :       // skip after something
    2554            0 :       return nullptr;
    2555              :     }
    2556              : 
    2557        10782 :   if (!skip_token (COLON))
    2558              :     {
    2559              :       // skip after something
    2560            0 :       return nullptr;
    2561              :     }
    2562              : 
    2563        21564 :   if (lexer.peek_token ()->get_id () == ELLIPSIS) // Named variadic
    2564              :     {
    2565           11 :       lexer.skip_token (); // Skip ellipsis
    2566           11 :       return std::make_unique<AST::VariadicParam> (
    2567           22 :         AST::VariadicParam (std::move (param_pattern), std::move (outer_attrs),
    2568           11 :                             locus));
    2569              :     }
    2570              :   else
    2571              :     {
    2572        10771 :       std::unique_ptr<AST::Type> param_type = parse_type ();
    2573        10771 :       if (param_type == nullptr)
    2574              :         {
    2575            0 :           return nullptr;
    2576              :         }
    2577        10771 :       return std::make_unique<AST::FunctionParam> (
    2578        21542 :         AST::FunctionParam (std::move (param_pattern), std::move (param_type),
    2579        10771 :                             std::move (outer_attrs), locus));
    2580        10771 :     }
    2581        11609 : }
    2582              : 
    2583              : /* Parses a function or method return type syntactical construction. Also
    2584              :  * handles a function return type not existing. */
    2585              : template <typename ManagedTokenSource>
    2586              : std::unique_ptr<AST::Type>
    2587        18642 : Parser<ManagedTokenSource>::parse_function_return_type ()
    2588              : {
    2589        37284 :   if (lexer.peek_token ()->get_id () != RETURN_TYPE)
    2590         5584 :     return nullptr;
    2591              : 
    2592              :   // skip return type, as it now obviously exists
    2593        13058 :   lexer.skip_token ();
    2594              : 
    2595        13058 :   std::unique_ptr<AST::Type> type = parse_type ();
    2596              : 
    2597        13058 :   return type;
    2598        13058 : }
    2599              : 
    2600              : /* Parses a "where clause" (in a function, struct, method, etc.). Also handles
    2601              :  * a where clause not existing, in which it will return
    2602              :  * WhereClause::create_empty(), which can be checked via
    2603              :  * WhereClause::is_empty(). */
    2604              : template <typename ManagedTokenSource>
    2605              : AST::WhereClause
    2606        32512 : Parser<ManagedTokenSource>::parse_where_clause ()
    2607              : {
    2608        32512 :   const_TokenPtr where_tok = lexer.peek_token ();
    2609        32512 :   if (where_tok->get_id () != WHERE)
    2610              :     {
    2611              :       // where clause doesn't exist, so create empty one
    2612        32223 :       return AST::WhereClause::create_empty ();
    2613              :     }
    2614              : 
    2615          289 :   lexer.skip_token ();
    2616              : 
    2617              :   /* parse where clause items - this is not a separate rule in the reference
    2618              :    * so won't be here */
    2619          289 :   std::vector<std::unique_ptr<AST::WhereClauseItem>> where_clause_items;
    2620              : 
    2621          289 :   std::vector<AST::LifetimeParam> for_lifetimes;
    2622          578 :   if (lexer.peek_token ()->get_id () == FOR)
    2623            1 :     for_lifetimes = parse_for_lifetimes ();
    2624              : 
    2625              :   /* HACK: where clauses end with a right curly or semicolon or equals in all
    2626              :    * uses currently */
    2627          289 :   const_TokenPtr t = lexer.peek_token ();
    2628          588 :   while (t->get_id () != LEFT_CURLY && t->get_id () != SEMICOLON
    2629          580 :          && t->get_id () != EQUAL)
    2630              :     {
    2631          299 :       std::unique_ptr<AST::WhereClauseItem> where_clause_item
    2632              :         = parse_where_clause_item (for_lifetimes);
    2633              : 
    2634          299 :       if (where_clause_item == nullptr)
    2635              :         {
    2636            0 :           Error error (t->get_locus (), "failed to parse where clause item");
    2637            0 :           add_error (std::move (error));
    2638              : 
    2639            0 :           return AST::WhereClause::create_empty ();
    2640            0 :         }
    2641              : 
    2642          299 :       where_clause_items.push_back (std::move (where_clause_item));
    2643              : 
    2644              :       // also skip comma if it exists
    2645          598 :       if (lexer.peek_token ()->get_id () != COMMA)
    2646              :         break;
    2647              : 
    2648          291 :       lexer.skip_token ();
    2649          291 :       t = lexer.peek_token ();
    2650              :     }
    2651              : 
    2652          289 :   where_clause_items.shrink_to_fit ();
    2653          289 :   return AST::WhereClause (std::move (where_clause_items));
    2654          289 : }
    2655              : 
    2656              : /* Parses a where clause item (lifetime or type bound). Does not parse any
    2657              :  * commas. */
    2658              : template <typename ManagedTokenSource>
    2659              : std::unique_ptr<AST::WhereClauseItem>
    2660          299 : Parser<ManagedTokenSource>::parse_where_clause_item (
    2661              :   const std::vector<AST::LifetimeParam> &outer_for_lifetimes)
    2662              : {
    2663              :   // shitty cheat way of determining lifetime or type bound - test for
    2664              :   // lifetime
    2665          299 :   const_TokenPtr t = lexer.peek_token ();
    2666              : 
    2667          299 :   if (t->get_id () == LIFETIME)
    2668            2 :     return parse_lifetime_where_clause_item ();
    2669              :   else
    2670          297 :     return parse_type_bound_where_clause_item (outer_for_lifetimes);
    2671          299 : }
    2672              : 
    2673              : // Parses a lifetime where clause item.
    2674              : template <typename ManagedTokenSource>
    2675              : std::unique_ptr<AST::LifetimeWhereClauseItem>
    2676            2 : Parser<ManagedTokenSource>::parse_lifetime_where_clause_item ()
    2677              : {
    2678            2 :   auto parsed_lifetime = parse_lifetime (false);
    2679            2 :   if (!parsed_lifetime)
    2680              :     {
    2681              :       // TODO: error here?
    2682            0 :       return nullptr;
    2683              :     }
    2684            2 :   auto lifetime = parsed_lifetime.value ();
    2685              : 
    2686            2 :   if (!skip_token (COLON))
    2687              :     {
    2688              :       // TODO: skip after somewhere
    2689            0 :       return nullptr;
    2690              :     }
    2691              : 
    2692            2 :   std::vector<AST::Lifetime> lifetime_bounds = parse_lifetime_bounds ();
    2693              :   // TODO: have end token passed in?
    2694              : 
    2695            2 :   location_t locus = lifetime.get_locus ();
    2696              : 
    2697              :   return std::unique_ptr<AST::LifetimeWhereClauseItem> (
    2698            2 :     new AST::LifetimeWhereClauseItem (std::move (lifetime),
    2699            2 :                                       std::move (lifetime_bounds), locus));
    2700            4 : }
    2701              : 
    2702              : // Parses a type bound where clause item.
    2703              : template <typename ManagedTokenSource>
    2704              : std::unique_ptr<AST::TypeBoundWhereClauseItem>
    2705          297 : Parser<ManagedTokenSource>::parse_type_bound_where_clause_item (
    2706              :   const std::vector<AST::LifetimeParam> &outer_for_lifetimes)
    2707              : {
    2708          297 :   std::vector<AST::LifetimeParam> for_lifetimes = outer_for_lifetimes;
    2709              : 
    2710          297 :   std::unique_ptr<AST::Type> type = parse_type ();
    2711          297 :   if (type == nullptr)
    2712              :     {
    2713            0 :       return nullptr;
    2714              :     }
    2715              : 
    2716          297 :   if (!skip_token (COLON))
    2717              :     {
    2718              :       // TODO: skip after somewhere
    2719            0 :       return nullptr;
    2720              :     }
    2721              : 
    2722          594 :   if (lexer.peek_token ()->get_id () == FOR)
    2723              :     {
    2724            8 :       auto for_lifetimes_inner = parse_for_lifetimes ();
    2725            8 :       for_lifetimes.insert (for_lifetimes.end (), for_lifetimes_inner.begin (),
    2726              :                             for_lifetimes_inner.end ());
    2727            8 :     }
    2728              : 
    2729              :   // parse type param bounds if they exist
    2730          297 :   std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds
    2731              :     = parse_type_param_bounds ();
    2732              : 
    2733          297 :   location_t locus = lexer.peek_token ()->get_locus ();
    2734              : 
    2735              :   return std::unique_ptr<AST::TypeBoundWhereClauseItem> (
    2736          297 :     new AST::TypeBoundWhereClauseItem (std::move (for_lifetimes),
    2737              :                                        std::move (type),
    2738          297 :                                        std::move (type_param_bounds), locus));
    2739          297 : }
    2740              : 
    2741              : // Parses a for lifetimes clause, including the for keyword and angle
    2742              : // brackets.
    2743              : template <typename ManagedTokenSource>
    2744              : std::vector<AST::LifetimeParam>
    2745           26 : Parser<ManagedTokenSource>::parse_for_lifetimes ()
    2746              : {
    2747           26 :   std::vector<AST::LifetimeParam> params;
    2748              : 
    2749           26 :   if (!skip_token (FOR))
    2750              :     {
    2751              :       // skip after somewhere?
    2752              :       return params;
    2753              :     }
    2754              : 
    2755           26 :   if (!skip_token (LEFT_ANGLE))
    2756              :     {
    2757              :       // skip after somewhere?
    2758              :       return params;
    2759              :     }
    2760              : 
    2761              :   /* cannot specify end token due to parsing problems with '>' tokens being
    2762              :    * nested */
    2763           26 :   params = parse_lifetime_params_objs (Parse::Utils::is_right_angle_tok);
    2764              : 
    2765           26 :   if (!skip_generics_right_angle ())
    2766              :     {
    2767              :       // DEBUG
    2768            0 :       rust_debug ("failed to skip generics right angle after (supposedly) "
    2769              :                   "finished parsing where clause items");
    2770              :       // ok, well this gets called.
    2771              : 
    2772              :       // skip after somewhere?
    2773            0 :       return params;
    2774              :     }
    2775              : 
    2776              :   return params;
    2777              : }
    2778              : 
    2779              : // Parses type parameter bounds in where clause or generic arguments.
    2780              : template <typename ManagedTokenSource>
    2781              : std::vector<std::unique_ptr<AST::TypeParamBound>>
    2782          944 : Parser<ManagedTokenSource>::parse_type_param_bounds ()
    2783              : {
    2784          944 :   std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
    2785              : 
    2786          944 :   std::unique_ptr<AST::TypeParamBound> initial_bound
    2787              :     = parse_type_param_bound ();
    2788              : 
    2789              :   // quick exit if null
    2790          944 :   if (initial_bound == nullptr)
    2791              :     {
    2792              :       /* error? type param bounds must have at least one term, but are bounds
    2793              :        * optional? */
    2794              :       return type_param_bounds;
    2795              :     }
    2796          944 :   type_param_bounds.push_back (std::move (initial_bound));
    2797              : 
    2798         1894 :   while (lexer.peek_token ()->get_id () == PLUS)
    2799              :     {
    2800            3 :       lexer.skip_token ();
    2801              : 
    2802            3 :       std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
    2803            3 :       if (bound == nullptr)
    2804              :         {
    2805              :           /* not an error: bound is allowed to be null as trailing plus is
    2806              :            * allowed */
    2807              :           return type_param_bounds;
    2808              :         }
    2809              : 
    2810            3 :       type_param_bounds.push_back (std::move (bound));
    2811              :     }
    2812              : 
    2813          944 :   type_param_bounds.shrink_to_fit ();
    2814              :   return type_param_bounds;
    2815          944 : }
    2816              : 
    2817              : /* Parses type parameter bounds in where clause or generic arguments, with end
    2818              :  * token handling. */
    2819              : template <typename ManagedTokenSource>
    2820              : template <typename EndTokenPred>
    2821              : std::vector<std::unique_ptr<AST::TypeParamBound>>
    2822          584 : Parser<ManagedTokenSource>::parse_type_param_bounds (EndTokenPred is_end_token)
    2823              : {
    2824          584 :   std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
    2825              : 
    2826          584 :   std::unique_ptr<AST::TypeParamBound> initial_bound
    2827              :     = parse_type_param_bound ();
    2828              : 
    2829              :   // quick exit if null
    2830          584 :   if (initial_bound == nullptr)
    2831              :     {
    2832              :       /* error? type param bounds must have at least one term, but are bounds
    2833              :        * optional? */
    2834            0 :       return type_param_bounds;
    2835              :     }
    2836          584 :   type_param_bounds.push_back (std::move (initial_bound));
    2837              : 
    2838         1312 :   while (lexer.peek_token ()->get_id () == PLUS)
    2839              :     {
    2840           72 :       lexer.skip_token ();
    2841              : 
    2842              :       // break if end token character
    2843          144 :       if (is_end_token (lexer.peek_token ()->get_id ()))
    2844              :         break;
    2845              : 
    2846           72 :       std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
    2847           72 :       if (bound == nullptr)
    2848              :         {
    2849              :           // TODO how wise is it to ditch all bounds if only one failed?
    2850            0 :           Error error (lexer.peek_token ()->get_locus (),
    2851              :                        "failed to parse type param bound in type param bounds");
    2852            0 :           add_error (std::move (error));
    2853              : 
    2854            0 :           return {};
    2855            0 :         }
    2856              : 
    2857           72 :       type_param_bounds.push_back (std::move (bound));
    2858              :     }
    2859              : 
    2860          584 :   type_param_bounds.shrink_to_fit ();
    2861          584 :   return type_param_bounds;
    2862          584 : }
    2863              : 
    2864              : /* Parses a single type parameter bound in a where clause or generic argument.
    2865              :  * Does not parse the '+' between arguments. */
    2866              : template <typename ManagedTokenSource>
    2867              : std::unique_ptr<AST::TypeParamBound>
    2868         1623 : Parser<ManagedTokenSource>::parse_type_param_bound ()
    2869              : {
    2870              :   // shitty cheat way of determining lifetime or trait bound - test for
    2871              :   // lifetime
    2872         1623 :   const_TokenPtr t = lexer.peek_token ();
    2873         1623 :   switch (t->get_id ())
    2874              :     {
    2875           12 :     case LIFETIME:
    2876           12 :       return std::unique_ptr<AST::Lifetime> (
    2877           24 :         new AST::Lifetime (parse_lifetime (false).value ()));
    2878         1611 :     case LEFT_PAREN:
    2879              :     case QUESTION_MARK:
    2880              :     case FOR:
    2881              :     case IDENTIFIER:
    2882              :     case SUPER:
    2883              :     case SELF:
    2884              :     case SELF_ALIAS:
    2885              :     case CRATE:
    2886              :     case DOLLAR_SIGN:
    2887              :     case SCOPE_RESOLUTION:
    2888         1611 :       return parse_trait_bound ();
    2889            0 :     default:
    2890              :       // don't error - assume this is fine TODO
    2891            0 :       return nullptr;
    2892              :     }
    2893         1623 : }
    2894              : 
    2895              : // Parses a trait bound type param bound.
    2896              : template <typename ManagedTokenSource>
    2897              : std::unique_ptr<AST::TraitBound>
    2898         1917 : Parser<ManagedTokenSource>::parse_trait_bound ()
    2899              : {
    2900         1917 :   bool has_parens = false;
    2901         1917 :   bool has_question_mark = false;
    2902              : 
    2903         3834 :   location_t locus = lexer.peek_token ()->get_locus ();
    2904              : 
    2905              :   /* parse optional `for lifetimes`. */
    2906         1917 :   std::vector<AST::LifetimeParam> for_lifetimes;
    2907         3834 :   if (lexer.peek_token ()->get_id () == FOR)
    2908           14 :     for_lifetimes = parse_for_lifetimes ();
    2909              : 
    2910              :   // handle trait bound being in parentheses
    2911         3834 :   if (lexer.peek_token ()->get_id () == LEFT_PAREN)
    2912              :     {
    2913            0 :       has_parens = true;
    2914            0 :       lexer.skip_token ();
    2915              :     }
    2916              : 
    2917              :   // handle having question mark (optional)
    2918         3834 :   if (lexer.peek_token ()->get_id () == QUESTION_MARK)
    2919              :     {
    2920          309 :       has_question_mark = true;
    2921          309 :       lexer.skip_token ();
    2922              :     }
    2923              : 
    2924              :   // handle TypePath
    2925         1917 :   AST::TypePath type_path = parse_type_path ();
    2926         1917 :   if (type_path.is_error ())
    2927            2 :     return nullptr;
    2928              : 
    2929              :   // handle closing parentheses
    2930         1915 :   if (has_parens)
    2931              :     {
    2932            0 :       if (!skip_token (RIGHT_PAREN))
    2933              :         {
    2934            0 :           return nullptr;
    2935              :         }
    2936              :     }
    2937              : 
    2938              :   return std::unique_ptr<AST::TraitBound> (
    2939         1915 :     new AST::TraitBound (std::move (type_path), locus, has_parens,
    2940         1915 :                          has_question_mark, std::move (for_lifetimes)));
    2941         1917 : }
    2942              : 
    2943              : // Parses lifetime bounds.
    2944              : template <typename ManagedTokenSource>
    2945              : std::vector<AST::Lifetime>
    2946            2 : Parser<ManagedTokenSource>::parse_lifetime_bounds ()
    2947              : {
    2948            2 :   std::vector<AST::Lifetime> lifetime_bounds;
    2949              : 
    2950            2 :   while (true)
    2951              :     {
    2952            2 :       auto lifetime = parse_lifetime (false);
    2953              : 
    2954              :       // quick exit for parsing failure
    2955            2 :       if (!lifetime)
    2956              :         break;
    2957              : 
    2958            2 :       lifetime_bounds.push_back (std::move (lifetime.value ()));
    2959              : 
    2960              :       /* plus is maybe not allowed at end - spec defines it weirdly, so
    2961              :        * assuming allowed at end */
    2962            4 :       if (lexer.peek_token ()->get_id () != PLUS)
    2963              :         break;
    2964              : 
    2965            0 :       lexer.skip_token ();
    2966              :     }
    2967              : 
    2968            2 :   lifetime_bounds.shrink_to_fit ();
    2969            2 :   return lifetime_bounds;
    2970              : }
    2971              : 
    2972              : // Parses lifetime bounds, with added check for ending token.
    2973              : template <typename ManagedTokenSource>
    2974              : template <typename EndTokenPred>
    2975              : std::vector<AST::Lifetime>
    2976            1 : Parser<ManagedTokenSource>::parse_lifetime_bounds (EndTokenPred is_end_token)
    2977              : {
    2978            1 :   std::vector<AST::Lifetime> lifetime_bounds;
    2979              : 
    2980            4 :   while (!is_end_token (lexer.peek_token ()->get_id ()))
    2981              :     {
    2982            1 :       auto lifetime = parse_lifetime (false);
    2983              : 
    2984            1 :       if (!lifetime)
    2985              :         {
    2986              :           /* TODO: is it worth throwing away all lifetime bound info just
    2987              :            * because one failed? */
    2988            0 :           Error error (lexer.peek_token ()->get_locus (),
    2989              :                        "failed to parse lifetime in lifetime bounds");
    2990            0 :           add_error (std::move (error));
    2991              : 
    2992            0 :           return {};
    2993            0 :         }
    2994              : 
    2995            1 :       lifetime_bounds.push_back (std::move (lifetime.value ()));
    2996              : 
    2997              :       /* plus is maybe not allowed at end - spec defines it weirdly, so
    2998              :        * assuming allowed at end */
    2999            2 :       if (lexer.peek_token ()->get_id () != PLUS)
    3000              :         break;
    3001              : 
    3002            0 :       lexer.skip_token ();
    3003              :     }
    3004              : 
    3005            1 :   lifetime_bounds.shrink_to_fit ();
    3006            1 :   return lifetime_bounds;
    3007            1 : }
    3008              : 
    3009              : /* Parses a lifetime token (named, 'static, or '_). Also handles lifetime not
    3010              :  * existing. */
    3011              : template <typename ManagedTokenSource>
    3012              : tl::expected<AST::Lifetime, Parse::Error::Lifetime>
    3013         4486 : Parser<ManagedTokenSource>::parse_lifetime (bool allow_elided)
    3014              : {
    3015         4486 :   const_TokenPtr lifetime_tok = lexer.peek_token ();
    3016         4486 :   if (lifetime_tok->get_id () != LIFETIME)
    3017              :     {
    3018         3679 :       if (allow_elided)
    3019              :         {
    3020            0 :           return AST::Lifetime::elided ();
    3021              :         }
    3022              :       else
    3023              :         {
    3024         3679 :           return tl::make_unexpected<Parse::Error::Lifetime> ({});
    3025              :         }
    3026              :     }
    3027          807 :   lexer.skip_token ();
    3028              : 
    3029         1614 :   return lifetime_from_token (lifetime_tok);
    3030         4486 : }
    3031              : 
    3032              : template <typename ManagedTokenSource>
    3033              : AST::Lifetime
    3034          847 : Parser<ManagedTokenSource>::lifetime_from_token (const_TokenPtr tok)
    3035              : {
    3036          847 :   location_t locus = tok->get_locus ();
    3037          847 :   std::string lifetime_ident = tok->get_str ();
    3038              : 
    3039          847 :   if (lifetime_ident == "static")
    3040              :     {
    3041           85 :       return AST::Lifetime (AST::Lifetime::STATIC, "", locus);
    3042              :     }
    3043          762 :   else if (lifetime_ident == "_")
    3044              :     {
    3045              :       // Explicitly and implicitly elided lifetimes follow the same rules.
    3046           43 :       return AST::Lifetime (AST::Lifetime::WILDCARD, "", locus);
    3047              :     }
    3048              :   else
    3049              :     {
    3050         1438 :       return AST::Lifetime (AST::Lifetime::NAMED, std::move (lifetime_ident),
    3051          719 :                             locus);
    3052              :     }
    3053          847 : }
    3054              : 
    3055              : template <typename ManagedTokenSource>
    3056              : std::unique_ptr<AST::ExternalTypeItem>
    3057            4 : Parser<ManagedTokenSource>::parse_external_type_item (AST::Visibility vis,
    3058              :                                                       AST::AttrVec outer_attrs)
    3059              : {
    3060            4 :   location_t locus = lexer.peek_token ()->get_locus ();
    3061            4 :   skip_token (TYPE);
    3062              : 
    3063            4 :   const_TokenPtr alias_name_tok = expect_token (IDENTIFIER);
    3064            4 :   if (alias_name_tok == nullptr)
    3065              :     {
    3066            0 :       Error error (lexer.peek_token ()->get_locus (),
    3067              :                    "could not parse identifier in external opaque type");
    3068            0 :       add_error (std::move (error));
    3069              : 
    3070            0 :       skip_after_semicolon ();
    3071            0 :       return nullptr;
    3072            0 :     }
    3073              : 
    3074            4 :   if (!skip_token (SEMICOLON))
    3075            1 :     return nullptr;
    3076              : 
    3077              :   return std::unique_ptr<AST::ExternalTypeItem> (
    3078            9 :     new AST::ExternalTypeItem (alias_name_tok->get_str (), std::move (vis),
    3079            3 :                                std::move (outer_attrs), std::move (locus)));
    3080            4 : }
    3081              : 
    3082              : // Parses a "type alias" (typedef) item.
    3083              : template <typename ManagedTokenSource>
    3084              : std::unique_ptr<AST::TypeAlias>
    3085         1298 : Parser<ManagedTokenSource>::parse_type_alias (AST::Visibility vis,
    3086              :                                               AST::AttrVec outer_attrs)
    3087              : {
    3088         1298 :   location_t locus = lexer.peek_token ()->get_locus ();
    3089         1298 :   skip_token (TYPE);
    3090              : 
    3091              :   // TODO: use this token for identifier when finished that
    3092         1298 :   const_TokenPtr alias_name_tok = expect_token (IDENTIFIER);
    3093         1298 :   if (alias_name_tok == nullptr)
    3094              :     {
    3095            0 :       Error error (lexer.peek_token ()->get_locus (),
    3096              :                    "could not parse identifier in type alias");
    3097            0 :       add_error (std::move (error));
    3098              : 
    3099            0 :       skip_after_semicolon ();
    3100            0 :       return nullptr;
    3101            0 :     }
    3102         1298 :   Identifier alias_name{alias_name_tok};
    3103              : 
    3104              :   // parse generic params, which may not exist
    3105         1298 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    3106              :     = parse_generic_params_in_angles ();
    3107              : 
    3108              :   // parse where clause, which may not exist
    3109         1298 :   AST::WhereClause where_clause = parse_where_clause ();
    3110              : 
    3111         1298 :   if (!skip_token (EQUAL))
    3112              :     {
    3113            0 :       skip_after_semicolon ();
    3114            0 :       return nullptr;
    3115              :     }
    3116              : 
    3117         1298 :   std::unique_ptr<AST::Type> type_to_alias = parse_type ();
    3118              : 
    3119         1298 :   if (!skip_token (SEMICOLON))
    3120              :     {
    3121              :       // should be skipping past this, not the next line
    3122            0 :       return nullptr;
    3123              :     }
    3124              : 
    3125              :   return std::unique_ptr<AST::TypeAlias> (
    3126         1298 :     new AST::TypeAlias (std::move (alias_name), std::move (generic_params),
    3127              :                         std::move (where_clause), std::move (type_to_alias),
    3128         1298 :                         std::move (vis), std::move (outer_attrs), locus));
    3129         2596 : }
    3130              : 
    3131              : // Parse a struct item AST node.
    3132              : template <typename ManagedTokenSource>
    3133              : std::unique_ptr<AST::Struct>
    3134         2704 : Parser<ManagedTokenSource>::parse_struct (AST::Visibility vis,
    3135              :                                           AST::AttrVec outer_attrs)
    3136              : {
    3137              :   /* TODO: determine best way to parse the proper struct vs tuple struct -
    3138              :    * share most of initial constructs so lookahead might be impossible, and if
    3139              :    * not probably too expensive. Best way is probably unified parsing for the
    3140              :    * initial parts and then pass them in as params to more derived functions.
    3141              :    * Alternatively, just parse everything in this one function - do this if
    3142              :    * function not too long. */
    3143              : 
    3144              :   /* Proper struct <- 'struct' IDENTIFIER generic_params? where_clause? ( '{'
    3145              :    * struct_fields? '}' | ';' ) */
    3146              :   /* Tuple struct <- 'struct' IDENTIFIER generic_params? '(' tuple_fields? ')'
    3147              :    * where_clause? ';' */
    3148         2704 :   location_t locus = lexer.peek_token ()->get_locus ();
    3149         2704 :   skip_token (STRUCT_KW);
    3150              : 
    3151              :   // parse struct name
    3152         2704 :   const_TokenPtr name_tok = expect_token (IDENTIFIER);
    3153         2704 :   if (name_tok == nullptr)
    3154              :     {
    3155              :       // skip after somewhere?
    3156            1 :       return nullptr;
    3157              :     }
    3158         2703 :   Identifier struct_name{name_tok};
    3159              : 
    3160              :   // parse generic params, which may or may not exist
    3161         2703 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    3162              :     = parse_generic_params_in_angles ();
    3163              : 
    3164              :   // branch on next token - determines whether proper struct or tuple struct
    3165         5406 :   if (lexer.peek_token ()->get_id () == LEFT_PAREN)
    3166              :     {
    3167              :       // tuple struct
    3168              : 
    3169              :       // skip left parenthesis
    3170          992 :       lexer.skip_token ();
    3171              : 
    3172              :       // parse tuple fields
    3173          992 :       std::vector<AST::TupleField> tuple_fields;
    3174              :       // Might be empty tuple for unit tuple struct.
    3175         1984 :       if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    3176           23 :         tuple_fields = std::vector<AST::TupleField> ();
    3177              :       else
    3178          969 :         tuple_fields = parse_tuple_fields ();
    3179              : 
    3180              :       // tuple parameters must have closing parenthesis
    3181          992 :       if (!skip_token (RIGHT_PAREN))
    3182              :         {
    3183            1 :           skip_after_semicolon ();
    3184            1 :           return nullptr;
    3185              :         }
    3186              : 
    3187              :       // parse where clause, which is optional
    3188          991 :       AST::WhereClause where_clause = parse_where_clause ();
    3189              : 
    3190          991 :       if (!skip_token (SEMICOLON))
    3191              :         {
    3192              :           // can't skip after semicolon because it's meant to be here
    3193            0 :           return nullptr;
    3194              :         }
    3195              : 
    3196          991 :       return std::unique_ptr<AST::TupleStruct> (
    3197          991 :         new AST::TupleStruct (std::move (tuple_fields), std::move (struct_name),
    3198              :                               std::move (generic_params),
    3199              :                               std::move (where_clause), std::move (vis),
    3200          991 :                               std::move (outer_attrs), locus));
    3201          992 :     }
    3202              : 
    3203              :   // assume it is a proper struct being parsed and continue outside of switch
    3204              :   // - label only here to suppress warning
    3205              : 
    3206              :   // parse where clause, which is optional
    3207         1711 :   AST::WhereClause where_clause = parse_where_clause ();
    3208              : 
    3209              :   // branch on next token - determines whether struct is a unit struct
    3210         1711 :   const_TokenPtr t = lexer.peek_token ();
    3211         1711 :   switch (t->get_id ())
    3212              :     {
    3213          951 :     case LEFT_CURLY:
    3214              :       {
    3215              :         // struct with body
    3216              : 
    3217              :         // skip curly bracket
    3218          951 :         lexer.skip_token ();
    3219              : 
    3220              :         // parse struct fields, if any
    3221          951 :         std::vector<AST::StructField> struct_fields
    3222              :           = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
    3223              : 
    3224          951 :         if (!skip_token (RIGHT_CURLY))
    3225              :           {
    3226              :             // skip somewhere?
    3227            0 :             return nullptr;
    3228              :           }
    3229              : 
    3230          951 :         return std::unique_ptr<AST::StructStruct> (new AST::StructStruct (
    3231              :           std::move (struct_fields), std::move (struct_name),
    3232              :           std::move (generic_params), std::move (where_clause), false,
    3233          951 :           std::move (vis), std::move (outer_attrs), locus));
    3234          951 :       }
    3235          759 :     case SEMICOLON:
    3236              :       // unit struct declaration
    3237              : 
    3238          759 :       lexer.skip_token ();
    3239              : 
    3240          759 :       return std::unique_ptr<AST::StructStruct> (
    3241         1518 :         new AST::StructStruct (std::move (struct_name),
    3242              :                                std::move (generic_params),
    3243              :                                std::move (where_clause), std::move (vis),
    3244          759 :                                std::move (outer_attrs), locus));
    3245            1 :     default:
    3246            1 :       add_error (Error (t->get_locus (),
    3247              :                         "unexpected token %qs in struct declaration",
    3248              :                         t->get_token_description ()));
    3249              : 
    3250              :       // skip somewhere?
    3251            1 :       return nullptr;
    3252              :     }
    3253         2703 : }
    3254              : 
    3255              : // Parses struct fields in struct declarations.
    3256              : template <typename ManagedTokenSource>
    3257              : std::vector<AST::StructField>
    3258            0 : Parser<ManagedTokenSource>::parse_struct_fields ()
    3259              : {
    3260            0 :   std::vector<AST::StructField> fields;
    3261              : 
    3262            0 :   AST::StructField initial_field = parse_struct_field ();
    3263              : 
    3264              :   // Return empty field list if no field there
    3265            0 :   if (initial_field.is_error ())
    3266              :     return fields;
    3267              : 
    3268            0 :   fields.push_back (std::move (initial_field));
    3269              : 
    3270            0 :   while (lexer.peek_token ()->get_id () == COMMA)
    3271              :     {
    3272            0 :       lexer.skip_token ();
    3273              : 
    3274            0 :       AST::StructField field = parse_struct_field ();
    3275              : 
    3276            0 :       if (field.is_error ())
    3277              :         {
    3278              :           // would occur with trailing comma, so allowed
    3279              :           break;
    3280              :         }
    3281              : 
    3282            0 :       fields.push_back (std::move (field));
    3283              :     }
    3284              : 
    3285            0 :   fields.shrink_to_fit ();
    3286              :   return fields;
    3287              :   // TODO: template if possible (parse_non_ptr_seq)
    3288            0 : }
    3289              : 
    3290              : // Parses struct fields in struct declarations.
    3291              : template <typename ManagedTokenSource>
    3292              : template <typename EndTokenPred>
    3293              : std::vector<AST::StructField>
    3294         1157 : Parser<ManagedTokenSource>::parse_struct_fields (EndTokenPred is_end_tok)
    3295              : {
    3296         1157 :   std::vector<AST::StructField> fields;
    3297              : 
    3298         1157 :   AST::StructField initial_field = parse_struct_field ();
    3299              : 
    3300              :   // Return empty field list if no field there
    3301         1157 :   if (initial_field.is_error ())
    3302           51 :     return fields;
    3303              : 
    3304         1106 :   fields.push_back (std::move (initial_field));
    3305              : 
    3306         4540 :   while (lexer.peek_token ()->get_id () == COMMA)
    3307              :     {
    3308         2085 :       lexer.skip_token ();
    3309              : 
    3310         4170 :       if (is_end_tok (lexer.peek_token ()->get_id ()))
    3311              :         break;
    3312              : 
    3313         1164 :       AST::StructField field = parse_struct_field ();
    3314         1164 :       if (field.is_error ())
    3315              :         {
    3316              :           /* TODO: should every field be ditched just because one couldn't be
    3317              :            * parsed? */
    3318            0 :           Error error (lexer.peek_token ()->get_locus (),
    3319              :                        "failed to parse struct field in struct fields");
    3320            0 :           add_error (std::move (error));
    3321              : 
    3322            0 :           return {};
    3323            0 :         }
    3324              : 
    3325         1164 :       fields.push_back (std::move (field));
    3326              :     }
    3327              : 
    3328         1106 :   fields.shrink_to_fit ();
    3329         1106 :   return fields;
    3330              :   // TODO: template if possible (parse_non_ptr_seq)
    3331         1157 : }
    3332              : 
    3333              : // Parses a single struct field (in a struct definition). Does not parse
    3334              : // commas.
    3335              : template <typename ManagedTokenSource>
    3336              : AST::StructField
    3337         2321 : Parser<ManagedTokenSource>::parse_struct_field ()
    3338              : {
    3339              :   // parse outer attributes, if they exist
    3340         2321 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    3341              : 
    3342              :   // parse visibility, if it exists
    3343         2321 :   auto vis = parse_visibility ();
    3344         2321 :   if (!vis)
    3345            0 :     return AST::StructField::create_error ();
    3346              : 
    3347         2321 :   location_t locus = lexer.peek_token ()->get_locus ();
    3348              : 
    3349              :   // parse field name
    3350         2321 :   const_TokenPtr field_name_tok = lexer.peek_token ();
    3351         2321 :   if (field_name_tok->get_id () != IDENTIFIER)
    3352              :     {
    3353              :       // if not identifier, assumes there is no struct field and exits - not
    3354              :       // necessarily error
    3355           51 :       return AST::StructField::create_error ();
    3356              :     }
    3357         2270 :   Identifier field_name{field_name_tok};
    3358         2270 :   lexer.skip_token ();
    3359              : 
    3360         2270 :   if (!skip_token (COLON))
    3361              :     {
    3362              :       // skip after somewhere?
    3363            0 :       return AST::StructField::create_error ();
    3364              :     }
    3365              : 
    3366              :   // parse field type - this is required
    3367         2270 :   std::unique_ptr<AST::Type> field_type = parse_type ();
    3368         2270 :   if (field_type == nullptr)
    3369              :     {
    3370            0 :       Error error (lexer.peek_token ()->get_locus (),
    3371              :                    "could not parse type in struct field definition");
    3372            0 :       add_error (std::move (error));
    3373              : 
    3374              :       // skip after somewhere
    3375            0 :       return AST::StructField::create_error ();
    3376            0 :     }
    3377              : 
    3378         4540 :   return AST::StructField (std::move (field_name), std::move (field_type),
    3379         2270 :                            std::move (vis.value ()), locus,
    3380         2270 :                            std::move (outer_attrs));
    3381         9182 : }
    3382              : 
    3383              : // Parses tuple fields in tuple/tuple struct declarations.
    3384              : template <typename ManagedTokenSource>
    3385              : std::vector<AST::TupleField>
    3386         1395 : Parser<ManagedTokenSource>::parse_tuple_fields ()
    3387              : {
    3388         1395 :   std::vector<AST::TupleField> fields;
    3389              : 
    3390         1395 :   AST::TupleField initial_field = parse_tuple_field ();
    3391              : 
    3392              :   // Return empty field list if no field there
    3393         1395 :   if (initial_field.is_error ())
    3394              :     {
    3395            1 :       return fields;
    3396              :     }
    3397              : 
    3398         1394 :   fields.push_back (std::move (initial_field));
    3399              : 
    3400              :   // maybe think of a better control structure here - do-while with an initial
    3401              :   // error state? basically, loop through field list until can't find any more
    3402              :   // params HACK: all current syntax uses of tuple fields have them ending
    3403              :   // with a right paren token
    3404         1394 :   const_TokenPtr t = lexer.peek_token ();
    3405         2133 :   while (t->get_id () == COMMA)
    3406              :     {
    3407              :       // skip comma if applies - e.g. trailing comma
    3408          740 :       lexer.skip_token ();
    3409              : 
    3410              :       // break out due to right paren if it exists
    3411         1480 :       if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    3412              :         {
    3413              :           break;
    3414              :         }
    3415              : 
    3416          739 :       AST::TupleField field = parse_tuple_field ();
    3417          739 :       if (field.is_error ())
    3418              :         {
    3419            0 :           Error error (lexer.peek_token ()->get_locus (),
    3420              :                        "failed to parse tuple field in tuple fields");
    3421            0 :           add_error (std::move (error));
    3422              : 
    3423            0 :           return std::vector<AST::TupleField> ();
    3424            0 :         }
    3425              : 
    3426          739 :       fields.push_back (std::move (field));
    3427              : 
    3428          739 :       t = lexer.peek_token ();
    3429              :     }
    3430              : 
    3431         1394 :   fields.shrink_to_fit ();
    3432         1394 :   return fields;
    3433              : 
    3434              :   // TODO: this shares basically all code with function params and struct
    3435              :   // fields
    3436              :   // - templates?
    3437         1395 : }
    3438              : 
    3439              : /* Parses a single tuple struct field in a tuple struct definition. Does not
    3440              :  * parse commas. */
    3441              : template <typename ManagedTokenSource>
    3442              : AST::TupleField
    3443         2134 : Parser<ManagedTokenSource>::parse_tuple_field ()
    3444              : {
    3445              :   // parse outer attributes if they exist
    3446         2134 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    3447              : 
    3448              :   // parse visibility if it exists
    3449         2134 :   auto visibility = parse_visibility ();
    3450         2134 :   if (!visibility)
    3451            0 :     return AST::TupleField::create_error ();
    3452              : 
    3453         2134 :   location_t locus = lexer.peek_token ()->get_locus ();
    3454              : 
    3455              :   // parse type, which is required
    3456         2134 :   std::unique_ptr<AST::Type> field_type = parse_type ();
    3457         2134 :   if (field_type == nullptr)
    3458              :     {
    3459              :       // error if null
    3460            1 :       Error error (lexer.peek_token ()->get_locus (),
    3461              :                    "could not parse type in tuple struct field");
    3462            1 :       add_error (std::move (error));
    3463              : 
    3464              :       // skip after something
    3465            1 :       return AST::TupleField::create_error ();
    3466            1 :     }
    3467              : 
    3468         2133 :   return AST::TupleField (std::move (field_type),
    3469         2133 :                           std::move (visibility.value ()), locus,
    3470         2133 :                           std::move (outer_attrs));
    3471         4268 : }
    3472              : 
    3473              : // Parses a Rust "enum" tagged union item definition.
    3474              : template <typename ManagedTokenSource>
    3475              : std::unique_ptr<AST::Enum>
    3476          558 : Parser<ManagedTokenSource>::parse_enum (AST::Visibility vis,
    3477              :                                         AST::AttrVec outer_attrs)
    3478              : {
    3479          558 :   location_t locus = lexer.peek_token ()->get_locus ();
    3480          558 :   skip_token (ENUM_KW);
    3481              : 
    3482              :   // parse enum name
    3483          558 :   const_TokenPtr enum_name_tok = expect_token (IDENTIFIER);
    3484          558 :   if (enum_name_tok == nullptr)
    3485            1 :     return nullptr;
    3486              : 
    3487          557 :   Identifier enum_name = {enum_name_tok};
    3488              : 
    3489              :   // parse generic params (of enum container, not enum variants) if they exist
    3490          557 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    3491              :     = parse_generic_params_in_angles ();
    3492              : 
    3493              :   // parse where clause if it exists
    3494          557 :   AST::WhereClause where_clause = parse_where_clause ();
    3495              : 
    3496          557 :   if (!skip_token (LEFT_CURLY))
    3497              :     {
    3498            0 :       skip_after_end_block ();
    3499            0 :       return nullptr;
    3500              :     }
    3501              : 
    3502              :   // parse actual enum variant definitions
    3503          557 :   std::vector<std::unique_ptr<AST::EnumItem>> enum_items
    3504              :     = parse_enum_items ([] (TokenId id) { return id == RIGHT_CURLY; });
    3505              : 
    3506          557 :   if (!skip_token (RIGHT_CURLY))
    3507              :     {
    3508            1 :       skip_after_end_block ();
    3509            1 :       return nullptr;
    3510              :     }
    3511              : 
    3512              :   return std::unique_ptr<AST::Enum> (
    3513          556 :     new AST::Enum (std::move (enum_name), std::move (vis),
    3514              :                    std::move (generic_params), std::move (where_clause),
    3515          556 :                    std::move (enum_items), std::move (outer_attrs), locus));
    3516         1114 : }
    3517              : 
    3518              : // Parses the enum variants inside an enum definiton.
    3519              : template <typename ManagedTokenSource>
    3520              : std::vector<std::unique_ptr<AST::EnumItem>>
    3521            0 : Parser<ManagedTokenSource>::parse_enum_items ()
    3522              : {
    3523            0 :   std::vector<std::unique_ptr<AST::EnumItem>> items;
    3524              : 
    3525            0 :   auto initial_item = parse_enum_item ();
    3526              : 
    3527              :   // Return empty item list if no field there
    3528            0 :   if (!initial_item)
    3529              :     return items;
    3530              : 
    3531            0 :   items.push_back (std::move (initial_item.value ()));
    3532              : 
    3533            0 :   while (lexer.peek_token ()->get_id () == COMMA)
    3534              :     {
    3535            0 :       lexer.skip_token ();
    3536              : 
    3537            0 :       auto item = parse_enum_item ();
    3538            0 :       if (!item)
    3539              :         {
    3540              :           // this would occur with a trailing comma, which is allowed
    3541              :           break;
    3542              :         }
    3543              : 
    3544            0 :       items.push_back (std::move (item.value ()));
    3545              :     }
    3546              : 
    3547            0 :   items.shrink_to_fit ();
    3548              :   return items;
    3549              : 
    3550              :   /* TODO: use template if doable (parse_non_ptr_sequence) */
    3551            0 : }
    3552              : 
    3553              : // Parses the enum variants inside an enum definiton.
    3554              : template <typename ManagedTokenSource>
    3555              : template <typename EndTokenPred>
    3556              : std::vector<std::unique_ptr<AST::EnumItem>>
    3557          557 : Parser<ManagedTokenSource>::parse_enum_items (EndTokenPred is_end_tok)
    3558              : {
    3559          557 :   std::vector<std::unique_ptr<AST::EnumItem>> items;
    3560              : 
    3561          557 :   auto initial_item = parse_enum_item ();
    3562              : 
    3563              :   // Return empty item list if no field there
    3564          557 :   if (!initial_item)
    3565           16 :     return items;
    3566              : 
    3567          541 :   items.push_back (std::move (initial_item.value ()));
    3568              : 
    3569         2580 :   while (lexer.peek_token ()->get_id () == COMMA)
    3570              :     {
    3571         1254 :       lexer.skip_token ();
    3572              : 
    3573         2508 :       if (is_end_tok (lexer.peek_token ()->get_id ()))
    3574              :         break;
    3575              : 
    3576          749 :       auto item = parse_enum_item ();
    3577          749 :       if (!item)
    3578              :         {
    3579              :           /* TODO should this ignore all successfully parsed enum items just
    3580              :            * because one failed? */
    3581            0 :           Error error (lexer.peek_token ()->get_locus (),
    3582              :                        "failed to parse enum item in enum items");
    3583            0 :           add_error (std::move (error));
    3584              : 
    3585            0 :           return {};
    3586            0 :         }
    3587              : 
    3588          749 :       items.push_back (std::move (item.value ()));
    3589              :     }
    3590              : 
    3591          541 :   items.shrink_to_fit ();
    3592          541 :   return items;
    3593              : 
    3594              :   /* TODO: use template if doable (parse_non_ptr_sequence) */
    3595          557 : }
    3596              : 
    3597              : /* Parses a single enum variant item in an enum definition. Does not parse
    3598              :  * commas. */
    3599              : template <typename ManagedTokenSource>
    3600              : tl::expected<std::unique_ptr<AST::EnumItem>, Parse::Error::EnumVariant>
    3601         1306 : Parser<ManagedTokenSource>::parse_enum_item ()
    3602              : {
    3603              :   // parse outer attributes if they exist
    3604         1306 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    3605              : 
    3606              :   // parse visibility, which may or may not exist
    3607         1306 :   auto vis_res = parse_visibility ();
    3608         1306 :   if (!vis_res)
    3609              :     return Parse::Error::EnumVariant::make_child_error ();
    3610         1306 :   auto vis = vis_res.value ();
    3611              : 
    3612              :   // parse name for enum item, which is required
    3613         1306 :   const_TokenPtr item_name_tok = lexer.peek_token ();
    3614         1306 :   if (item_name_tok->get_id () != IDENTIFIER)
    3615              :     {
    3616              :       // this may not be an error but it means there is no enum item here
    3617           32 :       return Parse::Error::EnumVariant::make_not_identifier (item_name_tok);
    3618              :     }
    3619         1290 :   lexer.skip_token ();
    3620         1290 :   Identifier item_name{item_name_tok};
    3621              : 
    3622              :   // branch based on next token
    3623         1290 :   const_TokenPtr t = lexer.peek_token ();
    3624         1290 :   switch (t->get_id ())
    3625              :     {
    3626          444 :     case LEFT_PAREN:
    3627              :       {
    3628              :         // tuple enum item
    3629          444 :         lexer.skip_token ();
    3630              : 
    3631          444 :         std::vector<AST::TupleField> tuple_fields;
    3632              :         // Might be empty tuple for unit tuple enum variant.
    3633          888 :         if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    3634           18 :           tuple_fields = std::vector<AST::TupleField> ();
    3635              :         else
    3636          426 :           tuple_fields = parse_tuple_fields ();
    3637              : 
    3638          444 :         if (!skip_token (RIGHT_PAREN))
    3639              :           {
    3640              :             // skip after somewhere
    3641              :             return Parse::Error::EnumVariant::make_unfinished_tuple_variant ();
    3642              :           }
    3643              : 
    3644          444 :         return std::unique_ptr<AST::EnumItemTuple> (new AST::EnumItemTuple (
    3645              :           std::move (item_name), std::move (vis), std::move (tuple_fields),
    3646          444 :           std::move (outer_attrs), item_name_tok->get_locus ()));
    3647          444 :       }
    3648           95 :     case LEFT_CURLY:
    3649              :       {
    3650              :         // struct enum item
    3651           95 :         lexer.skip_token ();
    3652              : 
    3653           95 :         std::vector<AST::StructField> struct_fields
    3654              :           = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
    3655              : 
    3656           95 :         if (!skip_token (RIGHT_CURLY))
    3657              :           {
    3658              :             // skip after somewhere
    3659              :             return Parse::Error::EnumVariant::make_unfinished_tuple_variant ();
    3660              :           }
    3661              : 
    3662           95 :         return std::unique_ptr<AST::EnumItemStruct> (new AST::EnumItemStruct (
    3663              :           std::move (item_name), std::move (vis), std::move (struct_fields),
    3664           95 :           std::move (outer_attrs), item_name_tok->get_locus ()));
    3665           95 :       }
    3666          283 :     case EQUAL:
    3667              :       {
    3668              :         // discriminant enum item
    3669          283 :         lexer.skip_token ();
    3670              : 
    3671          283 :         auto discriminant_expr = parse_expr ();
    3672          283 :         if (!discriminant_expr)
    3673              :           return Parse::Error::EnumVariant::make_child_error ();
    3674              : 
    3675          283 :         return std::make_unique<AST::EnumItemDiscriminant> (
    3676              :           std::move (item_name), std::move (vis),
    3677          283 :           std::move (discriminant_expr.value ()), std::move (outer_attrs),
    3678          566 :           item_name_tok->get_locus ());
    3679          283 :       }
    3680          468 :     default:
    3681              :       // regular enum with just an identifier
    3682          468 :       return std::make_unique<AST::EnumItem> (std::move (item_name),
    3683              :                                               std::move (vis),
    3684              :                                               std::move (outer_attrs),
    3685          468 :                                               item_name_tok->get_locus ());
    3686              :     }
    3687         3902 : }
    3688              : 
    3689              : // Parses a C-style (and C-compat) untagged union declaration.
    3690              : template <typename ManagedTokenSource>
    3691              : std::unique_ptr<AST::Union>
    3692          111 : Parser<ManagedTokenSource>::parse_union (AST::Visibility vis,
    3693              :                                          AST::AttrVec outer_attrs)
    3694              : {
    3695              :   /* hack - "weak keyword" by finding identifier called "union" (lookahead in
    3696              :    * item switch) */
    3697          111 :   const_TokenPtr union_keyword = expect_token (IDENTIFIER);
    3698          111 :   rust_assert (union_keyword->get_str () == Values::WeakKeywords::UNION);
    3699          111 :   location_t locus = union_keyword->get_locus ();
    3700              : 
    3701              :   // parse actual union name
    3702          111 :   const_TokenPtr union_name_tok = expect_token (IDENTIFIER);
    3703          111 :   if (union_name_tok == nullptr)
    3704              :     {
    3705            0 :       skip_after_next_block ();
    3706            0 :       return nullptr;
    3707              :     }
    3708          111 :   Identifier union_name{union_name_tok};
    3709              : 
    3710              :   // parse optional generic parameters
    3711          111 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    3712              :     = parse_generic_params_in_angles ();
    3713              : 
    3714              :   // parse optional where clause
    3715          111 :   AST::WhereClause where_clause = parse_where_clause ();
    3716              : 
    3717          111 :   if (!skip_token (LEFT_CURLY))
    3718              :     {
    3719            0 :       skip_after_end_block ();
    3720            0 :       return nullptr;
    3721              :     }
    3722              : 
    3723              :   /* parse union inner items as "struct fields" because hey, syntax reuse.
    3724              :    * Spec said so. */
    3725          111 :   std::vector<AST::StructField> union_fields
    3726              :     = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
    3727              : 
    3728          111 :   if (!skip_token (RIGHT_CURLY))
    3729              :     {
    3730              :       // skip after somewhere
    3731            0 :       return nullptr;
    3732              :     }
    3733              : 
    3734              :   return std::unique_ptr<AST::Union> (
    3735          111 :     new AST::Union (std::move (union_name), std::move (vis),
    3736              :                     std::move (generic_params), std::move (where_clause),
    3737          111 :                     std::move (union_fields), std::move (outer_attrs), locus));
    3738          333 : }
    3739              : 
    3740              : /* Parses a "constant item" (compile-time constant to maybe "inline"
    3741              :  * throughout the program - like constexpr). */
    3742              : template <typename ManagedTokenSource>
    3743              : std::unique_ptr<AST::ConstantItem>
    3744          554 : Parser<ManagedTokenSource>::parse_const_item (AST::Visibility vis,
    3745              :                                               AST::AttrVec outer_attrs)
    3746              : {
    3747          554 :   location_t locus = lexer.peek_token ()->get_locus ();
    3748          554 :   skip_token (CONST);
    3749              : 
    3750              :   /* get constant identifier - this is either a proper identifier or the _
    3751              :    * wildcard */
    3752          554 :   const_TokenPtr ident_tok = lexer.peek_token ();
    3753              :   // make default identifier the underscore wildcard one
    3754          554 :   std::string ident (Values::Keywords::UNDERSCORE);
    3755          554 :   switch (ident_tok->get_id ())
    3756              :     {
    3757          551 :     case IDENTIFIER:
    3758          551 :       ident = ident_tok->get_str ();
    3759          551 :       lexer.skip_token ();
    3760              :       break;
    3761            3 :     case UNDERSCORE:
    3762              :       // do nothing - identifier is already "_"
    3763            3 :       lexer.skip_token ();
    3764              :       break;
    3765            0 :     default:
    3766            0 :       add_error (
    3767            0 :         Error (ident_tok->get_locus (),
    3768              :                "expected item name (identifier or %<_%>) in constant item "
    3769              :                "declaration - found %qs",
    3770              :                ident_tok->get_token_description ()));
    3771              : 
    3772            0 :       skip_after_semicolon ();
    3773            0 :       return nullptr;
    3774              :     }
    3775              : 
    3776          554 :   if (!skip_token (COLON))
    3777              :     {
    3778            0 :       skip_after_semicolon ();
    3779            0 :       return nullptr;
    3780              :     }
    3781              : 
    3782              :   // parse constant type (required)
    3783          554 :   std::unique_ptr<AST::Type> type = parse_type ();
    3784              : 
    3785              :   // A const with no given expression value
    3786         1108 :   if (lexer.peek_token ()->get_id () == SEMICOLON)
    3787              :     {
    3788            4 :       lexer.skip_token ();
    3789              :       return std::unique_ptr<AST::ConstantItem> (
    3790            8 :         new AST::ConstantItem (std::move (ident), std::move (vis),
    3791              :                                std::move (type), std::move (outer_attrs),
    3792            4 :                                locus));
    3793              :     }
    3794              : 
    3795          550 :   if (!skip_token (EQUAL))
    3796              :     {
    3797            0 :       skip_after_semicolon ();
    3798            0 :       return nullptr;
    3799              :     }
    3800              : 
    3801              :   // parse constant expression (required)
    3802          550 :   auto expr = parse_expr ();
    3803          550 :   if (!expr)
    3804            1 :     return nullptr;
    3805              : 
    3806          549 :   if (!skip_token (SEMICOLON))
    3807              :     {
    3808              :       // skip somewhere?
    3809            0 :       return nullptr;
    3810              :     }
    3811              : 
    3812              :   return std::unique_ptr<AST::ConstantItem> (
    3813         1098 :     new AST::ConstantItem (std::move (ident), std::move (vis), std::move (type),
    3814          549 :                            std::move (expr.value ()), std::move (outer_attrs),
    3815          549 :                            locus));
    3816         1108 : }
    3817              : 
    3818              : // Parses a "static item" (static storage item, with 'static lifetime).
    3819              : template <typename ManagedTokenSource>
    3820              : std::unique_ptr<AST::StaticItem>
    3821           68 : Parser<ManagedTokenSource>::parse_static_item (AST::Visibility vis,
    3822              :                                                AST::AttrVec outer_attrs)
    3823              : {
    3824           68 :   location_t locus = lexer.peek_token ()->get_locus ();
    3825           68 :   skip_token (STATIC_KW);
    3826              : 
    3827              :   // determine whether static item is mutable
    3828           68 :   bool is_mut = false;
    3829          136 :   if (lexer.peek_token ()->get_id () == MUT)
    3830              :     {
    3831            4 :       is_mut = true;
    3832            4 :       lexer.skip_token ();
    3833              :     }
    3834              : 
    3835           68 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    3836           68 :   if (ident_tok == nullptr)
    3837            1 :     return nullptr;
    3838              : 
    3839           67 :   Identifier ident{ident_tok};
    3840              : 
    3841           67 :   if (!skip_token (COLON))
    3842              :     {
    3843            1 :       skip_after_semicolon ();
    3844            1 :       return nullptr;
    3845              :     }
    3846              : 
    3847              :   // parse static item type (required)
    3848           66 :   std::unique_ptr<AST::Type> type = parse_type ();
    3849              : 
    3850           66 :   if (!skip_token (EQUAL))
    3851              :     {
    3852            0 :       skip_after_semicolon ();
    3853            0 :       return nullptr;
    3854              :     }
    3855              : 
    3856              :   // parse static item expression (required)
    3857           66 :   auto expr = parse_expr ();
    3858           66 :   if (!expr)
    3859            1 :     return nullptr;
    3860              : 
    3861           65 :   if (!skip_token (SEMICOLON))
    3862              :     {
    3863              :       // skip after somewhere
    3864            0 :       return nullptr;
    3865              :     }
    3866              : 
    3867              :   return std::unique_ptr<AST::StaticItem> (
    3868          130 :     new AST::StaticItem (std::move (ident), is_mut, std::move (type),
    3869           65 :                          std::move (expr.value ()), std::move (vis),
    3870           65 :                          std::move (outer_attrs), locus));
    3871          133 : }
    3872              : 
    3873              : // Parses a trait definition item, including unsafe ones.
    3874              : template <typename ManagedTokenSource>
    3875              : std::unique_ptr<AST::Trait>
    3876         3930 : Parser<ManagedTokenSource>::parse_trait (AST::Visibility vis,
    3877              :                                          AST::AttrVec outer_attrs)
    3878              : {
    3879         3930 :   location_t locus = lexer.peek_token ()->get_locus ();
    3880         3930 :   bool is_unsafe = false;
    3881         3930 :   bool is_auto_trait = false;
    3882              : 
    3883         7860 :   if (lexer.peek_token ()->get_id () == UNSAFE)
    3884              :     {
    3885           56 :       is_unsafe = true;
    3886           56 :       lexer.skip_token ();
    3887              :     }
    3888              : 
    3889         7860 :   if (lexer.peek_token ()->get_id () == AUTO)
    3890              :     {
    3891           20 :       is_auto_trait = true;
    3892           20 :       lexer.skip_token ();
    3893              :     }
    3894              : 
    3895         3930 :   skip_token (TRAIT);
    3896              : 
    3897              :   // parse trait name
    3898         3930 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    3899         3930 :   if (ident_tok == nullptr)
    3900            0 :     return nullptr;
    3901              : 
    3902         3930 :   Identifier ident{ident_tok};
    3903              : 
    3904              :   // parse generic parameters (if they exist)
    3905         3930 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    3906              :     = parse_generic_params_in_angles ();
    3907              : 
    3908              :   // create placeholder type param bounds in case they don't exist
    3909         3930 :   std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
    3910              : 
    3911              :   // parse type param bounds (if they exist)
    3912         7860 :   if (lexer.peek_token ()->get_id () == COLON)
    3913              :     {
    3914          540 :       lexer.skip_token ();
    3915              : 
    3916          540 :       type_param_bounds = parse_type_param_bounds (
    3917           72 :         [] (TokenId id) { return id == WHERE || id == LEFT_CURLY; });
    3918              :       // type_param_bounds = parse_type_param_bounds ();
    3919              :     }
    3920              : 
    3921              :   // parse where clause (if it exists)
    3922         3930 :   AST::WhereClause where_clause = parse_where_clause ();
    3923              : 
    3924         3930 :   if (!skip_token (LEFT_CURLY))
    3925              :     {
    3926            0 :       skip_after_end_block ();
    3927            0 :       return nullptr;
    3928              :     }
    3929              : 
    3930              :   // parse inner attrs (if they exist)
    3931         3930 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    3932              : 
    3933              :   // parse trait items
    3934         3930 :   std::vector<std::unique_ptr<AST::AssociatedItem>> trait_items;
    3935              : 
    3936         3930 :   const_TokenPtr t = lexer.peek_token ();
    3937         7296 :   while (t->get_id () != RIGHT_CURLY)
    3938              :     {
    3939         3366 :       std::unique_ptr<AST::AssociatedItem> trait_item = parse_trait_item ();
    3940              : 
    3941         3366 :       if (trait_item == nullptr)
    3942              :         {
    3943            0 :           Error error (lexer.peek_token ()->get_locus (),
    3944              :                        "failed to parse trait item in trait");
    3945            0 :           add_error (std::move (error));
    3946              : 
    3947            0 :           return nullptr;
    3948            0 :         }
    3949         3366 :       trait_items.push_back (std::move (trait_item));
    3950              : 
    3951         3366 :       t = lexer.peek_token ();
    3952              :     }
    3953              : 
    3954         3930 :   if (!skip_token (RIGHT_CURLY))
    3955              :     {
    3956              :       // skip after something
    3957            0 :       return nullptr;
    3958              :     }
    3959              : 
    3960         3930 :   trait_items.shrink_to_fit ();
    3961              :   return std::unique_ptr<AST::Trait> (
    3962         3930 :     new AST::Trait (std::move (ident), is_unsafe, is_auto_trait,
    3963              :                     std::move (generic_params), std::move (type_param_bounds),
    3964              :                     std::move (where_clause), std::move (trait_items),
    3965              :                     std::move (vis), std::move (outer_attrs),
    3966         3930 :                     std::move (inner_attrs), locus));
    3967         7860 : }
    3968              : 
    3969              : // Parses a trait item used inside traits (not trait, the Item).
    3970              : template <typename ManagedTokenSource>
    3971              : std::unique_ptr<AST::AssociatedItem>
    3972         3368 : Parser<ManagedTokenSource>::parse_trait_item ()
    3973              : {
    3974              :   // parse outer attributes (if they exist)
    3975         3368 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    3976              : 
    3977         3368 :   auto vis_res = parse_visibility ();
    3978         3368 :   if (!vis_res)
    3979            0 :     return nullptr;
    3980              : 
    3981         3368 :   auto vis = vis_res.value ();
    3982              : 
    3983              :   // lookahead to determine what type of trait item to parse
    3984         3368 :   const_TokenPtr tok = lexer.peek_token ();
    3985         3368 :   switch (tok->get_id ())
    3986              :     {
    3987            0 :     case SUPER:
    3988              :     case SELF:
    3989              :     case CRATE:
    3990              :     case DOLLAR_SIGN:
    3991              :       // these seem to be SimplePath tokens, so this is a macro invocation
    3992              :       // semi
    3993            0 :       return parse_macro_invocation_semi (std::move (outer_attrs));
    3994            1 :     case IDENTIFIER:
    3995            2 :       if (lexer.peek_token ()->get_str () == Values::WeakKeywords::DEFAULT)
    3996            0 :         return parse_function (std::move (vis), std::move (outer_attrs));
    3997              :       else
    3998            2 :         return parse_macro_invocation_semi (std::move (outer_attrs));
    3999          748 :     case TYPE:
    4000          748 :       return parse_trait_type (std::move (outer_attrs), vis);
    4001           41 :     case CONST:
    4002              :       // disambiguate with function qualifier
    4003           82 :       if (lexer.peek_token (1)->get_id () == IDENTIFIER)
    4004              :         {
    4005           80 :           return parse_trait_const (std::move (outer_attrs));
    4006              :         }
    4007              :       // else, fallthrough to function
    4008              :       // TODO: find out how to disable gcc "implicit fallthrough" error
    4009              :       gcc_fallthrough ();
    4010              :     case ASYNC:
    4011              :     case UNSAFE:
    4012              :     case EXTERN_KW:
    4013              :     case FN_KW:
    4014         5158 :       return parse_function (std::move (vis), std::move (outer_attrs));
    4015              :     default:
    4016              :       break;
    4017              :     }
    4018            0 :   add_error (Error (tok->get_locus (),
    4019              :                     "unrecognised token %qs for item in trait",
    4020              :                     tok->get_token_description ()));
    4021              :   // skip?
    4022            0 :   return nullptr;
    4023         6736 : }
    4024              : 
    4025              : // Parse a typedef trait item.
    4026              : template <typename ManagedTokenSource>
    4027              : std::unique_ptr<AST::TraitItemType>
    4028          748 : Parser<ManagedTokenSource>::parse_trait_type (AST::AttrVec outer_attrs,
    4029              :                                               AST::Visibility vis)
    4030              : {
    4031          748 :   location_t locus = lexer.peek_token ()->get_locus ();
    4032          748 :   skip_token (TYPE);
    4033              : 
    4034          748 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    4035          748 :   if (ident_tok == nullptr)
    4036            0 :     return nullptr;
    4037              : 
    4038         1496 :   Identifier ident{ident_tok};
    4039              : 
    4040              :   // Parse optional generic parameters for GATs (Generic Associated Types)
    4041          748 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
    4042         1496 :   if (lexer.peek_token ()->get_id () == LEFT_ANGLE)
    4043              :     {
    4044            9 :       generic_params = parse_generic_params_in_angles ();
    4045              :     }
    4046              : 
    4047          748 :   std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
    4048              : 
    4049              :   // parse optional colon
    4050         1496 :   if (lexer.peek_token ()->get_id () == COLON)
    4051              :     {
    4052           44 :       lexer.skip_token ();
    4053              : 
    4054              :       // parse optional type param bounds
    4055              :       bounds
    4056           44 :         = parse_type_param_bounds ([] (TokenId id) { return id == SEMICOLON; });
    4057              :       // bounds = parse_type_param_bounds ();
    4058              :     }
    4059              : 
    4060          748 :   if (!skip_token (SEMICOLON))
    4061              :     {
    4062              :       // skip?
    4063            0 :       return nullptr;
    4064              :     }
    4065              : 
    4066              :   return std::unique_ptr<AST::TraitItemType> (
    4067          748 :     new AST::TraitItemType (std::move (ident), std::move (generic_params),
    4068              :                             std::move (bounds), std::move (outer_attrs), vis,
    4069          748 :                             locus));
    4070          748 : }
    4071              : 
    4072              : // Parses a constant trait item.
    4073              : template <typename ManagedTokenSource>
    4074              : std::unique_ptr<AST::ConstantItem>
    4075           40 : Parser<ManagedTokenSource>::parse_trait_const (AST::AttrVec outer_attrs)
    4076              : {
    4077           40 :   location_t locus = lexer.peek_token ()->get_locus ();
    4078           40 :   skip_token (CONST);
    4079              : 
    4080              :   // parse constant item name
    4081           40 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    4082           40 :   if (ident_tok == nullptr)
    4083            0 :     return nullptr;
    4084              : 
    4085           40 :   Identifier ident{ident_tok};
    4086              : 
    4087           40 :   if (!skip_token (COLON))
    4088              :     {
    4089            0 :       skip_after_semicolon ();
    4090            0 :       return nullptr;
    4091              :     }
    4092              : 
    4093              :   // parse constant trait item type
    4094           40 :   std::unique_ptr<AST::Type> type = parse_type ();
    4095              : 
    4096              :   // parse constant trait body expression, if it exists
    4097           40 :   std::unique_ptr<AST::Expr> const_body = nullptr;
    4098           80 :   if (lexer.peek_token ()->get_id () == EQUAL)
    4099              :     {
    4100           12 :       lexer.skip_token ();
    4101              : 
    4102              :       // expression must exist, so parse it
    4103           12 :       auto expr = parse_expr ();
    4104           12 :       if (!expr)
    4105            0 :         return nullptr;
    4106           12 :       const_body = std::move (expr.value ());
    4107           12 :     }
    4108              : 
    4109           40 :   if (!skip_token (SEMICOLON))
    4110              :     {
    4111              :       // skip after something?
    4112            0 :       return nullptr;
    4113              :     }
    4114              : 
    4115          120 :   return std::unique_ptr<AST::ConstantItem> (new AST::ConstantItem (
    4116           80 :     std::move (ident), AST::Visibility::create_private (), std::move (type),
    4117           40 :     std::move (const_body), std::move (outer_attrs), locus));
    4118           80 : }
    4119              : 
    4120              : /* Parses a struct "impl" item (both inherent impl and trait impl can be
    4121              :  * parsed here), */
    4122              : template <typename ManagedTokenSource>
    4123              : std::unique_ptr<AST::Impl>
    4124         5303 : Parser<ManagedTokenSource>::parse_impl (AST::Visibility vis,
    4125              :                                         AST::AttrVec outer_attrs)
    4126              : {
    4127              :   /* Note that only trait impls are allowed to be unsafe. So if unsafe, it
    4128              :    * must be a trait impl. However, this isn't enough for full disambiguation,
    4129              :    * so don't branch here. */
    4130         5303 :   location_t locus = lexer.peek_token ()->get_locus ();
    4131         5303 :   bool is_unsafe = false;
    4132        10606 :   if (lexer.peek_token ()->get_id () == UNSAFE)
    4133              :     {
    4134           67 :       lexer.skip_token ();
    4135           67 :       is_unsafe = true;
    4136              :     }
    4137              : 
    4138         5303 :   if (!skip_token (IMPL))
    4139              :     {
    4140            0 :       skip_after_next_block ();
    4141            0 :       return nullptr;
    4142              :     }
    4143              : 
    4144              :   // parse generic params (shared by trait and inherent impls)
    4145         5303 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    4146              :     = parse_generic_params_in_angles ();
    4147              : 
    4148              :   // Again, trait impl-only feature, but optional one, so can be used for
    4149              :   // branching yet.
    4150         5303 :   bool has_exclam = false;
    4151        10606 :   if (lexer.peek_token ()->get_id () == EXCLAM)
    4152              :     {
    4153            6 :       lexer.skip_token ();
    4154            6 :       has_exclam = true;
    4155              :     }
    4156              : 
    4157              :   /* FIXME: code that doesn't look shit for TypePath. Also, make sure this
    4158              :    * doesn't parse too much and not work. */
    4159         5303 :   AST::TypePath type_path = parse_type_path ();
    4160        10405 :   if (type_path.is_error () || lexer.peek_token ()->get_id () != FOR)
    4161              :     {
    4162              :       /* cannot parse type path (or not for token next, at least), so must be
    4163              :        * inherent impl */
    4164              : 
    4165              :       // hacky conversion of TypePath stack object to Type pointer
    4166          986 :       std::unique_ptr<AST::Type> type = nullptr;
    4167          986 :       if (!type_path.is_error ())
    4168          785 :         type = std::unique_ptr<AST::TypePath> (
    4169          785 :           new AST::TypePath (std::move (type_path)));
    4170              :       else
    4171          201 :         type = parse_type ();
    4172              : 
    4173              :       // Type is required, so error if null
    4174          986 :       if (type == nullptr)
    4175              :         {
    4176            1 :           Error error (lexer.peek_token ()->get_locus (),
    4177              :                        "could not parse type in inherent impl");
    4178            1 :           add_error (std::move (error));
    4179              : 
    4180            1 :           skip_after_next_block ();
    4181            1 :           return nullptr;
    4182            1 :         }
    4183              : 
    4184              :       // parse optional where clause
    4185          985 :       AST::WhereClause where_clause = parse_where_clause ();
    4186              : 
    4187          985 :       if (!skip_token (LEFT_CURLY))
    4188              :         {
    4189              :           // TODO: does this still skip properly?
    4190            0 :           skip_after_end_block ();
    4191            0 :           return nullptr;
    4192              :         }
    4193              : 
    4194              :       // parse inner attributes (optional)
    4195          985 :       AST::AttrVec inner_attrs = parse_inner_attributes ();
    4196              : 
    4197              :       // parse inherent impl items
    4198          985 :       std::vector<std::unique_ptr<AST::AssociatedItem>> impl_items;
    4199              : 
    4200          985 :       const_TokenPtr t = lexer.peek_token ();
    4201         3765 :       while (t->get_id () != RIGHT_CURLY)
    4202              :         {
    4203         2790 :           std::unique_ptr<AST::AssociatedItem> impl_item
    4204              :             = parse_inherent_impl_item ();
    4205              : 
    4206         2790 :           if (impl_item == nullptr)
    4207              :             {
    4208           10 :               Error error (
    4209           10 :                 lexer.peek_token ()->get_locus (),
    4210              :                 "failed to parse inherent impl item in inherent impl");
    4211           10 :               add_error (std::move (error));
    4212              : 
    4213           10 :               return nullptr;
    4214           10 :             }
    4215              : 
    4216         2780 :           impl_items.push_back (std::move (impl_item));
    4217              : 
    4218         2780 :           t = lexer.peek_token ();
    4219              :         }
    4220              : 
    4221          975 :       if (!skip_token (RIGHT_CURLY))
    4222              :         {
    4223              :           // skip somewhere
    4224            0 :           return nullptr;
    4225              :         }
    4226              : 
    4227              :       // DEBUG
    4228          975 :       rust_debug ("successfully parsed inherent impl");
    4229              : 
    4230          975 :       impl_items.shrink_to_fit ();
    4231              : 
    4232          975 :       return std::unique_ptr<AST::InherentImpl> (new AST::InherentImpl (
    4233              :         std::move (impl_items), std::move (generic_params), std::move (type),
    4234              :         std::move (where_clause), std::move (vis), std::move (inner_attrs),
    4235          975 :         std::move (outer_attrs), locus));
    4236          986 :     }
    4237              :   else
    4238              :     {
    4239              :       // type path must both be valid and next token is for, so trait impl
    4240         4317 :       if (!skip_token (FOR))
    4241              :         {
    4242            0 :           skip_after_next_block ();
    4243            0 :           return nullptr;
    4244              :         }
    4245              : 
    4246              :       // parse type
    4247         4317 :       std::unique_ptr<AST::Type> type = parse_type ();
    4248              :       // ensure type is included as it is required
    4249         4317 :       if (type == nullptr)
    4250              :         {
    4251            0 :           Error error (lexer.peek_token ()->get_locus (),
    4252              :                        "could not parse type in trait impl");
    4253            0 :           add_error (std::move (error));
    4254              : 
    4255            0 :           skip_after_next_block ();
    4256            0 :           return nullptr;
    4257            0 :         }
    4258              : 
    4259              :       // parse optional where clause
    4260         4317 :       AST::WhereClause where_clause = parse_where_clause ();
    4261              : 
    4262         4317 :       if (!skip_token (LEFT_CURLY))
    4263              :         {
    4264              :           // TODO: does this still skip properly?
    4265            0 :           skip_after_end_block ();
    4266            0 :           return nullptr;
    4267              :         }
    4268              : 
    4269              :       // parse inner attributes (optional)
    4270         4317 :       AST::AttrVec inner_attrs = parse_inner_attributes ();
    4271              : 
    4272              :       // parse trait impl items
    4273         4317 :       std::vector<std::unique_ptr<AST::AssociatedItem>> impl_items;
    4274              : 
    4275         4317 :       const_TokenPtr t = lexer.peek_token ();
    4276        14609 :       while (t->get_id () != RIGHT_CURLY)
    4277              :         {
    4278         5149 :           std::unique_ptr<AST::AssociatedItem> impl_item
    4279              :             = parse_trait_impl_item ();
    4280              : 
    4281         5149 :           if (impl_item == nullptr)
    4282              :             {
    4283            3 :               Error error (lexer.peek_token ()->get_locus (),
    4284              :                            "failed to parse trait impl item in trait impl");
    4285            3 :               add_error (std::move (error));
    4286              : 
    4287            3 :               return nullptr;
    4288            3 :             }
    4289              : 
    4290         5146 :           impl_items.push_back (std::move (impl_item));
    4291              : 
    4292         5146 :           t = lexer.peek_token ();
    4293              : 
    4294              :           // DEBUG
    4295         5146 :           rust_debug ("successfully parsed a trait impl item");
    4296              :         }
    4297              :       // DEBUG
    4298         4314 :       rust_debug ("successfully finished trait impl items");
    4299              : 
    4300         4314 :       if (!skip_token (RIGHT_CURLY))
    4301              :         {
    4302              :           // skip somewhere
    4303            0 :           return nullptr;
    4304              :         }
    4305              : 
    4306              :       // DEBUG
    4307         4314 :       rust_debug ("successfully parsed trait impl");
    4308              : 
    4309         4314 :       impl_items.shrink_to_fit ();
    4310              : 
    4311         4314 :       return std::unique_ptr<AST::TraitImpl> (
    4312         8628 :         new AST::TraitImpl (std::move (type_path), is_unsafe, has_exclam,
    4313              :                             std::move (impl_items), std::move (generic_params),
    4314              :                             std::move (type), std::move (where_clause),
    4315              :                             std::move (vis), std::move (inner_attrs),
    4316         4314 :                             std::move (outer_attrs), locus));
    4317         4317 :     }
    4318         5303 : }
    4319              : 
    4320              : // Parses a single inherent impl item (item inside an inherent impl block).
    4321              : template <typename ManagedTokenSource>
    4322              : std::unique_ptr<AST::AssociatedItem>
    4323         2792 : Parser<ManagedTokenSource>::parse_inherent_impl_item ()
    4324              : {
    4325              :   // parse outer attributes (if they exist)
    4326         2792 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    4327              : 
    4328              :   // TODO: cleanup - currently an unreadable mess
    4329              : 
    4330              :   // branch on next token:
    4331         2792 :   const_TokenPtr t = lexer.peek_token ();
    4332         2792 :   switch (t->get_id ())
    4333              :     {
    4334            1 :     case IDENTIFIER:
    4335              :       // FIXME: Arthur: Do we need to some lookahead here?
    4336            2 :       return parse_macro_invocation_semi (outer_attrs);
    4337         2141 :     case SUPER:
    4338              :     case SELF:
    4339              :     case CRATE:
    4340              :     case PUB:
    4341              :       {
    4342              :         // visibility, so not a macro invocation semi - must be constant,
    4343              :         // function, or method
    4344         2141 :         auto vis_res = parse_visibility ();
    4345         2141 :         if (!vis_res)
    4346            0 :           return nullptr;
    4347         2141 :         auto vis = vis_res.value ();
    4348              : 
    4349              :         // TODO: is a recursive call to parse_inherent_impl_item better?
    4350         4282 :         switch (lexer.peek_token ()->get_id ())
    4351              :           {
    4352         1364 :           case EXTERN_KW:
    4353              :           case UNSAFE:
    4354              :           case FN_KW:
    4355              :             // function or method
    4356         2728 :             return parse_inherent_impl_function_or_method (std::move (vis),
    4357              :                                                            std::move (
    4358         1364 :                                                              outer_attrs));
    4359          777 :           case CONST:
    4360              :             // lookahead to resolve production - could be function/method or
    4361              :             // const item
    4362          777 :             t = lexer.peek_token (1);
    4363              : 
    4364          777 :             switch (t->get_id ())
    4365              :               {
    4366            1 :               case IDENTIFIER:
    4367              :               case UNDERSCORE:
    4368            2 :                 return parse_const_item (std::move (vis),
    4369            1 :                                          std::move (outer_attrs));
    4370          776 :               case UNSAFE:
    4371              :               case EXTERN_KW:
    4372              :               case FN_KW:
    4373         1552 :                 return parse_inherent_impl_function_or_method (std::move (vis),
    4374              :                                                                std::move (
    4375          776 :                                                                  outer_attrs));
    4376            0 :               default:
    4377            0 :                 add_error (Error (t->get_locus (),
    4378              :                                   "unexpected token %qs in some sort of const "
    4379              :                                   "item in inherent impl",
    4380              :                                   t->get_token_description ()));
    4381              : 
    4382            0 :                 lexer.skip_token (1); // TODO: is this right thing to do?
    4383            0 :                 return nullptr;
    4384              :               }
    4385            0 :           default:
    4386            0 :             add_error (
    4387            0 :               Error (t->get_locus (),
    4388              :                      "unrecognised token %qs for item in inherent impl",
    4389              :                      t->get_token_description ()));
    4390              :             // skip?
    4391            0 :             return nullptr;
    4392              :           }
    4393         4282 :       }
    4394          608 :     case ASYNC:
    4395              :     case EXTERN_KW:
    4396              :     case UNSAFE:
    4397              :     case FN_KW:
    4398              :       // function or method
    4399          608 :       return parse_inherent_impl_function_or_method (
    4400          608 :         AST::Visibility::create_private (), std::move (outer_attrs));
    4401           42 :     case CONST:
    4402              :       /* lookahead to resolve production - could be function/method or const
    4403              :        * item */
    4404           42 :       t = lexer.peek_token (1);
    4405              : 
    4406           42 :       switch (t->get_id ())
    4407              :         {
    4408           42 :         case IDENTIFIER:
    4409              :         case UNDERSCORE:
    4410           84 :           return parse_const_item (AST::Visibility::create_private (),
    4411           42 :                                    std::move (outer_attrs));
    4412            0 :         case UNSAFE:
    4413              :         case EXTERN_KW:
    4414              :         case FN_KW:
    4415            0 :           return parse_inherent_impl_function_or_method (
    4416            0 :             AST::Visibility::create_private (), std::move (outer_attrs));
    4417            0 :         default:
    4418            0 :           add_error (Error (t->get_locus (),
    4419              :                             "unexpected token %qs in some sort of const item "
    4420              :                             "in inherent impl",
    4421              :                             t->get_token_description ()));
    4422              : 
    4423            0 :           lexer.skip_token (1); // TODO: is this right thing to do?
    4424            0 :           return nullptr;
    4425              :         }
    4426              :       rust_unreachable ();
    4427            0 :     default:
    4428            0 :       add_error (Error (t->get_locus (),
    4429              :                         "unrecognised token %qs for item in inherent impl",
    4430              :                         t->get_token_description ()));
    4431              : 
    4432              :       // skip?
    4433            0 :       return nullptr;
    4434              :     }
    4435         2792 : }
    4436              : 
    4437              : /* For internal use only by parse_inherent_impl_item() - splits giant method
    4438              :  * into smaller ones and prevents duplication of logic. Strictly, this parses
    4439              :  * a function or method item inside an inherent impl item block. */
    4440              : // TODO: make this a templated function with "return type" as type param -
    4441              : // InherentImplItem is this specialisation of the template while TraitImplItem
    4442              : // will be the other.
    4443              : template <typename ManagedTokenSource>
    4444              : std::unique_ptr<AST::AssociatedItem>
    4445         2748 : Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (
    4446              :   AST::Visibility vis, AST::AttrVec outer_attrs)
    4447              : {
    4448         2748 :   location_t locus = lexer.peek_token ()->get_locus ();
    4449              :   // parse function or method qualifiers
    4450         2748 :   auto qualifiers = parse_function_qualifiers ();
    4451         2748 :   if (!qualifiers)
    4452            0 :     return nullptr;
    4453              : 
    4454         2748 :   skip_token (FN_KW);
    4455              : 
    4456              :   // parse function or method name
    4457         2748 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    4458         2748 :   if (ident_tok == nullptr)
    4459            7 :     return nullptr;
    4460              : 
    4461         2741 :   Identifier ident{ident_tok};
    4462              : 
    4463              :   // parse generic params
    4464         2741 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    4465              :     = parse_generic_params_in_angles ();
    4466              : 
    4467         2741 :   if (!skip_token (LEFT_PAREN))
    4468              :     {
    4469              :       // skip after somewhere?
    4470            0 :       return nullptr;
    4471              :     }
    4472              : 
    4473              :   // now for function vs method disambiguation - method has opening "self"
    4474              :   // param
    4475         2741 :   auto initial_param = parse_self_param ();
    4476              : 
    4477         2741 :   if (!initial_param.has_value ()
    4478         2741 :       && initial_param.error ().kind != Parse::Error::Self::Kind::NOT_SELF)
    4479            3 :     return nullptr;
    4480              : 
    4481              :   /* FIXME: ensure that self param doesn't accidently consume tokens for a
    4482              :    * function one idea is to lookahead up to 4 tokens to see whether self is
    4483              :    * one of them */
    4484         2738 :   bool is_method = false;
    4485         2738 :   if (initial_param.has_value ())
    4486              :     {
    4487         1844 :       if ((*initial_param)->is_self ())
    4488              :         is_method = true;
    4489              : 
    4490              :       /* skip comma so function and method regular params can be parsed in
    4491              :        * same way */
    4492         3688 :       if (lexer.peek_token ()->get_id () == COMMA)
    4493         1193 :         lexer.skip_token ();
    4494              :     }
    4495              : 
    4496              :   // parse trait function params
    4497         2738 :   std::vector<std::unique_ptr<AST::Param>> function_params
    4498              :     = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
    4499              : 
    4500         2738 :   if (initial_param.has_value ())
    4501         1844 :     function_params.insert (function_params.begin (),
    4502         1844 :                             std::move (*initial_param));
    4503              : 
    4504         2738 :   if (!skip_token (RIGHT_PAREN))
    4505              :     {
    4506            0 :       skip_after_end_block ();
    4507            0 :       return nullptr;
    4508              :     }
    4509              : 
    4510              :   // parse return type (optional)
    4511         2738 :   std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
    4512              : 
    4513              :   // parse where clause (optional)
    4514         2738 :   AST::WhereClause where_clause = parse_where_clause ();
    4515              : 
    4516         2738 :   tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
    4517         5476 :   if (lexer.peek_token ()->get_id () == SEMICOLON)
    4518            2 :     lexer.skip_token ();
    4519              :   else
    4520              :     {
    4521         2736 :       auto result = parse_block_expr ();
    4522              : 
    4523         2736 :       if (!result)
    4524              :         {
    4525            0 :           Error error (
    4526            0 :             lexer.peek_token ()->get_locus (),
    4527              :             "could not parse definition in inherent impl %s definition",
    4528              :             is_method ? "method" : "function");
    4529            0 :           add_error (std::move (error));
    4530              : 
    4531            0 :           skip_after_end_block ();
    4532            0 :           return nullptr;
    4533            0 :         }
    4534         2736 :       body = std::move (result.value ());
    4535         2736 :     }
    4536              : 
    4537         2738 :   return std::unique_ptr<AST::Function> (
    4538        10950 :     new AST::Function (std::move (ident), std::move (qualifiers.value ()),
    4539              :                        std::move (generic_params), std::move (function_params),
    4540              :                        std::move (return_type), std::move (where_clause),
    4541              :                        std::move (body), std::move (vis),
    4542         2738 :                        std::move (outer_attrs), locus));
    4543         8227 : }
    4544              : 
    4545              : // Parses a single trait impl item (item inside a trait impl block).
    4546              : template <typename ManagedTokenSource>
    4547              : std::unique_ptr<AST::AssociatedItem>
    4548         5252 : Parser<ManagedTokenSource>::parse_trait_impl_item ()
    4549              : {
    4550              :   // parse outer attributes (if they exist)
    4551         5252 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    4552              : 
    4553         5252 :   auto vis_res = parse_visibility ();
    4554         5252 :   if (!vis_res)
    4555            0 :     return nullptr;
    4556         5252 :   auto visibility = vis_res.value ();
    4557              : 
    4558              :   // branch on next token:
    4559         5252 :   const_TokenPtr t = lexer.peek_token ();
    4560         5252 :   switch (t->get_id ())
    4561              :     {
    4562            0 :     case SUPER:
    4563              :     case SELF:
    4564              :     case CRATE:
    4565              :     case DOLLAR_SIGN:
    4566              :       // these seem to be SimplePath tokens, so this is a macro invocation
    4567              :       // semi
    4568            0 :       return parse_macro_invocation_semi (std::move (outer_attrs));
    4569           57 :     case IDENTIFIER:
    4570          114 :       if (lexer.peek_token ()->get_str () == Values::WeakKeywords::DEFAULT)
    4571           52 :         return parse_trait_impl_function_or_method (visibility,
    4572           26 :                                                     std::move (outer_attrs));
    4573              :       else
    4574           62 :         return parse_macro_invocation_semi (std::move (outer_attrs));
    4575         1236 :     case TYPE:
    4576         2472 :       return parse_type_alias (visibility, std::move (outer_attrs));
    4577         3925 :     case EXTERN_KW:
    4578              :     case UNSAFE:
    4579              :     case FN_KW:
    4580              :       // function or method
    4581         7850 :       return parse_trait_impl_function_or_method (visibility,
    4582         3925 :                                                   std::move (outer_attrs));
    4583            1 :     case ASYNC:
    4584            2 :       return parse_async_item (visibility, std::move (outer_attrs));
    4585           33 :     case CONST:
    4586              :       // lookahead to resolve production - could be function/method or const
    4587              :       // item
    4588           33 :       t = lexer.peek_token (1);
    4589              : 
    4590           33 :       switch (t->get_id ())
    4591              :         {
    4592           32 :         case IDENTIFIER:
    4593              :         case UNDERSCORE:
    4594           64 :           return parse_const_item (visibility, std::move (outer_attrs));
    4595            1 :         case UNSAFE:
    4596              :         case EXTERN_KW:
    4597              :         case FN_KW:
    4598            2 :           return parse_trait_impl_function_or_method (visibility,
    4599            1 :                                                       std::move (outer_attrs));
    4600            0 :         default:
    4601            0 :           add_error (Error (
    4602              :             t->get_locus (),
    4603              :             "unexpected token %qs in some sort of const item in trait impl",
    4604              :             t->get_token_description ()));
    4605              : 
    4606            0 :           lexer.skip_token (1); // TODO: is this right thing to do?
    4607            0 :           return nullptr;
    4608              :         }
    4609              :       rust_unreachable ();
    4610              :     default:
    4611              :       break;
    4612              :     }
    4613            0 :   add_error (Error (t->get_locus (),
    4614              :                     "unrecognised token %qs for item in trait impl",
    4615              :                     t->get_token_description ()));
    4616              : 
    4617              :   // skip?
    4618            0 :   return nullptr;
    4619        10504 : }
    4620              : 
    4621              : /* For internal use only by parse_trait_impl_item() - splits giant method into
    4622              :  * smaller ones and prevents duplication of logic. Strictly, this parses a
    4623              :  * function or method item inside a trait impl item block. */
    4624              : template <typename ManagedTokenSource>
    4625              : std::unique_ptr<AST::AssociatedItem>
    4626         3952 : Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (
    4627              :   AST::Visibility vis, AST::AttrVec outer_attrs)
    4628              : {
    4629              :   // this shares virtually all logic with
    4630              :   // parse_inherent_impl_function_or_method
    4631              :   // - template?
    4632         3952 :   location_t locus = lexer.peek_token ()->get_locus ();
    4633              : 
    4634              :   // parse function or method qualifiers
    4635         3952 :   auto qualifiers = parse_function_qualifiers ();
    4636         3952 :   if (!qualifiers)
    4637            2 :     return nullptr;
    4638              : 
    4639         3950 :   skip_token (FN_KW);
    4640              : 
    4641              :   // parse function or method name
    4642         3950 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    4643         3950 :   if (ident_tok == nullptr)
    4644              :     {
    4645            0 :       return nullptr;
    4646              :     }
    4647         3950 :   Identifier ident{ident_tok};
    4648              : 
    4649              :   // DEBUG:
    4650         3950 :   rust_debug (
    4651              :     "about to start parsing generic params in trait impl function or method");
    4652              : 
    4653              :   // parse generic params
    4654         3950 :   std::vector<std::unique_ptr<AST::GenericParam>> generic_params
    4655              :     = parse_generic_params_in_angles ();
    4656              : 
    4657              :   // DEBUG:
    4658         3950 :   rust_debug (
    4659              :     "finished parsing generic params in trait impl function or method");
    4660              : 
    4661         3950 :   if (!skip_token (LEFT_PAREN))
    4662              :     {
    4663              :       // skip after somewhere?
    4664            0 :       return nullptr;
    4665              :     }
    4666              : 
    4667              :   // now for function vs method disambiguation - method has opening "self"
    4668              :   // param
    4669         3950 :   auto initial_param = parse_self_param ();
    4670              : 
    4671         3950 :   if (!initial_param.has_value ()
    4672         3950 :       && initial_param.error ().kind != Parse::Error::Self::Kind::NOT_SELF)
    4673            0 :     return nullptr;
    4674              : 
    4675              :   // FIXME: ensure that self param doesn't accidently consume tokens for a
    4676              :   // function
    4677         3950 :   bool is_method = false;
    4678         3950 :   if (initial_param.has_value ())
    4679              :     {
    4680         3708 :       if ((*initial_param)->is_self ())
    4681              :         is_method = true;
    4682              : 
    4683              :       // skip comma so function and method regular params can be parsed in
    4684              :       // same way
    4685         7416 :       if (lexer.peek_token ()->get_id () == COMMA)
    4686              :         {
    4687         2001 :           lexer.skip_token ();
    4688              :         }
    4689              : 
    4690              :       // DEBUG
    4691         3708 :       rust_debug ("successfully parsed self param in method trait impl item");
    4692              :     }
    4693              : 
    4694              :   // DEBUG
    4695         3950 :   rust_debug (
    4696              :     "started to parse function params in function or method trait impl item");
    4697              : 
    4698              :   // parse trait function params (only if next token isn't right paren)
    4699         3950 :   std::vector<std::unique_ptr<AST::Param>> function_params;
    4700         7900 :   if (lexer.peek_token ()->get_id () != RIGHT_PAREN)
    4701              :     {
    4702              :       function_params
    4703         2158 :         = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
    4704              : 
    4705         2158 :       if (function_params.empty ())
    4706              :         {
    4707            0 :           Error error (
    4708            0 :             lexer.peek_token ()->get_locus (),
    4709              :             "failed to parse function params in trait impl %s definition",
    4710              :             is_method ? "method" : "function");
    4711            0 :           add_error (std::move (error));
    4712              : 
    4713            0 :           skip_after_next_block ();
    4714            0 :           return nullptr;
    4715            0 :         }
    4716              :     }
    4717              : 
    4718         3950 :   if (initial_param.has_value ())
    4719         3708 :     function_params.insert (function_params.begin (),
    4720         3708 :                             std::move (*initial_param));
    4721              : 
    4722              :   // DEBUG
    4723         3950 :   rust_debug ("successfully parsed function params in function or method "
    4724              :               "trait impl item");
    4725              : 
    4726         3950 :   if (!skip_token (RIGHT_PAREN))
    4727              :     {
    4728            0 :       skip_after_next_block ();
    4729            0 :       return nullptr;
    4730              :     }
    4731              : 
    4732              :   // parse return type (optional)
    4733         3950 :   std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
    4734              : 
    4735              :   // DEBUG
    4736         3950 :   rust_debug (
    4737              :     "successfully parsed return type in function or method trait impl item");
    4738              : 
    4739              :   // parse where clause (optional)
    4740         3950 :   AST::WhereClause where_clause = parse_where_clause ();
    4741              : 
    4742              :   // DEBUG
    4743         3950 :   rust_debug (
    4744              :     "successfully parsed where clause in function or method trait impl item");
    4745              : 
    4746              :   // parse function definition (in block) - semicolon not allowed
    4747         3950 :   tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
    4748              : 
    4749         7900 :   if (lexer.peek_token ()->get_id () == SEMICOLON)
    4750            1 :     lexer.skip_token ();
    4751              :   else
    4752              :     {
    4753         3949 :       auto result = parse_block_expr ();
    4754         3949 :       if (!result)
    4755              :         {
    4756            1 :           Error error (lexer.peek_token ()->get_locus (),
    4757              :                        "could not parse definition in trait impl %s definition",
    4758              :                        is_method ? "method" : "function");
    4759            1 :           add_error (std::move (error));
    4760              : 
    4761            1 :           skip_after_end_block ();
    4762            1 :           return nullptr;
    4763            1 :         }
    4764         3948 :       body = std::move (result.value ());
    4765         3949 :     }
    4766              : 
    4767         3949 :   return std::unique_ptr<AST::Function> (
    4768        15795 :     new AST::Function (std::move (ident), std::move (qualifiers.value ()),
    4769              :                        std::move (generic_params), std::move (function_params),
    4770              :                        std::move (return_type), std::move (where_clause),
    4771              :                        std::move (body), std::move (vis),
    4772         3949 :                        std::move (outer_attrs), locus));
    4773        11850 : }
    4774              : 
    4775              : // Parses an extern block of declarations.
    4776              : template <typename ManagedTokenSource>
    4777              : std::unique_ptr<AST::ExternBlock>
    4778         1627 : Parser<ManagedTokenSource>::parse_extern_block (AST::Visibility vis,
    4779              :                                                 AST::AttrVec outer_attrs)
    4780              : {
    4781         1627 :   location_t locus = lexer.peek_token ()->get_locus ();
    4782         1627 :   skip_token (EXTERN_KW);
    4783              : 
    4784              :   // detect optional abi name
    4785         1627 :   std::string abi;
    4786         1627 :   const_TokenPtr next_tok = lexer.peek_token ();
    4787         1627 :   if (next_tok->get_id () == STRING_LITERAL)
    4788              :     {
    4789         1627 :       lexer.skip_token ();
    4790         1627 :       abi = next_tok->get_str ();
    4791              :     }
    4792              : 
    4793         1627 :   if (!skip_token (LEFT_CURLY))
    4794              :     {
    4795            0 :       skip_after_end_block ();
    4796            0 :       return nullptr;
    4797              :     }
    4798              : 
    4799         1627 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    4800              : 
    4801              :   // parse declarations inside extern block
    4802         1627 :   std::vector<std::unique_ptr<AST::ExternalItem>> extern_items;
    4803              : 
    4804         1627 :   const_TokenPtr t = lexer.peek_token ();
    4805         4137 :   while (t->get_id () != RIGHT_CURLY)
    4806              :     {
    4807         2511 :       std::unique_ptr<AST::ExternalItem> extern_item = parse_external_item ();
    4808              : 
    4809         2511 :       if (extern_item == nullptr)
    4810              :         {
    4811            1 :           Error error (t->get_locus (),
    4812              :                        "failed to parse external item despite not reaching "
    4813              :                        "end of extern block");
    4814            1 :           add_error (std::move (error));
    4815              : 
    4816            1 :           return nullptr;
    4817            1 :         }
    4818              : 
    4819         2510 :       extern_items.push_back (std::move (extern_item));
    4820              : 
    4821         2510 :       t = lexer.peek_token ();
    4822              :     }
    4823              : 
    4824         1626 :   if (!skip_token (RIGHT_CURLY))
    4825              :     {
    4826              :       // skip somewhere
    4827            0 :       return nullptr;
    4828              :     }
    4829              : 
    4830         1626 :   extern_items.shrink_to_fit ();
    4831              : 
    4832              :   return std::unique_ptr<AST::ExternBlock> (
    4833         1626 :     new AST::ExternBlock (std::move (abi), std::move (extern_items),
    4834              :                           std::move (vis), std::move (inner_attrs),
    4835         1626 :                           std::move (outer_attrs), locus));
    4836         3254 : }
    4837              : 
    4838              : // Parses a single extern block item (static or function declaration).
    4839              : template <typename ManagedTokenSource>
    4840              : std::unique_ptr<AST::ExternalItem>
    4841         2514 : Parser<ManagedTokenSource>::parse_external_item ()
    4842              : {
    4843              :   // parse optional outer attributes
    4844         2514 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    4845              : 
    4846         2514 :   location_t locus = lexer.peek_token ()->get_locus ();
    4847              : 
    4848              :   // parse optional visibility
    4849         2514 :   auto vis_res = parse_visibility ();
    4850         2514 :   if (!vis_res)
    4851            0 :     return nullptr;
    4852         2514 :   auto vis = vis_res.value ();
    4853              : 
    4854         2514 :   const_TokenPtr t = lexer.peek_token ();
    4855         2514 :   switch (t->get_id ())
    4856              :     {
    4857            2 :     case IDENTIFIER:
    4858            4 :       return parse_macro_invocation_semi (outer_attrs);
    4859            1 :     case STATIC_KW:
    4860              :       {
    4861              :         // parse extern static item
    4862            1 :         lexer.skip_token ();
    4863              : 
    4864              :         // parse mut (optional)
    4865            1 :         bool has_mut = false;
    4866            2 :         if (lexer.peek_token ()->get_id () == MUT)
    4867              :           {
    4868            0 :             lexer.skip_token ();
    4869            0 :             has_mut = true;
    4870              :           }
    4871              : 
    4872              :         // parse identifier
    4873            1 :         const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    4874            1 :         if (ident_tok == nullptr)
    4875              :           {
    4876            0 :             skip_after_semicolon ();
    4877            0 :             return nullptr;
    4878              :           }
    4879            1 :         Identifier ident{ident_tok};
    4880              : 
    4881            1 :         if (!skip_token (COLON))
    4882              :           {
    4883            0 :             skip_after_semicolon ();
    4884            0 :             return nullptr;
    4885              :           }
    4886              : 
    4887              :         // parse type (required)
    4888            1 :         std::unique_ptr<AST::Type> type = parse_type ();
    4889            1 :         if (type == nullptr)
    4890              :           {
    4891            0 :             Error error (lexer.peek_token ()->get_locus (),
    4892              :                          "failed to parse type in external static item");
    4893            0 :             add_error (std::move (error));
    4894              : 
    4895            0 :             skip_after_semicolon ();
    4896            0 :             return nullptr;
    4897            0 :           }
    4898              : 
    4899            1 :         if (!skip_token (SEMICOLON))
    4900              :           {
    4901              :             // skip after somewhere?
    4902            0 :             return nullptr;
    4903              :           }
    4904              : 
    4905            1 :         return std::unique_ptr<AST::ExternalStaticItem> (
    4906            2 :           new AST::ExternalStaticItem (std::move (ident), std::move (type),
    4907              :                                        has_mut, std::move (vis),
    4908            1 :                                        std::move (outer_attrs), locus));
    4909            3 :       }
    4910         2507 :     case FN_KW:
    4911         5014 :       return parse_function (std::move (vis), std::move (outer_attrs), true);
    4912              : 
    4913            4 :     case TYPE:
    4914            4 :       return parse_external_type_item (std::move (vis),
    4915            4 :                                        std::move (outer_attrs));
    4916            0 :     default:
    4917              :       // error
    4918            0 :       add_error (
    4919            0 :         Error (t->get_locus (),
    4920              :                "unrecognised token %qs in extern block item declaration",
    4921              :                t->get_token_description ()));
    4922              : 
    4923            0 :       skip_after_semicolon ();
    4924            0 :       return nullptr;
    4925              :     }
    4926         5028 : }
    4927              : 
    4928              : // Parses a statement (will further disambiguate any statement).
    4929              : template <typename ManagedTokenSource>
    4930              : std::unique_ptr<AST::Stmt>
    4931          741 : Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
    4932              : {
    4933              :   // quick exit for empty statement
    4934              :   // FIXME: Can we have empty statements without semicolons? Just nothing?
    4935          741 :   const_TokenPtr t = lexer.peek_token ();
    4936          741 :   if (t->get_id () == SEMICOLON)
    4937              :     {
    4938           30 :       lexer.skip_token ();
    4939           30 :       return std::unique_ptr<AST::EmptyStmt> (
    4940           30 :         new AST::EmptyStmt (t->get_locus ()));
    4941              :     }
    4942              : 
    4943              :   // parse outer attributes
    4944          711 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    4945              : 
    4946              :   // parsing this will be annoying because of the many different possibilities
    4947              :   /* best may be just to copy paste in parse_item switch, and failing that try
    4948              :    * to parse outer attributes, and then pass them in to either a let
    4949              :    * statement or (fallback) expression statement. */
    4950              :   // FIXME: think of a way to do this without such a large switch?
    4951          711 :   t = lexer.peek_token ();
    4952          711 :   switch (t->get_id ())
    4953              :     {
    4954          200 :     case LET:
    4955              :       // let statement
    4956          200 :       return parse_let_stmt (std::move (outer_attrs), restrictions);
    4957          186 :     case PUB:
    4958              :     case MOD:
    4959              :     case EXTERN_KW:
    4960              :     case USE:
    4961              :     case FN_KW:
    4962              :     case TYPE:
    4963              :     case STRUCT_KW:
    4964              :     case ENUM_KW:
    4965              :     case CONST:
    4966              :     case STATIC_KW:
    4967              :     case AUTO:
    4968              :     case TRAIT:
    4969              :     case IMPL:
    4970              :     case MACRO:
    4971              :     /* TODO: implement union keyword but not really because of
    4972              :      * context-dependence crappy hack way to parse a union written below to
    4973              :      * separate it from the good code. */
    4974              :     // case UNION:
    4975              :     case UNSAFE: // maybe - unsafe traits are a thing
    4976              :       /* if any of these (should be all possible VisItem prefixes), parse a
    4977              :        * VisItem can't parse item because would require reparsing outer
    4978              :        * attributes */
    4979              :       // may also be unsafe block
    4980          372 :       if (lexer.peek_token (1)->get_id () == LEFT_CURLY)
    4981              :         {
    4982            1 :           return parse_expr_stmt (std::move (outer_attrs), restrictions);
    4983              :         }
    4984              :       else
    4985              :         {
    4986          185 :           return parse_vis_item (std::move (outer_attrs));
    4987              :         }
    4988              :       break;
    4989              :     // crappy hack to do union "keyword"
    4990          220 :     case IDENTIFIER:
    4991          220 :       if (t->get_str () == Values::WeakKeywords::UNION
    4992          220 :           && lexer.peek_token (1)->get_id () == IDENTIFIER)
    4993              :         {
    4994            0 :           return parse_vis_item (std::move (outer_attrs));
    4995              :           // or should this go straight to parsing union?
    4996              :         }
    4997          440 :       else if (is_macro_rules_def (t))
    4998              :         {
    4999              :           // macro_rules! macro item
    5000            2 :           return parse_macro_rules_def (std::move (outer_attrs));
    5001              :         }
    5002              :       gcc_fallthrough ();
    5003              :       // TODO: find out how to disable gcc "implicit fallthrough" warning
    5004              :     default:
    5005              :       // fallback: expression statement
    5006          323 :       return parse_expr_stmt (std::move (outer_attrs), restrictions);
    5007              :       break;
    5008              :     }
    5009          711 : }
    5010              : 
    5011              : // Parses a let statement.
    5012              : template <typename ManagedTokenSource>
    5013              : std::unique_ptr<AST::LetStmt>
    5014        12954 : Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs,
    5015              :                                             ParseRestrictions restrictions)
    5016              : {
    5017        12954 :   location_t locus = lexer.peek_token ()->get_locus ();
    5018        12954 :   skip_token (LET);
    5019              : 
    5020              :   // parse pattern (required)
    5021        12954 :   std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
    5022        12954 :   if (pattern == nullptr)
    5023              :     {
    5024            0 :       Error error (lexer.peek_token ()->get_locus (),
    5025              :                    "failed to parse pattern in let statement");
    5026            0 :       add_error (std::move (error));
    5027              : 
    5028            0 :       skip_after_semicolon ();
    5029            0 :       return nullptr;
    5030            0 :     }
    5031              : 
    5032              :   // parse type declaration (optional)
    5033        12954 :   std::unique_ptr<AST::Type> type = nullptr;
    5034        25908 :   if (lexer.peek_token ()->get_id () == COLON)
    5035              :     {
    5036              :       // must have a type declaration
    5037         2032 :       lexer.skip_token ();
    5038              : 
    5039         2032 :       type = parse_type ();
    5040         2032 :       if (type == nullptr)
    5041              :         {
    5042            0 :           Error error (lexer.peek_token ()->get_locus (),
    5043              :                        "failed to parse type in let statement");
    5044            0 :           add_error (std::move (error));
    5045              : 
    5046            0 :           skip_after_semicolon ();
    5047            0 :           return nullptr;
    5048            0 :         }
    5049              :     }
    5050              : 
    5051              :   // parse expression to set variable to (optional)
    5052        12954 :   std::unique_ptr<AST::Expr> expr = nullptr;
    5053        25908 :   if (lexer.peek_token ()->get_id () == EQUAL)
    5054              :     {
    5055              :       // must have an expression
    5056        11901 :       lexer.skip_token ();
    5057              : 
    5058        11901 :       auto expr_res = parse_expr ();
    5059        11901 :       if (!expr_res)
    5060              :         {
    5061           22 :           skip_after_semicolon ();
    5062           22 :           return nullptr;
    5063              :         }
    5064        11879 :       expr = std::move (expr_res.value ());
    5065        11901 :     }
    5066              : 
    5067        12932 :   tl::optional<std::unique_ptr<AST::Expr>> else_expr = tl::nullopt;
    5068        12932 :   if (maybe_skip_token (ELSE))
    5069              :     {
    5070            5 :       auto block_expr = parse_block_expr ();
    5071            5 :       if (block_expr)
    5072              :         else_expr = tl::optional<std::unique_ptr<AST::Expr>>{
    5073           10 :           std::move (block_expr.value ())};
    5074              :       else
    5075              :         else_expr = tl::nullopt;
    5076            5 :     }
    5077              : 
    5078        12932 :   if (restrictions.consume_semi)
    5079              :     {
    5080              :       // `stmt` macro variables are parsed without a semicolon, but should be
    5081              :       // parsed as a full statement when interpolated. This should be handled
    5082              :       // by having the interpolated statement be distinguishable from normal
    5083              :       // tokens, e.g. by NT tokens.
    5084        12787 :       if (restrictions.allow_close_after_expr_stmt)
    5085           55 :         maybe_skip_token (SEMICOLON);
    5086        12732 :       else if (!skip_token (SEMICOLON))
    5087            1 :         return nullptr;
    5088              :     }
    5089              : 
    5090              :   return std::unique_ptr<AST::LetStmt> (
    5091        25867 :     new AST::LetStmt (std::move (pattern), std::move (expr), std::move (type),
    5092        12931 :                       std::move (else_expr), std::move (outer_attrs), locus));
    5093        12954 : }
    5094              : 
    5095              : template <typename ManagedTokenSource>
    5096              : tl::optional<AST::GenericArg>
    5097         3844 : Parser<ManagedTokenSource>::parse_generic_arg ()
    5098              : {
    5099         3844 :   auto tok = lexer.peek_token ();
    5100         3844 :   std::unique_ptr<AST::Expr> expr = nullptr;
    5101              : 
    5102         3844 :   switch (tok->get_id ())
    5103              :     {
    5104         2477 :     case IDENTIFIER:
    5105              :       {
    5106              :         // This is a bit of a weird situation: With an identifier token, we
    5107              :         // could either have a valid type or a macro (FIXME: anything else?). So
    5108              :         // we need one bit of lookahead to differentiate if this is really
    5109         2477 :         auto next_tok = lexer.peek_token (1);
    5110         2477 :         if (next_tok->get_id () == LEFT_ANGLE
    5111         2466 :             || next_tok->get_id () == SCOPE_RESOLUTION
    5112         4940 :             || next_tok->get_id () == EXCLAM)
    5113              :           {
    5114           22 :             auto type = parse_type ();
    5115           22 :             if (type)
    5116           22 :               return AST::GenericArg::create_type (std::move (type));
    5117              :             else
    5118            0 :               return tl::nullopt;
    5119           22 :           }
    5120         2455 :         else if (next_tok->get_id () == COLON)
    5121              :           {
    5122            1 :             lexer.skip_token (); // skip ident
    5123            1 :             lexer.skip_token (); // skip colon
    5124              : 
    5125            1 :             auto tok = lexer.peek_token ();
    5126            1 :             std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
    5127              :               = parse_type_param_bounds ();
    5128              : 
    5129            1 :             auto type = std::unique_ptr<AST::TraitObjectType> (
    5130            1 :               new AST::TraitObjectType (std::move (bounds), tok->get_locus (),
    5131              :                                         false));
    5132            1 :             if (type)
    5133            1 :               return AST::GenericArg::create_type (std::move (type));
    5134              :             else
    5135              :               return tl::nullopt;
    5136            2 :           }
    5137         2454 :         lexer.skip_token ();
    5138         9816 :         return AST::GenericArg::create_ambiguous (tok->get_str (),
    5139         2454 :                                                   tok->get_locus ());
    5140         2477 :       }
    5141           19 :     case LEFT_CURLY:
    5142              :       {
    5143           19 :         auto res = parse_block_expr ();
    5144           19 :         if (res)
    5145           19 :           expr = std::move (res.value ());
    5146              :         else
    5147            0 :           return tl::nullopt;
    5148           19 :       }
    5149              :       break;
    5150           91 :     case MINUS:
    5151              :     case STRING_LITERAL:
    5152              :     case CHAR_LITERAL:
    5153              :     case INT_LITERAL:
    5154              :     case FLOAT_LITERAL:
    5155              :     case TRUE_LITERAL:
    5156              :     case FALSE_LITERAL:
    5157              :       {
    5158           91 :         auto res = parse_literal_expr ();
    5159           91 :         if (res)
    5160           91 :           expr = std::move (res.value ());
    5161              :         else
    5162            0 :           return tl::nullopt;
    5163           91 :       }
    5164              :       break;
    5165              :     // FIXME: Because of this, error reporting is garbage for const generic
    5166              :     // parameter's default values
    5167         1257 :     default:
    5168              :       {
    5169         1257 :         auto type = parse_type ();
    5170              :         // FIXME: Find a better way to do this?
    5171         1257 :         if (type)
    5172         1256 :           return AST::GenericArg::create_type (std::move (type));
    5173              :         else
    5174            1 :           return tl::nullopt;
    5175         1257 :       }
    5176              :     }
    5177              : 
    5178          110 :   if (!expr)
    5179            0 :     return tl::nullopt;
    5180              : 
    5181          110 :   return AST::GenericArg::create_const (std::move (expr));
    5182         3844 : }
    5183              : 
    5184              : // Parses the generic arguments in each path segment.
    5185              : template <typename ManagedTokenSource>
    5186              : AST::GenericArgs
    5187         3644 : Parser<ManagedTokenSource>::parse_path_generic_args ()
    5188              : {
    5189         7288 :   if (lexer.peek_token ()->get_id () == LEFT_SHIFT)
    5190            5 :     lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE);
    5191              : 
    5192         3644 :   if (!skip_token (LEFT_ANGLE))
    5193              :     {
    5194              :       // skip after somewhere?
    5195            0 :       return AST::GenericArgs::create_empty ();
    5196              :     }
    5197              : 
    5198              :   // We need to parse all lifetimes, then parse types and const generics in
    5199              :   // any order.
    5200              : 
    5201              :   // try to parse lifetimes first
    5202         3644 :   std::vector<AST::Lifetime> lifetime_args;
    5203              : 
    5204         3644 :   const_TokenPtr t = lexer.peek_token ();
    5205         3644 :   location_t locus = t->get_locus ();
    5206         7294 :   while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
    5207              :     {
    5208         3650 :       auto lifetime = parse_lifetime (false);
    5209         3650 :       if (!lifetime)
    5210              :         {
    5211              :           // not necessarily an error
    5212              :           break;
    5213              :         }
    5214              : 
    5215           41 :       lifetime_args.push_back (std::move (lifetime.value ()));
    5216              : 
    5217              :       // if next token isn't comma, then it must be end of list
    5218           82 :       if (lexer.peek_token ()->get_id () != COMMA)
    5219              :         {
    5220              :           break;
    5221              :         }
    5222              :       // skip comma
    5223            7 :       lexer.skip_token ();
    5224              : 
    5225            7 :       t = lexer.peek_token ();
    5226              :     }
    5227              : 
    5228              :   // try to parse types and const generics second
    5229         3644 :   std::vector<AST::GenericArg> generic_args;
    5230              : 
    5231              :   // TODO: think of better control structure
    5232         3644 :   t = lexer.peek_token ();
    5233        11077 :   while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
    5234              :     {
    5235              :       // FIXME: Is it fine to break if there is one binding? Can't there be
    5236              :       // bindings in between types?
    5237              : 
    5238              :       // ensure not binding being parsed as type accidently
    5239         6215 :       if (t->get_id () == IDENTIFIER
    5240         6444 :           && lexer.peek_token (1)->get_id () == EQUAL)
    5241              :         break;
    5242              : 
    5243         3824 :       auto arg = parse_generic_arg ();
    5244         3824 :       if (arg)
    5245              :         {
    5246         3824 :           generic_args.emplace_back (std::move (arg.value ()));
    5247              :         }
    5248              : 
    5249              :       // FIXME: Do we need to break if we encounter an error?
    5250              : 
    5251              :       // if next token isn't comma, then it must be end of list
    5252         7648 :       if (lexer.peek_token ()->get_id () != COMMA)
    5253              :         break;
    5254              : 
    5255              :       // skip comma
    5256          287 :       lexer.skip_token ();
    5257          287 :       t = lexer.peek_token ();
    5258              :     }
    5259              : 
    5260              :   // try to parse bindings third
    5261         3644 :   std::vector<AST::GenericArgsBinding> binding_args;
    5262              : 
    5263              :   // TODO: think of better control structure
    5264         3644 :   t = lexer.peek_token ();
    5265         3717 :   while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
    5266              :     {
    5267           73 :       AST::GenericArgsBinding binding = parse_generic_args_binding ();
    5268           73 :       if (binding.is_error ())
    5269              :         {
    5270              :           // not necessarily an error
    5271              :           break;
    5272              :         }
    5273              : 
    5274           73 :       binding_args.push_back (std::move (binding));
    5275              : 
    5276              :       // if next token isn't comma, then it must be end of list
    5277          146 :       if (lexer.peek_token ()->get_id () != COMMA)
    5278              :         {
    5279              :           break;
    5280              :         }
    5281              :       // skip comma
    5282            1 :       lexer.skip_token ();
    5283              : 
    5284            1 :       t = lexer.peek_token ();
    5285              :     }
    5286              : 
    5287              :   // skip any trailing commas
    5288         7288 :   if (lexer.peek_token ()->get_id () == COMMA)
    5289            0 :     lexer.skip_token ();
    5290              : 
    5291         3644 :   if (!skip_generics_right_angle ())
    5292            0 :     return AST::GenericArgs::create_empty ();
    5293              : 
    5294         3644 :   lifetime_args.shrink_to_fit ();
    5295         3644 :   generic_args.shrink_to_fit ();
    5296         3644 :   binding_args.shrink_to_fit ();
    5297              : 
    5298         3644 :   return AST::GenericArgs (std::move (lifetime_args), std::move (generic_args),
    5299         3644 :                            std::move (binding_args), locus);
    5300         7288 : }
    5301              : 
    5302              : // Parses a binding in a generic args path segment.
    5303              : template <typename ManagedTokenSource>
    5304              : AST::GenericArgsBinding
    5305           73 : Parser<ManagedTokenSource>::parse_generic_args_binding ()
    5306              : {
    5307           73 :   const_TokenPtr ident_tok = lexer.peek_token ();
    5308           73 :   if (ident_tok->get_id () != IDENTIFIER)
    5309              :     {
    5310              :       // allow non error-inducing use
    5311              :       // skip somewhere?
    5312            0 :       return AST::GenericArgsBinding::create_error ();
    5313              :     }
    5314           73 :   lexer.skip_token ();
    5315           73 :   Identifier ident{ident_tok};
    5316              : 
    5317           73 :   if (!skip_token (EQUAL))
    5318              :     {
    5319              :       // skip after somewhere?
    5320            0 :       return AST::GenericArgsBinding::create_error ();
    5321              :     }
    5322              : 
    5323              :   // parse type (required)
    5324           73 :   std::unique_ptr<AST::Type> type = parse_type ();
    5325           73 :   if (type == nullptr)
    5326              :     {
    5327              :       // skip somewhere?
    5328            0 :       return AST::GenericArgsBinding::create_error ();
    5329              :     }
    5330              : 
    5331          146 :   return AST::GenericArgsBinding (std::move (ident), std::move (type),
    5332          146 :                                   ident_tok->get_locus ());
    5333          146 : }
    5334              : 
    5335              : // Parses a self param. Also handles self param not existing.
    5336              : template <typename ManagedTokenSource>
    5337              : tl::expected<std::unique_ptr<AST::Param>, Parse::Error::Self>
    5338        18615 : Parser<ManagedTokenSource>::parse_self_param ()
    5339              : {
    5340        18615 :   bool has_reference = false;
    5341        18615 :   AST::Lifetime lifetime = AST::Lifetime::elided ();
    5342              : 
    5343        18615 :   location_t locus = lexer.peek_token ()->get_locus ();
    5344              : 
    5345              :   // TODO: Feels off, find a better way to clearly express this
    5346        74460 :   std::vector<std::vector<TokenId>> ptrs
    5347              :     = {{ASTERISK, SELF} /* *self */,
    5348              :        {ASTERISK, CONST, SELF} /* *const self */,
    5349              :        {ASTERISK, MUT, SELF} /* *mut self */};
    5350              : 
    5351        74454 :   for (auto &s : ptrs)
    5352              :     {
    5353              :       size_t i = 0;
    5354        55853 :       for (i = 0; i < s.size (); i++)
    5355       111700 :         if (lexer.peek_token (i)->get_id () != s[i])
    5356              :           break;
    5357        55842 :       if (i == s.size ())
    5358              :         {
    5359            3 :           Error error (lexer.peek_token ()->get_locus (),
    5360              :                        "cannot pass %<self%> by raw pointer");
    5361            3 :           add_error (std::move (error));
    5362            3 :           return Parse::Error::Self::make_self_raw_pointer ();
    5363            3 :         }
    5364              :     }
    5365              : 
    5366              :   // Trying to find those patterns:
    5367              :   //
    5368              :   // &'lifetime mut self
    5369              :   // &'lifetime self
    5370              :   // & mut self
    5371              :   // & self
    5372              :   // mut self
    5373              :   // self
    5374              :   //
    5375              :   // If not found, it is probably a function, exit and let function parsing
    5376              :   // handle it.
    5377              :   bool is_self = false;
    5378       111672 :   for (size_t i = 0; i < 5; i++)
    5379       186120 :     if (lexer.peek_token (i)->get_id () == SELF)
    5380         7808 :       is_self = true;
    5381              : 
    5382        18612 :   if (!is_self)
    5383              :     return Parse::Error::Self::make_not_self ();
    5384              : 
    5385              :   // test if self is a reference parameter
    5386        15610 :   if (lexer.peek_token ()->get_id () == AMP)
    5387              :     {
    5388         4639 :       has_reference = true;
    5389         4639 :       lexer.skip_token ();
    5390              : 
    5391              :       // now test whether it has a lifetime
    5392         9278 :       if (lexer.peek_token ()->get_id () == LIFETIME)
    5393              :         {
    5394              :           // something went wrong somehow
    5395            4 :           if (auto parsed_lifetime = parse_lifetime (true))
    5396              :             {
    5397            2 :               lifetime = parsed_lifetime.value ();
    5398              :             }
    5399              :           else
    5400              :             {
    5401            0 :               Error error (lexer.peek_token ()->get_locus (),
    5402              :                            "failed to parse lifetime in self param");
    5403            0 :               add_error (std::move (error));
    5404              : 
    5405              :               // skip after somewhere?
    5406            0 :               return Parse::Error::Self::make_parsing_error ();
    5407            0 :             }
    5408              :         }
    5409              :     }
    5410              : 
    5411              :   // test for mut
    5412         7805 :   bool has_mut = false;
    5413        15610 :   if (lexer.peek_token ()->get_id () == MUT)
    5414              :     {
    5415          348 :       has_mut = true;
    5416          348 :       lexer.skip_token ();
    5417              :     }
    5418              : 
    5419              :   // skip self token
    5420         7805 :   const_TokenPtr self_tok = lexer.peek_token ();
    5421         7805 :   if (self_tok->get_id () != SELF)
    5422              :     {
    5423              :       // skip after somewhere?
    5424              :       return Parse::Error::Self::make_not_self ();
    5425              :     }
    5426         7803 :   lexer.skip_token ();
    5427              : 
    5428              :   // parse optional type
    5429         7803 :   std::unique_ptr<AST::Type> type = nullptr;
    5430        15606 :   if (lexer.peek_token ()->get_id () == COLON)
    5431              :     {
    5432            1 :       lexer.skip_token ();
    5433              : 
    5434              :       // type is now required
    5435            1 :       type = parse_type ();
    5436            1 :       if (type == nullptr)
    5437              :         {
    5438            0 :           Error error (lexer.peek_token ()->get_locus (),
    5439              :                        "could not parse type in self param");
    5440            0 :           add_error (std::move (error));
    5441              : 
    5442              :           // skip after somewhere?
    5443            0 :           return Parse::Error::Self::make_parsing_error ();
    5444            0 :         }
    5445              :     }
    5446              : 
    5447              :   // ensure that cannot have both type and reference
    5448         7803 :   if (type && has_reference)
    5449              :     {
    5450            0 :       Error error (
    5451            0 :         lexer.peek_token ()->get_locus (),
    5452              :         "cannot have both a reference and a type specified in a self param");
    5453            0 :       add_error (std::move (error));
    5454              : 
    5455              :       // skip after somewhere?
    5456            0 :       return Parse::Error::Self::make_parsing_error ();
    5457            0 :     }
    5458              : 
    5459         7803 :   if (has_reference)
    5460              :     {
    5461         4639 :       return std::make_unique<AST::SelfParam> (std::move (lifetime), has_mut,
    5462         4639 :                                                locus);
    5463              :     }
    5464              :   else
    5465              :     {
    5466              :       // note that type may be nullptr here and that's fine
    5467         3164 :       return std::make_unique<AST::SelfParam> (std::move (type), has_mut,
    5468         3164 :                                                locus);
    5469              :     }
    5470        26418 : }
    5471              : 
    5472              : /* Parses an expression or macro statement. */
    5473              : template <typename ManagedTokenSource>
    5474              : std::unique_ptr<AST::Stmt>
    5475          324 : Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
    5476              :                                              ParseRestrictions restrictions)
    5477              : {
    5478          324 :   location_t locus = lexer.peek_token ()->get_locus ();
    5479              : 
    5480          324 :   tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr;
    5481              : 
    5482          648 :   switch (lexer.peek_token ()->get_id ())
    5483              :     {
    5484          220 :     case IDENTIFIER:
    5485              :     case CRATE:
    5486              :     case SUPER:
    5487              :     case SELF:
    5488              :     case SELF_ALIAS:
    5489              :     case DOLLAR_SIGN:
    5490              :     case SCOPE_RESOLUTION:
    5491              :       {
    5492          220 :         AST::PathInExpression path = parse_path_in_expression ();
    5493              :         tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    5494          220 :           null_denotation;
    5495              : 
    5496          440 :         if (lexer.peek_token ()->get_id () == EXCLAM)
    5497              :           {
    5498           61 :             std::unique_ptr<AST::MacroInvocation> invoc
    5499          122 :               = parse_macro_invocation_partial (std::move (path),
    5500              :                                                 std::move (outer_attrs));
    5501              : 
    5502           61 :             if (restrictions.consume_semi && maybe_skip_token (SEMICOLON))
    5503              :               {
    5504           59 :                 invoc->add_semicolon ();
    5505              :                 // Macro invocation with semicolon.
    5506           59 :                 return invoc;
    5507              :               }
    5508              : 
    5509            2 :             TokenId after_macro = lexer.peek_token ()->get_id ();
    5510              : 
    5511            2 :             if (restrictions.allow_close_after_expr_stmt
    5512            2 :                 && (after_macro == RIGHT_PAREN || after_macro == RIGHT_CURLY
    5513              :                     || after_macro == RIGHT_SQUARE))
    5514            2 :               return invoc;
    5515              : 
    5516            0 :             if (invoc->get_invoc_data ().get_delim_tok_tree ().get_delim_type ()
    5517              :                   == AST::CURLY
    5518            0 :                 && after_macro != DOT && after_macro != QUESTION_MARK)
    5519              :               {
    5520            0 :                 rust_debug ("braced macro statement");
    5521            0 :                 return invoc;
    5522              :               }
    5523              : 
    5524            0 :             null_denotation = std::move (invoc);
    5525           61 :           }
    5526              :         else
    5527              :           {
    5528              :             null_denotation
    5529          318 :               = null_denotation_path (std::move (path), {}, restrictions);
    5530              :           }
    5531              : 
    5532          636 :         expr = left_denotations (std::move (null_denotation), LBP_LOWEST,
    5533              :                                  std::move (outer_attrs), restrictions);
    5534              :         break;
    5535          220 :       }
    5536          104 :     default:
    5537          104 :       restrictions.expr_can_be_stmt = true;
    5538          208 :       expr = parse_expr (std::move (outer_attrs), restrictions);
    5539          104 :       break;
    5540              :     }
    5541              : 
    5542          263 :   if (!expr)
    5543              :     {
    5544              :       // expr is required, error
    5545            0 :       Error error (lexer.peek_token ()->get_locus (),
    5546              :                    "failed to parse expr in expr statement");
    5547            0 :       add_error (std::move (error));
    5548              : 
    5549            0 :       skip_after_semicolon ();
    5550            0 :       return nullptr;
    5551            0 :     }
    5552              : 
    5553          263 :   bool has_semi = false;
    5554              : 
    5555          263 :   if (restrictions.consume_semi)
    5556              :     {
    5557          249 :       if (maybe_skip_token (SEMICOLON))
    5558              :         {
    5559          138 :           has_semi = true;
    5560              :         }
    5561          111 :       else if (expr.value ()->is_expr_without_block ())
    5562              :         {
    5563           10 :           if (restrictions.allow_close_after_expr_stmt)
    5564              :             {
    5565           10 :               TokenId id = lexer.peek_token ()->get_id ();
    5566           10 :               if (id != RIGHT_PAREN && id != RIGHT_CURLY && id != RIGHT_SQUARE)
    5567              :                 {
    5568            3 :                   expect_token (SEMICOLON);
    5569            3 :                   return nullptr;
    5570              :                 }
    5571              :             }
    5572              :           else
    5573              :             {
    5574            0 :               expect_token (SEMICOLON);
    5575            0 :               return nullptr;
    5576              :             }
    5577              :         }
    5578              :     }
    5579              : 
    5580          260 :   return std::make_unique<AST::ExprStmt> (std::move (expr.value ()), locus,
    5581          260 :                                           has_semi);
    5582          324 : }
    5583              : 
    5584              : // Parses a loop label used in loop expressions.
    5585              : template <typename ManagedTokenSource>
    5586              : tl::expected<AST::LoopLabel, Parse::Error::LoopLabel>
    5587           40 : Parser<ManagedTokenSource>::parse_loop_label (const_TokenPtr tok)
    5588              : {
    5589              :   // parse lifetime - if doesn't exist, assume no label
    5590           40 :   if (tok->get_id () != LIFETIME)
    5591              :     {
    5592              :       // not necessarily an error
    5593              :       return Parse::Error::LoopLabel::make_not_loop_label ();
    5594              :     }
    5595              :   /* FIXME: check for named lifetime requirement here? or check in semantic
    5596              :    * analysis phase? */
    5597           40 :   AST::Lifetime label = lifetime_from_token (tok);
    5598              : 
    5599           40 :   if (!skip_token (COLON))
    5600              :     {
    5601              :       // skip somewhere?
    5602           40 :       Parse::Error::LoopLabel::make_missing_colon ();
    5603              :     }
    5604              : 
    5605              :   return tl::expected<AST::LoopLabel, Parse::Error::LoopLabel> (
    5606           40 :     AST::LoopLabel (std::move (label), tok->get_locus ()));
    5607           40 : }
    5608              : 
    5609              : // Parses the "pattern" part of the match arm (the 'case x:' equivalent).
    5610              : template <typename ManagedTokenSource>
    5611              : AST::MatchArm
    5612         2151 : Parser<ManagedTokenSource>::parse_match_arm ()
    5613              : {
    5614              :   // parse optional outer attributes
    5615         2151 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    5616              : 
    5617              :   // DEBUG
    5618         2151 :   rust_debug ("about to start parsing match arm patterns");
    5619              : 
    5620              :   // break early if find right curly
    5621         4302 :   if (lexer.peek_token ()->get_id () == RIGHT_CURLY)
    5622              :     {
    5623              :       // not an error
    5624            0 :       return AST::MatchArm::create_error ();
    5625              :     }
    5626              : 
    5627              :   // parse match arm patterns - at least 1 is required
    5628         2151 :   std::unique_ptr<AST::Pattern> match_arm_pattern
    5629              :     = parse_match_arm_pattern (RIGHT_CURLY);
    5630         2151 :   if (match_arm_pattern == nullptr)
    5631              :     {
    5632            0 :       Error error (lexer.peek_token ()->get_locus (),
    5633              :                    "failed to parse any patterns in match arm");
    5634            0 :       add_error (std::move (error));
    5635              : 
    5636              :       // skip somewhere?
    5637            0 :       return AST::MatchArm::create_error ();
    5638            0 :     }
    5639              : 
    5640              :   // DEBUG
    5641         2151 :   rust_debug ("successfully parsed match arm patterns");
    5642              : 
    5643              :   // parse match arm guard expr if it exists
    5644         2151 :   std::unique_ptr<AST::Expr> guard_expr = nullptr;
    5645         4302 :   if (lexer.peek_token ()->get_id () == IF)
    5646              :     {
    5647            1 :       lexer.skip_token ();
    5648              : 
    5649            1 :       auto guard_expr_res = parse_expr ();
    5650            1 :       if (!guard_expr_res)
    5651              :         {
    5652            0 :           Error error (lexer.peek_token ()->get_locus (),
    5653              :                        "failed to parse guard expression in match arm");
    5654            0 :           add_error (std::move (error));
    5655              : 
    5656              :           // skip somewhere?
    5657            0 :           return AST::MatchArm::create_error ();
    5658            0 :         }
    5659            1 :       guard_expr = std::move (guard_expr_res.value ());
    5660            1 :     }
    5661              : 
    5662              :   // DEBUG
    5663         2151 :   rust_debug ("successfully parsed match arm");
    5664              : 
    5665         4302 :   return AST::MatchArm (std::move (match_arm_pattern),
    5666         4302 :                         lexer.peek_token ()->get_locus (),
    5667         2151 :                         std::move (guard_expr), std::move (outer_attrs));
    5668         2151 : }
    5669              : 
    5670              : /* Parses the patterns used in a match arm. End token id is the id of the
    5671              :  * token that would exist after the patterns are done (e.g. '}' for match
    5672              :  * expr, '=' for if let and while let). */
    5673              : template <typename ManagedTokenSource>
    5674              : std::unique_ptr<AST::Pattern>
    5675         2186 : Parser<ManagedTokenSource>::parse_match_arm_pattern (TokenId end_token_id)
    5676              : {
    5677              :   // skip optional leading '|'
    5678         4372 :   if (lexer.peek_token ()->get_id () == PIPE)
    5679            0 :     lexer.skip_token ();
    5680              :   /* TODO: do I even need to store the result of this? can't be used.
    5681              :    * If semantically different, I need a wrapped "match arm patterns" object
    5682              :    * for this. */
    5683              : 
    5684         2186 :   std::unique_ptr<AST::Pattern> pattern;
    5685              : 
    5686              :   // quick break out if end_token_id
    5687         4372 :   if (lexer.peek_token ()->get_id () == end_token_id)
    5688            1 :     return pattern;
    5689              : 
    5690              :   // parse required pattern - if doesn't exist, return empty
    5691         2185 :   std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern ();
    5692         2185 :   if (initial_pattern == nullptr)
    5693              :     {
    5694              :       // FIXME: should this be an error?
    5695            0 :       return pattern;
    5696              :     }
    5697              : 
    5698         2185 :   return initial_pattern;
    5699         2186 : }
    5700              : 
    5701              : // Parses a single parameter used in a closure definition.
    5702              : template <typename ManagedTokenSource>
    5703              : AST::ClosureParam
    5704           72 : Parser<ManagedTokenSource>::parse_closure_param ()
    5705              : {
    5706           72 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    5707              : 
    5708              :   // parse pattern (which is required)
    5709           72 :   std::unique_ptr<AST::Pattern> pattern = parse_pattern_no_alt ();
    5710           72 :   if (pattern == nullptr)
    5711              :     {
    5712              :       // not necessarily an error
    5713            0 :       return AST::ClosureParam::create_error ();
    5714              :     }
    5715              : 
    5716              :   // parse optional type of param
    5717           72 :   std::unique_ptr<AST::Type> type = nullptr;
    5718          144 :   if (lexer.peek_token ()->get_id () == COLON)
    5719              :     {
    5720           63 :       lexer.skip_token ();
    5721              : 
    5722              :       // parse type, which is now required
    5723           63 :       type = parse_type ();
    5724           63 :       if (type == nullptr)
    5725              :         {
    5726            0 :           Error error (lexer.peek_token ()->get_locus (),
    5727              :                        "failed to parse type in closure parameter");
    5728            0 :           add_error (std::move (error));
    5729              : 
    5730              :           // skip somewhere?
    5731            0 :           return AST::ClosureParam::create_error ();
    5732            0 :         }
    5733              :     }
    5734              : 
    5735           72 :   location_t loc = pattern->get_locus ();
    5736           72 :   return AST::ClosureParam (std::move (pattern), loc, std::move (type),
    5737           72 :                             std::move (outer_attrs));
    5738           72 : }
    5739              : 
    5740              : // Parses a type (will further disambiguate any type).
    5741              : template <typename ManagedTokenSource>
    5742              : std::unique_ptr<AST::Type>
    5743        44011 : Parser<ManagedTokenSource>::parse_type (bool save_errors)
    5744              : {
    5745              :   /* rules for all types:
    5746              :    * NeverType:               '!'
    5747              :    * SliceType:               '[' Type ']'
    5748              :    * InferredType:            '_'
    5749              :    * MacroInvocation:         SimplePath '!' DelimTokenTree
    5750              :    * ParenthesisedType:       '(' Type ')'
    5751              :    * ImplTraitType:           'impl' TypeParamBounds
    5752              :    *  TypeParamBounds (not type)  TypeParamBound ( '+' TypeParamBound )* '+'?
    5753              :    *  TypeParamBound          Lifetime | TraitBound
    5754              :    * ImplTraitTypeOneBound:   'impl' TraitBound
    5755              :    * TraitObjectType:         'dyn'? TypeParamBounds
    5756              :    * TraitObjectTypeOneBound: 'dyn'? TraitBound
    5757              :    *  TraitBound              '?'? ForLifetimes? TypePath | '(' '?'?
    5758              :    * ForLifetimes? TypePath ')' BareFunctionType:        ForLifetimes?
    5759              :    * FunctionQualifiers 'fn' etc. ForLifetimes (not type) 'for' '<'
    5760              :    * LifetimeParams '>' FunctionQualifiers      ( 'async' | 'const' )?
    5761              :    * 'unsafe'?
    5762              :    * ('extern' abi?)? QualifiedPathInType:     '<' Type ( 'as' TypePath )? '>'
    5763              :    * (
    5764              :    * '::' TypePathSegment )+ TypePath:                '::'? TypePathSegment (
    5765              :    * '::' TypePathSegment)* ArrayType:               '[' Type ';' Expr ']'
    5766              :    * ReferenceType:           '&' Lifetime? 'mut'? TypeNoBounds
    5767              :    * RawPointerType:          '*' ( 'mut' | 'const' ) TypeNoBounds
    5768              :    * TupleType:               '(' Type etc. - regular tuple stuff. Also
    5769              :    * regular tuple vs parenthesised precedence
    5770              :    *
    5771              :    * Disambiguate between macro and type path via type path being parsed, and
    5772              :    * then if '!' found, convert type path to simple path for macro. Usual
    5773              :    * disambiguation for tuple vs parenthesised. For ImplTraitType and
    5774              :    * TraitObjectType individual disambiguations, they seem more like "special
    5775              :    * cases", so probably just try to parse the more general ImplTraitType or
    5776              :    * TraitObjectType and return OneBound versions if they satisfy those
    5777              :    * criteria. */
    5778              : 
    5779        44011 :   const_TokenPtr t = lexer.peek_token ();
    5780        44011 :   switch (t->get_id ())
    5781              :     {
    5782          187 :     case EXCLAM:
    5783              :       // never type - can't be macro as no path beforehand
    5784          187 :       lexer.skip_token ();
    5785          187 :       return std::unique_ptr<AST::NeverType> (
    5786          187 :         new AST::NeverType (t->get_locus ()));
    5787          861 :     case LEFT_SQUARE:
    5788              :       // slice type or array type - requires further disambiguation
    5789          861 :       return parse_slice_or_array_type ();
    5790          309 :     case LEFT_SHIFT:
    5791              :     case LEFT_ANGLE:
    5792              :       {
    5793              :         // qualified path in type
    5794          309 :         AST::QualifiedPathInType path = parse_qualified_path_in_type ();
    5795          309 :         if (path.is_error ())
    5796              :           {
    5797            0 :             if (save_errors)
    5798              :               {
    5799            0 :                 Error error (t->get_locus (),
    5800              :                              "failed to parse qualified path in type");
    5801            0 :                 add_error (std::move (error));
    5802            0 :               }
    5803              : 
    5804            0 :             return nullptr;
    5805              :           }
    5806          309 :         return std::unique_ptr<AST::QualifiedPathInType> (
    5807          309 :           new AST::QualifiedPathInType (std::move (path)));
    5808          309 :       }
    5809          104 :     case UNDERSCORE:
    5810              :       // inferred type
    5811          104 :       lexer.skip_token ();
    5812          104 :       return std::unique_ptr<AST::InferredType> (
    5813          104 :         new AST::InferredType (t->get_locus ()));
    5814         2728 :     case ASTERISK:
    5815              :       // raw pointer type
    5816         2728 :       return parse_raw_pointer_type ();
    5817         4290 :     case AMP: // does this also include AMP_AMP?
    5818              :     case LOGICAL_AND:
    5819              :       // reference type
    5820         4290 :       return parse_reference_type ();
    5821            0 :     case LIFETIME:
    5822              :       {
    5823              :         /* probably a lifetime bound, so probably type param bounds in
    5824              :          * TraitObjectType */
    5825            0 :         std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
    5826              :           = parse_type_param_bounds ();
    5827              : 
    5828            0 :         return std::unique_ptr<AST::TraitObjectType> (
    5829            0 :           new AST::TraitObjectType (std::move (bounds), t->get_locus (),
    5830            0 :                                     false));
    5831            0 :       }
    5832        34910 :     case IDENTIFIER:
    5833              :     case SUPER:
    5834              :     case SELF:
    5835              :     case SELF_ALIAS:
    5836              :     case CRATE:
    5837              :     case DOLLAR_SIGN:
    5838              :     case SCOPE_RESOLUTION:
    5839              :       {
    5840              :         // macro invocation or type path - requires further disambiguation.
    5841              :         /* for parsing path component of each rule, perhaps parse it as a
    5842              :          * typepath and attempt conversion to simplepath if a trailing '!' is
    5843              :          * found */
    5844              :         /* Type path also includes TraitObjectTypeOneBound BUT if it starts
    5845              :          * with it, it is exactly the same as a TypePath syntactically, so
    5846              :          * this is a syntactical ambiguity. As such, the parser will parse it
    5847              :          * as a TypePath. This, however, does not prevent TraitObjectType from
    5848              :          * starting with a typepath. */
    5849              : 
    5850              :         // parse path as type path
    5851        34910 :         AST::TypePath path = parse_type_path ();
    5852        34910 :         if (path.is_error ())
    5853              :           {
    5854            0 :             if (save_errors)
    5855              :               {
    5856            0 :                 Error error (t->get_locus (),
    5857              :                              "failed to parse path as first component of type");
    5858            0 :                 add_error (std::move (error));
    5859            0 :               }
    5860              : 
    5861            0 :             return nullptr;
    5862              :           }
    5863        34910 :         location_t locus = path.get_locus ();
    5864              : 
    5865              :         // branch on next token
    5866        34910 :         t = lexer.peek_token ();
    5867        34910 :         switch (t->get_id ())
    5868              :           {
    5869           29 :           case EXCLAM:
    5870              :             {
    5871              :               // macro invocation
    5872              :               // convert to simple path
    5873           29 :               AST::SimplePath macro_path = path.as_simple_path ();
    5874           29 :               if (macro_path.is_empty ())
    5875              :                 {
    5876            0 :                   if (save_errors)
    5877              :                     {
    5878            0 :                       Error error (t->get_locus (),
    5879              :                                    "failed to parse simple path in macro "
    5880              :                                    "invocation (for type)");
    5881            0 :                       add_error (std::move (error));
    5882            0 :                     }
    5883              : 
    5884            0 :                   return nullptr;
    5885              :                 }
    5886              : 
    5887           29 :               lexer.skip_token ();
    5888              : 
    5889           29 :               auto tok_tree = parse_delim_token_tree ();
    5890           29 :               if (!tok_tree)
    5891            0 :                 return nullptr;
    5892              : 
    5893           87 :               return AST::MacroInvocation::Regular (
    5894           58 :                 AST::MacroInvocData (std::move (macro_path),
    5895           29 :                                      std::move (tok_tree.value ())),
    5896           29 :                 {}, locus);
    5897           58 :             }
    5898            0 :           case PLUS:
    5899              :             {
    5900              :               // type param bounds
    5901            0 :               std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
    5902              : 
    5903              :               // convert type path to trait bound
    5904            0 :               std::unique_ptr<AST::TraitBound> path_bound (
    5905            0 :                 new AST::TraitBound (std::move (path), locus, false, false));
    5906            0 :               bounds.push_back (std::move (path_bound));
    5907              : 
    5908              :               /* parse rest of bounds - FIXME: better way to find when to stop
    5909              :                * parsing */
    5910            0 :               while (t->get_id () == PLUS)
    5911              :                 {
    5912            0 :                   lexer.skip_token ();
    5913              : 
    5914              :                   // parse bound if it exists - if not, assume end of sequence
    5915            0 :                   std::unique_ptr<AST::TypeParamBound> bound
    5916              :                     = parse_type_param_bound ();
    5917            0 :                   if (bound == nullptr)
    5918              :                     {
    5919              :                       break;
    5920              :                     }
    5921            0 :                   bounds.push_back (std::move (bound));
    5922              : 
    5923            0 :                   t = lexer.peek_token ();
    5924              :                 }
    5925              : 
    5926            0 :               return std::unique_ptr<AST::TraitObjectType> (
    5927            0 :                 new AST::TraitObjectType (std::move (bounds), locus, false));
    5928            0 :             }
    5929        34881 :           default:
    5930              :             // assume that this is a type path and not an error
    5931        34881 :             return std::unique_ptr<AST::TypePath> (
    5932        34881 :               new AST::TypePath (std::move (path)));
    5933              :           }
    5934        34910 :       }
    5935          405 :     case LEFT_PAREN:
    5936              :       /* tuple type or parenthesised type - requires further disambiguation
    5937              :        * (the usual). ok apparently can be a parenthesised TraitBound too, so
    5938              :        * could be TraitObjectTypeOneBound or TraitObjectType */
    5939          405 :       return parse_paren_prefixed_type ();
    5940            3 :     case FOR:
    5941              :       // TraitObjectTypeOneBound or BareFunctionType
    5942            3 :       return parse_for_prefixed_type ();
    5943           61 :     case ASYNC:
    5944              :     case CONST:
    5945              :     case UNSAFE:
    5946              :     case EXTERN_KW:
    5947              :     case FN_KW:
    5948              :       // bare function type (with no for lifetimes)
    5949           61 :       return parse_bare_function_type (std::vector<AST::LifetimeParam> ());
    5950          120 :     case IMPL:
    5951          120 :       lexer.skip_token ();
    5952          240 :       if (lexer.peek_token ()->get_id () == LIFETIME)
    5953              :         {
    5954              :           /* cannot be one bound because lifetime prevents it from being
    5955              :            * traitbound */
    5956            0 :           std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
    5957              :             = parse_type_param_bounds ();
    5958              : 
    5959            0 :           return std::unique_ptr<AST::ImplTraitType> (
    5960            0 :             new AST::ImplTraitType (std::move (bounds), t->get_locus ()));
    5961            0 :         }
    5962              :       else
    5963              :         {
    5964              :           // should be trait bound, so parse trait bound
    5965          120 :           std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
    5966          120 :           if (initial_bound == nullptr)
    5967              :             {
    5968            0 :               if (save_errors)
    5969              :                 {
    5970            0 :                   Error error (lexer.peek_token ()->get_locus (),
    5971              :                                "failed to parse ImplTraitType initial bound");
    5972            0 :                   add_error (std::move (error));
    5973            0 :                 }
    5974              : 
    5975            0 :               return nullptr;
    5976              :             }
    5977              : 
    5978          120 :           location_t locus = t->get_locus ();
    5979              : 
    5980              :           // short cut if next token isn't '+'
    5981          120 :           t = lexer.peek_token ();
    5982          120 :           if (t->get_id () != PLUS)
    5983              :             {
    5984          120 :               return std::unique_ptr<AST::ImplTraitTypeOneBound> (
    5985          120 :                 new AST::ImplTraitTypeOneBound (std::move (initial_bound),
    5986          120 :                                                 locus));
    5987              :             }
    5988              : 
    5989              :           // parse additional type param bounds
    5990            0 :           std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
    5991            0 :           bounds.push_back (std::move (initial_bound));
    5992            0 :           while (t->get_id () == PLUS)
    5993              :             {
    5994            0 :               lexer.skip_token ();
    5995              : 
    5996              :               // parse bound if it exists
    5997            0 :               std::unique_ptr<AST::TypeParamBound> bound
    5998              :                 = parse_type_param_bound ();
    5999            0 :               if (bound == nullptr)
    6000              :                 {
    6001              :                   // not an error as trailing plus may exist
    6002              :                   break;
    6003              :                 }
    6004            0 :               bounds.push_back (std::move (bound));
    6005              : 
    6006            0 :               t = lexer.peek_token ();
    6007              :             }
    6008              : 
    6009            0 :           return std::unique_ptr<AST::ImplTraitType> (
    6010            0 :             new AST::ImplTraitType (std::move (bounds), locus));
    6011          120 :         }
    6012           29 :     case DYN:
    6013              :     case QUESTION_MARK:
    6014              :       {
    6015              :         // either TraitObjectType or TraitObjectTypeOneBound
    6016           29 :         bool has_dyn = false;
    6017           29 :         if (t->get_id () == DYN)
    6018              :           {
    6019           29 :             lexer.skip_token ();
    6020           29 :             has_dyn = true;
    6021              :           }
    6022              : 
    6023           58 :         if (lexer.peek_token ()->get_id () == LIFETIME)
    6024              :           {
    6025              :             /* cannot be one bound because lifetime prevents it from being
    6026              :              * traitbound */
    6027            0 :             std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
    6028              :               = parse_type_param_bounds ();
    6029              : 
    6030            0 :             return std::unique_ptr<AST::TraitObjectType> (
    6031            0 :               new AST::TraitObjectType (std::move (bounds), t->get_locus (),
    6032            0 :                                         has_dyn));
    6033            0 :           }
    6034              :         else
    6035              :           {
    6036              :             // should be trait bound, so parse trait bound
    6037           29 :             std::unique_ptr<AST::TraitBound> initial_bound
    6038              :               = parse_trait_bound ();
    6039           29 :             if (initial_bound == nullptr)
    6040              :               {
    6041            2 :                 if (save_errors)
    6042              :                   {
    6043            2 :                     Error error (
    6044            2 :                       lexer.peek_token ()->get_locus (),
    6045              :                       "failed to parse TraitObjectType initial bound");
    6046            2 :                     add_error (std::move (error));
    6047            2 :                   }
    6048              : 
    6049            2 :                 return nullptr;
    6050              :               }
    6051              : 
    6052              :             // short cut if next token isn't '+'
    6053           27 :             t = lexer.peek_token ();
    6054           27 :             if (t->get_id () != PLUS)
    6055              :               {
    6056              :                 // convert trait bound to value object
    6057           14 :                 AST::TraitBound value_bound (*initial_bound);
    6058              : 
    6059              :                 // DEBUG: removed as unique ptr, so should auto delete
    6060              :                 // delete initial_bound;
    6061              : 
    6062           14 :                 return std::unique_ptr<AST::TraitObjectTypeOneBound> (
    6063           28 :                   new AST::TraitObjectTypeOneBound (std::move (value_bound),
    6064           14 :                                                     t->get_locus (), has_dyn));
    6065           14 :               }
    6066              : 
    6067              :             // parse additional type param bounds
    6068           13 :             std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
    6069           13 :             bounds.push_back (std::move (initial_bound));
    6070           33 :             while (t->get_id () == PLUS)
    6071              :               {
    6072           20 :                 lexer.skip_token ();
    6073              : 
    6074              :                 // parse bound if it exists
    6075           20 :                 std::unique_ptr<AST::TypeParamBound> bound
    6076              :                   = parse_type_param_bound ();
    6077           20 :                 if (bound == nullptr)
    6078              :                   {
    6079              :                     // not an error as trailing plus may exist
    6080              :                     break;
    6081              :                   }
    6082           20 :                 bounds.push_back (std::move (bound));
    6083              : 
    6084           20 :                 t = lexer.peek_token ();
    6085              :               }
    6086              : 
    6087           13 :             return std::unique_ptr<AST::TraitObjectType> (
    6088           13 :               new AST::TraitObjectType (std::move (bounds), t->get_locus (),
    6089           13 :                                         has_dyn));
    6090           29 :           }
    6091              :       }
    6092            4 :     default:
    6093            4 :       if (save_errors)
    6094            4 :         add_error (Error (t->get_locus (), "unrecognised token %qs in type",
    6095              :                           t->get_token_description ()));
    6096              : 
    6097            4 :       return nullptr;
    6098              :     }
    6099        44011 : }
    6100              : 
    6101              : /* Parses a type that has '(' as its first character. Returns a tuple type,
    6102              :  * parenthesised type, TraitObjectTypeOneBound, or TraitObjectType depending
    6103              :  * on following characters. */
    6104              : template <typename ManagedTokenSource>
    6105              : std::unique_ptr<AST::Type>
    6106          405 : Parser<ManagedTokenSource>::parse_paren_prefixed_type ()
    6107              : {
    6108              :   /* NOTE: Syntactical ambiguity of a parenthesised trait bound is considered
    6109              :    * a trait bound, not a parenthesised type, so that it can still be used in
    6110              :    * type param bounds. */
    6111              : 
    6112              :   /* NOTE: this implementation is really shit but I couldn't think of a better
    6113              :    * one. It requires essentially breaking polymorphism and downcasting via
    6114              :    * virtual method abuse, as it was copied from the rustc implementation (in
    6115              :    * which types are reified due to tagged union), after a more OOP attempt by
    6116              :    * me failed. */
    6117          405 :   location_t left_delim_locus = lexer.peek_token ()->get_locus ();
    6118              : 
    6119              :   // skip left delim
    6120          405 :   lexer.skip_token ();
    6121              :   /* while next token isn't close delim, parse comma-separated types, saving
    6122              :    * whether trailing comma happens */
    6123          405 :   const_TokenPtr t = lexer.peek_token ();
    6124          405 :   bool trailing_comma = true;
    6125          405 :   std::vector<std::unique_ptr<AST::Type>> types;
    6126              : 
    6127          732 :   while (t->get_id () != RIGHT_PAREN)
    6128              :     {
    6129          632 :       std::unique_ptr<AST::Type> type = parse_type ();
    6130          632 :       if (type == nullptr)
    6131              :         {
    6132            0 :           Error error (t->get_locus (),
    6133              :                        "failed to parse type inside parentheses (probably "
    6134              :                        "tuple or parenthesised)");
    6135            0 :           add_error (std::move (error));
    6136              : 
    6137            0 :           return nullptr;
    6138            0 :         }
    6139          632 :       types.push_back (std::move (type));
    6140              : 
    6141          632 :       t = lexer.peek_token ();
    6142          632 :       if (t->get_id () != COMMA)
    6143              :         {
    6144          305 :           trailing_comma = false;
    6145              :           break;
    6146              :         }
    6147          327 :       lexer.skip_token ();
    6148              : 
    6149          327 :       t = lexer.peek_token ();
    6150              :     }
    6151              : 
    6152          405 :   if (!skip_token (RIGHT_PAREN))
    6153              :     {
    6154            0 :       return nullptr;
    6155              :     }
    6156              : 
    6157              :   // if only one type and no trailing comma, then not a tuple type
    6158          405 :   if (types.size () == 1 && !trailing_comma)
    6159              :     {
    6160              :       // must be a TraitObjectType (with more than one bound)
    6161           10 :       if (lexer.peek_token ()->get_id () == PLUS)
    6162              :         {
    6163              :           // create type param bounds vector
    6164            0 :           std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
    6165              : 
    6166              :           // HACK: convert type to traitbound and add to bounds
    6167            0 :           std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
    6168            0 :           std::unique_ptr<AST::TraitBound> converted_bound (
    6169            0 :             released_ptr->to_trait_bound (true));
    6170            0 :           if (converted_bound == nullptr)
    6171              :             {
    6172            0 :               Error error (
    6173            0 :                 lexer.peek_token ()->get_locus (),
    6174              :                 "failed to hackily converted parsed type to trait bound");
    6175            0 :               add_error (std::move (error));
    6176              : 
    6177            0 :               return nullptr;
    6178            0 :             }
    6179            0 :           bounds.push_back (std::move (converted_bound));
    6180              : 
    6181            0 :           t = lexer.peek_token ();
    6182            0 :           while (t->get_id () == PLUS)
    6183              :             {
    6184            0 :               lexer.skip_token ();
    6185              : 
    6186              :               // attempt to parse typeparambound
    6187            0 :               std::unique_ptr<AST::TypeParamBound> bound
    6188              :                 = parse_type_param_bound ();
    6189            0 :               if (bound == nullptr)
    6190              :                 {
    6191              :                   // not an error if null
    6192              :                   break;
    6193              :                 }
    6194            0 :               bounds.push_back (std::move (bound));
    6195              : 
    6196            0 :               t = lexer.peek_token ();
    6197              :             }
    6198              : 
    6199            0 :           return std::unique_ptr<AST::TraitObjectType> (
    6200            0 :             new AST::TraitObjectType (std::move (bounds), left_delim_locus,
    6201            0 :                                       false));
    6202            0 :         }
    6203              :       else
    6204              :         {
    6205              :           // release vector pointer
    6206            5 :           std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
    6207              :           /* HACK: attempt to convert to trait bound. if fails, parenthesised
    6208              :            * type */
    6209            5 :           std::unique_ptr<AST::TraitBound> converted_bound (
    6210            5 :             released_ptr->to_trait_bound (true));
    6211            5 :           if (converted_bound == nullptr)
    6212              :             {
    6213              :               // parenthesised type
    6214            5 :               return std::unique_ptr<AST::ParenthesisedType> (
    6215            5 :                 new AST::ParenthesisedType (std::move (released_ptr),
    6216            5 :                                             left_delim_locus));
    6217              :             }
    6218              :           else
    6219              :             {
    6220              :               // trait object type (one bound)
    6221              : 
    6222              :               // get value semantics trait bound
    6223            0 :               AST::TraitBound value_bound (*converted_bound);
    6224              : 
    6225            0 :               return std::unique_ptr<AST::TraitObjectTypeOneBound> (
    6226            0 :                 new AST::TraitObjectTypeOneBound (value_bound,
    6227            0 :                                                   left_delim_locus));
    6228            0 :             }
    6229            5 :         }
    6230              :     }
    6231              :   else
    6232              :     {
    6233          400 :       return std::unique_ptr<AST::TupleType> (
    6234          400 :         new AST::TupleType (std::move (types), left_delim_locus));
    6235              :     }
    6236              :   /* TODO: ensure that this ensures that dynamic dispatch for traits is not
    6237              :    * lost somehow */
    6238          405 : }
    6239              : 
    6240              : /* Parses a type that has 'for' as its first character. This means it has a
    6241              :  * "for lifetimes", so returns either a BareFunctionType, TraitObjectType, or
    6242              :  * TraitObjectTypeOneBound depending on following characters. */
    6243              : template <typename ManagedTokenSource>
    6244              : std::unique_ptr<AST::Type>
    6245            3 : Parser<ManagedTokenSource>::parse_for_prefixed_type ()
    6246              : {
    6247            3 :   location_t for_locus = lexer.peek_token ()->get_locus ();
    6248              :   // parse for lifetimes in type
    6249            3 :   std::vector<AST::LifetimeParam> for_lifetimes = parse_for_lifetimes ();
    6250              : 
    6251              :   // branch on next token - either function or a trait type
    6252            3 :   const_TokenPtr t = lexer.peek_token ();
    6253            3 :   switch (t->get_id ())
    6254              :     {
    6255            3 :     case ASYNC:
    6256              :     case CONST:
    6257              :     case UNSAFE:
    6258              :     case EXTERN_KW:
    6259              :     case FN_KW:
    6260            3 :       return parse_bare_function_type (std::move (for_lifetimes));
    6261            0 :     case SCOPE_RESOLUTION:
    6262              :     case IDENTIFIER:
    6263              :     case SUPER:
    6264              :     case SELF:
    6265              :     case SELF_ALIAS:
    6266              :     case CRATE:
    6267              :     case DOLLAR_SIGN:
    6268              :       {
    6269              :         // path, so trait type
    6270              : 
    6271              :         // parse type path to finish parsing trait bound
    6272            0 :         AST::TypePath path = parse_type_path ();
    6273              : 
    6274            0 :         t = lexer.peek_token ();
    6275            0 :         if (t->get_id () != PLUS)
    6276              :           {
    6277              :             // must be one-bound trait type
    6278              :             // create trait bound value object
    6279            0 :             AST::TraitBound bound (std::move (path), for_locus, false, false,
    6280              :                                    std::move (for_lifetimes));
    6281              : 
    6282            0 :             return std::unique_ptr<AST::TraitObjectTypeOneBound> (
    6283            0 :               new AST::TraitObjectTypeOneBound (std::move (bound), for_locus));
    6284            0 :           }
    6285              : 
    6286              :         /* more than one bound trait type (or at least parsed as it - could be
    6287              :          * trailing '+') create trait bound pointer and bounds */
    6288            0 :         std::unique_ptr<AST::TraitBound> initial_bound (
    6289            0 :           new AST::TraitBound (std::move (path), for_locus, false, false,
    6290              :                                std::move (for_lifetimes)));
    6291            0 :         std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
    6292            0 :         bounds.push_back (std::move (initial_bound));
    6293              : 
    6294            0 :         while (t->get_id () == PLUS)
    6295              :           {
    6296            0 :             lexer.skip_token ();
    6297              : 
    6298              :             // parse type param bound if it exists
    6299            0 :             std::unique_ptr<AST::TypeParamBound> bound
    6300              :               = parse_type_param_bound ();
    6301            0 :             if (bound == nullptr)
    6302              :               {
    6303              :                 // not an error - e.g. trailing plus
    6304            0 :                 return nullptr;
    6305              :               }
    6306            0 :             bounds.push_back (std::move (bound));
    6307              : 
    6308            0 :             t = lexer.peek_token ();
    6309              :           }
    6310              : 
    6311            0 :         return std::unique_ptr<AST::TraitObjectType> (
    6312            0 :           new AST::TraitObjectType (std::move (bounds), for_locus, false));
    6313            0 :       }
    6314            0 :     default:
    6315              :       // error
    6316            0 :       add_error (Error (t->get_locus (),
    6317              :                         "unrecognised token %qs in bare function type or trait "
    6318              :                         "object type or trait object type one bound",
    6319              :                         t->get_token_description ()));
    6320              : 
    6321            0 :       return nullptr;
    6322              :     }
    6323            3 : }
    6324              : 
    6325              : // Parses a maybe named param used in bare function types.
    6326              : template <typename ManagedTokenSource>
    6327              : AST::MaybeNamedParam
    6328           48 : Parser<ManagedTokenSource>::parse_maybe_named_param (AST::AttrVec outer_attrs)
    6329              : {
    6330              :   /* Basically guess that param is named if first token is identifier or
    6331              :    * underscore and second token is semicolon. This should probably have no
    6332              :    * exceptions. rustc uses backtracking to parse these, but at the time of
    6333              :    * writing gccrs has no backtracking capabilities. */
    6334           48 :   const_TokenPtr current = lexer.peek_token ();
    6335           48 :   const_TokenPtr next = lexer.peek_token (1);
    6336              : 
    6337           48 :   Identifier name;
    6338           48 :   AST::MaybeNamedParam::ParamKind kind = AST::MaybeNamedParam::UNNAMED;
    6339              : 
    6340           48 :   if (current->get_id () == IDENTIFIER && next->get_id () == COLON)
    6341              :     {
    6342              :       // named param
    6343            1 :       name = {current};
    6344            1 :       kind = AST::MaybeNamedParam::IDENTIFIER;
    6345            1 :       lexer.skip_token (1);
    6346              :     }
    6347           47 :   else if (current->get_id () == UNDERSCORE && next->get_id () == COLON)
    6348              :     {
    6349              :       // wildcard param
    6350           12 :       name = {Values::Keywords::UNDERSCORE, current->get_locus ()};
    6351            6 :       kind = AST::MaybeNamedParam::WILDCARD;
    6352            6 :       lexer.skip_token (1);
    6353              :     }
    6354              : 
    6355              :   // parse type (required)
    6356           48 :   std::unique_ptr<AST::Type> type = parse_type ();
    6357           48 :   if (type == nullptr)
    6358              :     {
    6359            0 :       Error error (lexer.peek_token ()->get_locus (),
    6360              :                    "failed to parse type in maybe named param");
    6361            0 :       add_error (std::move (error));
    6362              : 
    6363            0 :       return AST::MaybeNamedParam::create_error ();
    6364            0 :     }
    6365              : 
    6366           96 :   return AST::MaybeNamedParam (std::move (name), kind, std::move (type),
    6367           48 :                                std::move (outer_attrs), current->get_locus ());
    6368           96 : }
    6369              : 
    6370              : /* Parses a bare function type (with the given for lifetimes for convenience -
    6371              :  * does not parse them itself). */
    6372              : template <typename ManagedTokenSource>
    6373              : std::unique_ptr<AST::BareFunctionType>
    6374           66 : Parser<ManagedTokenSource>::parse_bare_function_type (
    6375              :   std::vector<AST::LifetimeParam> for_lifetimes)
    6376              : {
    6377              :   // TODO: pass in for lifetime location as param
    6378           66 :   location_t best_try_locus = lexer.peek_token ()->get_locus ();
    6379              : 
    6380           66 :   auto qualifiers = parse_function_qualifiers ();
    6381           66 :   if (!qualifiers)
    6382            0 :     return nullptr;
    6383              : 
    6384           66 :   if (!skip_token (FN_KW))
    6385            0 :     return nullptr;
    6386              : 
    6387           66 :   if (!skip_token (LEFT_PAREN))
    6388            0 :     return nullptr;
    6389              : 
    6390              :   // parse function params, if they exist
    6391           66 :   std::vector<AST::MaybeNamedParam> params;
    6392           66 :   bool is_variadic = false;
    6393           66 :   AST::AttrVec variadic_attrs;
    6394              : 
    6395           66 :   const_TokenPtr t = lexer.peek_token ();
    6396          120 :   while (t->get_id () != RIGHT_PAREN)
    6397              :     {
    6398           48 :       AST::AttrVec temp_attrs = parse_outer_attributes ();
    6399              : 
    6400           96 :       if (lexer.peek_token ()->get_id () == ELLIPSIS)
    6401              :         {
    6402            0 :           lexer.skip_token ();
    6403            0 :           is_variadic = true;
    6404            0 :           variadic_attrs = std::move (temp_attrs);
    6405              : 
    6406            0 :           t = lexer.peek_token ();
    6407              : 
    6408            0 :           if (t->get_id () != RIGHT_PAREN)
    6409              :             {
    6410            0 :               Error error (t->get_locus (),
    6411              :                            "expected right parentheses after variadic in maybe "
    6412              :                            "named function "
    6413              :                            "parameters, found %qs",
    6414              :                            t->get_token_description ());
    6415            0 :               add_error (std::move (error));
    6416              : 
    6417            0 :               return nullptr;
    6418            0 :             }
    6419              : 
    6420              :           break;
    6421              :         }
    6422              : 
    6423           48 :       AST::MaybeNamedParam param
    6424           48 :         = parse_maybe_named_param (std::move (temp_attrs));
    6425           48 :       if (param.is_error ())
    6426              :         {
    6427            0 :           Error error (
    6428            0 :             lexer.peek_token ()->get_locus (),
    6429              :             "failed to parse maybe named param in bare function type");
    6430            0 :           add_error (std::move (error));
    6431              : 
    6432            0 :           return nullptr;
    6433            0 :         }
    6434           48 :       params.push_back (std::move (param));
    6435              : 
    6436           96 :       if (lexer.peek_token ()->get_id () != COMMA)
    6437              :         break;
    6438              : 
    6439            6 :       lexer.skip_token ();
    6440            6 :       t = lexer.peek_token ();
    6441              :     }
    6442              : 
    6443           66 :   if (!skip_token (RIGHT_PAREN))
    6444            0 :     return nullptr;
    6445              : 
    6446              :   // bare function return type, if exists
    6447           66 :   std::unique_ptr<AST::TypeNoBounds> return_type = nullptr;
    6448          132 :   if (lexer.peek_token ()->get_id () == RETURN_TYPE)
    6449              :     {
    6450           53 :       lexer.skip_token ();
    6451              : 
    6452              :       // parse required TypeNoBounds
    6453           53 :       return_type = parse_type_no_bounds ();
    6454           53 :       if (return_type == nullptr)
    6455              :         {
    6456            0 :           Error error (lexer.peek_token ()->get_locus (),
    6457              :                        "failed to parse return type (type no bounds) in bare "
    6458              :                        "function type");
    6459            0 :           add_error (std::move (error));
    6460              : 
    6461            0 :           return nullptr;
    6462            0 :         }
    6463              :     }
    6464              : 
    6465           66 :   return std::unique_ptr<AST::BareFunctionType> (new AST::BareFunctionType (
    6466           66 :     std::move (for_lifetimes), std::move (qualifiers.value ()),
    6467              :     std::move (params), is_variadic, std::move (variadic_attrs),
    6468           66 :     std::move (return_type), best_try_locus));
    6469          132 : }
    6470              : 
    6471              : template <typename ManagedTokenSource>
    6472              : std::unique_ptr<AST::ReferenceType>
    6473         4314 : Parser<ManagedTokenSource>::parse_reference_type_inner (location_t locus)
    6474              : {
    6475              :   // parse optional lifetime
    6476         4314 :   AST::Lifetime lifetime = AST::Lifetime::elided ();
    6477         8628 :   if (lexer.peek_token ()->get_id () == LIFETIME)
    6478              :     {
    6479          468 :       auto parsed_lifetime = parse_lifetime (true);
    6480          468 :       if (parsed_lifetime)
    6481              :         {
    6482          468 :           lifetime = parsed_lifetime.value ();
    6483              :         }
    6484              :       else
    6485              :         {
    6486            0 :           Error error (lexer.peek_token ()->get_locus (),
    6487              :                        "failed to parse lifetime in reference type");
    6488            0 :           add_error (std::move (error));
    6489              : 
    6490            0 :           return nullptr;
    6491            0 :         }
    6492          468 :     }
    6493              : 
    6494         4314 :   bool is_mut = false;
    6495         8628 :   if (lexer.peek_token ()->get_id () == MUT)
    6496              :     {
    6497          304 :       lexer.skip_token ();
    6498          304 :       is_mut = true;
    6499              :     }
    6500              : 
    6501              :   // parse type no bounds, which is required
    6502         4314 :   std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
    6503         4314 :   if (type == nullptr)
    6504              :     {
    6505            0 :       Error error (lexer.peek_token ()->get_locus (),
    6506              :                    "failed to parse referenced type in reference type");
    6507            0 :       add_error (std::move (error));
    6508              : 
    6509            0 :       return nullptr;
    6510            0 :     }
    6511              : 
    6512              :   return std::unique_ptr<AST::ReferenceType> (
    6513        12942 :     new AST::ReferenceType (is_mut, std::move (type), locus,
    6514         4314 :                             std::move (lifetime)));
    6515         4314 : }
    6516              : 
    6517              : // Parses a reference type (mutable or immutable, with given lifetime).
    6518              : template <typename ManagedTokenSource>
    6519              : std::unique_ptr<AST::ReferenceType>
    6520         4314 : Parser<ManagedTokenSource>::parse_reference_type ()
    6521              : {
    6522         4314 :   auto t = lexer.peek_token ();
    6523         4314 :   auto locus = t->get_locus ();
    6524              : 
    6525         4314 :   switch (t->get_id ())
    6526              :     {
    6527         4300 :     case AMP:
    6528         4300 :       skip_token (AMP);
    6529         4300 :       return parse_reference_type_inner (locus);
    6530           14 :     case LOGICAL_AND:
    6531           14 :       skip_token (LOGICAL_AND);
    6532              :       return std::unique_ptr<AST::ReferenceType> (
    6533           42 :         new AST::ReferenceType (false, parse_reference_type_inner (locus),
    6534           14 :                                 locus));
    6535            0 :     default:
    6536            0 :       rust_unreachable ();
    6537              :     }
    6538         4314 : }
    6539              : 
    6540              : // Parses a raw (unsafe) pointer type.
    6541              : template <typename ManagedTokenSource>
    6542              : std::unique_ptr<AST::RawPointerType>
    6543         6540 : Parser<ManagedTokenSource>::parse_raw_pointer_type ()
    6544              : {
    6545         6540 :   location_t locus = lexer.peek_token ()->get_locus ();
    6546         6540 :   skip_token (ASTERISK);
    6547              : 
    6548         6540 :   AST::RawPointerType::PointerType kind = AST::RawPointerType::CONST;
    6549              : 
    6550              :   // branch on next token for pointer kind info
    6551         6540 :   const_TokenPtr t = lexer.peek_token ();
    6552         6540 :   switch (t->get_id ())
    6553              :     {
    6554          721 :     case MUT:
    6555          721 :       kind = AST::RawPointerType::MUT;
    6556          721 :       lexer.skip_token ();
    6557              :       break;
    6558         5819 :     case CONST:
    6559         5819 :       kind = AST::RawPointerType::CONST;
    6560         5819 :       lexer.skip_token ();
    6561              :       break;
    6562            0 :     default:
    6563            0 :       add_error (Error (t->get_locus (),
    6564              :                         "unrecognised token %qs in raw pointer type",
    6565              :                         t->get_token_description ()));
    6566              : 
    6567            0 :       return nullptr;
    6568              :     }
    6569              : 
    6570              :   // parse type no bounds (required)
    6571         6540 :   std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
    6572         6540 :   if (type == nullptr)
    6573              :     {
    6574            0 :       Error error (lexer.peek_token ()->get_locus (),
    6575              :                    "failed to parse pointed type of raw pointer type");
    6576            0 :       add_error (std::move (error));
    6577              : 
    6578            0 :       return nullptr;
    6579            0 :     }
    6580              : 
    6581              :   return std::unique_ptr<AST::RawPointerType> (
    6582         6540 :     new AST::RawPointerType (kind, std::move (type), locus));
    6583         6540 : }
    6584              : 
    6585              : /* Parses a slice or array type, depending on following arguments (as
    6586              :  * lookahead is not possible). */
    6587              : template <typename ManagedTokenSource>
    6588              : std::unique_ptr<AST::TypeNoBounds>
    6589         1517 : Parser<ManagedTokenSource>::parse_slice_or_array_type ()
    6590              : {
    6591         1517 :   location_t locus = lexer.peek_token ()->get_locus ();
    6592         1517 :   skip_token (LEFT_SQUARE);
    6593              : 
    6594              :   // parse inner type (required)
    6595         1517 :   std::unique_ptr<AST::Type> inner_type = parse_type ();
    6596         1517 :   if (inner_type == nullptr)
    6597              :     {
    6598            0 :       Error error (lexer.peek_token ()->get_locus (),
    6599              :                    "failed to parse inner type in slice or array type");
    6600            0 :       add_error (std::move (error));
    6601              : 
    6602            0 :       return nullptr;
    6603            0 :     }
    6604              : 
    6605              :   // branch on next token
    6606         1517 :   const_TokenPtr t = lexer.peek_token ();
    6607         1517 :   switch (t->get_id ())
    6608              :     {
    6609          842 :     case RIGHT_SQUARE:
    6610              :       // slice type
    6611          842 :       lexer.skip_token ();
    6612              : 
    6613          842 :       return std::unique_ptr<AST::SliceType> (
    6614          842 :         new AST::SliceType (std::move (inner_type), locus));
    6615          675 :     case SEMICOLON:
    6616              :       {
    6617              :         // array type
    6618          675 :         lexer.skip_token ();
    6619              : 
    6620              :         // parse required array size expression
    6621          675 :         auto size = parse_anon_const ();
    6622              : 
    6623          675 :         if (!size)
    6624              :           {
    6625            1 :             Error error (lexer.peek_token ()->get_locus (),
    6626              :                          "failed to parse size expression in array type");
    6627            1 :             add_error (std::move (error));
    6628              : 
    6629            1 :             return nullptr;
    6630            1 :           }
    6631              : 
    6632          674 :         if (!skip_token (RIGHT_SQUARE))
    6633              :           {
    6634            0 :             return nullptr;
    6635              :           }
    6636              : 
    6637          674 :         return std::unique_ptr<AST::ArrayType> (
    6638         1337 :           new AST::ArrayType (std::move (inner_type), std::move (*size),
    6639          674 :                               locus));
    6640          675 :       }
    6641            0 :     default:
    6642              :       // error
    6643            0 :       add_error (
    6644            0 :         Error (t->get_locus (),
    6645              :                "unrecognised token %qs in slice or array type after inner type",
    6646              :                t->get_token_description ()));
    6647              : 
    6648            0 :       return nullptr;
    6649              :     }
    6650         1517 : }
    6651              : 
    6652              : // Parses a type, taking into account type boundary disambiguation.
    6653              : template <typename ManagedTokenSource>
    6654              : std::unique_ptr<AST::TypeNoBounds>
    6655        16101 : Parser<ManagedTokenSource>::parse_type_no_bounds ()
    6656              : {
    6657        16101 :   const_TokenPtr t = lexer.peek_token ();
    6658        16101 :   switch (t->get_id ())
    6659              :     {
    6660            1 :     case EXCLAM:
    6661              :       // never type - can't be macro as no path beforehand
    6662            1 :       lexer.skip_token ();
    6663            1 :       return std::unique_ptr<AST::NeverType> (
    6664            1 :         new AST::NeverType (t->get_locus ()));
    6665          656 :     case LEFT_SQUARE:
    6666              :       // slice type or array type - requires further disambiguation
    6667          656 :       return parse_slice_or_array_type ();
    6668           22 :     case LEFT_SHIFT:
    6669              :     case LEFT_ANGLE:
    6670              :       {
    6671              :         // qualified path in type
    6672           22 :         AST::QualifiedPathInType path = parse_qualified_path_in_type ();
    6673           22 :         if (path.is_error ())
    6674              :           {
    6675            0 :             Error error (t->get_locus (),
    6676              :                          "failed to parse qualified path in type");
    6677            0 :             add_error (std::move (error));
    6678              : 
    6679            0 :             return nullptr;
    6680            0 :           }
    6681           22 :         return std::unique_ptr<AST::QualifiedPathInType> (
    6682           22 :           new AST::QualifiedPathInType (std::move (path)));
    6683           22 :       }
    6684          104 :     case UNDERSCORE:
    6685              :       // inferred type
    6686          104 :       lexer.skip_token ();
    6687          104 :       return std::unique_ptr<AST::InferredType> (
    6688          104 :         new AST::InferredType (t->get_locus ()));
    6689         3812 :     case ASTERISK:
    6690              :       // raw pointer type
    6691         3812 :       return parse_raw_pointer_type ();
    6692           24 :     case AMP: // does this also include AMP_AMP? Yes! Which is... LOGICAL_AND?
    6693              :     case LOGICAL_AND:
    6694              :       // reference type
    6695           24 :       return parse_reference_type ();
    6696            0 :     case LIFETIME:
    6697              :       /* probably a lifetime bound, so probably type param bounds in
    6698              :        * TraitObjectType. this is not allowed, but detection here for error
    6699              :        * message */
    6700            0 :       add_error (Error (t->get_locus (),
    6701              :                         "lifetime bounds (i.e. in type param bounds, in "
    6702              :                         "TraitObjectType) are not allowed as TypeNoBounds"));
    6703              : 
    6704            0 :       return nullptr;
    6705        11305 :     case IDENTIFIER:
    6706              :     case SUPER:
    6707              :     case SELF:
    6708              :     case SELF_ALIAS:
    6709              :     case CRATE:
    6710              :     case DOLLAR_SIGN:
    6711              :     case SCOPE_RESOLUTION:
    6712              :       {
    6713              :         // macro invocation or type path - requires further disambiguation.
    6714              :         /* for parsing path component of each rule, perhaps parse it as a
    6715              :          * typepath and attempt conversion to simplepath if a trailing '!' is
    6716              :          * found */
    6717              :         /* Type path also includes TraitObjectTypeOneBound BUT if it starts
    6718              :          * with it, it is exactly the same as a TypePath syntactically, so
    6719              :          * this is a syntactical ambiguity. As such, the parser will parse it
    6720              :          * as a TypePath. This, however, does not prevent TraitObjectType from
    6721              :          * starting with a typepath. */
    6722              : 
    6723              :         // parse path as type path
    6724        11305 :         AST::TypePath path = parse_type_path ();
    6725        11305 :         if (path.is_error ())
    6726              :           {
    6727            0 :             Error error (
    6728              :               t->get_locus (),
    6729              :               "failed to parse path as first component of type no bounds");
    6730            0 :             add_error (std::move (error));
    6731              : 
    6732            0 :             return nullptr;
    6733            0 :           }
    6734        11305 :         location_t locus = path.get_locus ();
    6735              : 
    6736              :         // branch on next token
    6737        11305 :         t = lexer.peek_token ();
    6738        11305 :         switch (t->get_id ())
    6739              :           {
    6740            1 :           case EXCLAM:
    6741              :             {
    6742              :               // macro invocation
    6743              :               // convert to simple path
    6744            1 :               AST::SimplePath macro_path = path.as_simple_path ();
    6745            1 :               if (macro_path.is_empty ())
    6746              :                 {
    6747            0 :                   Error error (t->get_locus (),
    6748              :                                "failed to parse simple path in macro "
    6749              :                                "invocation (for type)");
    6750            0 :                   add_error (std::move (error));
    6751              : 
    6752            0 :                   return nullptr;
    6753            0 :                 }
    6754              : 
    6755            1 :               lexer.skip_token ();
    6756              : 
    6757            1 :               auto tok_tree = parse_delim_token_tree ();
    6758            1 :               if (!tok_tree)
    6759            0 :                 return nullptr;
    6760              : 
    6761            3 :               return AST::MacroInvocation::Regular (
    6762            2 :                 AST::MacroInvocData (std::move (macro_path),
    6763            1 :                                      std::move (tok_tree.value ())),
    6764            1 :                 {}, locus);
    6765            2 :             }
    6766        11304 :           default:
    6767              :             // assume that this is a type path and not an error
    6768        11304 :             return std::unique_ptr<AST::TypePath> (
    6769        11304 :               new AST::TypePath (std::move (path)));
    6770              :           }
    6771        11305 :       }
    6772           18 :     case LEFT_PAREN:
    6773              :       /* tuple type or parenthesised type - requires further disambiguation
    6774              :        * (the usual). ok apparently can be a parenthesised TraitBound too, so
    6775              :        * could be TraitObjectTypeOneBound */
    6776           18 :       return parse_paren_prefixed_type_no_bounds ();
    6777            2 :     case FOR:
    6778              :     case ASYNC:
    6779              :     case CONST:
    6780              :     case UNSAFE:
    6781              :     case EXTERN_KW:
    6782              :     case FN_KW:
    6783              :       // bare function type (with no for lifetimes)
    6784            2 :       return parse_bare_function_type (std::vector<AST::LifetimeParam> ());
    6785           14 :     case IMPL:
    6786           14 :       lexer.skip_token ();
    6787           28 :       if (lexer.peek_token ()->get_id () == LIFETIME)
    6788              :         {
    6789              :           /* cannot be one bound because lifetime prevents it from being
    6790              :            * traitbound not allowed as type no bounds, only here for error
    6791              :            * message */
    6792            0 :           Error error (
    6793            0 :             lexer.peek_token ()->get_locus (),
    6794              :             "lifetime (probably lifetime bound, in type param "
    6795              :             "bounds, in ImplTraitType) is not allowed in TypeNoBounds");
    6796            0 :           add_error (std::move (error));
    6797              : 
    6798            0 :           return nullptr;
    6799            0 :         }
    6800              :       else
    6801              :         {
    6802              :           // should be trait bound, so parse trait bound
    6803           14 :           std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
    6804           14 :           if (initial_bound == nullptr)
    6805              :             {
    6806            0 :               Error error (lexer.peek_token ()->get_locus (),
    6807              :                            "failed to parse ImplTraitTypeOneBound bound");
    6808            0 :               add_error (std::move (error));
    6809              : 
    6810            0 :               return nullptr;
    6811            0 :             }
    6812              : 
    6813           14 :           location_t locus = t->get_locus ();
    6814              : 
    6815              :           // ensure not a trait with multiple bounds
    6816           14 :           t = lexer.peek_token ();
    6817           14 :           if (t->get_id () == PLUS)
    6818              :             {
    6819            0 :               Error error (t->get_locus (),
    6820              :                            "plus after trait bound means an ImplTraitType, "
    6821              :                            "which is not allowed as a TypeNoBounds");
    6822            0 :               add_error (std::move (error));
    6823              : 
    6824            0 :               return nullptr;
    6825            0 :             }
    6826              : 
    6827           14 :           return std::unique_ptr<AST::ImplTraitTypeOneBound> (
    6828           14 :             new AST::ImplTraitTypeOneBound (std::move (initial_bound), locus));
    6829           14 :         }
    6830          143 :     case DYN:
    6831              :     case QUESTION_MARK:
    6832              :       {
    6833              :         // either TraitObjectTypeOneBound
    6834          143 :         bool has_dyn = false;
    6835          143 :         if (t->get_id () == DYN)
    6836              :           {
    6837          143 :             lexer.skip_token ();
    6838          143 :             has_dyn = true;
    6839              :           }
    6840              : 
    6841          286 :         if (lexer.peek_token ()->get_id () == LIFETIME)
    6842              :           {
    6843              :             /* means that cannot be TraitObjectTypeOneBound - so here for
    6844              :              * error message */
    6845            0 :             Error error (lexer.peek_token ()->get_locus (),
    6846              :                          "lifetime as bound in TraitObjectTypeOneBound "
    6847              :                          "is not allowed, so cannot be TypeNoBounds");
    6848            0 :             add_error (std::move (error));
    6849              : 
    6850            0 :             return nullptr;
    6851            0 :           }
    6852              : 
    6853              :         // should be trait bound, so parse trait bound
    6854          143 :         std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
    6855          143 :         if (initial_bound == nullptr)
    6856              :           {
    6857            0 :             Error error (
    6858            0 :               lexer.peek_token ()->get_locus (),
    6859              :               "failed to parse TraitObjectTypeOneBound initial bound");
    6860            0 :             add_error (std::move (error));
    6861              : 
    6862            0 :             return nullptr;
    6863            0 :           }
    6864              : 
    6865          143 :         location_t locus = t->get_locus ();
    6866              : 
    6867              :         // detect error with plus as next token
    6868          143 :         t = lexer.peek_token ();
    6869          143 :         if (t->get_id () == PLUS)
    6870              :           {
    6871            0 :             Error error (t->get_locus (),
    6872              :                          "plus after trait bound means a TraitObjectType, "
    6873              :                          "which is not allowed as a TypeNoBounds");
    6874            0 :             add_error (std::move (error));
    6875              : 
    6876            0 :             return nullptr;
    6877            0 :           }
    6878              : 
    6879              :         // convert trait bound to value object
    6880          143 :         AST::TraitBound value_bound (*initial_bound);
    6881              : 
    6882          143 :         return std::unique_ptr<AST::TraitObjectTypeOneBound> (
    6883          286 :           new AST::TraitObjectTypeOneBound (std::move (value_bound), locus,
    6884          143 :                                             has_dyn));
    6885          143 :       }
    6886            0 :     default:
    6887            0 :       add_error (Error (t->get_locus (),
    6888              :                         "unrecognised token %qs in type no bounds",
    6889              :                         t->get_token_description ()));
    6890              : 
    6891            0 :       return nullptr;
    6892              :     }
    6893        16101 : }
    6894              : 
    6895              : // Parses a type no bounds beginning with '('.
    6896              : template <typename ManagedTokenSource>
    6897              : std::unique_ptr<AST::TypeNoBounds>
    6898           18 : Parser<ManagedTokenSource>::parse_paren_prefixed_type_no_bounds ()
    6899              : {
    6900              :   /* NOTE: this could probably be parsed without the HACK solution of
    6901              :    * parse_paren_prefixed_type, but I was lazy. So FIXME for future.*/
    6902              : 
    6903              :   /* NOTE: again, syntactical ambiguity of a parenthesised trait bound is
    6904              :    * considered a trait bound, not a parenthesised type, so that it can still
    6905              :    * be used in type param bounds. */
    6906              : 
    6907           18 :   location_t left_paren_locus = lexer.peek_token ()->get_locus ();
    6908              : 
    6909              :   // skip left delim
    6910           18 :   lexer.skip_token ();
    6911              :   /* while next token isn't close delim, parse comma-separated types, saving
    6912              :    * whether trailing comma happens */
    6913           18 :   const_TokenPtr t = lexer.peek_token ();
    6914           18 :   bool trailing_comma = true;
    6915           18 :   std::vector<std::unique_ptr<AST::Type>> types;
    6916              : 
    6917           18 :   while (t->get_id () != RIGHT_PAREN)
    6918              :     {
    6919            2 :       std::unique_ptr<AST::Type> type = parse_type ();
    6920            2 :       if (type == nullptr)
    6921              :         {
    6922            0 :           Error error (t->get_locus (),
    6923              :                        "failed to parse type inside parentheses (probably "
    6924              :                        "tuple or parenthesised)");
    6925            0 :           add_error (std::move (error));
    6926              : 
    6927            0 :           return nullptr;
    6928            0 :         }
    6929            2 :       types.push_back (std::move (type));
    6930              : 
    6931            2 :       t = lexer.peek_token ();
    6932            2 :       if (t->get_id () != COMMA)
    6933              :         {
    6934            2 :           trailing_comma = false;
    6935              :           break;
    6936              :         }
    6937            0 :       lexer.skip_token ();
    6938              : 
    6939            0 :       t = lexer.peek_token ();
    6940              :     }
    6941              : 
    6942           18 :   if (!skip_token (RIGHT_PAREN))
    6943              :     {
    6944            0 :       return nullptr;
    6945              :     }
    6946              : 
    6947              :   // if only one type and no trailing comma, then not a tuple type
    6948           18 :   if (types.size () == 1 && !trailing_comma)
    6949              :     {
    6950              :       // must be a TraitObjectType (with more than one bound)
    6951            4 :       if (lexer.peek_token ()->get_id () == PLUS)
    6952              :         {
    6953              :           // error - this is not allowed for type no bounds
    6954            0 :           Error error (lexer.peek_token ()->get_locus (),
    6955              :                        "plus (implying TraitObjectType as type param "
    6956              :                        "bounds) is not allowed in type no bounds");
    6957            0 :           add_error (std::move (error));
    6958              : 
    6959            0 :           return nullptr;
    6960            0 :         }
    6961              :       else
    6962              :         {
    6963              :           // release vector pointer
    6964            2 :           std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
    6965              :           /* HACK: attempt to convert to trait bound. if fails, parenthesised
    6966              :            * type */
    6967            2 :           std::unique_ptr<AST::TraitBound> converted_bound (
    6968            2 :             released_ptr->to_trait_bound (true));
    6969            2 :           if (converted_bound == nullptr)
    6970              :             {
    6971              :               // parenthesised type
    6972            2 :               return std::unique_ptr<AST::ParenthesisedType> (
    6973            2 :                 new AST::ParenthesisedType (std::move (released_ptr),
    6974            2 :                                             left_paren_locus));
    6975              :             }
    6976              :           else
    6977              :             {
    6978              :               // trait object type (one bound)
    6979              : 
    6980              :               // get value semantics trait bound
    6981            0 :               AST::TraitBound value_bound (*converted_bound);
    6982              : 
    6983            0 :               return std::unique_ptr<AST::TraitObjectTypeOneBound> (
    6984            0 :                 new AST::TraitObjectTypeOneBound (value_bound,
    6985            0 :                                                   left_paren_locus));
    6986            0 :             }
    6987            2 :         }
    6988              :     }
    6989              :   else
    6990              :     {
    6991           16 :       return std::unique_ptr<AST::TupleType> (
    6992           16 :         new AST::TupleType (std::move (types), left_paren_locus));
    6993              :     }
    6994              :   /* TODO: ensure that this ensures that dynamic dispatch for traits is not
    6995              :    * lost somehow */
    6996           18 : }
    6997              : 
    6998              : // Parses tuple struct items if they exist. Does not parse parentheses.
    6999              : template <typename ManagedTokenSource>
    7000              : std::unique_ptr<AST::TupleStructItems>
    7001          926 : Parser<ManagedTokenSource>::parse_tuple_struct_items ()
    7002              : {
    7003          926 :   std::vector<std::unique_ptr<AST::Pattern>> lower_patterns;
    7004              : 
    7005              :   // DEBUG
    7006          926 :   rust_debug ("started parsing tuple struct items");
    7007              : 
    7008              :   // check for '..' at front
    7009         1852 :   if (lexer.peek_token ()->get_id () == DOT_DOT)
    7010              :     {
    7011              :       // only parse upper patterns
    7012           16 :       lexer.skip_token ();
    7013              : 
    7014              :       // DEBUG
    7015           16 :       rust_debug ("'..' at front in tuple struct items detected");
    7016              : 
    7017           16 :       std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
    7018              : 
    7019           16 :       const_TokenPtr t = lexer.peek_token ();
    7020           33 :       while (t->get_id () == COMMA)
    7021              :         {
    7022           17 :           lexer.skip_token ();
    7023              : 
    7024              :           // break if right paren
    7025           34 :           if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    7026              :             break;
    7027              : 
    7028              :           // parse pattern, which is now required
    7029           17 :           std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
    7030           17 :           if (pattern == nullptr)
    7031              :             {
    7032            0 :               Error error (lexer.peek_token ()->get_locus (),
    7033              :                            "failed to parse pattern in tuple struct items");
    7034            0 :               add_error (std::move (error));
    7035              : 
    7036            0 :               return nullptr;
    7037            0 :             }
    7038           17 :           upper_patterns.push_back (std::move (pattern));
    7039              : 
    7040           17 :           t = lexer.peek_token ();
    7041              :         }
    7042              : 
    7043              :       // DEBUG
    7044           16 :       rust_debug (
    7045              :         "finished parsing tuple struct items ranged (upper/none only)");
    7046              : 
    7047           16 :       return std::unique_ptr<AST::TupleStructItemsHasRest> (
    7048           16 :         new AST::TupleStructItemsHasRest (std::move (lower_patterns),
    7049           16 :                                           std::move (upper_patterns)));
    7050           16 :     }
    7051              : 
    7052              :   // has at least some lower patterns
    7053          910 :   const_TokenPtr t = lexer.peek_token ();
    7054         1868 :   while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT)
    7055              :     {
    7056              :       // DEBUG
    7057          958 :       rust_debug ("about to parse pattern in tuple struct items");
    7058              : 
    7059              :       // parse pattern, which is required
    7060          958 :       std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
    7061          958 :       if (pattern == nullptr)
    7062              :         {
    7063            0 :           Error error (t->get_locus (),
    7064              :                        "failed to parse pattern in tuple struct items");
    7065            0 :           add_error (std::move (error));
    7066              : 
    7067            0 :           return nullptr;
    7068            0 :         }
    7069          958 :       lower_patterns.push_back (std::move (pattern));
    7070              : 
    7071              :       // DEBUG
    7072          958 :       rust_debug ("successfully parsed pattern in tuple struct items");
    7073              : 
    7074         1916 :       if (lexer.peek_token ()->get_id () != COMMA)
    7075              :         {
    7076              :           // DEBUG
    7077          864 :           rust_debug ("broke out of parsing patterns in tuple struct "
    7078              :                       "items as no comma");
    7079              : 
    7080              :           break;
    7081              :         }
    7082           94 :       lexer.skip_token ();
    7083           94 :       t = lexer.peek_token ();
    7084              :     }
    7085              : 
    7086              :   // branch on next token
    7087          910 :   t = lexer.peek_token ();
    7088          910 :   switch (t->get_id ())
    7089              :     {
    7090          886 :     case RIGHT_PAREN:
    7091          886 :       return std::unique_ptr<AST::TupleStructItemsNoRest> (
    7092          886 :         new AST::TupleStructItemsNoRest (std::move (lower_patterns)));
    7093           23 :     case DOT_DOT:
    7094              :       {
    7095              :         // has an upper range that must be parsed separately
    7096           23 :         lexer.skip_token ();
    7097              : 
    7098           23 :         std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
    7099              : 
    7100           23 :         t = lexer.peek_token ();
    7101           25 :         while (t->get_id () == COMMA)
    7102              :           {
    7103            2 :             lexer.skip_token ();
    7104              : 
    7105              :             // break if next token is right paren
    7106            4 :             if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    7107              :               break;
    7108              : 
    7109              :             // parse pattern, which is required
    7110            2 :             std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
    7111            2 :             if (pattern == nullptr)
    7112              :               {
    7113            0 :                 Error error (lexer.peek_token ()->get_locus (),
    7114              :                              "failed to parse pattern in tuple struct items");
    7115            0 :                 add_error (std::move (error));
    7116              : 
    7117            0 :                 return nullptr;
    7118            0 :               }
    7119            2 :             upper_patterns.push_back (std::move (pattern));
    7120              : 
    7121            2 :             t = lexer.peek_token ();
    7122              :           }
    7123              : 
    7124           23 :         return std::unique_ptr<AST::TupleStructItemsHasRest> (
    7125           23 :           new AST::TupleStructItemsHasRest (std::move (lower_patterns),
    7126           23 :                                             std::move (upper_patterns)));
    7127           23 :       }
    7128            1 :     default:
    7129              :       // error
    7130            1 :       add_error (Error (t->get_locus (),
    7131              :                         "unexpected token %qs in tuple struct items",
    7132              :                         t->get_token_description ()));
    7133              : 
    7134            1 :       return nullptr;
    7135              :     }
    7136          926 : }
    7137              : 
    7138              : /* Parses a statement or expression (depending on whether a trailing semicolon
    7139              :  * exists). Useful for block expressions where it cannot be determined through
    7140              :  * lookahead whether it is a statement or expression to be parsed. */
    7141              : template <typename ManagedTokenSource>
    7142              : tl::expected<ExprOrStmt, Parse::Error::Node>
    7143        40415 : Parser<ManagedTokenSource>::parse_stmt_or_expr ()
    7144              : {
    7145              :   // quick exit for empty statement
    7146        40415 :   const_TokenPtr t = lexer.peek_token ();
    7147        40415 :   if (t->get_id () == SEMICOLON)
    7148              :     {
    7149           18 :       lexer.skip_token ();
    7150           18 :       std::unique_ptr<AST::EmptyStmt> stmt (
    7151           18 :         new AST::EmptyStmt (t->get_locus ()));
    7152           36 :       return ExprOrStmt (std::move (stmt));
    7153           18 :     }
    7154              : 
    7155              :   // parse outer attributes
    7156        40397 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    7157        40397 :   ParseRestrictions restrictions;
    7158        40397 :   restrictions.expr_can_be_stmt = true;
    7159              : 
    7160              :   // Defered child error checking: we need to check for a semicolon
    7161        40397 :   tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr;
    7162              : 
    7163              :   // parsing this will be annoying because of the many different possibilities
    7164              :   /* best may be just to copy paste in parse_item switch, and failing that try
    7165              :    * to parse outer attributes, and then pass them in to either a let
    7166              :    * statement or (fallback) expression statement. */
    7167              :   // FIXME: think of a way to do this without such a large switch?
    7168              : 
    7169              :   /* FIXME: for expressions at least, the only way that they can really be
    7170              :    * parsed properly in this way is if they don't support operators on them.
    7171              :    * They must be pratt-parsed otherwise. As such due to composability, only
    7172              :    * explicit statements will have special cases here. This should roughly
    7173              :    * correspond to "expr-with-block", but this warning is here in case it
    7174              :    * isn't the case. */
    7175        40397 :   t = lexer.peek_token ();
    7176        40397 :   switch (t->get_id ())
    7177              :     {
    7178        12754 :     case LET:
    7179              :       {
    7180              :         // let statement
    7181        12754 :         std::unique_ptr<AST::LetStmt> stmt (
    7182        12754 :           parse_let_stmt (std::move (outer_attrs)));
    7183        25508 :         return ExprOrStmt (std::move (stmt));
    7184        12754 :       }
    7185          351 :     case PUB:
    7186              :     case MOD:
    7187              :     case EXTERN_KW:
    7188              :     case USE:
    7189              :     case FN_KW:
    7190              :     case TYPE:
    7191              :     case STRUCT_KW:
    7192              :     case ENUM_KW:
    7193              :     case CONST:
    7194              :     case STATIC_KW:
    7195              :     case AUTO:
    7196              :     case TRAIT:
    7197              :     case IMPL:
    7198              :       {
    7199          351 :         std::unique_ptr<AST::VisItem> item (
    7200          351 :           parse_vis_item (std::move (outer_attrs)));
    7201          702 :         return ExprOrStmt (std::move (item));
    7202          351 :       }
    7203              :     /* TODO: implement union keyword but not really because of
    7204              :      * context-dependence crappy hack way to parse a union written below to
    7205              :      * separate it from the good code. */
    7206              :     // case UNION:
    7207         3152 :     case UNSAFE:
    7208              :       { // maybe - unsafe traits are a thing
    7209              :         /* if any of these (should be all possible VisItem prefixes), parse a
    7210              :          * VisItem - can't parse item because would require reparsing outer
    7211              :          * attributes */
    7212         3152 :         const_TokenPtr t2 = lexer.peek_token (1);
    7213         3152 :         switch (t2->get_id ())
    7214              :           {
    7215         3138 :           case LEFT_CURLY:
    7216              :             {
    7217              :               // unsafe block: parse as expression
    7218         6276 :               expr = parse_expr (std::move (outer_attrs), restrictions);
    7219              :               break;
    7220              :             }
    7221            0 :           case AUTO:
    7222              :           case TRAIT:
    7223              :             {
    7224              :               // unsafe trait
    7225            0 :               std::unique_ptr<AST::VisItem> item (
    7226            0 :                 parse_vis_item (std::move (outer_attrs)));
    7227            0 :               return ExprOrStmt (std::move (item));
    7228            0 :             }
    7229           14 :           case EXTERN_KW:
    7230              :           case FN_KW:
    7231              :             {
    7232              :               // unsafe function
    7233           14 :               std::unique_ptr<AST::VisItem> item (
    7234           14 :                 parse_vis_item (std::move (outer_attrs)));
    7235           28 :               return ExprOrStmt (std::move (item));
    7236           14 :             }
    7237            0 :           case IMPL:
    7238              :             {
    7239              :               // unsafe trait impl
    7240            0 :               std::unique_ptr<AST::VisItem> item (
    7241            0 :                 parse_vis_item (std::move (outer_attrs)));
    7242            0 :               return ExprOrStmt (std::move (item));
    7243            0 :             }
    7244            0 :           default:
    7245            0 :             add_error (Error (t2->get_locus (),
    7246              :                               "unrecognised token %qs after parsing unsafe - "
    7247              :                               "expected beginning of expression or statement",
    7248              :                               t->get_token_description ()));
    7249              : 
    7250              :             // skip somewhere?
    7251              :             return tl::unexpected<Parse::Error::Node> (
    7252            0 :               Parse::Error::Node::MALFORMED);
    7253              :           }
    7254              :         break;
    7255         3152 :       }
    7256              :       /* FIXME: this is either a macro invocation or macro invocation semi.
    7257              :        * start parsing to determine which one it is. */
    7258              :       // FIXME: old code there
    7259              : 
    7260              :     // crappy hack to do union "keyword"
    7261        12969 :     case IDENTIFIER:
    7262        23310 :       if (t->get_str () == Values::WeakKeywords::UNION
    7263        13005 :           && lexer.peek_token (1)->get_id () == IDENTIFIER)
    7264              :         {
    7265            1 :           std::unique_ptr<AST::VisItem> item (
    7266            1 :             parse_vis_item (std::move (outer_attrs)));
    7267            2 :           return ExprOrStmt (std::move (item));
    7268              :           // or should this go straight to parsing union?
    7269            1 :         }
    7270        23308 :       else if (t->get_str () == Values::WeakKeywords::MACRO_RULES
    7271        12975 :                && lexer.peek_token (1)->get_id () == EXCLAM)
    7272              :         {
    7273              :           // macro_rules! macro item
    7274            7 :           std::unique_ptr<AST::Item> item (
    7275            7 :             parse_macro_rules_def (std::move (outer_attrs)));
    7276           14 :           return ExprOrStmt (std::move (item));
    7277            7 :         }
    7278              :       gcc_fallthrough ();
    7279              :     case SUPER:
    7280              :     case SELF:
    7281              :     case SELF_ALIAS:
    7282              :     case CRATE:
    7283              :     case SCOPE_RESOLUTION:
    7284              :     case DOLLAR_SIGN:
    7285              :       {
    7286        15417 :         AST::PathInExpression path = parse_path_in_expression ();
    7287              :         tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    7288        15417 :           null_denotation;
    7289              : 
    7290        30834 :         if (lexer.peek_token ()->get_id () == EXCLAM)
    7291              :           {
    7292          590 :             std::unique_ptr<AST::MacroInvocation> invoc
    7293         1180 :               = parse_macro_invocation_partial (std::move (path),
    7294              :                                                 std::move (outer_attrs));
    7295          590 :             if (invoc == nullptr)
    7296              :               return tl::unexpected<Parse::Error::Node> (
    7297            0 :                 Parse::Error::Node::CHILD_ERROR);
    7298              : 
    7299          590 :             if (restrictions.consume_semi && maybe_skip_token (SEMICOLON))
    7300              :               {
    7301          398 :                 invoc->add_semicolon ();
    7302              :                 // Macro invocation with semicolon.
    7303          398 :                 return ExprOrStmt (
    7304          796 :                   std::unique_ptr<AST::Stmt> (std::move (invoc)));
    7305              :               }
    7306              : 
    7307          384 :             TokenId after_macro = lexer.peek_token ()->get_id ();
    7308              : 
    7309          192 :             AST::DelimType delim_type = invoc->get_invoc_data ()
    7310          192 :                                           .get_delim_tok_tree ()
    7311          192 :                                           .get_delim_type ();
    7312              : 
    7313          192 :             if (delim_type == AST::CURLY && after_macro != DOT
    7314            6 :                 && after_macro != QUESTION_MARK)
    7315              :               {
    7316            6 :                 rust_debug ("braced macro statement");
    7317            6 :                 return ExprOrStmt (
    7318           12 :                   std::unique_ptr<AST::Stmt> (std::move (invoc)));
    7319              :               }
    7320              : 
    7321          186 :             null_denotation = std::move (invoc);
    7322          590 :           }
    7323              :         else
    7324              :           {
    7325              :             null_denotation
    7326        29654 :               = null_denotation_path (std::move (path), {}, restrictions);
    7327              :           }
    7328              : 
    7329        60048 :         expr = left_denotations (std::move (null_denotation), LBP_LOWEST,
    7330              :                                  std::move (outer_attrs), restrictions);
    7331              :         break;
    7332        15417 :       }
    7333         8715 :     default:
    7334              :       /* expression statement or expression itself - parse
    7335              :        * expression then make it statement if semi afterwards */
    7336        17400 :       expr = parse_expr (std::move (outer_attrs), restrictions);
    7337         8715 :       break;
    7338              :     }
    7339              : 
    7340        26866 :   const_TokenPtr after_expr = lexer.peek_token ();
    7341        26866 :   if (after_expr->get_id () == SEMICOLON)
    7342              :     {
    7343              :       // must be expression statement
    7344         8351 :       lexer.skip_token ();
    7345              : 
    7346         8351 :       if (expr)
    7347              :         {
    7348         8350 :           return ExprOrStmt (
    7349        16700 :             std::make_unique<AST::ExprStmt> (std::move (expr.value ()),
    7350        25050 :                                              t->get_locus (), true));
    7351              :         }
    7352              :       else
    7353              :         {
    7354              :           return tl::unexpected<Parse::Error::Node> (
    7355            1 :             Parse::Error::Node::CHILD_ERROR);
    7356              :         }
    7357              :     }
    7358              : 
    7359        18515 :   if (expr)
    7360              :     {
    7361              :       // block expression statement.
    7362        18484 :       if (!expr.value ()->is_expr_without_block ()
    7363        18484 :           && after_expr->get_id () != RIGHT_CURLY)
    7364         2285 :         return ExprOrStmt (
    7365         4570 :           std::make_unique<AST::ExprStmt> (std::move (expr.value ()),
    7366         6855 :                                            t->get_locus (), false));
    7367              : 
    7368              :       // Check if expr_without_block is properly terminated
    7369        16199 :       if (expr.value ()->is_expr_without_block ()
    7370        16199 :           && after_expr->get_id () != RIGHT_CURLY)
    7371              :         {
    7372              :           // expr_without_block must be followed by ';' or '}'
    7373            1 :           Error error (after_expr->get_locus (),
    7374              :                        "expected %<;%> or %<}%> after expression, found %qs",
    7375              :                        after_expr->get_token_description ());
    7376            1 :           add_error (std::move (error));
    7377              :           return tl::unexpected<Parse::Error::Node> (
    7378            1 :             Parse::Error::Node::MALFORMED);
    7379            1 :         }
    7380              :     }
    7381              : 
    7382              :   // return expression
    7383        16229 :   if (expr)
    7384        32396 :     return ExprOrStmt (std::move (expr.value ()));
    7385              :   else
    7386           31 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
    7387        67263 : }
    7388              : 
    7389              : } // namespace Rust
    7390              : 
    7391              : #include "rust-parse-impl-utils.hxx"
    7392              : #include "rust-parse-impl-attribute.hxx"
    7393              : #include "rust-parse-impl-ttree.hxx"
    7394              : #include "rust-parse-impl-macro.hxx"
    7395              : #include "rust-parse-impl-path.hxx"
    7396              : #include "rust-parse-impl-pattern.hxx"
    7397              : #include "rust-parse-impl-expr.hxx"
        

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.