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