LCOV - code coverage report
Current view: top level - gcc/rust/parse - rust-parse-impl-expr.hxx (source / functions) Coverage Total Hit
Test: gcc.info Lines: 64.6 % 1970 1272
Test Date: 2026-06-20 15:32:29 Functions: 48.1 % 156 75
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2025-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              : /* DO NOT INCLUDE ANYWHERE - this is automatically included
      20              :  *   by rust-parse-impl.h
      21              :  * This is also the reason why there are no include guards. */
      22              : 
      23              : #include "rust-parse.h"
      24              : 
      25              : namespace Rust {
      26              : 
      27              : // Parses a block expression, including the curly braces at start and end.
      28              : template <typename ManagedTokenSource>
      29              : tl::expected<std::unique_ptr<AST::BlockExpr>, Parse::Error::Node>
      30        23247 : Parser<ManagedTokenSource>::parse_block_expr (
      31              :   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label,
      32              :   location_t pratt_parsed_loc)
      33              : {
      34        23247 :   location_t locus = pratt_parsed_loc;
      35        23247 :   if (locus == UNKNOWN_LOCATION)
      36              :     {
      37        21707 :       locus = lexer.peek_token ()->get_locus ();
      38        21707 :       if (!skip_token (LEFT_CURLY))
      39              :         {
      40            0 :           skip_after_end_block ();
      41              :           return tl::unexpected<Parse::Error::Node> (
      42            0 :             Parse::Error::Node::MALFORMED);
      43              :         }
      44              :     }
      45              : 
      46        23247 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
      47              : 
      48              :   // parse statements and expression
      49        23247 :   std::vector<std::unique_ptr<AST::Stmt>> stmts;
      50        23247 :   std::unique_ptr<AST::Expr> expr = nullptr;
      51              : 
      52        23247 :   const_TokenPtr t = lexer.peek_token ();
      53        63727 :   while (t->get_id () != RIGHT_CURLY)
      54              :     {
      55        40480 :       auto expr_or_stmt = parse_stmt_or_expr ();
      56        40480 :       if (!expr_or_stmt)
      57              :         {
      58           34 :           skip_after_end_block ();
      59              :           return tl::unexpected<Parse::Error::Node> (
      60           34 :             Parse::Error::Node::CHILD_ERROR);
      61              :         }
      62              : 
      63        40446 :       t = lexer.peek_token ();
      64              : 
      65        40446 :       if (expr_or_stmt->stmt != nullptr)
      66              :         {
      67        24213 :           stmts.push_back (std::move (expr_or_stmt->stmt));
      68              :         }
      69              :       else
      70              :         {
      71              :           // assign to expression and end parsing inside
      72        16233 :           expr = std::move (expr_or_stmt->expr);
      73              :         }
      74              :     }
      75              : 
      76        23213 :   location_t end_locus = t->get_locus ();
      77              : 
      78        23213 :   if (!skip_token (RIGHT_CURLY))
      79              :     {
      80              :       // We don't need to throw an error as it already reported by skip_token
      81            0 :       skip_after_end_block ();
      82            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
      83              :     }
      84              : 
      85              :   // grammar allows for empty block expressions
      86              : 
      87        23213 :   stmts.shrink_to_fit ();
      88              : 
      89        23213 :   return std::unique_ptr<AST::BlockExpr> (
      90        23215 :     new AST::BlockExpr (std::move (stmts), std::move (expr),
      91              :                         std::move (inner_attrs), std::move (outer_attrs),
      92        23213 :                         std::move (label), locus, end_locus));
      93        23247 : }
      94              : 
      95              : /* Parse an anonymous const expression. This can be a regular const expression
      96              :  * or an underscore for deferred const inference */
      97              : template <typename ManagedTokenSource>
      98              : tl::expected<AST::AnonConst, Parse::Error::Node>
      99          675 : Parser<ManagedTokenSource>::parse_anon_const ()
     100              : {
     101          675 :   auto current = lexer.peek_token ();
     102          675 :   auto locus = current->get_locus ();
     103              : 
     104              :   // Special case deferred inference constants
     105          675 :   if (maybe_skip_token (UNDERSCORE))
     106           11 :     return AST::AnonConst (locus);
     107              : 
     108          664 :   auto expr = parse_expr ();
     109              : 
     110          664 :   if (!expr)
     111            1 :     return tl::make_unexpected (Parse::Error::Node{});
     112              : 
     113         1326 :   return AST::AnonConst (std::move (expr.value ()), locus);
     114          664 : }
     115              : 
     116              : /* Parse a "const block", a block preceded by the `const` keyword whose
     117              :  * statements can be const evaluated and used in constant contexts */
     118              : template <typename ManagedTokenSource>
     119              : tl::expected<std::unique_ptr<AST::ConstBlock>, Parse::Error::Node>
     120           15 : Parser<ManagedTokenSource>::parse_const_block_expr (AST::AttrVec outer_attrs,
     121              :                                                     location_t locus)
     122              : {
     123           15 :   auto block_res = parse_block_expr ();
     124              : 
     125           15 :   if (!block_res)
     126              :     {
     127            0 :       add_error (Error (locus, "failed to parse inner block in const block"));
     128            0 :       skip_after_end_block ();
     129              : 
     130              :       return tl::unexpected<Parse::Error::Node> (
     131            0 :         Parse::Error::Node::CHILD_ERROR);
     132              :     }
     133           15 :   auto block = std::move (block_res.value ());
     134              : 
     135           15 :   auto block_locus = block->get_locus ();
     136              : 
     137           30 :   return std::make_unique<AST::ConstBlock> (AST::AnonConst (std::move (block),
     138              :                                                             block_locus),
     139           15 :                                             locus, std::move (outer_attrs));
     140           15 : }
     141              : 
     142              : /* Parses a "grouped" expression (expression in parentheses), used to control
     143              :  * precedence. */
     144              : template <typename ManagedTokenSource>
     145              : tl::expected<std::unique_ptr<AST::GroupedExpr>, Parse::Error::Node>
     146            0 : Parser<ManagedTokenSource>::parse_grouped_expr (AST::AttrVec outer_attrs)
     147              : {
     148            0 :   location_t locus = lexer.peek_token ()->get_locus ();
     149            0 :   skip_token (LEFT_PAREN);
     150              : 
     151            0 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
     152              : 
     153              :   // parse required expr inside parentheses
     154            0 :   auto expr_in_parens = parse_expr ();
     155            0 :   if (!expr_in_parens)
     156              :     {
     157              :       // skip after somewhere?
     158              :       // error?
     159              :       return tl::unexpected<Parse::Error::Node> (
     160            0 :         Parse::Error::Node::CHILD_ERROR);
     161              :     }
     162              : 
     163            0 :   if (!skip_token (RIGHT_PAREN))
     164              :     {
     165              :       // skip after somewhere?
     166            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     167              :     }
     168              : 
     169            0 :   return std::unique_ptr<AST::GroupedExpr> (
     170            0 :     new AST::GroupedExpr (std::move (expr_in_parens.value ()),
     171              :                           std::move (inner_attrs), std::move (outer_attrs),
     172            0 :                           locus));
     173            0 : }
     174              : 
     175              : // Parses a closure expression (closure definition).
     176              : template <typename ManagedTokenSource>
     177              : tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Node>
     178            0 : Parser<ManagedTokenSource>::parse_closure_expr (AST::AttrVec outer_attrs)
     179              : {
     180            0 :   location_t locus = lexer.peek_token ()->get_locus ();
     181              :   // detect optional "move"
     182            0 :   bool has_move = false;
     183            0 :   if (lexer.peek_token ()->get_id () == MOVE)
     184              :     {
     185            0 :       lexer.skip_token ();
     186            0 :       has_move = true;
     187              :     }
     188              : 
     189              :   // handle parameter list
     190            0 :   std::vector<AST::ClosureParam> params;
     191              : 
     192            0 :   const_TokenPtr t = lexer.peek_token ();
     193            0 :   switch (t->get_id ())
     194              :     {
     195            0 :     case OR:
     196              :       // skip token, no parameters
     197            0 :       lexer.skip_token ();
     198              :       break;
     199            0 :     case PIPE:
     200              :       // actually may have parameters
     201            0 :       lexer.skip_token ();
     202            0 :       t = lexer.peek_token ();
     203              : 
     204            0 :       while (t->get_id () != PIPE)
     205              :         {
     206            0 :           AST::ClosureParam param = parse_closure_param ();
     207            0 :           if (param.is_error ())
     208              :             {
     209              :               // TODO is this really an error?
     210            0 :               Error error (t->get_locus (), "could not parse closure param");
     211            0 :               add_error (std::move (error));
     212              : 
     213              :               break;
     214            0 :             }
     215            0 :           params.push_back (std::move (param));
     216              : 
     217            0 :           if (lexer.peek_token ()->get_id () != COMMA)
     218              :             {
     219            0 :               lexer.skip_token ();
     220              :               // not an error but means param list is done
     221              :               break;
     222              :             }
     223              :           // skip comma
     224            0 :           lexer.skip_token ();
     225              : 
     226            0 :           t = lexer.peek_token ();
     227              :         }
     228            0 :       params.shrink_to_fit ();
     229              :       break;
     230            0 :     default:
     231            0 :       add_error (Error (t->get_locus (),
     232              :                         "unexpected token %qs in closure expression - expected "
     233              :                         "%<|%> or %<||%>",
     234              :                         t->get_token_description ()));
     235              : 
     236              :       // skip somewhere?
     237            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     238              :     }
     239              : 
     240              :   // again branch based on next token
     241            0 :   t = lexer.peek_token ();
     242            0 :   if (t->get_id () == RETURN_TYPE)
     243              :     {
     244              :       // must be return type closure with block expr
     245              : 
     246              :       // skip "return type" token
     247            0 :       lexer.skip_token ();
     248              : 
     249              :       // parse actual type, which is required
     250            0 :       std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
     251            0 :       if (type == nullptr)
     252              :         {
     253              :           // error
     254            0 :           Error error (t->get_locus (), "failed to parse type for closure");
     255            0 :           add_error (std::move (error));
     256              : 
     257              :           // skip somewhere?
     258              :           return tl::unexpected<Parse::Error::Node> (
     259            0 :             Parse::Error::Node::CHILD_ERROR);
     260            0 :         }
     261              : 
     262              :       // parse block expr, which is required
     263            0 :       auto block = parse_block_expr ();
     264            0 :       if (!block)
     265              :         {
     266              :           // error
     267            0 :           Error error (lexer.peek_token ()->get_locus (),
     268              :                        "failed to parse block expr in closure");
     269            0 :           add_error (std::move (error));
     270              : 
     271              :           // skip somewhere?
     272              :           return tl::unexpected<Parse::Error::Node> (
     273            0 :             Parse::Error::Node::CHILD_ERROR);
     274            0 :         }
     275              : 
     276            0 :       return std::unique_ptr<AST::ClosureExprInnerTyped> (
     277            0 :         new AST::ClosureExprInnerTyped (std::move (type),
     278            0 :                                         std::move (block.value ()),
     279              :                                         std::move (params), locus, has_move,
     280            0 :                                         std::move (outer_attrs)));
     281            0 :     }
     282              :   else
     283              :     {
     284              :       // must be expr-only closure
     285              : 
     286              :       // parse expr, which is required
     287            0 :       auto expr = parse_expr ();
     288            0 :       if (!expr)
     289              :         {
     290            0 :           Error error (t->get_locus (),
     291              :                        "failed to parse expression in closure");
     292            0 :           add_error (std::move (error));
     293              : 
     294              :           // skip somewhere?
     295              :           return tl::unexpected<Parse::Error::Node> (
     296            0 :             Parse::Error::Node::CHILD_ERROR);
     297            0 :         }
     298              : 
     299            0 :       return std::unique_ptr<AST::ClosureExprInner> (
     300            0 :         new AST::ClosureExprInner (std::move (expr.value ()),
     301              :                                    std::move (params), locus, has_move,
     302            0 :                                    std::move (outer_attrs)));
     303            0 :     }
     304            0 : }
     305              : 
     306              : // Parses a literal token (to literal expression).
     307              : template <typename ManagedTokenSource>
     308              : tl::expected<std::unique_ptr<AST::LiteralExpr>, Parse::Error::Node>
     309         3665 : Parser<ManagedTokenSource>::parse_literal_expr (AST::AttrVec outer_attrs)
     310              : {
     311              :   // TODO: change if literal representation in lexer changes
     312              : 
     313         3665 :   std::string literal_value;
     314         3665 :   AST::Literal::LitType type = AST::Literal::STRING;
     315              : 
     316              :   // branch based on token
     317         3665 :   const_TokenPtr t = lexer.peek_token ();
     318         3665 :   switch (t->get_id ())
     319              :     {
     320            2 :     case CHAR_LITERAL:
     321            2 :       type = AST::Literal::CHAR;
     322            2 :       literal_value = t->get_str ();
     323            2 :       lexer.skip_token ();
     324              :       break;
     325          293 :     case STRING_LITERAL:
     326          293 :       type = AST::Literal::STRING;
     327          293 :       literal_value = t->get_str ();
     328          293 :       lexer.skip_token ();
     329              :       break;
     330            0 :     case BYTE_CHAR_LITERAL:
     331            0 :       type = AST::Literal::BYTE;
     332            0 :       literal_value = t->get_str ();
     333            0 :       lexer.skip_token ();
     334              :       break;
     335            1 :     case BYTE_STRING_LITERAL:
     336            1 :       type = AST::Literal::BYTE_STRING;
     337            1 :       literal_value = t->get_str ();
     338            1 :       lexer.skip_token ();
     339              :       break;
     340            0 :     case RAW_STRING_LITERAL:
     341            0 :       type = AST::Literal::RAW_STRING;
     342            0 :       literal_value = t->get_str ();
     343            0 :       lexer.skip_token ();
     344              :       break;
     345         3362 :     case INT_LITERAL:
     346         3362 :       type = AST::Literal::INT;
     347         3362 :       literal_value = LiteralResolve::evaluate_integer_literal (t);
     348         3362 :       lexer.skip_token ();
     349              :       break;
     350            1 :     case FLOAT_LITERAL:
     351            1 :       type = AST::Literal::FLOAT;
     352            1 :       literal_value = LiteralResolve::evaluate_float_literal (t);
     353            1 :       lexer.skip_token ();
     354              :       break;
     355              :     // case BOOL_LITERAL
     356              :     // use true and false keywords rather than "bool literal" Rust terminology
     357            0 :     case TRUE_LITERAL:
     358            0 :       type = AST::Literal::BOOL;
     359            0 :       literal_value = Values::Keywords::TRUE_LITERAL;
     360            0 :       lexer.skip_token ();
     361              :       break;
     362            1 :     case FALSE_LITERAL:
     363            1 :       type = AST::Literal::BOOL;
     364            1 :       literal_value = Values::Keywords::FALSE_LITERAL;
     365            1 :       lexer.skip_token ();
     366              :       break;
     367            5 :     default:
     368              :       // error - cannot be a literal expr
     369            5 :       add_error (Error (t->get_locus (),
     370              :                         "unexpected token %qs when parsing literal expression",
     371              :                         t->get_token_description ()));
     372              : 
     373              :       // skip?
     374            5 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     375              :     }
     376              : 
     377         3660 :   auto type_hint
     378         3660 :     = (t->get_id () == INT_LITERAL || t->get_id () == FLOAT_LITERAL)
     379        10386 :         ? LiteralResolve::resolve_literal_suffix (t)
     380          297 :         : t->get_type_hint ();
     381              : 
     382              :   // create literal based on stuff in switch
     383         3660 :   return std::unique_ptr<AST::LiteralExpr> (
     384         7320 :     new AST::LiteralExpr (std::move (literal_value), std::move (type),
     385         3660 :                           type_hint, std::move (outer_attrs), t->get_locus ()));
     386         3665 : }
     387              : 
     388              : template <typename ManagedTokenSource>
     389              : tl::expected<std::unique_ptr<AST::BoxExpr>, Parse::Error::Node>
     390            2 : Parser<ManagedTokenSource>::parse_box_expr (AST::AttrVec outer_attrs,
     391              :                                             location_t pratt_parsed_loc)
     392              : {
     393            2 :   location_t locus = pratt_parsed_loc;
     394            2 :   if (locus == UNKNOWN_LOCATION)
     395              :     {
     396            0 :       locus = lexer.peek_token ()->get_locus ();
     397            0 :       skip_token (BOX);
     398              :     }
     399              : 
     400            2 :   ParseRestrictions restrictions;
     401              :   restrictions.expr_can_be_null = false;
     402              : 
     403            2 :   auto expr = parse_expr (AST::AttrVec (), restrictions);
     404            2 :   if (!expr)
     405            0 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     406              : 
     407            2 :   return std::unique_ptr<AST::BoxExpr> (
     408            2 :     new AST::BoxExpr (std::move (expr.value ()), std::move (outer_attrs),
     409            2 :                       locus));
     410            2 : }
     411              : 
     412              : // Parses a return expression (including any expression to return).
     413              : template <typename ManagedTokenSource>
     414              : tl::expected<std::unique_ptr<AST::ReturnExpr>, Parse::Error::Node>
     415          549 : Parser<ManagedTokenSource>::parse_return_expr (AST::AttrVec outer_attrs,
     416              :                                                location_t pratt_parsed_loc)
     417              : {
     418          549 :   location_t locus = pratt_parsed_loc;
     419          549 :   if (locus == UNKNOWN_LOCATION)
     420              :     {
     421            0 :       locus = lexer.peek_token ()->get_locus ();
     422            0 :       skip_token (RETURN_KW);
     423              :     }
     424              : 
     425              :   // parse expression to return, if it exists
     426          549 :   ParseRestrictions restrictions;
     427          549 :   restrictions.expr_can_be_null = true;
     428          549 :   auto returned_expr = parse_expr (AST::AttrVec (), restrictions);
     429          549 :   tl::optional<std::unique_ptr<AST::Expr>> expr = tl::nullopt;
     430          549 :   if (returned_expr)
     431          516 :     expr = std::move (returned_expr.value ());
     432              : 
     433          549 :   return std::make_unique<AST::ReturnExpr> (std::move (expr),
     434          549 :                                             std::move (outer_attrs), locus);
     435          549 : }
     436              : 
     437              : // Parses a try expression.
     438              : template <typename ManagedTokenSource>
     439              : tl::expected<std::unique_ptr<AST::TryExpr>, Parse::Error::Node>
     440            1 : Parser<ManagedTokenSource>::parse_try_expr (AST::AttrVec outer_attrs,
     441              :                                             location_t pratt_parsed_loc)
     442              : {
     443            1 :   location_t locus = pratt_parsed_loc;
     444            1 :   if (locus == UNKNOWN_LOCATION)
     445              :     {
     446            0 :       locus = lexer.peek_token ()->get_locus ();
     447            0 :       skip_token (TRY);
     448              :     }
     449              : 
     450            1 :   auto block_expr = parse_block_expr ();
     451              : 
     452            1 :   if (!block_expr)
     453            0 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     454              : 
     455            1 :   return std::unique_ptr<AST::TryExpr> (
     456            1 :     new AST::TryExpr (std::move (block_expr.value ()), std::move (outer_attrs),
     457            1 :                       locus));
     458            1 : }
     459              : 
     460              : /* Parses a break expression (including any label to break to AND any return
     461              :  * expression). */
     462              : template <typename ManagedTokenSource>
     463              : tl::expected<std::unique_ptr<AST::BreakExpr>, Parse::Error::Node>
     464           81 : Parser<ManagedTokenSource>::parse_break_expr (AST::AttrVec outer_attrs,
     465              :                                               location_t pratt_parsed_loc)
     466              : {
     467           81 :   location_t locus = pratt_parsed_loc;
     468           81 :   if (locus == UNKNOWN_LOCATION)
     469              :     {
     470            0 :       locus = lexer.peek_token ()->get_locus ();
     471            0 :       skip_token (BREAK);
     472              :     }
     473              : 
     474           81 :   auto parsed_label = parse_lifetime (false);
     475           81 :   auto label = (parsed_label)
     476           81 :                  ? tl::optional<AST::Lifetime> (parsed_label.value ())
     477              :                  : tl::nullopt;
     478              : 
     479              :   // parse break return expression if it exists
     480           81 :   ParseRestrictions restrictions;
     481           81 :   restrictions.expr_can_be_null = true;
     482           81 :   auto return_expr = parse_expr (AST::AttrVec (), restrictions);
     483              : 
     484           81 :   if (return_expr)
     485           24 :     return std::unique_ptr<AST::BreakExpr> (
     486           72 :       new AST::BreakExpr (std::move (label), std::move (return_expr.value ()),
     487           24 :                           std::move (outer_attrs), locus));
     488           57 :   else if (return_expr.error () == Parse::Error::Expr::NULL_EXPR)
     489           57 :     return std::unique_ptr<AST::BreakExpr> (
     490          114 :       new AST::BreakExpr (std::move (label), tl::nullopt,
     491           57 :                           std::move (outer_attrs), locus));
     492              :   else
     493            0 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     494          106 : }
     495              : 
     496              : // Parses a continue expression (including any label to continue from).
     497              : template <typename ManagedTokenSource>
     498              : std::unique_ptr<AST::ContinueExpr>
     499           17 : Parser<ManagedTokenSource>::parse_continue_expr (AST::AttrVec outer_attrs,
     500              :                                                  location_t pratt_parsed_loc)
     501              : {
     502           17 :   location_t locus = pratt_parsed_loc;
     503           17 :   if (locus == UNKNOWN_LOCATION)
     504              :     {
     505            0 :       locus = lexer.peek_token ()->get_locus ();
     506            0 :       skip_token (CONTINUE);
     507              :     }
     508              : 
     509           17 :   auto parsed_label = parse_lifetime (false);
     510           17 :   auto label = (parsed_label)
     511           17 :                  ? tl::optional<AST::Lifetime> (parsed_label.value ())
     512              :                  : tl::nullopt;
     513              : 
     514              :   return std::make_unique<AST::ContinueExpr> (std::move (label),
     515           17 :                                               std::move (outer_attrs), locus);
     516           17 : }
     517              : 
     518              : /* Parses an if expression of any kind, including with else, else if, else if
     519              :  * let, and neither. Note that any outer attributes will be ignored because if
     520              :  * expressions don't support them. */
     521              : template <typename ManagedTokenSource>
     522              : tl::expected<std::unique_ptr<AST::IfExpr>, Parse::Error::Node>
     523         2453 : Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
     524              :                                            location_t pratt_parsed_loc)
     525              : {
     526              :   // TODO: make having outer attributes an error?
     527         2453 :   location_t locus = pratt_parsed_loc;
     528         2453 :   if (locus == UNKNOWN_LOCATION)
     529              :     {
     530          323 :       locus = lexer.peek_token ()->get_locus ();
     531          323 :       if (!skip_token (IF))
     532              :         {
     533            0 :           skip_after_end_block ();
     534              :           return tl::unexpected<Parse::Error::Node> (
     535            0 :             Parse::Error::Node::MALFORMED);
     536              :         }
     537              :     }
     538              : 
     539              :   // detect accidental if let
     540         4906 :   if (lexer.peek_token ()->get_id () == LET)
     541              :     {
     542            0 :       Error error (lexer.peek_token ()->get_locus (),
     543              :                    "if let expression probably exists, but is being parsed "
     544              :                    "as an if expression. This may be a parser error");
     545            0 :       add_error (std::move (error));
     546              : 
     547              :       // skip somewhere?
     548            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     549            0 :     }
     550              : 
     551              :   /* parse required condition expr - HACK to prevent struct expr from being
     552              :    * parsed */
     553         2453 :   ParseRestrictions no_struct_expr;
     554         2453 :   no_struct_expr.can_be_struct_expr = false;
     555         2453 :   auto condition = parse_expr ({}, no_struct_expr);
     556         2453 :   if (!condition)
     557              :     {
     558            0 :       Error error (lexer.peek_token ()->get_locus (),
     559              :                    "failed to parse condition expression in if expression");
     560            0 :       add_error (std::move (error));
     561              : 
     562              :       // skip somewhere?
     563              :       return tl::unexpected<Parse::Error::Node> (
     564            0 :         Parse::Error::Node::CHILD_ERROR);
     565            0 :     }
     566              : 
     567              :   // parse required block expr
     568         2453 :   auto if_body = parse_block_expr ();
     569         2453 :   if (!if_body)
     570            1 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     571              : 
     572              :   // branch to parse end or else (and then else, else if, or else if let)
     573         4904 :   if (lexer.peek_token ()->get_id () != ELSE)
     574              :     {
     575              :       // single selection - end of if expression
     576         1239 :       return std::unique_ptr<AST::IfExpr> (
     577         1239 :         new AST::IfExpr (std::move (condition.value ()),
     578         1239 :                          std::move (if_body.value ()), std::move (outer_attrs),
     579         1239 :                          locus));
     580              :     }
     581              :   else
     582              :     {
     583              :       // double or multiple selection - branch on end, else if, or else if let
     584              : 
     585              :       // skip "else"
     586         1213 :       lexer.skip_token ();
     587              : 
     588              :       // branch on whether next token is '{' or 'if'
     589         1213 :       const_TokenPtr t = lexer.peek_token ();
     590         1213 :       switch (t->get_id ())
     591              :         {
     592          890 :         case LEFT_CURLY:
     593              :           {
     594              :             // double selection - else
     595              :             // parse else block expr (required)
     596          890 :             auto else_body = parse_block_expr ();
     597          890 :             if (!else_body)
     598              :               {
     599            0 :                 Error error (lexer.peek_token ()->get_locus (),
     600              :                              "failed to parse else body block expression in "
     601              :                              "if expression");
     602            0 :                 add_error (std::move (error));
     603              : 
     604              :                 // skip somewhere?
     605              :                 return tl::unexpected<Parse::Error::Node> (
     606            0 :                   Parse::Error::Node::CHILD_ERROR);
     607            0 :               }
     608              : 
     609          890 :             return std::unique_ptr<AST::IfExprConseqElse> (
     610         1780 :               new AST::IfExprConseqElse (std::move (condition.value ()),
     611          890 :                                          std::move (if_body.value ()),
     612          890 :                                          std::move (else_body.value ()),
     613          890 :                                          std::move (outer_attrs), locus));
     614          890 :           }
     615          323 :         case IF:
     616              :           {
     617              :             // multiple selection - else if or else if let
     618              :             // branch on whether next token is 'let' or not
     619          646 :             if (lexer.peek_token (1)->get_id () == LET)
     620              :               {
     621              :                 // parse if let expr (required)
     622            1 :                 auto if_let_expr = parse_if_let_expr ();
     623            1 :                 if (!if_let_expr)
     624              :                   {
     625            0 :                     Error error (lexer.peek_token ()->get_locus (),
     626              :                                  "failed to parse (else) if let expression "
     627              :                                  "after if expression");
     628            0 :                     add_error (std::move (error));
     629              : 
     630              :                     // skip somewhere?
     631              :                     return tl::unexpected<Parse::Error::Node> (
     632            0 :                       Parse::Error::Node::CHILD_ERROR);
     633            0 :                   }
     634              : 
     635            1 :                 return std::unique_ptr<AST::IfExprConseqElse> (
     636            2 :                   new AST::IfExprConseqElse (std::move (condition.value ()),
     637            1 :                                              std::move (if_body.value ()),
     638            1 :                                              std::move (if_let_expr.value ()),
     639            1 :                                              std::move (outer_attrs), locus));
     640            1 :               }
     641              :             else
     642              :               {
     643              :                 // parse if expr (required)
     644          322 :                 auto if_expr = parse_if_expr ();
     645          322 :                 if (!if_expr)
     646              :                   {
     647            0 :                     Error error (lexer.peek_token ()->get_locus (),
     648              :                                  "failed to parse (else) if expression after "
     649              :                                  "if expression");
     650            0 :                     add_error (std::move (error));
     651              : 
     652              :                     // skip somewhere?
     653              :                     return tl::unexpected<Parse::Error::Node> (
     654            0 :                       Parse::Error::Node::CHILD_ERROR);
     655            0 :                   }
     656              : 
     657          322 :                 return std::unique_ptr<AST::IfExprConseqElse> (
     658          644 :                   new AST::IfExprConseqElse (std::move (condition.value ()),
     659          322 :                                              std::move (if_body.value ()),
     660          322 :                                              std::move (if_expr.value ()),
     661          322 :                                              std::move (outer_attrs), locus));
     662          322 :               }
     663              :           }
     664            0 :         default:
     665              :           // error - invalid token
     666            0 :           add_error (Error (t->get_locus (),
     667              :                             "unexpected token %qs after else in if expression",
     668              :                             t->get_token_description ()));
     669              : 
     670              :           // skip somewhere?
     671              :           return tl::unexpected<Parse::Error::Node> (
     672            0 :             Parse::Error::Node::MALFORMED);
     673              :         }
     674         1213 :     }
     675         4906 : }
     676              : 
     677              : /* Parses an if let expression of any kind, including with else, else if, else
     678              :  * if let, and none. Note that any outer attributes will be ignored as if let
     679              :  * expressions don't support them. */
     680              : template <typename ManagedTokenSource>
     681              : tl::expected<std::unique_ptr<AST::IfLetExpr>, Parse::Error::Node>
     682           31 : Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
     683              :                                                location_t pratt_parsed_loc)
     684              : {
     685              :   // TODO: make having outer attributes an error?
     686           31 :   location_t locus = pratt_parsed_loc;
     687           31 :   if (locus == UNKNOWN_LOCATION)
     688              :     {
     689            1 :       locus = lexer.peek_token ()->get_locus ();
     690            1 :       if (!skip_token (IF))
     691              :         {
     692            0 :           skip_after_end_block ();
     693              :           return tl::unexpected<Parse::Error::Node> (
     694            0 :             Parse::Error::Node::MALFORMED);
     695              :         }
     696              :     }
     697              : 
     698              :   // detect accidental if expr parsed as if let expr
     699           62 :   if (lexer.peek_token ()->get_id () != LET)
     700              :     {
     701            0 :       Error error (lexer.peek_token ()->get_locus (),
     702              :                    "if expression probably exists, but is being parsed as an "
     703              :                    "if let expression. This may be a parser error");
     704            0 :       add_error (std::move (error));
     705              : 
     706              :       // skip somewhere?
     707            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     708            0 :     }
     709           31 :   lexer.skip_token ();
     710              : 
     711              :   // parse match arm patterns (which are required)
     712           31 :   std::unique_ptr<AST::Pattern> match_arm_pattern
     713              :     = parse_match_arm_pattern (EQUAL);
     714           31 :   if (match_arm_pattern == nullptr)
     715              :     {
     716            0 :       Error error (
     717            0 :         lexer.peek_token ()->get_locus (),
     718              :         "failed to parse any match arm patterns in if let expression");
     719            0 :       add_error (std::move (error));
     720              : 
     721              :       // skip somewhere?
     722              :       return tl::unexpected<Parse::Error::Node> (
     723            0 :         Parse::Error::Node::CHILD_ERROR);
     724            0 :     }
     725              : 
     726           31 :   if (!skip_token (EQUAL))
     727              :     {
     728              :       // skip somewhere?
     729            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     730              :     }
     731              : 
     732              :   // parse expression (required) - HACK to prevent struct expr being parsed
     733           31 :   ParseRestrictions no_struct_expr;
     734           31 :   no_struct_expr.can_be_struct_expr = false;
     735           31 :   auto scrutinee_expr = parse_expr ({}, no_struct_expr);
     736           31 :   if (!scrutinee_expr)
     737              :     {
     738            0 :       Error error (lexer.peek_token ()->get_locus (),
     739              :                    "failed to parse scrutinee expression in if let expression");
     740            0 :       add_error (std::move (error));
     741              : 
     742              :       // skip somewhere?
     743              :       return tl::unexpected<Parse::Error::Node> (
     744            0 :         Parse::Error::Node::CHILD_ERROR);
     745            0 :     }
     746              :   /* TODO: check for expression not being a struct expression or lazy boolean
     747              :    * expression here? or actually probably in semantic analysis. */
     748              : 
     749              :   // parse block expression (required)
     750           31 :   auto if_let_body = parse_block_expr ();
     751           31 :   if (!if_let_body)
     752              :     {
     753            0 :       Error error (
     754            0 :         lexer.peek_token ()->get_locus (),
     755              :         "failed to parse if let body block expression in if let expression");
     756            0 :       add_error (std::move (error));
     757              : 
     758              :       // skip somewhere?
     759              :       return tl::unexpected<Parse::Error::Node> (
     760            0 :         Parse::Error::Node::CHILD_ERROR);
     761            0 :     }
     762              : 
     763              :   // branch to parse end or else (and then else, else if, or else if let)
     764           62 :   if (lexer.peek_token ()->get_id () != ELSE)
     765              :     {
     766              :       // single selection - end of if let expression
     767           38 :       return std::unique_ptr<AST::IfLetExpr> (new AST::IfLetExpr (
     768           19 :         std::move (match_arm_pattern), std::move (scrutinee_expr.value ()),
     769           38 :         std::move (if_let_body.value ()), std::move (outer_attrs), locus));
     770              :     }
     771              :   else
     772              :     {
     773              :       // double or multiple selection - branch on end, else if, or else if let
     774              : 
     775              :       // skip "else"
     776           12 :       lexer.skip_token ();
     777              : 
     778              :       // branch on whether next token is '{' or 'if'
     779           12 :       const_TokenPtr t = lexer.peek_token ();
     780           12 :       switch (t->get_id ())
     781              :         {
     782           11 :         case LEFT_CURLY:
     783              :           {
     784              :             // double selection - else
     785              :             // parse else block expr (required)
     786           11 :             auto else_body = parse_block_expr ();
     787           11 :             if (!else_body)
     788              :               {
     789            0 :                 Error error (lexer.peek_token ()->get_locus (),
     790              :                              "failed to parse else body block expression in "
     791              :                              "if let expression");
     792            0 :                 add_error (std::move (error));
     793              : 
     794              :                 // skip somewhere?
     795              :                 return tl::unexpected<Parse::Error::Node> (
     796            0 :                   Parse::Error::Node::CHILD_ERROR);
     797            0 :               }
     798              : 
     799           11 :             return std::unique_ptr<AST::IfLetExprConseqElse> (
     800           22 :               new AST::IfLetExprConseqElse (std::move (match_arm_pattern),
     801           11 :                                             std::move (scrutinee_expr.value ()),
     802           11 :                                             std::move (if_let_body.value ()),
     803           11 :                                             std::move (else_body.value ()),
     804           11 :                                             std::move (outer_attrs), locus));
     805           11 :           }
     806            1 :         case IF:
     807              :           {
     808              :             // multiple selection - else if or else if let
     809              :             // branch on whether next token is 'let' or not
     810            2 :             if (lexer.peek_token (1)->get_id () == LET)
     811              :               {
     812              :                 // parse if let expr (required)
     813            0 :                 auto if_let_expr = parse_if_let_expr ();
     814            0 :                 if (!if_let_expr)
     815              :                   {
     816            0 :                     Error error (lexer.peek_token ()->get_locus (),
     817              :                                  "failed to parse (else) if let expression "
     818              :                                  "after if let expression");
     819            0 :                     add_error (std::move (error));
     820              : 
     821              :                     // skip somewhere?
     822              :                     return tl::unexpected<Parse::Error::Node> (
     823            0 :                       Parse::Error::Node::CHILD_ERROR);
     824            0 :                   }
     825              : 
     826            0 :                 return std::unique_ptr<AST::IfLetExprConseqElse> (
     827            0 :                   new AST::IfLetExprConseqElse (
     828              :                     std::move (match_arm_pattern),
     829            0 :                     std::move (scrutinee_expr.value ()),
     830            0 :                     std::move (if_let_body.value ()),
     831            0 :                     std::move (if_let_expr.value ()), std::move (outer_attrs),
     832            0 :                     locus));
     833            0 :               }
     834              :             else
     835              :               {
     836              :                 // parse if expr (required)
     837            1 :                 auto if_expr = parse_if_expr ();
     838            1 :                 if (!if_expr)
     839              :                   {
     840            0 :                     Error error (lexer.peek_token ()->get_locus (),
     841              :                                  "failed to parse (else) if expression after "
     842              :                                  "if let expression");
     843            0 :                     add_error (std::move (error));
     844              : 
     845              :                     // skip somewhere?
     846              :                     return tl::unexpected<Parse::Error::Node> (
     847            0 :                       Parse::Error::Node::CHILD_ERROR);
     848            0 :                   }
     849              : 
     850            1 :                 return std::unique_ptr<AST::IfLetExprConseqElse> (
     851            2 :                   new AST::IfLetExprConseqElse (
     852              :                     std::move (match_arm_pattern),
     853            1 :                     std::move (scrutinee_expr.value ()),
     854            1 :                     std::move (if_let_body.value ()),
     855            1 :                     std::move (if_expr.value ()), std::move (outer_attrs),
     856            1 :                     locus));
     857            1 :               }
     858              :           }
     859            0 :         default:
     860              :           // error - invalid token
     861            0 :           add_error (
     862            0 :             Error (t->get_locus (),
     863              :                    "unexpected token %qs after else in if let expression",
     864              :                    t->get_token_description ()));
     865              : 
     866              :           // skip somewhere?
     867              :           return tl::unexpected<Parse::Error::Node> (
     868            0 :             Parse::Error::Node::MALFORMED);
     869              :         }
     870           12 :     }
     871           62 : }
     872              : 
     873              : /* TODO: possibly decide on different method of handling label (i.e. not
     874              :  * parameter) */
     875              : 
     876              : /* Parses a "loop" infinite loop expression. Label is not parsed and should be
     877              :  * parsed via parse_labelled_loop_expr, which would call this. */
     878              : template <typename ManagedTokenSource>
     879              : tl::expected<std::unique_ptr<AST::LoopExpr>, Parse::Error::Node>
     880          115 : Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs,
     881              :                                              tl::optional<AST::LoopLabel> label,
     882              :                                              location_t pratt_parsed_loc)
     883              : {
     884          115 :   location_t locus = pratt_parsed_loc;
     885          115 :   if (locus == UNKNOWN_LOCATION)
     886              :     {
     887           38 :       if (label)
     888           38 :         locus = label->get_locus ();
     889              :       else
     890            0 :         locus = lexer.peek_token ()->get_locus ();
     891              : 
     892           38 :       if (!skip_token (LOOP))
     893              :         {
     894            0 :           skip_after_end_block ();
     895              :           return tl::unexpected<Parse::Error::Node> (
     896            0 :             Parse::Error::Node::MALFORMED);
     897              :         }
     898              :     }
     899              :   else
     900              :     {
     901           77 :       if (label)
     902            0 :         locus = label->get_locus ();
     903              :     }
     904              : 
     905              :   // parse loop body, which is required
     906          115 :   auto loop_body = parse_block_expr ();
     907          115 :   if (!loop_body)
     908            1 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     909              : 
     910          114 :   return std::unique_ptr<AST::LoopExpr> (
     911          152 :     new AST::LoopExpr (std::move (loop_body.value ()), locus, std::move (label),
     912          114 :                        std::move (outer_attrs)));
     913          115 : }
     914              : 
     915              : /* Parses a "while" loop expression. Label is not parsed and should be parsed
     916              :  * via parse_labelled_loop_expr, which would call this. */
     917              : template <typename ManagedTokenSource>
     918              : tl::expected<std::unique_ptr<AST::WhileLoopExpr>, Parse::Error::Node>
     919           79 : Parser<ManagedTokenSource>::parse_while_loop_expr (
     920              :   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label,
     921              :   location_t pratt_parsed_loc)
     922              : {
     923           79 :   location_t locus = pratt_parsed_loc;
     924           79 :   if (locus == UNKNOWN_LOCATION)
     925              :     {
     926            2 :       if (label)
     927            2 :         locus = label->get_locus ();
     928              :       else
     929            0 :         locus = lexer.peek_token ()->get_locus ();
     930              : 
     931            2 :       if (!skip_token (WHILE))
     932              :         {
     933            0 :           skip_after_end_block ();
     934              :           return tl::unexpected<Parse::Error::Node> (
     935            0 :             Parse::Error::Node::MALFORMED);
     936              :         }
     937              :     }
     938              :   else
     939              :     {
     940           77 :       if (label)
     941            0 :         locus = label->get_locus ();
     942              :     }
     943              : 
     944              :   // ensure it isn't a while let loop
     945          158 :   if (lexer.peek_token ()->get_id () == LET)
     946              :     {
     947            0 :       Error error (lexer.peek_token ()->get_locus (),
     948              :                    "appears to be while let loop but is being parsed by "
     949              :                    "while loop - this may be a compiler issue");
     950            0 :       add_error (std::move (error));
     951              : 
     952              :       // skip somewhere?
     953            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     954            0 :     }
     955              : 
     956              :   // parse loop predicate (required) with HACK to prevent struct expr parsing
     957           79 :   ParseRestrictions no_struct_expr;
     958           79 :   no_struct_expr.can_be_struct_expr = false;
     959           79 :   auto predicate = parse_expr ({}, no_struct_expr);
     960           79 :   if (!predicate)
     961              :     {
     962            0 :       Error error (lexer.peek_token ()->get_locus (),
     963              :                    "failed to parse predicate expression in while loop");
     964            0 :       add_error (std::move (error));
     965              : 
     966              :       // skip somewhere?
     967              :       return tl::unexpected<Parse::Error::Node> (
     968            0 :         Parse::Error::Node::CHILD_ERROR);
     969            0 :     }
     970              :   /* TODO: check that it isn't struct expression here? actually, probably in
     971              :    * semantic analysis */
     972              : 
     973              :   // parse loop body (required)
     974           79 :   auto body = parse_block_expr ();
     975           79 :   if (!body)
     976              :     {
     977            0 :       Error error (lexer.peek_token ()->get_locus (),
     978              :                    "failed to parse loop body block expression in while loop");
     979            0 :       add_error (std::move (error));
     980              : 
     981              :       // skip somewhere
     982              :       return tl::unexpected<Parse::Error::Node> (
     983            0 :         Parse::Error::Node::CHILD_ERROR);
     984            0 :     }
     985              : 
     986           79 :   return std::unique_ptr<AST::WhileLoopExpr> (
     987          160 :     new AST::WhileLoopExpr (std::move (predicate.value ()),
     988           79 :                             std::move (body.value ()), locus, std::move (label),
     989           79 :                             std::move (outer_attrs)));
     990          158 : }
     991              : 
     992              : /* Parses a "while let" loop expression. Label is not parsed and should be
     993              :  * parsed via parse_labelled_loop_expr, which would call this. */
     994              : template <typename ManagedTokenSource>
     995              : tl::expected<std::unique_ptr<AST::WhileLetLoopExpr>, Parse::Error::Node>
     996            5 : Parser<ManagedTokenSource>::parse_while_let_loop_expr (
     997              :   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label)
     998              : {
     999            5 :   location_t locus = UNKNOWN_LOCATION;
    1000            5 :   if (label)
    1001            1 :     locus = label->get_locus ();
    1002              :   else
    1003            8 :     locus = lexer.peek_token ()->get_locus ();
    1004            5 :   maybe_skip_token (WHILE);
    1005              : 
    1006              :   /* check for possible accidental recognition of a while loop as a while let
    1007              :    * loop */
    1008           10 :   if (lexer.peek_token ()->get_id () != LET)
    1009              :     {
    1010            0 :       Error error (lexer.peek_token ()->get_locus (),
    1011              :                    "appears to be a while loop but is being parsed by "
    1012              :                    "while let loop - this may be a compiler issue");
    1013            0 :       add_error (std::move (error));
    1014              : 
    1015              :       // skip somewhere
    1016            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1017            0 :     }
    1018              :   // as this token is definitely let now, save the computation of comparison
    1019            5 :   lexer.skip_token ();
    1020              : 
    1021              :   // parse predicate patterns
    1022            5 :   std::unique_ptr<AST::Pattern> predicate_pattern
    1023              :     = parse_match_arm_pattern (EQUAL);
    1024              :   // ensure that there is at least 1 pattern
    1025            5 :   if (predicate_pattern == nullptr)
    1026              :     {
    1027            1 :       Error error (lexer.peek_token ()->get_locus (),
    1028              :                    "should be at least 1 pattern");
    1029            1 :       add_error (std::move (error));
    1030            1 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1031            1 :     }
    1032              : 
    1033            4 :   if (!skip_token (EQUAL))
    1034              :     {
    1035              :       // skip somewhere?
    1036            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1037              :     }
    1038              : 
    1039              :   /* parse predicate expression, which is required (and HACK to prevent struct
    1040              :    * expr) */
    1041            4 :   ParseRestrictions no_struct_expr;
    1042            4 :   no_struct_expr.can_be_struct_expr = false;
    1043            4 :   auto predicate_expr = parse_expr ({}, no_struct_expr);
    1044            4 :   if (!predicate_expr)
    1045              :     {
    1046            0 :       Error error (lexer.peek_token ()->get_locus (),
    1047              :                    "failed to parse predicate expression in while let loop");
    1048            0 :       add_error (std::move (error));
    1049              : 
    1050              :       // skip somewhere?
    1051              :       return tl::unexpected<Parse::Error::Node> (
    1052            0 :         Parse::Error::Node::CHILD_ERROR);
    1053            0 :     }
    1054              :   /* TODO: ensure that struct expression is not parsed? Actually, probably in
    1055              :    * semantic analysis. */
    1056              : 
    1057              :   // parse loop body, which is required
    1058            4 :   auto body = parse_block_expr ();
    1059            4 :   if (!body)
    1060              :     {
    1061            0 :       Error error (lexer.peek_token ()->get_locus (),
    1062              :                    "failed to parse block expr (loop body) of while let loop");
    1063            0 :       add_error (std::move (error));
    1064              : 
    1065              :       // skip somewhere?
    1066              :       return tl::unexpected<Parse::Error::Node> (
    1067            0 :         Parse::Error::Node::CHILD_ERROR);
    1068            0 :     }
    1069              : 
    1070            4 :   return std::unique_ptr<AST::WhileLetLoopExpr> (
    1071            9 :     new AST::WhileLetLoopExpr (std::move (predicate_pattern),
    1072            4 :                                std::move (predicate_expr.value ()),
    1073            4 :                                std::move (body.value ()), locus,
    1074            4 :                                std::move (label), std::move (outer_attrs)));
    1075            9 : }
    1076              : 
    1077              : /* Parses a "for" iterative loop. Label is not parsed and should be parsed via
    1078              :  * parse_labelled_loop_expr, which would call this. */
    1079              : template <typename ManagedTokenSource>
    1080              : tl::expected<std::unique_ptr<AST::ForLoopExpr>, Parse::Error::Node>
    1081           20 : Parser<ManagedTokenSource>::parse_for_loop_expr (
    1082              :   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label)
    1083              : {
    1084           20 :   location_t locus = UNKNOWN_LOCATION;
    1085           20 :   if (label)
    1086            0 :     locus = label->get_locus ();
    1087              :   else
    1088           40 :     locus = lexer.peek_token ()->get_locus ();
    1089           20 :   maybe_skip_token (FOR);
    1090              : 
    1091              :   // parse pattern, which is required
    1092           20 :   std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
    1093           20 :   if (!pattern)
    1094              :     {
    1095            0 :       Error error (lexer.peek_token ()->get_locus (),
    1096              :                    "failed to parse iterator pattern in for loop");
    1097            0 :       add_error (std::move (error));
    1098              : 
    1099              :       // skip somewhere?
    1100              :       return tl::unexpected<Parse::Error::Node> (
    1101            0 :         Parse::Error::Node::CHILD_ERROR);
    1102            0 :     }
    1103              : 
    1104           20 :   if (!skip_token (IN))
    1105              :     {
    1106              :       // skip somewhere?
    1107            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1108              :     }
    1109              : 
    1110              :   /* parse iterator expression, which is required - also HACK to prevent
    1111              :    * struct expr */
    1112           20 :   ParseRestrictions no_struct_expr;
    1113           20 :   no_struct_expr.can_be_struct_expr = false;
    1114           20 :   auto expr = parse_expr ({}, no_struct_expr);
    1115           20 :   if (!expr)
    1116              :     {
    1117            0 :       Error error (lexer.peek_token ()->get_locus (),
    1118              :                    "failed to parse iterator expression in for loop");
    1119            0 :       add_error (std::move (error));
    1120              : 
    1121              :       // skip somewhere?
    1122              :       return tl::unexpected<Parse::Error::Node> (
    1123            0 :         Parse::Error::Node::CHILD_ERROR);
    1124            0 :     }
    1125              :   // TODO: check to ensure this isn't struct expr? Or in semantic analysis.
    1126              : 
    1127              :   // parse loop body, which is required
    1128           20 :   auto body = parse_block_expr ();
    1129           20 :   if (!body)
    1130              :     {
    1131            0 :       Error error (lexer.peek_token ()->get_locus (),
    1132              :                    "failed to parse loop body block expression in for loop");
    1133            0 :       add_error (std::move (error));
    1134              : 
    1135              :       // skip somewhere?
    1136              :       return tl::unexpected<Parse::Error::Node> (
    1137            0 :         Parse::Error::Node::CHILD_ERROR);
    1138            0 :     }
    1139           20 :   return std::unique_ptr<AST::ForLoopExpr> (
    1140           40 :     new AST::ForLoopExpr (std::move (pattern), std::move (expr.value ()),
    1141           20 :                           std::move (body.value ()), locus, std::move (label),
    1142           20 :                           std::move (outer_attrs)));
    1143           40 : }
    1144              : 
    1145              : // Parses a loop expression with label (any kind of loop - disambiguates).
    1146              : template <typename ManagedTokenSource>
    1147              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Node>
    1148           43 : Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
    1149              :                                                       AST::AttrVec outer_attrs)
    1150              : {
    1151              :   // parse loop label (required)
    1152           86 :   auto parsed_label = parse_loop_label (tok);
    1153           43 :   if (!parsed_label)
    1154              :     {
    1155              :       /* TODO: decide whether it should not work if there is no label, or parse
    1156              :        * it with no label at the moment, I will make it not work with no label
    1157              :        * because that's the implication. */
    1158              : 
    1159            0 :       if (parsed_label.error ().kind
    1160              :           == Parse::Error::LoopLabel::Kind::NOT_LOOP_LABEL)
    1161              :         {
    1162            0 :           Error error (tok->get_locus (),
    1163              :                        "expected lifetime in labelled loop expr (to parse loop "
    1164              :                        "label) - found %qs",
    1165              :                        tok->get_token_description ());
    1166            0 :           add_error (std::move (error));
    1167              :           return tl::unexpected<Parse::Error::Node> (
    1168            0 :             Parse::Error::Node::CHILD_ERROR);
    1169            0 :         }
    1170              : 
    1171              :       else
    1172              :         {
    1173            0 :           Error error (lexer.peek_token ()->get_locus (),
    1174              :                        "failed to parse loop label in labelled loop expr");
    1175            0 :           add_error (std::move (error));
    1176              : 
    1177              :           // skip?
    1178              :           return tl::unexpected<Parse::Error::Node> (
    1179            0 :             Parse::Error::Node::CHILD_ERROR);
    1180            0 :         }
    1181              :     }
    1182              : 
    1183           43 :   auto label = parsed_label
    1184              :                  ? tl::optional<AST::LoopLabel> (parsed_label.value ())
    1185              :                  : tl::nullopt;
    1186              : 
    1187              :   // branch on next token
    1188           43 :   const_TokenPtr t = lexer.peek_token ();
    1189           43 :   switch (t->get_id ())
    1190              :     {
    1191           38 :     case LOOP:
    1192          114 :       return parse_loop_expr (std::move (outer_attrs), std::move (label));
    1193            0 :     case FOR:
    1194            0 :       return parse_for_loop_expr (std::move (outer_attrs), std::move (label));
    1195            3 :     case WHILE:
    1196              :       // further disambiguate into while vs while let
    1197            6 :       if (lexer.peek_token (1)->get_id () == LET)
    1198            3 :         return parse_while_let_loop_expr (std::move (outer_attrs),
    1199            1 :                                           std::move (label));
    1200              :       else
    1201            6 :         return parse_while_loop_expr (std::move (outer_attrs),
    1202            2 :                                       std::move (label));
    1203            2 :     case LEFT_CURLY:
    1204            6 :       return parse_block_expr (std::move (outer_attrs), std::move (label));
    1205            0 :     default:
    1206              :       // error
    1207            0 :       add_error (Error (t->get_locus (),
    1208              :                         "unexpected token %qs when parsing labelled loop",
    1209              :                         t->get_token_description ()));
    1210              : 
    1211              :       // skip?
    1212              :       return tl::unexpected<Parse::Error::Node> (
    1213            0 :         Parse::Error::Node::CHILD_ERROR);
    1214              :     }
    1215           86 : }
    1216              : 
    1217              : // Parses a match expression.
    1218              : template <typename ManagedTokenSource>
    1219              : tl::expected<std::unique_ptr<AST::MatchExpr>, Parse::Error::Node>
    1220          916 : Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
    1221              :                                               location_t pratt_parsed_loc)
    1222              : {
    1223          916 :   location_t locus = pratt_parsed_loc;
    1224          916 :   if (locus == UNKNOWN_LOCATION)
    1225              :     {
    1226            0 :       locus = lexer.peek_token ()->get_locus ();
    1227            0 :       skip_token (MATCH_KW);
    1228              :     }
    1229              : 
    1230              :   /* parse scrutinee expression, which is required (and HACK to prevent struct
    1231              :    * expr) */
    1232          916 :   ParseRestrictions no_struct_expr;
    1233          916 :   no_struct_expr.can_be_struct_expr = false;
    1234          916 :   auto scrutinee = parse_expr ({}, no_struct_expr);
    1235          916 :   if (!scrutinee)
    1236              :     {
    1237            1 :       Error error (lexer.peek_token ()->get_locus (),
    1238              :                    "failed to parse scrutinee expression in match expression");
    1239            1 :       add_error (std::move (error));
    1240              : 
    1241              :       // skip somewhere?
    1242              :       return tl::unexpected<Parse::Error::Node> (
    1243            1 :         Parse::Error::Node::CHILD_ERROR);
    1244            1 :     }
    1245              :   /* TODO: check for scrutinee expr not being struct expr? or do so in
    1246              :    * semantic analysis */
    1247              : 
    1248          915 :   if (!skip_token (LEFT_CURLY))
    1249              :     {
    1250              :       // skip somewhere?
    1251            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1252              :     }
    1253              : 
    1254              :   // parse inner attributes (if they exist)
    1255          915 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    1256              : 
    1257              :   // parse match arms (if they exist)
    1258              :   // std::vector<std::unique_ptr<AST::MatchCase> > match_arms;
    1259          915 :   std::vector<AST::MatchCase> match_arms;
    1260              : 
    1261              :   // parse match cases
    1262         8243 :   while (lexer.peek_token ()->get_id () != RIGHT_CURLY)
    1263              :     {
    1264              :       // parse match arm itself, which is required
    1265         2153 :       AST::MatchArm arm = parse_match_arm ();
    1266         2153 :       if (arm.is_error ())
    1267              :         {
    1268              :           // TODO is this worth throwing everything away?
    1269            0 :           Error error (lexer.peek_token ()->get_locus (),
    1270              :                        "failed to parse match arm in match arms");
    1271            0 :           add_error (std::move (error));
    1272              : 
    1273              :           return tl::unexpected<Parse::Error::Node> (
    1274            0 :             Parse::Error::Node::CHILD_ERROR);
    1275            0 :         }
    1276              : 
    1277         2153 :       if (!skip_token (MATCH_ARROW))
    1278              :         {
    1279              :           // skip after somewhere?
    1280              :           // TODO is returning here a good idea? or is break better?
    1281              :           return tl::unexpected<Parse::Error::Node> (
    1282            0 :             Parse::Error::Node::MALFORMED);
    1283              :         }
    1284              : 
    1285         2153 :       ParseRestrictions restrictions;
    1286         2153 :       restrictions.expr_can_be_stmt = true;
    1287              : 
    1288         2153 :       auto expr = parse_expr ({}, restrictions);
    1289              : 
    1290         2153 :       if (!expr)
    1291              :         {
    1292              :           /* We don't need to throw an error as it already reported by
    1293              :            * parse_expr
    1294              :            */
    1295              :           return tl::unexpected<Parse::Error::Node> (
    1296            2 :             Parse::Error::Node::CHILD_ERROR);
    1297              :         }
    1298              : 
    1299         2151 :       bool is_expr_without_block = expr.value ()->is_expr_without_block ();
    1300              : 
    1301         2151 :       match_arms.push_back (
    1302         4302 :         AST::MatchCase (std::move (arm), std::move (expr.value ())));
    1303              : 
    1304              :       // handle comma presence
    1305         4302 :       if (lexer.peek_token ()->get_id () != COMMA)
    1306              :         {
    1307          569 :           if (!is_expr_without_block)
    1308              :             {
    1309              :               // allowed even if not final case
    1310              :               continue;
    1311              :             }
    1312           21 :           else if (is_expr_without_block
    1313           42 :                    && lexer.peek_token ()->get_id () != RIGHT_CURLY)
    1314              :             {
    1315              :               // not allowed if not final case
    1316            1 :               Error error (lexer.peek_token ()->get_locus (),
    1317              :                            "exprwithoutblock requires comma after match case "
    1318              :                            "expression in match arm (if not final case)");
    1319            1 :               add_error (std::move (error));
    1320              : 
    1321              :               return tl::unexpected<Parse::Error::Node> (
    1322            1 :                 Parse::Error::Node::MALFORMED);
    1323            1 :             }
    1324              :           else
    1325              :             {
    1326              :               // otherwise, must be final case, so fine
    1327              :               break;
    1328              :             }
    1329              :         }
    1330         1582 :       lexer.skip_token ();
    1331              :     }
    1332              : 
    1333          912 :   if (!skip_token (RIGHT_CURLY))
    1334              :     {
    1335              :       // skip somewhere?
    1336            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1337              :     }
    1338              : 
    1339          912 :   match_arms.shrink_to_fit ();
    1340              : 
    1341          912 :   return std::unique_ptr<AST::MatchExpr> (
    1342          912 :     new AST::MatchExpr (std::move (scrutinee.value ()), std::move (match_arms),
    1343              :                         std::move (inner_attrs), std::move (outer_attrs),
    1344          912 :                         locus));
    1345          915 : }
    1346              : 
    1347              : // Parses an async block expression.
    1348              : template <typename ManagedTokenSource>
    1349              : tl::expected<std::unique_ptr<AST::AsyncBlockExpr>, Parse::Error::Node>
    1350            0 : Parser<ManagedTokenSource>::parse_async_block_expr (AST::AttrVec outer_attrs)
    1351              : {
    1352            0 :   location_t locus = lexer.peek_token ()->get_locus ();
    1353            0 :   skip_token (ASYNC);
    1354              : 
    1355              :   // detect optional move token
    1356            0 :   bool has_move = false;
    1357            0 :   if (lexer.peek_token ()->get_id () == MOVE)
    1358              :     {
    1359            0 :       lexer.skip_token ();
    1360            0 :       has_move = true;
    1361              :     }
    1362              : 
    1363              :   // parse block expression (required)
    1364            0 :   auto block_expr = parse_block_expr ();
    1365            0 :   if (!block_expr)
    1366              :     {
    1367            0 :       Error error (
    1368            0 :         lexer.peek_token ()->get_locus (),
    1369              :         "failed to parse block expression of async block expression");
    1370            0 :       add_error (std::move (error));
    1371              : 
    1372              :       // skip somewhere?
    1373              :       return tl::unexpected<Parse::Error::Node> (
    1374            0 :         Parse::Error::Node::CHILD_ERROR);
    1375            0 :     }
    1376              : 
    1377            0 :   return std::unique_ptr<AST::AsyncBlockExpr> (
    1378            0 :     new AST::AsyncBlockExpr (std::move (block_expr.value ()), has_move,
    1379            0 :                              std::move (outer_attrs), locus));
    1380            0 : }
    1381              : 
    1382              : // Parses an unsafe block expression.
    1383              : template <typename ManagedTokenSource>
    1384              : tl::expected<std::unique_ptr<AST::UnsafeBlockExpr>, Parse::Error::Node>
    1385         3622 : Parser<ManagedTokenSource>::parse_unsafe_block_expr (
    1386              :   AST::AttrVec outer_attrs, location_t pratt_parsed_loc)
    1387              : {
    1388         3622 :   location_t locus = pratt_parsed_loc;
    1389         3622 :   if (locus == UNKNOWN_LOCATION)
    1390              :     {
    1391            0 :       locus = lexer.peek_token ()->get_locus ();
    1392            0 :       skip_token (UNSAFE);
    1393              :     }
    1394              : 
    1395              :   // parse block expression (required)
    1396         3622 :   auto block_expr = parse_block_expr ();
    1397         3622 :   if (!block_expr)
    1398              :     {
    1399            0 :       Error error (
    1400            0 :         lexer.peek_token ()->get_locus (),
    1401              :         "failed to parse block expression of unsafe block expression");
    1402            0 :       add_error (std::move (error));
    1403              : 
    1404              :       // skip somewhere?
    1405              :       return tl::unexpected<Parse::Error::Node> (
    1406            0 :         Parse::Error::Node::CHILD_ERROR);
    1407            0 :     }
    1408         3622 :   return std::unique_ptr<AST::UnsafeBlockExpr> (
    1409         3622 :     new AST::UnsafeBlockExpr (std::move (block_expr.value ()),
    1410         3622 :                               std::move (outer_attrs), locus));
    1411         3622 : }
    1412              : 
    1413              : // Parses an array definition expression.
    1414              : template <typename ManagedTokenSource>
    1415              : tl::expected<std::unique_ptr<AST::ArrayExpr>, Parse::Error::Node>
    1416          407 : Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
    1417              :                                               location_t pratt_parsed_loc)
    1418              : {
    1419          407 :   location_t locus = pratt_parsed_loc;
    1420          407 :   if (locus == UNKNOWN_LOCATION)
    1421              :     {
    1422            0 :       locus = lexer.peek_token ()->get_locus ();
    1423            0 :       skip_token (LEFT_SQUARE);
    1424              :     }
    1425              : 
    1426              :   // parse optional inner attributes
    1427          407 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    1428              : 
    1429              :   // parse the "array elements" section, which is optional
    1430          814 :   if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
    1431              :     {
    1432              :       // no array elements
    1433            1 :       lexer.skip_token ();
    1434              : 
    1435            1 :       std::vector<std::unique_ptr<AST::Expr>> exprs;
    1436            1 :       auto array_elems
    1437              :         = std::make_unique<AST::ArrayElemsValues> (std::move (exprs), locus);
    1438            1 :       return std::make_unique<AST::ArrayExpr> (std::move (array_elems),
    1439              :                                                std::move (inner_attrs),
    1440            1 :                                                std::move (outer_attrs), locus);
    1441            1 :     }
    1442              :   else
    1443              :     {
    1444              :       // should have array elements
    1445              :       // parse initial expression, which is required for either
    1446          406 :       auto initial_expr = parse_expr ();
    1447          406 :       if (!initial_expr)
    1448              :         {
    1449            0 :           Error error (lexer.peek_token ()->get_locus (),
    1450              :                        "could not parse expression in array expression "
    1451              :                        "(even though arrayelems seems to be present)");
    1452            0 :           add_error (std::move (error));
    1453              : 
    1454              :           // skip somewhere?
    1455              :           return tl::unexpected<Parse::Error::Node> (
    1456            0 :             Parse::Error::Node::CHILD_ERROR);
    1457            0 :         }
    1458              : 
    1459          812 :       if (lexer.peek_token ()->get_id () == SEMICOLON)
    1460              :         {
    1461              :           // copy array elems
    1462          123 :           lexer.skip_token ();
    1463              : 
    1464              :           // parse copy amount expression (required)
    1465          123 :           auto copy_amount = parse_expr ();
    1466          123 :           if (!copy_amount)
    1467              :             {
    1468            0 :               Error error (lexer.peek_token ()->get_locus (),
    1469              :                            "could not parse copy amount expression in array "
    1470              :                            "expression (arrayelems)");
    1471            0 :               add_error (std::move (error));
    1472              : 
    1473              :               // skip somewhere?
    1474              :               return tl::unexpected<Parse::Error::Node> (
    1475            0 :                 Parse::Error::Node::CHILD_ERROR);
    1476            0 :             }
    1477              : 
    1478          123 :           skip_token (RIGHT_SQUARE);
    1479              : 
    1480          123 :           std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
    1481          246 :             new AST::ArrayElemsCopied (std::move (initial_expr.value ()),
    1482          123 :                                        std::move (copy_amount.value ()),
    1483              :                                        locus));
    1484          123 :           return std::unique_ptr<AST::ArrayExpr> (
    1485          123 :             new AST::ArrayExpr (std::move (copied_array_elems),
    1486              :                                 std::move (inner_attrs),
    1487          123 :                                 std::move (outer_attrs), locus));
    1488          246 :         }
    1489          566 :       else if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
    1490              :         {
    1491              :           // single-element array expression
    1492           34 :           std::vector<std::unique_ptr<AST::Expr>> exprs;
    1493           34 :           exprs.reserve (1);
    1494           34 :           exprs.push_back (std::move (initial_expr.value ()));
    1495           34 :           exprs.shrink_to_fit ();
    1496              : 
    1497           34 :           skip_token (RIGHT_SQUARE);
    1498              : 
    1499           34 :           std::unique_ptr<AST::ArrayElemsValues> array_elems (
    1500           34 :             new AST::ArrayElemsValues (std::move (exprs), locus));
    1501           34 :           return std::unique_ptr<AST::ArrayExpr> (
    1502           34 :             new AST::ArrayExpr (std::move (array_elems),
    1503              :                                 std::move (inner_attrs),
    1504           34 :                                 std::move (outer_attrs), locus));
    1505           34 :         }
    1506          498 :       else if (lexer.peek_token ()->get_id () == COMMA)
    1507              :         {
    1508              :           // multi-element array expression (or trailing comma)
    1509          249 :           std::vector<std::unique_ptr<AST::Expr>> exprs;
    1510          249 :           exprs.push_back (std::move (initial_expr.value ()));
    1511              : 
    1512          249 :           const_TokenPtr t = lexer.peek_token ();
    1513         1141 :           while (t->get_id () == COMMA)
    1514              :             {
    1515          899 :               lexer.skip_token ();
    1516              : 
    1517              :               // quick break if right square bracket
    1518         1798 :               if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
    1519              :                 break;
    1520              : 
    1521              :               // parse expression (required)
    1522          892 :               auto expr = parse_expr ();
    1523          892 :               if (!expr)
    1524              :                 {
    1525            0 :                   Error error (lexer.peek_token ()->get_locus (),
    1526              :                                "failed to parse element in array expression");
    1527            0 :                   add_error (std::move (error));
    1528              : 
    1529              :                   // skip somewhere?
    1530              :                   return tl::unexpected<Parse::Error::Node> (
    1531            0 :                     Parse::Error::Node::CHILD_ERROR);
    1532            0 :                 }
    1533          892 :               exprs.push_back (std::move (expr.value ()));
    1534              : 
    1535          892 :               t = lexer.peek_token ();
    1536              :             }
    1537              : 
    1538          249 :           skip_token (RIGHT_SQUARE);
    1539              : 
    1540          249 :           exprs.shrink_to_fit ();
    1541              : 
    1542          249 :           std::unique_ptr<AST::ArrayElemsValues> array_elems (
    1543          249 :             new AST::ArrayElemsValues (std::move (exprs), locus));
    1544          249 :           return std::unique_ptr<AST::ArrayExpr> (
    1545          249 :             new AST::ArrayExpr (std::move (array_elems),
    1546              :                                 std::move (inner_attrs),
    1547          249 :                                 std::move (outer_attrs), locus));
    1548          498 :         }
    1549              :       else
    1550              :         {
    1551              :           // error
    1552            0 :           Error error (lexer.peek_token ()->get_locus (),
    1553              :                        "unexpected token %qs in array expression (arrayelems)",
    1554            0 :                        lexer.peek_token ()->get_token_description ());
    1555            0 :           add_error (std::move (error));
    1556              : 
    1557              :           // skip somewhere?
    1558              :           return tl::unexpected<Parse::Error::Node> (
    1559            0 :             Parse::Error::Node::MALFORMED);
    1560            0 :         }
    1561          406 :     }
    1562          407 : }
    1563              : 
    1564              : // Parses a grouped or tuple expression (disambiguates).
    1565              : template <typename ManagedTokenSource>
    1566              : tl::expected<std::unique_ptr<AST::ExprWithoutBlock>, Parse::Error::Node>
    1567          859 : Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
    1568              :   AST::AttrVec outer_attrs, location_t pratt_parsed_loc)
    1569              : {
    1570              :   // adjustment to allow Pratt parsing to reuse function without copy-paste
    1571          859 :   location_t locus = pratt_parsed_loc;
    1572          859 :   if (locus == UNKNOWN_LOCATION)
    1573              :     {
    1574            0 :       locus = lexer.peek_token ()->get_locus ();
    1575            0 :       skip_token (LEFT_PAREN);
    1576              :     }
    1577              : 
    1578              :   // parse optional inner attributes
    1579          859 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    1580              : 
    1581         1718 :   if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    1582              :     {
    1583              :       // must be empty tuple
    1584          155 :       lexer.skip_token ();
    1585              : 
    1586              :       // create tuple with empty tuple elems
    1587          155 :       return std::unique_ptr<AST::TupleExpr> (
    1588          155 :         new AST::TupleExpr (std::vector<std::unique_ptr<AST::Expr>> (),
    1589              :                             std::move (inner_attrs), std::move (outer_attrs),
    1590          155 :                             locus));
    1591              :     }
    1592              : 
    1593              :   // parse first expression (required)
    1594          704 :   auto first_expr = parse_expr ();
    1595          704 :   if (!first_expr)
    1596              :     {
    1597            0 :       Error error (lexer.peek_token ()->get_locus (),
    1598              :                    "failed to parse expression in grouped or tuple expression");
    1599            0 :       add_error (std::move (error));
    1600              : 
    1601              :       // skip after somewhere?
    1602              :       return tl::unexpected<Parse::Error::Node> (
    1603            0 :         Parse::Error::Node::CHILD_ERROR);
    1604            0 :     }
    1605              : 
    1606              :   // detect whether grouped expression with right parentheses as next token
    1607         1408 :   if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    1608              :     {
    1609              :       // must be grouped expr
    1610          316 :       lexer.skip_token ();
    1611              : 
    1612              :       // create grouped expr
    1613          316 :       return std::unique_ptr<AST::GroupedExpr> (
    1614          316 :         new AST::GroupedExpr (std::move (first_expr.value ()),
    1615              :                               std::move (inner_attrs), std::move (outer_attrs),
    1616          316 :                               locus));
    1617              :     }
    1618          776 :   else if (lexer.peek_token ()->get_id () == COMMA)
    1619              :     {
    1620              :       // tuple expr
    1621          387 :       std::vector<std::unique_ptr<AST::Expr>> exprs;
    1622          387 :       exprs.push_back (std::move (first_expr.value ()));
    1623              : 
    1624              :       // parse potential other tuple exprs
    1625          387 :       const_TokenPtr t = lexer.peek_token ();
    1626          918 :       while (t->get_id () == COMMA)
    1627              :         {
    1628          560 :           lexer.skip_token ();
    1629              : 
    1630              :           // break out if right paren
    1631         1120 :           if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    1632              :             break;
    1633              : 
    1634              :           // parse expr, which is now required
    1635          531 :           auto expr = parse_expr ();
    1636          531 :           if (!expr)
    1637              :             {
    1638            0 :               Error error (lexer.peek_token ()->get_locus (),
    1639              :                            "failed to parse expr in tuple expr");
    1640            0 :               add_error (std::move (error));
    1641              : 
    1642              :               // skip somewhere?
    1643              :               return tl::unexpected<Parse::Error::Node> (
    1644            0 :                 Parse::Error::Node::CHILD_ERROR);
    1645            0 :             }
    1646          531 :           exprs.push_back (std::move (expr.value ()));
    1647              : 
    1648          531 :           t = lexer.peek_token ();
    1649              :         }
    1650              : 
    1651              :       // skip right paren
    1652          387 :       skip_token (RIGHT_PAREN);
    1653              : 
    1654          387 :       return std::unique_ptr<AST::TupleExpr> (
    1655          387 :         new AST::TupleExpr (std::move (exprs), std::move (inner_attrs),
    1656          387 :                             std::move (outer_attrs), locus));
    1657          387 :     }
    1658              :   else
    1659              :     {
    1660              :       // error
    1661            1 :       const_TokenPtr t = lexer.peek_token ();
    1662            1 :       Error error (t->get_locus (),
    1663              :                    "unexpected token %qs in grouped or tuple expression "
    1664              :                    "(parenthesised expression) - expected %<)%> for grouped "
    1665              :                    "expr and %<,%> for tuple expr",
    1666              :                    t->get_token_description ());
    1667            1 :       add_error (std::move (error));
    1668              : 
    1669              :       // skip somewhere?
    1670            1 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1671            2 :     }
    1672          859 : }
    1673              : 
    1674              : // Parses a struct expression field.
    1675              : template <typename ManagedTokenSource>
    1676              : tl::expected<std::unique_ptr<AST::StructExprField>,
    1677              :              Parse::Error::StructExprField>
    1678         2249 : Parser<ManagedTokenSource>::parse_struct_expr_field ()
    1679              : {
    1680         2249 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    1681         2249 :   const_TokenPtr t = lexer.peek_token ();
    1682         2249 :   switch (t->get_id ())
    1683              :     {
    1684         2205 :     case IDENTIFIER:
    1685         4410 :       if (lexer.peek_token (1)->get_id () == COLON)
    1686              :         {
    1687              :           // struct expr field with identifier and expr
    1688         1989 :           Identifier ident = {t};
    1689         1989 :           lexer.skip_token (1);
    1690              : 
    1691              :           // parse expression (required)
    1692         1989 :           auto expr = parse_expr ();
    1693         1989 :           if (!expr)
    1694              :             {
    1695            0 :               Error error (t->get_locus (),
    1696              :                            "failed to parse struct expression field with "
    1697              :                            "identifier and expression");
    1698            0 :               add_error (std::move (error));
    1699              : 
    1700              :               return tl::unexpected<Parse::Error::StructExprField> (
    1701            0 :                 Parse::Error::StructExprField::CHILD_ERROR);
    1702            0 :             }
    1703              : 
    1704         1989 :           return std::unique_ptr<AST::StructExprFieldIdentifierValue> (
    1705         3978 :             new AST::StructExprFieldIdentifierValue (std::move (ident),
    1706         1989 :                                                      std::move (expr.value ()),
    1707              :                                                      std::move (outer_attrs),
    1708         1989 :                                                      t->get_locus ()));
    1709         1989 :         }
    1710              :       else
    1711              :         {
    1712              :           // struct expr field with identifier only
    1713          216 :           Identifier ident{t};
    1714          216 :           lexer.skip_token ();
    1715              : 
    1716          216 :           return std::unique_ptr<AST::StructExprFieldIdentifier> (
    1717          432 :             new AST::StructExprFieldIdentifier (std::move (ident),
    1718              :                                                 std::move (outer_attrs),
    1719          216 :                                                 t->get_locus ()));
    1720          216 :         }
    1721           44 :     case INT_LITERAL:
    1722              :       {
    1723              :         // parse tuple index field
    1724           44 :         int index = atoi (t->get_str ().c_str ());
    1725           44 :         lexer.skip_token ();
    1726              : 
    1727           44 :         if (!skip_token (COLON))
    1728              :           {
    1729              :             // skip somewhere?
    1730              :             return tl::unexpected<Parse::Error::StructExprField> (
    1731            0 :               Parse::Error::StructExprField::MALFORMED);
    1732              :           }
    1733              : 
    1734              :         // parse field expression (required)
    1735           44 :         auto expr = parse_expr ();
    1736           44 :         if (!expr)
    1737              :           {
    1738            0 :             Error error (t->get_locus (),
    1739              :                          "failed to parse expr in struct (or enum) expr "
    1740              :                          "field with tuple index");
    1741            0 :             add_error (std::move (error));
    1742              : 
    1743              :             return tl::unexpected<Parse::Error::StructExprField> (
    1744            0 :               Parse::Error::StructExprField::CHILD_ERROR);
    1745            0 :           }
    1746              : 
    1747           44 :         return std::unique_ptr<AST::StructExprFieldIndexValue> (
    1748           44 :           new AST::StructExprFieldIndexValue (index, std::move (expr.value ()),
    1749              :                                               std::move (outer_attrs),
    1750           44 :                                               t->get_locus ()));
    1751           44 :       }
    1752            0 :     case DOT_DOT:
    1753              :       /* this is a struct base and can't be parsed here, so just return
    1754              :        * nothing without erroring */
    1755              : 
    1756              :       return tl::unexpected<Parse::Error::StructExprField> (
    1757            0 :         Parse::Error::StructExprField::STRUCT_BASE);
    1758            0 :     default:
    1759            0 :       add_error (
    1760            0 :         Error (t->get_locus (),
    1761              :                "unrecognised token %qs as first token of struct expr field - "
    1762              :                "expected identifier or integer literal",
    1763              :                t->get_token_description ()));
    1764              : 
    1765              :       return tl::unexpected<Parse::Error::StructExprField> (
    1766            0 :         Parse::Error::StructExprField::MALFORMED);
    1767              :     }
    1768         2249 : }
    1769              : 
    1770              : /* Pratt parser impl of parse_expr. FIXME: this is only provisional and
    1771              :  * probably will be changed. */
    1772              : template <typename ManagedTokenSource>
    1773              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1774        72984 : Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
    1775              :                                         AST::AttrVec outer_attrs,
    1776              :                                         ParseRestrictions restrictions)
    1777              : {
    1778        72984 :   const_TokenPtr current_token = lexer.peek_token ();
    1779              :   // Special hack because we are allowed to return nullptr, in that case we
    1780              :   // don't want to skip the token, since we don't actually parse it. But if
    1781              :   // null isn't allowed it indicates an error, and we want to skip past that.
    1782              :   // So return early if it is one of the tokens that ends an expression
    1783              :   // (or at least cannot start a new expression).
    1784        72984 :   if (restrictions.expr_can_be_null)
    1785              :     {
    1786         1377 :       TokenId id = current_token->get_id ();
    1787         1377 :       if (id == SEMICOLON || id == RIGHT_PAREN || id == RIGHT_CURLY
    1788              :           || id == RIGHT_SQUARE || id == COMMA || id == LEFT_CURLY)
    1789              :         return tl::unexpected<Parse::Error::Expr> (
    1790          100 :           Parse::Error::Expr::NULL_EXPR);
    1791              :     }
    1792              : 
    1793        72884 :   ParseRestrictions null_denotation_restrictions = restrictions;
    1794        72884 :   null_denotation_restrictions.expr_can_be_stmt = false;
    1795              : 
    1796              :   // parse null denotation (unary part of expression)
    1797        72884 :   tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr
    1798        72884 :     = null_denotation ({}, null_denotation_restrictions);
    1799        72884 :   if (!expr)
    1800           66 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    1801        72818 :   if (expr.value () == nullptr)
    1802            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    1803              : 
    1804       145636 :   return left_denotations (std::move (expr), right_binding_power,
    1805        72818 :                            std::move (outer_attrs), restrictions);
    1806        72884 : }
    1807              : 
    1808              : // Parse expression with lowest left binding power.
    1809              : template <typename ManagedTokenSource>
    1810              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1811        55385 : Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
    1812              :                                         ParseRestrictions restrictions)
    1813              : {
    1814        55385 :   return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions);
    1815              : }
    1816              : 
    1817              : template <typename ManagedTokenSource>
    1818              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1819        88001 : Parser<ManagedTokenSource>::left_denotations (
    1820              :   tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr,
    1821              :   int right_binding_power, AST::AttrVec outer_attrs,
    1822              :   ParseRestrictions restrictions)
    1823              : {
    1824        88001 :   if (!expr)
    1825              :     {
    1826              :       // DEBUG
    1827            0 :       rust_debug ("null denotation is null; returning null for parse_expr");
    1828              :       return tl::unexpected<Parse::Error::Expr> (
    1829            0 :         Parse::Error::Expr::NULL_DENOTATION);
    1830              :     }
    1831              : 
    1832        88001 :   const_TokenPtr current_token = lexer.peek_token ();
    1833              : 
    1834        29120 :   if (restrictions.expr_can_be_stmt && !expr.value ()->is_expr_without_block ()
    1835         7677 :       && current_token->get_id () != DOT
    1836        95673 :       && current_token->get_id () != QUESTION_MARK)
    1837              :     {
    1838         7672 :       rust_debug ("statement expression with block");
    1839         7672 :       expr.value ()->set_outer_attrs (std::move (outer_attrs));
    1840        88001 :       return expr;
    1841              :     }
    1842              : 
    1843       107097 :   restrictions.expr_can_be_stmt = false;
    1844              : 
    1845              :   // stop parsing if find lower priority token - parse higher priority first
    1846       321291 :   while (right_binding_power < left_binding_power (current_token))
    1847              :     {
    1848        26770 :       lexer.skip_token ();
    1849              : 
    1850              :       // FIXME attributes should generally be applied to the null denotation.
    1851       107078 :       expr = left_denotation (current_token, std::move (expr.value ()),
    1852              :                               std::move (outer_attrs), restrictions);
    1853              : 
    1854        26770 :       if (!expr)
    1855              :         {
    1856              :           // DEBUG
    1857            2 :           rust_debug ("left denotation is null; returning null for parse_expr");
    1858              : 
    1859              :           return tl::unexpected<Parse::Error::Expr> (
    1860            2 :             Parse::Error::Expr::LEFT_DENOTATION);
    1861              :         }
    1862              : 
    1863        26768 :       current_token = lexer.peek_token ();
    1864              :     }
    1865              : 
    1866        88001 :   return expr;
    1867        88001 : }
    1868              : 
    1869              : /* Determines action to take when finding token at beginning of expression. */
    1870              : template <typename ManagedTokenSource>
    1871              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1872        72884 : Parser<ManagedTokenSource>::null_denotation (AST::AttrVec outer_attrs,
    1873              :                                              ParseRestrictions restrictions)
    1874              : {
    1875              :   /* note: tok is previous character in input stream, not current one, as
    1876              :    * parse_expr skips it before passing it in */
    1877              : 
    1878              :   /* as a Pratt parser (which works by decomposing expressions into a null
    1879              :    * denotation and then a left denotation), null denotations handle primaries
    1880              :    * and unary operands (but only prefix unary operands) */
    1881              : 
    1882        72884 :   auto tok = lexer.peek_token ();
    1883              : 
    1884        72884 :   switch (tok->get_id ())
    1885              :     {
    1886        34149 :     case IDENTIFIER:
    1887              :     case SELF:
    1888              :     case SELF_ALIAS:
    1889              :     case DOLLAR_SIGN:
    1890              :     case CRATE:
    1891              :     case SUPER:
    1892              :     case SCOPE_RESOLUTION:
    1893              :       {
    1894              :         // DEBUG
    1895        34149 :         rust_debug ("beginning null denotation identifier handling");
    1896              : 
    1897              :         /* best option: parse as path, then extract identifier, macro,
    1898              :          * struct/enum, or just path info from it */
    1899        34149 :         AST::PathInExpression path = parse_path_in_expression ();
    1900              : 
    1901        68298 :         return null_denotation_path (std::move (path), std::move (outer_attrs),
    1902        34149 :                                      restrictions);
    1903        34149 :       }
    1904            2 :     case HASH:
    1905              :       {
    1906              :         // Parse outer attributes and then the expression that follows
    1907            2 :         AST::AttrVec attrs = parse_outer_attributes ();
    1908              : 
    1909              :         // Merge with any existing outer attributes
    1910            2 :         if (!outer_attrs.empty ())
    1911            0 :           attrs.insert (attrs.begin (), outer_attrs.begin (),
    1912              :                         outer_attrs.end ());
    1913              : 
    1914              :         // Try to parse the expression that should follow the attributes
    1915            2 :         auto expr = parse_expr (std::move (attrs), restrictions);
    1916            2 :         if (!expr)
    1917              :           {
    1918              :             /* If parsing failed and we're at a semicolon, provide a better
    1919              :              * error
    1920              :              */
    1921            2 :             const_TokenPtr next_tok = lexer.peek_token ();
    1922            2 :             if (next_tok->get_id () == SEMICOLON)
    1923            0 :               add_error (Error (next_tok->get_locus (),
    1924              :                                 "expected expression, found %<;%>"));
    1925              :             return tl::unexpected<Parse::Error::Expr> (
    1926            2 :               Parse::Error::Expr::CHILD_ERROR);
    1927            2 :           }
    1928            0 :         return expr;
    1929            2 :       }
    1930        38733 :     default:
    1931        38733 :       if (tok->get_id () == LEFT_SHIFT)
    1932              :         {
    1933            2 :           lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE);
    1934            2 :           tok = lexer.peek_token ();
    1935              :         }
    1936              : 
    1937        38733 :       lexer.skip_token ();
    1938        77466 :       return null_denotation_not_path (std::move (tok), std::move (outer_attrs),
    1939        38733 :                                        restrictions);
    1940              :     }
    1941        72884 : }
    1942              : 
    1943              : // Handling of expresions that start with a path for `null_denotation`.
    1944              : template <typename ManagedTokenSource>
    1945              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1946        49146 : Parser<ManagedTokenSource>::null_denotation_path (
    1947              :   AST::PathInExpression path, AST::AttrVec outer_attrs,
    1948              :   ParseRestrictions restrictions)
    1949              : {
    1950        49146 :   rust_debug ("parsing null denotation after path");
    1951              : 
    1952              :   // HACK: always make "self" by itself a path (regardless of next
    1953              :   // tokens)
    1954        49146 :   if (path.is_single_segment () && path.get_segments ()[0].is_lower_self_seg ())
    1955              :     {
    1956              :       // HACK: add outer attrs to path
    1957         6889 :       path.set_outer_attrs (std::move (outer_attrs));
    1958        13778 :       return std::make_unique<AST::PathInExpression> (std::move (path));
    1959              :     }
    1960              : 
    1961              :   // branch on next token
    1962        42257 :   const_TokenPtr t = lexer.peek_token ();
    1963        42257 :   switch (t->get_id ())
    1964              :     {
    1965         1813 :     case EXCLAM:
    1966              :       {
    1967              :         // macro
    1968         1813 :         auto macro = parse_macro_invocation_partial (std::move (path),
    1969              :                                                      std::move (outer_attrs));
    1970         1813 :         if (macro == nullptr)
    1971              :           return tl::unexpected<Parse::Error::Expr> (
    1972            3 :             Parse::Error::Expr::CHILD_ERROR);
    1973         1810 :         return std::unique_ptr<AST::Expr> (std::move (macro));
    1974         1813 :       }
    1975         3002 :     case LEFT_CURLY:
    1976              :       {
    1977         3002 :         bool not_a_block = lexer.peek_token (1)->get_id () == IDENTIFIER
    1978         8298 :                            && (lexer.peek_token (2)->get_id () == COMMA
    1979         7556 :                                || (lexer.peek_token (2)->get_id () == COLON
    1980         4810 :                                    && (lexer.peek_token (4)->get_id () == COMMA
    1981          640 :                                        || !Parse::Utils::can_tok_start_type (
    1982         1430 :                                          lexer.peek_token (3)->get_id ()))));
    1983              : 
    1984              :         /* definitely not a block:
    1985              :          *  path '{' ident ','
    1986              :          *  path '{' ident ':' [anything] ','
    1987              :          *  path '{' ident ':' [not a type]
    1988              :          * otherwise, assume block expr and thus path */
    1989              :         // DEBUG
    1990        12008 :         rust_debug ("values of lookahead: '%s' '%s' '%s' '%s' ",
    1991              :                     lexer.peek_token (1)->get_token_description (),
    1992              :                     lexer.peek_token (2)->get_token_description (),
    1993              :                     lexer.peek_token (3)->get_token_description (),
    1994              :                     lexer.peek_token (4)->get_token_description ());
    1995              : 
    1996         6615 :         rust_debug ("can be struct expr: '%s', not a block: '%s'",
    1997              :                     restrictions.can_be_struct_expr ? "true" : "false",
    1998              :                     not_a_block ? "true" : "false");
    1999              : 
    2000              :         // struct/enum expr struct
    2001         3002 :         if (!restrictions.can_be_struct_expr && !not_a_block)
    2002              :           {
    2003              :             // HACK: add outer attrs to path
    2004         1623 :             path.set_outer_attrs (std::move (outer_attrs));
    2005         1623 :             return std::unique_ptr<AST::PathInExpression> (
    2006         1623 :               new AST::PathInExpression (std::move (path)));
    2007              :           }
    2008         1379 :         auto struct_expr
    2009         2758 :           = parse_struct_expr_struct_partial (std::move (path),
    2010              :                                               std::move (outer_attrs));
    2011         1379 :         if (struct_expr == nullptr)
    2012              :           {
    2013              :             return tl::unexpected<Parse::Error::Expr> (
    2014            0 :               Parse::Error::Expr::CHILD_ERROR);
    2015              :           }
    2016         1379 :         return struct_expr;
    2017         1379 :       }
    2018        12161 :     case LEFT_PAREN:
    2019              :       {
    2020              :         // struct/enum expr tuple
    2021        12161 :         if (!restrictions.can_be_struct_expr)
    2022              :           {
    2023              :             // assume path is returned
    2024              :             // HACK: add outer attributes to path
    2025          874 :             path.set_outer_attrs (std::move (outer_attrs));
    2026         1748 :             return std::make_unique<AST::PathInExpression> (std::move (path));
    2027              :           }
    2028        11287 :         auto tuple_expr
    2029        22574 :           = parse_struct_expr_tuple_partial (std::move (path),
    2030              :                                              std::move (outer_attrs));
    2031        11287 :         if (tuple_expr == nullptr)
    2032              :           {
    2033              :             return tl::unexpected<Parse::Error::Expr> (
    2034            0 :               Parse::Error::Expr::CHILD_ERROR);
    2035              :           }
    2036        11287 :         return tuple_expr;
    2037        11287 :       }
    2038        25281 :     default:
    2039              :       // assume path is returned if not single segment
    2040        25281 :       if (path.is_single_segment ())
    2041              :         {
    2042              :           // FIXME: This should probably be returned as a path.
    2043              :           /* HACK: may have to become permanent, but this is my current
    2044              :            * identifier expression */
    2045        72309 :           return std::unique_ptr<AST::IdentifierExpr> (new AST::IdentifierExpr (
    2046        96412 :             path.get_segments ()[0].get_ident_segment ().as_string (), {},
    2047        24103 :             path.get_locus ()));
    2048              :         }
    2049              :       // HACK: add outer attrs to path
    2050         1178 :       path.set_outer_attrs (std::move (outer_attrs));
    2051         1178 :       return std::unique_ptr<AST::PathInExpression> (
    2052         1178 :         new AST::PathInExpression (std::move (path)));
    2053              :     }
    2054              :   rust_unreachable ();
    2055        42257 : }
    2056              : 
    2057              : // Handling of expresions that do not start with a path for `null_denotation`.
    2058              : template <typename ManagedTokenSource>
    2059              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    2060        38733 : Parser<ManagedTokenSource>::null_denotation_not_path (
    2061              :   const_TokenPtr tok, AST::AttrVec outer_attrs, ParseRestrictions restrictions)
    2062              : {
    2063        38733 :   switch (tok->get_id ())
    2064              :     {
    2065              :     // FIXME: Handle in null_denotation_path?
    2066           99 :     case LEFT_SHIFT:
    2067              :     case LEFT_ANGLE:
    2068              :       {
    2069              :         // qualified path
    2070              :         // HACK: add outer attrs to path
    2071           99 :         AST::QualifiedPathInExpression path
    2072              :           = parse_qualified_path_in_expression (tok->get_locus ());
    2073           99 :         path.set_outer_attrs (std::move (outer_attrs));
    2074           99 :         return std::make_unique<AST::QualifiedPathInExpression> (
    2075           99 :           std::move (path));
    2076           99 :       }
    2077              :     // FIXME: delegate to parse_literal_expr instead? would have to rejig
    2078              :     // tokens and whatever.
    2079              :     // FIXME: for literal exprs, outer attrs should be passed in, and later
    2080              :     // error if it does not make up the entire statement.
    2081        17543 :     case INT_LITERAL:
    2082              :       // we should check the range, but ignore for now
    2083              :       // encode as int?
    2084        70172 :       return std::unique_ptr<AST::LiteralExpr> (new AST::LiteralExpr (
    2085        52629 :         LiteralResolve::evaluate_integer_literal (tok), AST::Literal::INT,
    2086        52629 :         LiteralResolve::resolve_literal_suffix (tok), {}, tok->get_locus ()));
    2087          347 :     case FLOAT_LITERAL:
    2088              :       // encode as float?
    2089         1388 :       return std::unique_ptr<AST::LiteralExpr> (new AST::LiteralExpr (
    2090         1041 :         LiteralResolve::evaluate_float_literal (tok), AST::Literal::FLOAT,
    2091         1041 :         LiteralResolve::resolve_literal_suffix (tok), {}, tok->get_locus ()));
    2092         2498 :     case STRING_LITERAL:
    2093         2498 :       return std::unique_ptr<AST::LiteralExpr> (
    2094         9992 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING,
    2095         2498 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2096           81 :     case BYTE_STRING_LITERAL:
    2097           81 :       return std::unique_ptr<AST::LiteralExpr> (
    2098          324 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE_STRING,
    2099           81 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2100           25 :     case RAW_STRING_LITERAL:
    2101           25 :       return std::unique_ptr<AST::LiteralExpr> (
    2102          100 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::RAW_STRING,
    2103           25 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2104          190 :     case CHAR_LITERAL:
    2105          190 :       return std::unique_ptr<AST::LiteralExpr> (
    2106          760 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::CHAR,
    2107          190 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2108           57 :     case BYTE_CHAR_LITERAL:
    2109           57 :       return std::unique_ptr<AST::LiteralExpr> (
    2110          228 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE,
    2111           57 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2112          727 :     case TRUE_LITERAL:
    2113          727 :       return std::unique_ptr<AST::LiteralExpr> (
    2114         2181 :         new AST::LiteralExpr (Values::Keywords::TRUE_LITERAL,
    2115              :                               AST::Literal::BOOL, tok->get_type_hint (), {},
    2116          727 :                               tok->get_locus ()));
    2117          535 :     case FALSE_LITERAL:
    2118          535 :       return std::unique_ptr<AST::LiteralExpr> (
    2119         1605 :         new AST::LiteralExpr (Values::Keywords::FALSE_LITERAL,
    2120              :                               AST::Literal::BOOL, tok->get_type_hint (), {},
    2121          535 :                               tok->get_locus ()));
    2122          859 :     case LEFT_PAREN:
    2123              :       {
    2124          859 :         auto grouped_or_tuple_expr
    2125          859 :           = parse_grouped_or_tuple_expr (std::move (outer_attrs),
    2126              :                                          tok->get_locus ());
    2127          859 :         if (grouped_or_tuple_expr)
    2128          858 :           return std::move (grouped_or_tuple_expr.value ());
    2129              :         else
    2130              :           return tl::unexpected<Parse::Error::Expr> (
    2131            1 :             Parse::Error::Expr::CHILD_ERROR);
    2132          859 :       }
    2133              : 
    2134              :     /*case PLUS: { // unary plus operator
    2135              :         // invoke parse_expr recursively with appropriate priority, etc. for
    2136              :     below AST::Expr* expr = parse_expr(LBP_UNARY_PLUS);
    2137              : 
    2138              :         if (expr == nullptr)
    2139              :             return nullptr;
    2140              :         // can only apply to integer and float expressions
    2141              :         if (expr->get_type() != integer_type_node || expr->get_type() !=
    2142              :     float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
    2143              :     plus must be int or float but it is %s", print_type(expr->get_type()));
    2144              :     return nullptr;
    2145              :         }
    2146              : 
    2147              :         return Tree(expr, tok->get_locus());
    2148              :     }*/
    2149              :     // Rust has no unary plus operator
    2150          414 :     case MINUS:
    2151              :       { // unary minus
    2152          414 :         ParseRestrictions entered_from_unary;
    2153          414 :         entered_from_unary.entered_from_unary = true;
    2154          414 :         if (!restrictions.can_be_struct_expr)
    2155           14 :           entered_from_unary.can_be_struct_expr = false;
    2156          414 :         auto expr = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary);
    2157              : 
    2158          414 :         if (!expr)
    2159              :           return tl::unexpected<Parse::Error::Expr> (
    2160            0 :             Parse::Error::Expr::CHILD_ERROR);
    2161              :         // can only apply to integer and float expressions
    2162              :         /*if (expr.get_type() != integer_type_node || expr.get_type() !=
    2163              :         float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
    2164              :         minus must be int or float but it is %s",
    2165              :         print_type(expr.get_type())); return Tree::error();
    2166              :         }*/
    2167              :         /* FIXME: when implemented the "get type" method on expr, ensure it is
    2168              :          * int or float type (except unsigned int). Actually, this would
    2169              :          * probably have to be done in semantic analysis (as type checking).
    2170              :          */
    2171              : 
    2172              :         /* FIXME: allow outer attributes on these expressions by having an
    2173              :          * outer attrs parameter in function*/
    2174          414 :         return std::make_unique<AST::NegationExpr> (std::move (expr.value ()),
    2175          414 :                                                     NegationOperator::NEGATE,
    2176              :                                                     std::move (outer_attrs),
    2177          828 :                                                     tok->get_locus ());
    2178          414 :       }
    2179          276 :     case EXCLAM:
    2180              :       { // logical or bitwise not
    2181          276 :         ParseRestrictions entered_from_unary;
    2182          276 :         entered_from_unary.entered_from_unary = true;
    2183          276 :         if (!restrictions.can_be_struct_expr)
    2184           74 :           entered_from_unary.can_be_struct_expr = false;
    2185          276 :         auto expr = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary);
    2186              : 
    2187          276 :         if (!expr)
    2188              :           return tl::unexpected<Parse::Error::Expr> (
    2189            0 :             Parse::Error::Expr::CHILD_ERROR);
    2190              :         // can only apply to boolean expressions
    2191              :         /*if (expr.get_type() != boolean_type_node) {
    2192              :             rust_error_at(tok->get_locus(),
    2193              :               "operand of logical not must be a boolean but it is %s",
    2194              :               print_type(expr.get_type()));
    2195              :             return Tree::error();
    2196              :         }*/
    2197              :         /* FIXME: type checking for boolean or integer expressions in semantic
    2198              :          * analysis */
    2199              : 
    2200              :         // FIXME: allow outer attributes on these expressions
    2201          276 :         return std::make_unique<AST::NegationExpr> (std::move (expr.value ()),
    2202          276 :                                                     NegationOperator::NOT,
    2203              :                                                     std::move (outer_attrs),
    2204          552 :                                                     tok->get_locus ());
    2205          276 :       }
    2206         3964 :     case ASTERISK:
    2207              :       {
    2208              :         /* pointer dereference only - HACK: as struct expressions should
    2209              :          * always be value expressions, cannot be dereferenced */
    2210         3964 :         ParseRestrictions entered_from_unary;
    2211         3964 :         entered_from_unary.entered_from_unary = true;
    2212         3964 :         entered_from_unary.can_be_struct_expr = false;
    2213         3964 :         auto expr = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary);
    2214         3964 :         if (!expr)
    2215              :           return tl::unexpected<Parse::Error::Expr> (
    2216            1 :             Parse::Error::Expr::CHILD_ERROR);
    2217              :         // FIXME: allow outer attributes on expression
    2218         3963 :         return std::make_unique<AST::DereferenceExpr> (std::move (
    2219         3963 :                                                          expr.value ()),
    2220              :                                                        std::move (outer_attrs),
    2221         7926 :                                                        tok->get_locus ());
    2222         3964 :       }
    2223         1432 :     case AMP:
    2224              :       {
    2225              :         // (single) "borrow" expression - shared (mutable) or immutable
    2226         1432 :         tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr
    2227              :           = tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2228         1432 :         Mutability mutability = Mutability::Imm;
    2229              :         bool raw_borrow = false;
    2230              : 
    2231         1432 :         ParseRestrictions entered_from_unary;
    2232         1432 :         entered_from_unary.entered_from_unary = true;
    2233         1432 :         if (!restrictions.can_be_struct_expr)
    2234            0 :           entered_from_unary.can_be_struct_expr = false;
    2235              : 
    2236            9 :         auto is_mutability = [] (const_TokenPtr token) {
    2237            9 :           return token->get_id () == CONST || token->get_id () == MUT;
    2238              :         };
    2239              : 
    2240         1432 :         auto t = lexer.peek_token ();
    2241              :         // Weak raw keyword, we look (1) ahead and treat it as an identifier if
    2242              :         // there is no mut nor const.
    2243         2837 :         if (t->get_id () == IDENTIFIER
    2244          741 :             && t->get_str () == Values::WeakKeywords::RAW
    2245         1485 :             && is_mutability (lexer.peek_token (1)))
    2246              :           {
    2247            7 :             lexer.skip_token ();
    2248           14 :             switch (lexer.peek_token ()->get_id ())
    2249              :               {
    2250            6 :               case MUT:
    2251            6 :                 mutability = Mutability::Mut;
    2252            6 :                 break;
    2253              :               case CONST:
    2254              :                 mutability = Mutability::Imm;
    2255              :                 break;
    2256            0 :               default:
    2257            0 :                 rust_error_at (lexer.peek_token ()->get_locus (),
    2258              :                                "raw borrow should be either const or mut");
    2259              :               }
    2260            7 :             lexer.skip_token ();
    2261            7 :             auto expr_result
    2262            7 :               = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
    2263            7 :             if (expr_result)
    2264            7 :               expr = std::move (expr_result.value ());
    2265              :             else
    2266              :               return tl::unexpected<Parse::Error::Expr> (
    2267            0 :                 Parse::Error::Expr::CHILD_ERROR);
    2268            7 :             raw_borrow = true;
    2269            7 :           }
    2270         1425 :         else if (t->get_id () == MUT)
    2271              :           {
    2272          403 :             lexer.skip_token ();
    2273          403 :             auto expr_result
    2274          403 :               = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
    2275          403 :             if (expr_result)
    2276          403 :               expr = std::move (expr_result.value ());
    2277              :             else
    2278              :               return tl::unexpected<Parse::Error::Expr> (
    2279            0 :                 Parse::Error::Expr::CHILD_ERROR);
    2280          403 :             mutability = Mutability::Mut;
    2281          403 :             raw_borrow = false;
    2282          403 :           }
    2283              :         else
    2284              :           {
    2285         1022 :             auto expr_result
    2286         1022 :               = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
    2287         1022 :             if (expr_result)
    2288         1021 :               expr = std::move (expr_result.value ());
    2289              :             else
    2290              :               return tl::unexpected<Parse::Error::Expr> (
    2291            1 :                 Parse::Error::Expr::CHILD_ERROR);
    2292         1021 :             raw_borrow = false;
    2293         1022 :           }
    2294              : 
    2295              :         // FIXME: allow outer attributes on expression
    2296         1431 :         return std::make_unique<AST::BorrowExpr> (std::move (expr.value ()),
    2297         1431 :                                                   mutability, raw_borrow, false,
    2298              :                                                   std::move (outer_attrs),
    2299         2862 :                                                   tok->get_locus ());
    2300         2863 :       }
    2301           23 :     case LOGICAL_AND:
    2302              :       {
    2303              :         // (double) "borrow" expression - shared (mutable) or immutable
    2304           23 :         std::unique_ptr<AST::Expr> expr = nullptr;
    2305           23 :         Mutability mutability = Mutability::Imm;
    2306              : 
    2307           23 :         ParseRestrictions entered_from_unary;
    2308           23 :         entered_from_unary.entered_from_unary = true;
    2309              : 
    2310           46 :         if (lexer.peek_token ()->get_id () == MUT)
    2311              :           {
    2312            0 :             lexer.skip_token ();
    2313            0 :             auto expr_res
    2314            0 :               = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
    2315            0 :             if (!expr_res)
    2316              :               return tl::unexpected<Parse::Error::Expr> (
    2317            0 :                 Parse::Error::Expr::CHILD_ERROR);
    2318            0 :             expr = std::move (expr_res.value ());
    2319            0 :             mutability = Mutability::Mut;
    2320            0 :           }
    2321              :         else
    2322              :           {
    2323           23 :             auto expr_result
    2324           23 :               = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
    2325           23 :             if (expr_result)
    2326           23 :               expr = std::move (expr_result.value ());
    2327              :             else
    2328              :               return tl::unexpected<Parse::Error::Expr> (
    2329            0 :                 Parse::Error::Expr::CHILD_ERROR);
    2330           23 :             mutability = Mutability::Imm;
    2331           23 :           }
    2332              : 
    2333              :         // FIXME: allow outer attributes on expression
    2334           23 :         return std::make_unique<AST::BorrowExpr> (std::move (expr), mutability,
    2335           23 :                                                   false, true,
    2336              :                                                   std::move (outer_attrs),
    2337           23 :                                                   tok->get_locus ());
    2338           23 :       }
    2339           73 :     case OR:
    2340              :     case PIPE:
    2341              :     case MOVE:
    2342              :       // closure expression
    2343              :       {
    2344          219 :         auto ret = parse_closure_expr_pratt (tok, std::move (outer_attrs));
    2345           73 :         if (ret)
    2346           73 :           return std::move (ret.value ());
    2347              :         else
    2348              :           return tl::unexpected<Parse::Error::Expr> (
    2349            0 :             Parse::Error::Expr::CHILD_ERROR);
    2350           73 :       }
    2351            9 :     case DOT_DOT:
    2352              :       // either "range to" or "range full" expressions
    2353              :       {
    2354            9 :         auto ret
    2355           27 :           = parse_nud_range_exclusive_expr (tok, std::move (outer_attrs));
    2356            9 :         if (ret)
    2357            9 :           return std::move (ret.value ());
    2358              :         else
    2359              :           return tl::unexpected<Parse::Error::Expr> (
    2360            0 :             Parse::Error::Expr::CHILD_ERROR);
    2361            9 :       }
    2362            0 :     case DOT_DOT_EQ:
    2363              :       // range to inclusive expr
    2364              :       {
    2365            0 :         auto ret = parse_range_to_inclusive_expr (tok, std::move (outer_attrs));
    2366            0 :         if (ret)
    2367            0 :           return std::move (ret.value ());
    2368              :         else
    2369              :           return tl::unexpected<Parse::Error::Expr> (
    2370            0 :             Parse::Error::Expr::CHILD_ERROR);
    2371            0 :       }
    2372          549 :     case RETURN_KW:
    2373              :       // FIXME: is this really a null denotation expression?
    2374              :       {
    2375          549 :         auto ret
    2376          549 :           = parse_return_expr (std::move (outer_attrs), tok->get_locus ());
    2377          549 :         if (ret)
    2378          549 :           return std::move (ret.value ());
    2379              :         else
    2380              :           return tl::unexpected<Parse::Error::Expr> (
    2381            0 :             Parse::Error::Expr::CHILD_ERROR);
    2382          549 :       }
    2383            1 :     case TRY:
    2384              :       // FIXME: is this really a null denotation expression?
    2385              :       {
    2386            1 :         auto ret = parse_try_expr (std::move (outer_attrs), tok->get_locus ());
    2387            1 :         if (ret)
    2388            1 :           return std::move (ret.value ());
    2389              :         else
    2390              :           return tl::unexpected<Parse::Error::Expr> (
    2391            0 :             Parse::Error::Expr::CHILD_ERROR);
    2392            1 :       }
    2393           81 :     case BREAK:
    2394              :       // FIXME: is this really a null denotation expression?
    2395              :       {
    2396           81 :         auto ret
    2397           81 :           = parse_break_expr (std::move (outer_attrs), tok->get_locus ());
    2398           81 :         if (ret)
    2399           81 :           return std::move (ret.value ());
    2400              :         else
    2401              :           return tl::unexpected<Parse::Error::Expr> (
    2402            0 :             Parse::Error::Expr::CHILD_ERROR);
    2403           81 :       }
    2404           17 :     case CONTINUE:
    2405           17 :       return parse_continue_expr (std::move (outer_attrs), tok->get_locus ());
    2406         1540 :     case LEFT_CURLY:
    2407              :       // ok - this is an expression with block for once.
    2408              :       {
    2409         1540 :         auto ret = parse_block_expr (std::move (outer_attrs), tl::nullopt,
    2410              :                                      tok->get_locus ());
    2411         1540 :         if (ret)
    2412         1539 :           return std::move (ret.value ());
    2413              :         else
    2414              :           return tl::unexpected<Parse::Error::Expr> (
    2415            1 :             Parse::Error::Expr::CHILD_ERROR);
    2416         1540 :       }
    2417         2160 :     case IF:
    2418              :       // if or if let, so more lookahead to find out
    2419         4320 :       if (lexer.peek_token ()->get_id () == LET)
    2420              :         {
    2421              :           // if let expr
    2422           30 :           auto ret
    2423           30 :             = parse_if_let_expr (std::move (outer_attrs), tok->get_locus ());
    2424           30 :           if (ret)
    2425           30 :             return std::move (ret.value ());
    2426              :           else
    2427              :             return tl::unexpected<Parse::Error::Expr> (
    2428            0 :               Parse::Error::Expr::CHILD_ERROR);
    2429           30 :         }
    2430              :       else
    2431              :         {
    2432              :           // if expr
    2433         2130 :           auto ret = parse_if_expr (std::move (outer_attrs), tok->get_locus ());
    2434         2130 :           if (ret)
    2435         2129 :             return std::move (ret.value ());
    2436              :           else
    2437              :             return tl::unexpected<Parse::Error::Expr> (
    2438            1 :               Parse::Error::Expr::CHILD_ERROR);
    2439         2130 :         }
    2440           43 :     case LIFETIME:
    2441              :       {
    2442          129 :         auto ret = parse_labelled_loop_expr (tok, std::move (outer_attrs));
    2443           43 :         if (ret)
    2444           43 :           return std::move (ret.value ());
    2445              :         else
    2446              :           return tl::unexpected<Parse::Error::Expr> (
    2447            0 :             Parse::Error::Expr::CHILD_ERROR);
    2448           43 :       }
    2449           77 :     case LOOP:
    2450              :       {
    2451           77 :         auto ret = parse_loop_expr (std::move (outer_attrs), tl::nullopt,
    2452              :                                     tok->get_locus ());
    2453           77 :         if (ret)
    2454           76 :           return std::move (ret.value ());
    2455              :         else
    2456              :           return tl::unexpected<Parse::Error::Expr> (
    2457            1 :             Parse::Error::Expr::CHILD_ERROR);
    2458           77 :       }
    2459           81 :     case WHILE:
    2460          162 :       if (lexer.peek_token ()->get_id () == LET)
    2461              :         {
    2462            4 :           auto ret = parse_while_let_loop_expr (std::move (outer_attrs));
    2463            4 :           if (ret)
    2464            3 :             return std::move (ret.value ());
    2465              :           else
    2466              :             return tl::unexpected<Parse::Error::Expr> (
    2467            1 :               Parse::Error::Expr::CHILD_ERROR);
    2468            4 :         }
    2469              :       else
    2470              :         {
    2471           77 :           auto ret = parse_while_loop_expr (std::move (outer_attrs),
    2472          154 :                                             tl::nullopt, tok->get_locus ());
    2473           77 :           if (ret)
    2474           77 :             return std::move (ret.value ());
    2475              :           else
    2476              :             return tl::unexpected<Parse::Error::Expr> (
    2477            0 :               Parse::Error::Expr::CHILD_ERROR);
    2478           77 :         }
    2479           20 :     case FOR:
    2480              :       {
    2481           20 :         auto ret = parse_for_loop_expr (std::move (outer_attrs), tl::nullopt);
    2482           20 :         if (ret)
    2483           20 :           return std::move (ret.value ());
    2484              :         else
    2485              :           return tl::unexpected<Parse::Error::Expr> (
    2486            0 :             Parse::Error::Expr::CHILD_ERROR);
    2487           20 :       }
    2488          916 :     case MATCH_KW:
    2489              :       // also an expression with block
    2490              :       {
    2491          916 :         auto ret
    2492          916 :           = parse_match_expr (std::move (outer_attrs), tok->get_locus ());
    2493          916 :         if (ret)
    2494          912 :           return std::move (ret.value ());
    2495              :         else
    2496              :           return tl::unexpected<Parse::Error::Expr> (
    2497            4 :             Parse::Error::Expr::CHILD_ERROR);
    2498          916 :       }
    2499              : 
    2500          407 :     case LEFT_SQUARE:
    2501              :       // array definition expr (not indexing)
    2502              :       {
    2503          407 :         auto ret
    2504          407 :           = parse_array_expr (std::move (outer_attrs), tok->get_locus ());
    2505          407 :         if (ret)
    2506          407 :           return std::move (ret.value ());
    2507              :         else
    2508              :           return tl::unexpected<Parse::Error::Expr> (
    2509            0 :             Parse::Error::Expr::CHILD_ERROR);
    2510          407 :       }
    2511         3622 :     case UNSAFE:
    2512              :       {
    2513         3622 :         auto ret = parse_unsafe_block_expr (std::move (outer_attrs),
    2514              :                                             tok->get_locus ());
    2515         3622 :         if (ret)
    2516         3622 :           return std::move (ret.value ());
    2517              :         else
    2518              :           return tl::unexpected<Parse::Error::Expr> (
    2519            0 :             Parse::Error::Expr::CHILD_ERROR);
    2520         3622 :       }
    2521            2 :     case BOX:
    2522              :       {
    2523            2 :         auto ret = parse_box_expr (std::move (outer_attrs), tok->get_locus ());
    2524            2 :         if (ret)
    2525            2 :           return std::move (ret.value ());
    2526              :         else
    2527              :           return tl::unexpected<Parse::Error::Expr> (
    2528            0 :             Parse::Error::Expr::CHILD_ERROR);
    2529            2 :       }
    2530            1 :     case UNDERSCORE:
    2531            1 :       add_error (
    2532            1 :         Error (tok->get_locus (),
    2533              :                "use of %qs is not allowed on the right-side of an assignment",
    2534              :                tok->get_token_description ()));
    2535            1 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2536           15 :     case CONST:
    2537              :       {
    2538           15 :         auto ret
    2539           15 :           = parse_const_block_expr (std::move (outer_attrs), tok->get_locus ());
    2540           15 :         if (ret)
    2541           15 :           return std::move (ret.value ());
    2542              :         else
    2543              :           return tl::unexpected<Parse::Error::Expr> (
    2544            0 :             Parse::Error::Expr::CHILD_ERROR);
    2545           15 :       }
    2546           49 :     default:
    2547           49 :       if (!restrictions.expr_can_be_null)
    2548           49 :         add_error (Error (tok->get_locus (),
    2549              :                           "found unexpected token %qs in null denotation",
    2550              :                           tok->get_token_description ()));
    2551           49 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2552              :     }
    2553              : }
    2554              : 
    2555              : /* Called for each token that can appear in infix (between) position. Can be
    2556              :  * operators or other punctuation. Returns a function pointer to member
    2557              :  * function that implements the left denotation for the token given. */
    2558              : template <typename ManagedTokenSource>
    2559              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    2560        26770 : Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok,
    2561              :                                              std::unique_ptr<AST::Expr> left,
    2562              :                                              AST::AttrVec outer_attrs,
    2563              :                                              ParseRestrictions restrictions)
    2564              : {
    2565              :   // Token passed in has already been skipped, so peek gives "next" token
    2566        26770 :   switch (tok->get_id ())
    2567              :     {
    2568              :     // FIXME: allow for outer attributes to be applied
    2569            1 :     case QUESTION_MARK:
    2570              :       {
    2571            1 :         location_t left_locus = left->get_locus ();
    2572              :         // error propagation expression - unary postfix
    2573            1 :         return std::make_unique<AST::ErrorPropagationExpr> (
    2574            1 :           std::move (left), std::move (outer_attrs), left_locus);
    2575              :       }
    2576         3041 :     case PLUS:
    2577              :       // sum expression - binary infix
    2578              :       /*return parse_binary_plus_expr (tok, std::move (left),
    2579              :                                      std::move (outer_attrs), restrictions);*/
    2580        12164 :       return parse_arithmetic_or_logical_expr (tok, std::move (left),
    2581              :                                                std::move (outer_attrs),
    2582              :                                                ArithmeticOrLogicalOperator::ADD,
    2583         3041 :                                                restrictions);
    2584         1027 :     case MINUS:
    2585              :       // difference expression - binary infix
    2586              :       /*return parse_binary_minus_expr (tok, std::move (left),
    2587              :                                       std::move (outer_attrs),
    2588              :          restrictions);*/
    2589         4108 :       return parse_arithmetic_or_logical_expr (
    2590              :         tok, std::move (left), std::move (outer_attrs),
    2591         1027 :         ArithmeticOrLogicalOperator::SUBTRACT, restrictions);
    2592          226 :     case ASTERISK:
    2593              :       // product expression - binary infix
    2594              :       /*return parse_binary_mult_expr (tok, std::move (left),
    2595              :                                      std::move (outer_attrs), restrictions);*/
    2596          904 :       return parse_arithmetic_or_logical_expr (
    2597              :         tok, std::move (left), std::move (outer_attrs),
    2598          226 :         ArithmeticOrLogicalOperator::MULTIPLY, restrictions);
    2599           34 :     case DIV:
    2600              :       // quotient expression - binary infix
    2601              :       /*return parse_binary_div_expr (tok, std::move (left),
    2602              :                                     std::move (outer_attrs), restrictions);*/
    2603          136 :       return parse_arithmetic_or_logical_expr (
    2604              :         tok, std::move (left), std::move (outer_attrs),
    2605           34 :         ArithmeticOrLogicalOperator::DIVIDE, restrictions);
    2606           36 :     case PERCENT:
    2607              :       // modulo expression - binary infix
    2608              :       /*return parse_binary_mod_expr (tok, std::move (left),
    2609              :                                     std::move (outer_attrs), restrictions);*/
    2610          144 :       return parse_arithmetic_or_logical_expr (
    2611              :         tok, std::move (left), std::move (outer_attrs),
    2612           36 :         ArithmeticOrLogicalOperator::MODULUS, restrictions);
    2613           52 :     case AMP:
    2614              :       // logical or bitwise and expression - binary infix
    2615              :       /*return parse_bitwise_and_expr (tok, std::move (left),
    2616              :                                      std::move (outer_attrs), restrictions);*/
    2617          208 :       return parse_arithmetic_or_logical_expr (
    2618              :         tok, std::move (left), std::move (outer_attrs),
    2619           52 :         ArithmeticOrLogicalOperator::BITWISE_AND, restrictions);
    2620           25 :     case PIPE:
    2621              :       // logical or bitwise or expression - binary infix
    2622              :       /*return parse_bitwise_or_expr (tok, std::move (left),
    2623              :                                     std::move (outer_attrs), restrictions);*/
    2624          100 :       return parse_arithmetic_or_logical_expr (
    2625              :         tok, std::move (left), std::move (outer_attrs),
    2626           25 :         ArithmeticOrLogicalOperator::BITWISE_OR, restrictions);
    2627           63 :     case CARET:
    2628              :       // logical or bitwise xor expression - binary infix
    2629              :       /*return parse_bitwise_xor_expr (tok, std::move (left),
    2630              :                                      std::move (outer_attrs), restrictions);*/
    2631          252 :       return parse_arithmetic_or_logical_expr (
    2632              :         tok, std::move (left), std::move (outer_attrs),
    2633           63 :         ArithmeticOrLogicalOperator::BITWISE_XOR, restrictions);
    2634           51 :     case LEFT_SHIFT:
    2635              :       // left shift expression - binary infix
    2636              :       /*return parse_left_shift_expr (tok, std::move (left),
    2637              :                                     std::move (outer_attrs), restrictions);*/
    2638          204 :       return parse_arithmetic_or_logical_expr (
    2639              :         tok, std::move (left), std::move (outer_attrs),
    2640           51 :         ArithmeticOrLogicalOperator::LEFT_SHIFT, restrictions);
    2641           19 :     case RIGHT_SHIFT:
    2642              :       // right shift expression - binary infix
    2643              :       /*return parse_right_shift_expr (tok, std::move (left),
    2644              :                                      std::move (outer_attrs), restrictions);*/
    2645           76 :       return parse_arithmetic_or_logical_expr (
    2646              :         tok, std::move (left), std::move (outer_attrs),
    2647           19 :         ArithmeticOrLogicalOperator::RIGHT_SHIFT, restrictions);
    2648          670 :     case EQUAL_EQUAL:
    2649              :       // equal to expression - binary infix (no associativity)
    2650              :       /*return parse_binary_equal_expr (tok, std::move (left),
    2651              :                                       std::move (outer_attrs),
    2652              :          restrictions);*/
    2653         2680 :       return parse_comparison_expr (tok, std::move (left),
    2654              :                                     std::move (outer_attrs),
    2655          670 :                                     ComparisonOperator::EQUAL, restrictions);
    2656          946 :     case NOT_EQUAL:
    2657              :       // not equal to expression - binary infix (no associativity)
    2658              :       /*return parse_binary_not_equal_expr (tok, std::move (left),
    2659              :                                           std::move (outer_attrs),
    2660              :                                           restrictions);*/
    2661         3784 :       return parse_comparison_expr (tok, std::move (left),
    2662              :                                     std::move (outer_attrs),
    2663              :                                     ComparisonOperator::NOT_EQUAL,
    2664          946 :                                     restrictions);
    2665          643 :     case RIGHT_ANGLE:
    2666              :       // greater than expression - binary infix (no associativity)
    2667              :       /*return parse_binary_greater_than_expr (tok, std::move (left),
    2668              :                                              std::move (outer_attrs),
    2669              :                                              restrictions);*/
    2670         2572 :       return parse_comparison_expr (tok, std::move (left),
    2671              :                                     std::move (outer_attrs),
    2672              :                                     ComparisonOperator::GREATER_THAN,
    2673          643 :                                     restrictions);
    2674          623 :     case LEFT_ANGLE:
    2675              :       // less than expression - binary infix (no associativity)
    2676              :       /*return parse_binary_less_than_expr (tok, std::move (left),
    2677              :                                           std::move (outer_attrs),
    2678              :                                           restrictions);*/
    2679         2492 :       return parse_comparison_expr (tok, std::move (left),
    2680              :                                     std::move (outer_attrs),
    2681              :                                     ComparisonOperator::LESS_THAN,
    2682          623 :                                     restrictions);
    2683          190 :     case GREATER_OR_EQUAL:
    2684              :       // greater than or equal to expression - binary infix (no associativity)
    2685              :       /*return parse_binary_greater_equal_expr (tok, std::move (left),
    2686              :                                               std::move (outer_attrs),
    2687              :                                               restrictions);*/
    2688          760 :       return parse_comparison_expr (tok, std::move (left),
    2689              :                                     std::move (outer_attrs),
    2690              :                                     ComparisonOperator::GREATER_OR_EQUAL,
    2691          190 :                                     restrictions);
    2692          224 :     case LESS_OR_EQUAL:
    2693              :       // less than or equal to expression - binary infix (no associativity)
    2694              :       /*return parse_binary_less_equal_expr (tok, std::move (left),
    2695              :                                            std::move (outer_attrs),
    2696              :                                            restrictions);*/
    2697          896 :       return parse_comparison_expr (tok, std::move (left),
    2698              :                                     std::move (outer_attrs),
    2699              :                                     ComparisonOperator::LESS_OR_EQUAL,
    2700          224 :                                     restrictions);
    2701           56 :     case OR:
    2702              :       // lazy logical or expression - binary infix
    2703          224 :       return parse_lazy_or_expr (tok, std::move (left), std::move (outer_attrs),
    2704           56 :                                  restrictions);
    2705          260 :     case LOGICAL_AND:
    2706              :       // lazy logical and expression - binary infix
    2707         1040 :       return parse_lazy_and_expr (tok, std::move (left),
    2708          260 :                                   std::move (outer_attrs), restrictions);
    2709         5167 :     case AS:
    2710              :       /* type cast expression - kind of binary infix (RHS is actually a
    2711              :        * TypeNoBounds) */
    2712        20668 :       return parse_type_cast_expr (tok, std::move (left),
    2713         5167 :                                    std::move (outer_attrs), restrictions);
    2714         2525 :     case EQUAL:
    2715              :       // assignment expression - binary infix (note right-to-left
    2716              :       // associativity)
    2717        10100 :       return parse_assig_expr (tok, std::move (left), std::move (outer_attrs),
    2718         2525 :                                restrictions);
    2719          160 :     case PLUS_EQ:
    2720              :       /* plus-assignment expression - binary infix (note right-to-left
    2721              :        * associativity) */
    2722              :       /*return parse_plus_assig_expr (tok, std::move (left),
    2723              :                                     std::move (outer_attrs), restrictions);*/
    2724          640 :       return parse_compound_assignment_expr (tok, std::move (left),
    2725              :                                              std::move (outer_attrs),
    2726              :                                              CompoundAssignmentOperator::ADD,
    2727          160 :                                              restrictions);
    2728          105 :     case MINUS_EQ:
    2729              :       /* minus-assignment expression - binary infix (note right-to-left
    2730              :        * associativity) */
    2731              :       /*return parse_minus_assig_expr (tok, std::move (left),
    2732              :                                      std::move (outer_attrs), restrictions);*/
    2733          420 :       return parse_compound_assignment_expr (
    2734              :         tok, std::move (left), std::move (outer_attrs),
    2735          105 :         CompoundAssignmentOperator::SUBTRACT, restrictions);
    2736            7 :     case ASTERISK_EQ:
    2737              :       /* multiply-assignment expression - binary infix (note right-to-left
    2738              :        * associativity) */
    2739              :       /*return parse_mult_assig_expr (tok, std::move (left),
    2740              :                                     std::move (outer_attrs), restrictions);*/
    2741           28 :       return parse_compound_assignment_expr (
    2742              :         tok, std::move (left), std::move (outer_attrs),
    2743            7 :         CompoundAssignmentOperator::MULTIPLY, restrictions);
    2744            7 :     case DIV_EQ:
    2745              :       /* division-assignment expression - binary infix (note right-to-left
    2746              :        * associativity) */
    2747              :       /*return parse_div_assig_expr (tok, std::move (left),
    2748              :                                    std::move (outer_attrs), restrictions);*/
    2749           28 :       return parse_compound_assignment_expr (tok, std::move (left),
    2750              :                                              std::move (outer_attrs),
    2751              :                                              CompoundAssignmentOperator::DIVIDE,
    2752            7 :                                              restrictions);
    2753            7 :     case PERCENT_EQ:
    2754              :       /* modulo-assignment expression - binary infix (note right-to-left
    2755              :        * associativity) */
    2756              :       /*return parse_mod_assig_expr (tok, std::move (left),
    2757              :                                    std::move (outer_attrs), restrictions);*/
    2758           28 :       return parse_compound_assignment_expr (
    2759              :         tok, std::move (left), std::move (outer_attrs),
    2760            7 :         CompoundAssignmentOperator::MODULUS, restrictions);
    2761           21 :     case AMP_EQ:
    2762              :       /* bitwise and-assignment expression - binary infix (note right-to-left
    2763              :        * associativity) */
    2764              :       /*return parse_and_assig_expr (tok, std::move (left),
    2765              :                                    std::move (outer_attrs), restrictions);*/
    2766           84 :       return parse_compound_assignment_expr (
    2767              :         tok, std::move (left), std::move (outer_attrs),
    2768           21 :         CompoundAssignmentOperator::BITWISE_AND, restrictions);
    2769           28 :     case PIPE_EQ:
    2770              :       /* bitwise or-assignment expression - binary infix (note right-to-left
    2771              :        * associativity) */
    2772              :       /*return parse_or_assig_expr (tok, std::move (left),
    2773              :                                   std::move (outer_attrs), restrictions);*/
    2774          112 :       return parse_compound_assignment_expr (
    2775              :         tok, std::move (left), std::move (outer_attrs),
    2776           28 :         CompoundAssignmentOperator::BITWISE_OR, restrictions);
    2777          336 :     case CARET_EQ:
    2778              :       /* bitwise xor-assignment expression - binary infix (note right-to-left
    2779              :        * associativity) */
    2780              :       /*return parse_xor_assig_expr (tok, std::move (left),
    2781              :                                    std::move (outer_attrs), restrictions);*/
    2782         1344 :       return parse_compound_assignment_expr (
    2783              :         tok, std::move (left), std::move (outer_attrs),
    2784          336 :         CompoundAssignmentOperator::BITWISE_XOR, restrictions);
    2785            7 :     case LEFT_SHIFT_EQ:
    2786              :       /* left shift-assignment expression - binary infix (note right-to-left
    2787              :        * associativity) */
    2788              :       /*return parse_left_shift_assig_expr (tok, std::move (left),
    2789              :                                           std::move (outer_attrs),
    2790              :                                           restrictions);*/
    2791           28 :       return parse_compound_assignment_expr (
    2792              :         tok, std::move (left), std::move (outer_attrs),
    2793            7 :         CompoundAssignmentOperator::LEFT_SHIFT, restrictions);
    2794            7 :     case RIGHT_SHIFT_EQ:
    2795              :       /* right shift-assignment expression - binary infix (note right-to-left
    2796              :        * associativity) */
    2797              :       /*return parse_right_shift_assig_expr (tok, std::move (left),
    2798              :                                            std::move (outer_attrs),
    2799              :                                            restrictions);*/
    2800           28 :       return parse_compound_assignment_expr (
    2801              :         tok, std::move (left), std::move (outer_attrs),
    2802            7 :         CompoundAssignmentOperator::RIGHT_SHIFT, restrictions);
    2803           78 :     case DOT_DOT:
    2804              :       /* range exclusive expression - binary infix (no associativity)
    2805              :        * either "range" or "range from" */
    2806          312 :       return parse_led_range_exclusive_expr (tok, std::move (left),
    2807              :                                              std::move (outer_attrs),
    2808           78 :                                              restrictions);
    2809            7 :     case DOT_DOT_EQ:
    2810              :       /* range inclusive expression - binary infix (no associativity)
    2811              :        * unambiguously RangeInclusiveExpr */
    2812           28 :       return parse_range_inclusive_expr (tok, std::move (left),
    2813            7 :                                          std::move (outer_attrs), restrictions);
    2814            0 :     case SCOPE_RESOLUTION:
    2815              :       // path expression - binary infix? FIXME should this even be parsed
    2816              :       // here?
    2817            0 :       add_error (
    2818            0 :         Error (tok->get_locus (),
    2819              :                "found scope resolution operator in left denotation "
    2820              :                "function - this should probably be handled elsewhere"));
    2821              : 
    2822            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2823         8840 :     case DOT:
    2824              :       {
    2825              :         /* field expression or method call - relies on parentheses after next
    2826              :          * identifier or await if token after is "await" (unary postfix) or
    2827              :          * tuple index if token after is a decimal int literal */
    2828              : 
    2829         8840 :         const_TokenPtr next_tok = lexer.peek_token ();
    2830         8840 :         if (next_tok->get_id () == IDENTIFIER
    2831         8840 :             && next_tok->get_str () == Values::Keywords::AWAIT)
    2832              :           {
    2833              :             // await expression
    2834            0 :             return parse_await_expr (tok, std::move (left),
    2835            0 :                                      std::move (outer_attrs));
    2836              :           }
    2837         8840 :         else if (next_tok->get_id () == INT_LITERAL)
    2838              :           {
    2839              :             // tuple index expression - TODO check for decimal int literal
    2840         3612 :             return parse_tuple_index_expr (tok, std::move (left),
    2841              :                                            std::move (outer_attrs),
    2842          903 :                                            restrictions);
    2843              :           }
    2844         7937 :         else if (next_tok->get_id () == FLOAT_LITERAL)
    2845              :           {
    2846              :             // Lexer has misidentified a tuple index as a float literal
    2847              :             // eg: `(x, (y, z)).1.0` -> 1.0 has been identified as a float
    2848              :             // literal. This means we should split it into three new separate
    2849              :             // tokens, the first tuple index, the dot and the second tuple
    2850              :             // index.
    2851            2 :             auto current_loc = next_tok->get_locus ();
    2852            2 :             auto str = next_tok->get_str ();
    2853            2 :             auto dot_pos = str.find (".");
    2854            2 :             auto prefix = str.substr (0, dot_pos);
    2855            2 :             auto suffix = str.substr (dot_pos + 1);
    2856            2 :             if (dot_pos == str.size () - 1)
    2857              :               {
    2858            1 :                 auto prefix_len = prefix.length ();
    2859            3 :                 lexer.split_current_token (
    2860            2 :                   {Token::make_int (current_loc, std::move (prefix), prefix_len,
    2861              :                                     IntegerLiteralBase::Decimal,
    2862              :                                     CORETYPE_PURE_DECIMAL),
    2863              :                    Token::make (DOT, current_loc + 1)});
    2864              :               }
    2865              :             else
    2866              :               {
    2867            1 :                 auto prefix_len = prefix.length ();
    2868            1 :                 auto suffix_len = suffix.length ();
    2869            5 :                 lexer.split_current_token (
    2870            2 :                   {Token::make_int (current_loc, std::move (prefix), prefix_len,
    2871              :                                     IntegerLiteralBase::Decimal,
    2872              :                                     CORETYPE_PURE_DECIMAL),
    2873              :                    Token::make (DOT, current_loc + 1),
    2874            2 :                    Token::make_int (current_loc + 2, std::move (suffix),
    2875              :                                     suffix_len, IntegerLiteralBase::Decimal,
    2876              :                                     CORETYPE_PURE_DECIMAL)});
    2877              :               }
    2878            8 :             return parse_tuple_index_expr (tok, std::move (left),
    2879              :                                            std::move (outer_attrs),
    2880            2 :                                            restrictions);
    2881            2 :           }
    2882        12331 :         else if (next_tok->get_id () == IDENTIFIER
    2883        18217 :                  && lexer.peek_token (1)->get_id () != LEFT_PAREN
    2884        18758 :                  && lexer.peek_token (1)->get_id () != SCOPE_RESOLUTION)
    2885              :           {
    2886              :             /* field expression (or should be) - FIXME: scope resolution right
    2887              :              * after identifier should always be method, I'm pretty sure */
    2888        19660 :             return parse_field_access_expr (tok, std::move (left),
    2889              :                                             std::move (outer_attrs),
    2890         4915 :                                             restrictions);
    2891              :           }
    2892              :         else
    2893              :           {
    2894              :             // method call (probably)
    2895        12080 :             return parse_method_call_expr (tok, std::move (left),
    2896              :                                            std::move (outer_attrs),
    2897         3020 :                                            restrictions);
    2898              :           }
    2899         8840 :       }
    2900          978 :     case LEFT_PAREN:
    2901              :       // function call - method call is based on dot notation first
    2902         3912 :       return parse_function_call_expr (tok, std::move (left),
    2903          978 :                                        std::move (outer_attrs), restrictions);
    2904          303 :     case LEFT_SQUARE:
    2905              :       // array or slice index expression (pseudo binary infix)
    2906         1212 :       return parse_index_expr (tok, std::move (left), std::move (outer_attrs),
    2907          303 :                                restrictions);
    2908            0 :     default:
    2909            0 :       add_error (Error (tok->get_locus (),
    2910              :                         "found unexpected token %qs in left denotation",
    2911              :                         tok->get_token_description ()));
    2912              : 
    2913            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2914              :     }
    2915              : }
    2916              : 
    2917              : /* Returns the left binding power for the given ArithmeticOrLogicalExpr type.
    2918              :  * TODO make constexpr? Would that even do anything useful? */
    2919              : inline binding_powers
    2920         4574 : get_lbp_for_arithmetic_or_logical_expr (
    2921              :   AST::ArithmeticOrLogicalExpr::ExprType expr_type)
    2922              : {
    2923         4574 :   switch (expr_type)
    2924              :     {
    2925              :     case ArithmeticOrLogicalOperator::ADD:
    2926              :       return LBP_PLUS;
    2927              :     case ArithmeticOrLogicalOperator::SUBTRACT:
    2928              :       return LBP_MINUS;
    2929              :     case ArithmeticOrLogicalOperator::MULTIPLY:
    2930              :       return LBP_MUL;
    2931              :     case ArithmeticOrLogicalOperator::DIVIDE:
    2932              :       return LBP_DIV;
    2933              :     case ArithmeticOrLogicalOperator::MODULUS:
    2934              :       return LBP_MOD;
    2935              :     case ArithmeticOrLogicalOperator::BITWISE_AND:
    2936              :       return LBP_AMP;
    2937              :     case ArithmeticOrLogicalOperator::BITWISE_OR:
    2938              :       return LBP_PIPE;
    2939              :     case ArithmeticOrLogicalOperator::BITWISE_XOR:
    2940              :       return LBP_CARET;
    2941              :     case ArithmeticOrLogicalOperator::LEFT_SHIFT:
    2942              :       return LBP_L_SHIFT;
    2943              :     case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
    2944              :       return LBP_R_SHIFT;
    2945            0 :     default:
    2946              :       // WTF? should not happen, this is an error
    2947            0 :       rust_unreachable ();
    2948              : 
    2949              :       return LBP_PLUS;
    2950              :     }
    2951              : }
    2952              : 
    2953              : // Parses an arithmetic or logical expression (with Pratt parsing).
    2954              : template <typename ManagedTokenSource>
    2955              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    2956         4574 : Parser<ManagedTokenSource>::parse_arithmetic_or_logical_expr (
    2957              :   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
    2958              :   AST::ArithmeticOrLogicalExpr::ExprType expr_type,
    2959              :   ParseRestrictions restrictions)
    2960              : {
    2961              :   // parse RHS (as tok has already been consumed in parse_expression)
    2962         4574 :   auto right = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type),
    2963         9148 :                            AST::AttrVec (), restrictions);
    2964         4574 :   if (!right)
    2965            2 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    2966              : 
    2967              :   // TODO: check types. actually, do so during semantic analysis
    2968         4572 :   location_t locus = left->get_locus ();
    2969              : 
    2970         4572 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    2971         9144 :     std::move (left), std::move (right.value ()), expr_type, locus);
    2972         4574 : }
    2973              : 
    2974              : // Parses a binary addition expression (with Pratt parsing).
    2975              : template <typename ManagedTokenSource>
    2976              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    2977            0 : Parser<ManagedTokenSource>::parse_binary_plus_expr (
    2978              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    2979              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    2980              : {
    2981              :   // parse RHS (as tok has already been consumed in parse_expression)
    2982            0 :   auto right = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions);
    2983            0 :   if (!right)
    2984            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    2985              : 
    2986              :   // TODO: check types. actually, do so during semantic analysis
    2987            0 :   location_t locus = left->get_locus ();
    2988              : 
    2989            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    2990            0 :     std::move (left), std::move (right.value ()),
    2991            0 :     ArithmeticOrLogicalOperator::ADD, locus);
    2992            0 : }
    2993              : 
    2994              : // Parses a binary subtraction expression (with Pratt parsing).
    2995              : template <typename ManagedTokenSource>
    2996              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    2997            0 : Parser<ManagedTokenSource>::parse_binary_minus_expr (
    2998              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    2999              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3000              : {
    3001              :   // parse RHS (as tok has already been consumed in parse_expression)
    3002            0 :   auto right = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions);
    3003            0 :   if (!right)
    3004            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3005              : 
    3006              :   // TODO: check types. actually, do so during semantic analysis
    3007            0 :   location_t locus = left->get_locus ();
    3008              : 
    3009            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3010            0 :     std::move (left), std::move (right.value ()),
    3011            0 :     ArithmeticOrLogicalOperator::SUBTRACT, locus);
    3012            0 : }
    3013              : 
    3014              : // Parses a binary multiplication expression (with Pratt parsing).
    3015              : template <typename ManagedTokenSource>
    3016              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3017            0 : Parser<ManagedTokenSource>::parse_binary_mult_expr (
    3018              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3019              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3020              : {
    3021              :   // parse RHS (as tok has already been consumed in parse_expression)
    3022            0 :   auto right = parse_expr (LBP_MUL, AST::AttrVec (), restrictions);
    3023            0 :   if (!right)
    3024            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3025              : 
    3026              :   // TODO: check types. actually, do so during semantic analysis
    3027            0 :   location_t locus = left->get_locus ();
    3028              : 
    3029            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3030            0 :     std::move (left), std::move (right.value ()),
    3031            0 :     ArithmeticOrLogicalOperator::MULTIPLY, locus);
    3032            0 : }
    3033              : 
    3034              : // Parses a binary division expression (with Pratt parsing).
    3035              : template <typename ManagedTokenSource>
    3036              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3037            0 : Parser<ManagedTokenSource>::parse_binary_div_expr (
    3038              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3039              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3040              : {
    3041              :   // parse RHS (as tok has already been consumed in parse_expression)
    3042            0 :   auto right = parse_expr (LBP_DIV, AST::AttrVec (), restrictions);
    3043            0 :   if (!right)
    3044            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3045              : 
    3046              :   // TODO: check types. actually, do so during semantic analysis
    3047            0 :   location_t locus = left->get_locus ();
    3048              : 
    3049            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3050            0 :     std::move (left), std::move (right.value ()),
    3051            0 :     ArithmeticOrLogicalOperator::DIVIDE, locus);
    3052            0 : }
    3053              : 
    3054              : // Parses a binary modulo expression (with Pratt parsing).
    3055              : template <typename ManagedTokenSource>
    3056              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3057            0 : Parser<ManagedTokenSource>::parse_binary_mod_expr (
    3058              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3059              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3060              : {
    3061              :   // parse RHS (as tok has already been consumed in parse_expression)
    3062            0 :   auto right = parse_expr (LBP_MOD, AST::AttrVec (), restrictions);
    3063            0 :   if (!right)
    3064            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3065              : 
    3066              :   // TODO: check types. actually, do so during semantic analysis
    3067            0 :   location_t locus = left->get_locus ();
    3068              : 
    3069            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3070            0 :     std::move (left), std::move (right.value ()),
    3071            0 :     ArithmeticOrLogicalOperator::MODULUS, locus);
    3072            0 : }
    3073              : 
    3074              : /* Parses a binary bitwise (or eager logical) and expression (with Pratt
    3075              :  * parsing). */
    3076              : template <typename ManagedTokenSource>
    3077              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3078            0 : Parser<ManagedTokenSource>::parse_bitwise_and_expr (
    3079              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3080              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3081              : {
    3082              :   // parse RHS (as tok has already been consumed in parse_expression)
    3083            0 :   auto right = parse_expr (LBP_AMP, AST::AttrVec (), restrictions);
    3084            0 :   if (!right)
    3085            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3086              : 
    3087              :   // TODO: check types. actually, do so during semantic analysis
    3088            0 :   location_t locus = left->get_locus ();
    3089              : 
    3090            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3091            0 :     std::move (left), std::move (right.value ()),
    3092            0 :     ArithmeticOrLogicalOperator::BITWISE_AND, locus);
    3093            0 : }
    3094              : 
    3095              : /* Parses a binary bitwise (or eager logical) or expression (with Pratt
    3096              :  * parsing). */
    3097              : template <typename ManagedTokenSource>
    3098              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3099            0 : Parser<ManagedTokenSource>::parse_bitwise_or_expr (
    3100              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3101              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3102              : {
    3103              :   // parse RHS (as tok has already been consumed in parse_expression)
    3104            0 :   auto right = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions);
    3105            0 :   if (!right)
    3106            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3107              : 
    3108              :   // TODO: check types. actually, do so during semantic analysis
    3109            0 :   location_t locus = left->get_locus ();
    3110              : 
    3111            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3112            0 :     std::move (left), std::move (right.value ()),
    3113            0 :     ArithmeticOrLogicalOperator::BITWISE_OR, locus);
    3114            0 : }
    3115              : 
    3116              : /* Parses a binary bitwise (or eager logical) xor expression (with Pratt
    3117              :  * parsing). */
    3118              : template <typename ManagedTokenSource>
    3119              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3120            0 : Parser<ManagedTokenSource>::parse_bitwise_xor_expr (
    3121              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3122              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3123              : {
    3124              :   // parse RHS (as tok has already been consumed in parse_expression)
    3125            0 :   auto right = parse_expr (LBP_CARET, AST::AttrVec (), restrictions);
    3126            0 :   if (!right)
    3127            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3128              : 
    3129              :   // TODO: check types. actually, do so during semantic analysis
    3130            0 :   location_t locus = left->get_locus ();
    3131              : 
    3132            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3133            0 :     std::move (left), std::move (right.value ()),
    3134            0 :     ArithmeticOrLogicalOperator::BITWISE_XOR, locus);
    3135            0 : }
    3136              : 
    3137              : // Parses a binary left shift expression (with Pratt parsing).
    3138              : template <typename ManagedTokenSource>
    3139              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3140            0 : Parser<ManagedTokenSource>::parse_left_shift_expr (
    3141              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3142              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3143              : {
    3144              :   // parse RHS (as tok has already been consumed in parse_expression)
    3145            0 :   auto right = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions);
    3146            0 :   if (!right)
    3147            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3148              : 
    3149              :   // TODO: check types. actually, do so during semantic analysis
    3150            0 :   location_t locus = left->get_locus ();
    3151              : 
    3152            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3153            0 :     std::move (left), std::move (right.value ()),
    3154            0 :     ArithmeticOrLogicalOperator::LEFT_SHIFT, locus);
    3155            0 : }
    3156              : 
    3157              : // Parses a binary right shift expression (with Pratt parsing).
    3158              : template <typename ManagedTokenSource>
    3159              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3160            0 : Parser<ManagedTokenSource>::parse_right_shift_expr (
    3161              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3162              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3163              : {
    3164              :   // parse RHS (as tok has already been consumed in parse_expression)
    3165            0 :   auto right = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions);
    3166            0 :   if (!right)
    3167            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3168              : 
    3169              :   // TODO: check types. actually, do so during semantic analysis
    3170            0 :   location_t locus = left->get_locus ();
    3171              : 
    3172            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3173            0 :     std::move (left), std::move (right.value ()),
    3174            0 :     ArithmeticOrLogicalOperator::RIGHT_SHIFT, locus);
    3175            0 : }
    3176              : 
    3177              : /* Returns the left binding power for the given ComparisonExpr type.
    3178              :  * TODO make constexpr? Would that even do anything useful? */
    3179              : inline binding_powers
    3180         3296 : get_lbp_for_comparison_expr (AST::ComparisonExpr::ExprType expr_type)
    3181              : {
    3182         3296 :   switch (expr_type)
    3183              :     {
    3184              :     case ComparisonOperator::EQUAL:
    3185              :       return LBP_EQUAL;
    3186              :     case ComparisonOperator::NOT_EQUAL:
    3187              :       return LBP_NOT_EQUAL;
    3188              :     case ComparisonOperator::GREATER_THAN:
    3189              :       return LBP_GREATER_THAN;
    3190              :     case ComparisonOperator::LESS_THAN:
    3191              :       return LBP_SMALLER_THAN;
    3192              :     case ComparisonOperator::GREATER_OR_EQUAL:
    3193              :       return LBP_GREATER_EQUAL;
    3194              :     case ComparisonOperator::LESS_OR_EQUAL:
    3195              :       return LBP_SMALLER_EQUAL;
    3196            0 :     default:
    3197              :       // WTF? should not happen, this is an error
    3198            0 :       rust_unreachable ();
    3199              : 
    3200              :       return LBP_EQUAL;
    3201              :     }
    3202              : }
    3203              : 
    3204              : /* Parses a ComparisonExpr of given type and LBP. TODO find a way to only
    3205              :  * specify one and have the other looked up - e.g. specify ExprType and
    3206              :  * binding power is looked up? */
    3207              : template <typename ManagedTokenSource>
    3208              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3209         3296 : Parser<ManagedTokenSource>::parse_comparison_expr (
    3210              :   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
    3211              :   AST::ComparisonExpr::ExprType expr_type, ParseRestrictions restrictions)
    3212              : {
    3213              :   // parse RHS (as tok has already been consumed in parse_expression)
    3214         3296 :   auto right = parse_expr (get_lbp_for_comparison_expr (expr_type),
    3215         6592 :                            AST::AttrVec (), restrictions);
    3216         3296 :   if (!right)
    3217            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3218              : 
    3219              :   // TODO: check types. actually, do so during semantic analysis
    3220         3296 :   location_t locus = left->get_locus ();
    3221              : 
    3222         3296 :   return std::make_unique<AST::ComparisonExpr> (std::move (left),
    3223         3296 :                                                 std::move (right.value ()),
    3224         3296 :                                                 expr_type, locus);
    3225         3296 : }
    3226              : 
    3227              : // Parses a binary equal to expression (with Pratt parsing).
    3228              : template <typename ManagedTokenSource>
    3229              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3230            0 : Parser<ManagedTokenSource>::parse_binary_equal_expr (
    3231              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3232              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3233              : {
    3234              :   // parse RHS (as tok has already been consumed in parse_expression)
    3235            0 :   auto right = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions);
    3236            0 :   if (!right)
    3237            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3238              : 
    3239              :   // TODO: check types. actually, do so during semantic analysis
    3240            0 :   location_t locus = left->get_locus ();
    3241              : 
    3242            0 :   return std::make_unique<AST::ComparisonExpr> (std::move (left),
    3243            0 :                                                 std::move (right.value ()),
    3244            0 :                                                 ComparisonOperator::EQUAL,
    3245            0 :                                                 locus);
    3246            0 : }
    3247              : 
    3248              : // Parses a binary not equal to expression (with Pratt parsing).
    3249              : template <typename ManagedTokenSource>
    3250              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3251            0 : Parser<ManagedTokenSource>::parse_binary_not_equal_expr (
    3252              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3253              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3254              : {
    3255              :   // parse RHS (as tok has already been consumed in parse_expression)
    3256            0 :   auto right = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions);
    3257            0 :   if (!right)
    3258            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3259              : 
    3260              :   // TODO: check types. actually, do so during semantic analysis
    3261            0 :   location_t locus = left->get_locus ();
    3262              : 
    3263            0 :   return std::make_unique<AST::ComparisonExpr> (std::move (left),
    3264            0 :                                                 std::move (right.value ()),
    3265            0 :                                                 ComparisonOperator::NOT_EQUAL,
    3266            0 :                                                 locus);
    3267            0 : }
    3268              : 
    3269              : // Parses a binary greater than expression (with Pratt parsing).
    3270              : template <typename ManagedTokenSource>
    3271              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3272            0 : Parser<ManagedTokenSource>::parse_binary_greater_than_expr (
    3273              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3274              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3275              : {
    3276              :   // parse RHS (as tok has already been consumed in parse_expression)
    3277            0 :   auto right = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions);
    3278            0 :   if (!right)
    3279            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3280              : 
    3281              :   // TODO: check types. actually, do so during semantic analysis
    3282            0 :   location_t locus = left->get_locus ();
    3283              : 
    3284            0 :   return std::make_unique<AST::ComparisonExpr> (
    3285            0 :     std::move (left), std::move (right.value ()),
    3286            0 :     ComparisonOperator::GREATER_THAN, locus);
    3287            0 : }
    3288              : 
    3289              : // Parses a binary less than expression (with Pratt parsing).
    3290              : template <typename ManagedTokenSource>
    3291              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3292            0 : Parser<ManagedTokenSource>::parse_binary_less_than_expr (
    3293              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3294              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3295              : {
    3296              :   // parse RHS (as tok has already been consumed in parse_expression)
    3297            0 :   auto right = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions);
    3298            0 :   if (!right)
    3299            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3300              : 
    3301              :   // TODO: check types. actually, do so during semantic analysis
    3302            0 :   location_t locus = left->get_locus ();
    3303              : 
    3304            0 :   return std::make_unique<AST::ComparisonExpr> (std::move (left),
    3305            0 :                                                 std::move (right.value ()),
    3306            0 :                                                 ComparisonOperator::LESS_THAN,
    3307            0 :                                                 locus);
    3308            0 : }
    3309              : 
    3310              : // Parses a binary greater than or equal to expression (with Pratt parsing).
    3311              : template <typename ManagedTokenSource>
    3312              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3313            0 : Parser<ManagedTokenSource>::parse_binary_greater_equal_expr (
    3314              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3315              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3316              : {
    3317              :   // parse RHS (as tok has already been consumed in parse_expression)
    3318            0 :   auto right = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions);
    3319            0 :   if (!right)
    3320            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3321              : 
    3322              :   // TODO: check types. actually, do so during semantic analysis
    3323            0 :   location_t locus = left->get_locus ();
    3324              : 
    3325            0 :   return std::make_unique<AST::ComparisonExpr> (
    3326            0 :     std::move (left), std::move (right.value ()),
    3327            0 :     ComparisonOperator::GREATER_OR_EQUAL, locus);
    3328            0 : }
    3329              : 
    3330              : // Parses a binary less than or equal to expression (with Pratt parsing).
    3331              : template <typename ManagedTokenSource>
    3332              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3333            0 : Parser<ManagedTokenSource>::parse_binary_less_equal_expr (
    3334              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3335              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3336              : {
    3337              :   // parse RHS (as tok has already been consumed in parse_expression)
    3338            0 :   auto right = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions);
    3339            0 :   if (!right)
    3340            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3341              : 
    3342              :   // TODO: check types. actually, do so during semantic analysis
    3343            0 :   location_t locus = left->get_locus ();
    3344              : 
    3345            0 :   return std::make_unique<AST::ComparisonExpr> (
    3346            0 :     std::move (left), std::move (right.value ()),
    3347            0 :     ComparisonOperator::LESS_OR_EQUAL, locus);
    3348            0 : }
    3349              : 
    3350              : // Parses a binary lazy boolean or expression (with Pratt parsing).
    3351              : template <typename ManagedTokenSource>
    3352              : tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
    3353           56 : Parser<ManagedTokenSource>::parse_lazy_or_expr (
    3354              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3355              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3356              : {
    3357              :   // parse RHS (as tok has already been consumed in parse_expression)
    3358           56 :   auto right = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions);
    3359           56 :   if (!right)
    3360            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3361              : 
    3362              :   // TODO: check types. actually, do so during semantic analysis
    3363           56 :   location_t locus = left->get_locus ();
    3364              : 
    3365           56 :   return std::make_unique<AST::LazyBooleanExpr> (
    3366           56 :     std::move (left), std::move (right.value ()),
    3367          112 :     LazyBooleanOperator::LOGICAL_OR, locus);
    3368           56 : }
    3369              : 
    3370              : // Parses a binary lazy boolean and expression (with Pratt parsing).
    3371              : template <typename ManagedTokenSource>
    3372              : tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
    3373          260 : Parser<ManagedTokenSource>::parse_lazy_and_expr (
    3374              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3375              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3376              : {
    3377              :   // parse RHS (as tok has already been consumed in parse_expression)
    3378          260 :   auto right = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions);
    3379          260 :   if (!right)
    3380            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3381              : 
    3382              :   // TODO: check types. actually, do so during semantic analysis
    3383          260 :   location_t locus = left->get_locus ();
    3384              : 
    3385          260 :   return std::make_unique<AST::LazyBooleanExpr> (
    3386          260 :     std::move (left), std::move (right.value ()),
    3387          520 :     LazyBooleanOperator::LOGICAL_AND, locus);
    3388          260 : }
    3389              : 
    3390              : // Parses a pseudo-binary infix type cast expression (with Pratt parsing).
    3391              : template <typename ManagedTokenSource>
    3392              : tl::expected<std::unique_ptr<AST::TypeCastExpr>, Parse::Error::Expr>
    3393         5167 : Parser<ManagedTokenSource>::parse_type_cast_expr (
    3394              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> expr_to_cast,
    3395              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED,
    3396              :   ParseRestrictions restrictions ATTRIBUTE_UNUSED)
    3397              : {
    3398              :   // parse RHS (as tok has already been consumed in parse_expression)
    3399         5167 :   auto type = parse_type_no_bounds ();
    3400         5167 :   if (!type)
    3401            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3402              :   // FIXME: how do I get precedence put in here?
    3403              : 
    3404              :   // TODO: check types. actually, do so during semantic analysis
    3405         5167 :   location_t locus = expr_to_cast->get_locus ();
    3406              : 
    3407         5167 :   return std::make_unique<AST::TypeCastExpr> (std::move (expr_to_cast),
    3408         5167 :                                               std::move (type), locus);
    3409         5167 : }
    3410              : 
    3411              : // Parses a binary assignment expression (with Pratt parsing).
    3412              : template <typename ManagedTokenSource>
    3413              : tl::expected<std::unique_ptr<AST::AssignmentExpr>, Parse::Error::Expr>
    3414         2525 : Parser<ManagedTokenSource>::parse_assig_expr (
    3415              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3416              :   AST::AttrVec outer_attrs, ParseRestrictions restrictions)
    3417              : {
    3418              :   // parse RHS (as tok has already been consumed in parse_expression)
    3419         2525 :   auto right = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions);
    3420         2525 :   if (!right)
    3421            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3422              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3423              : 
    3424         2525 :   location_t locus = left->get_locus ();
    3425              : 
    3426         2525 :   return std::make_unique<AST::AssignmentExpr> (std::move (left),
    3427         2525 :                                                 std::move (right.value ()),
    3428         2525 :                                                 std::move (outer_attrs), locus);
    3429         2525 : }
    3430              : 
    3431              : /* Returns the left binding power for the given CompoundAssignmentExpr type.
    3432              :  * TODO make constexpr? Would that even do anything useful? */
    3433              : inline binding_powers
    3434          685 : get_lbp_for_compound_assignment_expr (
    3435              :   AST::CompoundAssignmentExpr::ExprType expr_type)
    3436              : {
    3437          685 :   switch (expr_type)
    3438              :     {
    3439              :     case CompoundAssignmentOperator::ADD:
    3440              :       return LBP_PLUS;
    3441              :     case CompoundAssignmentOperator::SUBTRACT:
    3442              :       return LBP_MINUS;
    3443              :     case CompoundAssignmentOperator::MULTIPLY:
    3444              :       return LBP_MUL;
    3445              :     case CompoundAssignmentOperator::DIVIDE:
    3446              :       return LBP_DIV;
    3447              :     case CompoundAssignmentOperator::MODULUS:
    3448              :       return LBP_MOD;
    3449              :     case CompoundAssignmentOperator::BITWISE_AND:
    3450              :       return LBP_AMP;
    3451              :     case CompoundAssignmentOperator::BITWISE_OR:
    3452              :       return LBP_PIPE;
    3453              :     case CompoundAssignmentOperator::BITWISE_XOR:
    3454              :       return LBP_CARET;
    3455              :     case CompoundAssignmentOperator::LEFT_SHIFT:
    3456              :       return LBP_L_SHIFT;
    3457              :     case CompoundAssignmentOperator::RIGHT_SHIFT:
    3458              :       return LBP_R_SHIFT;
    3459            0 :     default:
    3460              :       // WTF? should not happen, this is an error
    3461            0 :       rust_unreachable ();
    3462              : 
    3463              :       return LBP_PLUS;
    3464              :     }
    3465              : }
    3466              : 
    3467              : // Parses a compound assignment expression (with Pratt parsing).
    3468              : template <typename ManagedTokenSource>
    3469              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3470          685 : Parser<ManagedTokenSource>::parse_compound_assignment_expr (
    3471              :   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
    3472              :   AST::CompoundAssignmentExpr::ExprType expr_type,
    3473              :   ParseRestrictions restrictions)
    3474              : {
    3475              :   // parse RHS (as tok has already been consumed in parse_expression)
    3476          685 :   auto right = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1,
    3477         1370 :                            AST::AttrVec (), restrictions);
    3478          685 :   if (!right)
    3479            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3480              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3481              : 
    3482              :   // TODO: check types. actually, do so during semantic analysis
    3483          685 :   location_t locus = left->get_locus ();
    3484              : 
    3485          685 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3486         1370 :     std::move (left), std::move (right.value ()), expr_type, locus);
    3487          685 : }
    3488              : 
    3489              : // Parses a binary add-assignment expression (with Pratt parsing).
    3490              : template <typename ManagedTokenSource>
    3491              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3492            0 : Parser<ManagedTokenSource>::parse_plus_assig_expr (
    3493              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3494              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3495              : {
    3496              :   // parse RHS (as tok has already been consumed in parse_expression)
    3497            0 :   auto right = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions);
    3498            0 :   if (!right)
    3499            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3500              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3501              : 
    3502              :   // TODO: check types. actually, do so during semantic analysis
    3503            0 :   location_t locus = left->get_locus ();
    3504              : 
    3505            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3506            0 :     std::move (left), std::move (right.value ()),
    3507            0 :     CompoundAssignmentOperator::ADD, locus);
    3508            0 : }
    3509              : 
    3510              : // Parses a binary minus-assignment expression (with Pratt parsing).
    3511              : template <typename ManagedTokenSource>
    3512              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3513            0 : Parser<ManagedTokenSource>::parse_minus_assig_expr (
    3514              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3515              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3516              : {
    3517              :   // parse RHS (as tok has already been consumed in parse_expression)
    3518            0 :   auto right = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions);
    3519            0 :   if (!right)
    3520            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3521              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3522              : 
    3523              :   // TODO: check types. actually, do so during semantic analysis
    3524            0 :   location_t locus = left->get_locus ();
    3525              : 
    3526            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3527            0 :     std::move (left), std::move (right.value ()),
    3528            0 :     CompoundAssignmentOperator::SUBTRACT, locus);
    3529            0 : }
    3530              : 
    3531              : // Parses a binary multiplication-assignment expression (with Pratt parsing).
    3532              : template <typename ManagedTokenSource>
    3533              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3534            0 : Parser<ManagedTokenSource>::parse_mult_assig_expr (
    3535              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3536              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3537              : {
    3538              :   // parse RHS (as tok has already been consumed in parse_expression)
    3539            0 :   auto right = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions);
    3540            0 :   if (!right)
    3541            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3542              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3543              : 
    3544              :   // TODO: check types. actually, do so during semantic analysis
    3545            0 :   location_t locus = left->get_locus ();
    3546              : 
    3547            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3548            0 :     std::move (left), std::move (right.value ()),
    3549            0 :     CompoundAssignmentOperator::MULTIPLY, locus);
    3550            0 : }
    3551              : 
    3552              : // Parses a binary division-assignment expression (with Pratt parsing).
    3553              : template <typename ManagedTokenSource>
    3554              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3555            0 : Parser<ManagedTokenSource>::parse_div_assig_expr (
    3556              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3557              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3558              : {
    3559              :   // parse RHS (as tok has already been consumed in parse_expression)
    3560            0 :   auto right = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions);
    3561            0 :   if (!right)
    3562            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3563              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3564              : 
    3565              :   // TODO: check types. actually, do so during semantic analysis
    3566            0 :   location_t locus = left->get_locus ();
    3567              : 
    3568            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3569            0 :     std::move (left), std::move (right.value ()),
    3570            0 :     CompoundAssignmentOperator::DIVIDE, locus);
    3571            0 : }
    3572              : 
    3573              : // Parses a binary modulo-assignment expression (with Pratt parsing).
    3574              : template <typename ManagedTokenSource>
    3575              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3576            0 : Parser<ManagedTokenSource>::parse_mod_assig_expr (
    3577              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3578              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3579              : {
    3580              :   // parse RHS (as tok has already been consumed in parse_expression)
    3581            0 :   auto right = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions);
    3582            0 :   if (!right)
    3583            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3584              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3585              : 
    3586              :   // TODO: check types. actually, do so during semantic analysis
    3587            0 :   location_t locus = left->get_locus ();
    3588              : 
    3589            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3590            0 :     std::move (left), std::move (right.value ()),
    3591            0 :     CompoundAssignmentOperator::MODULUS, locus);
    3592            0 : }
    3593              : 
    3594              : // Parses a binary and-assignment expression (with Pratt parsing).
    3595              : template <typename ManagedTokenSource>
    3596              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3597            0 : Parser<ManagedTokenSource>::parse_and_assig_expr (
    3598              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3599              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3600              : {
    3601              :   // parse RHS (as tok has already been consumed in parse_expression)
    3602            0 :   auto right = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions);
    3603            0 :   if (!right)
    3604            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3605              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3606              : 
    3607              :   // TODO: check types. actually, do so during semantic analysis
    3608            0 :   location_t locus = left->get_locus ();
    3609              : 
    3610            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3611            0 :     std::move (left), std::move (right.value ()),
    3612            0 :     CompoundAssignmentOperator::BITWISE_AND, locus);
    3613            0 : }
    3614              : 
    3615              : // Parses a binary or-assignment expression (with Pratt parsing).
    3616              : template <typename ManagedTokenSource>
    3617              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3618            0 : Parser<ManagedTokenSource>::parse_or_assig_expr (
    3619              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3620              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3621              : {
    3622              :   // parse RHS (as tok has already been consumed in parse_expression)
    3623            0 :   auto right = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions);
    3624            0 :   if (!right)
    3625            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3626              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3627              : 
    3628              :   // TODO: check types. actually, do so during semantic analysis
    3629            0 :   location_t locus = left->get_locus ();
    3630              : 
    3631            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3632            0 :     std::move (left), std::move (right.value ()),
    3633            0 :     CompoundAssignmentOperator::BITWISE_OR, locus);
    3634            0 : }
    3635              : 
    3636              : // Parses a binary xor-assignment expression (with Pratt parsing).
    3637              : template <typename ManagedTokenSource>
    3638              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3639            0 : Parser<ManagedTokenSource>::parse_xor_assig_expr (
    3640              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3641              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3642              : {
    3643              :   // parse RHS (as tok has already been consumed in parse_expression)
    3644            0 :   auto right = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions);
    3645            0 :   if (!right)
    3646            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3647              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3648              : 
    3649              :   // TODO: check types. actually, do so during semantic analysis
    3650            0 :   location_t locus = left->get_locus ();
    3651              : 
    3652            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3653            0 :     std::move (left), std::move (right.value ()),
    3654            0 :     CompoundAssignmentOperator::BITWISE_XOR, locus);
    3655            0 : }
    3656              : 
    3657              : // Parses a binary left shift-assignment expression (with Pratt parsing).
    3658              : template <typename ManagedTokenSource>
    3659              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3660            0 : Parser<ManagedTokenSource>::parse_left_shift_assig_expr (
    3661              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3662              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3663              : {
    3664              :   // parse RHS (as tok has already been consumed in parse_expression)
    3665            0 :   auto right
    3666            0 :     = parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
    3667            0 :   if (!right)
    3668            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3669              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3670              : 
    3671              :   // TODO: check types. actually, do so during semantic analysis
    3672            0 :   location_t locus = left->get_locus ();
    3673              : 
    3674            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3675            0 :     std::move (left), std::move (right.value ()),
    3676            0 :     CompoundAssignmentOperator::LEFT_SHIFT, locus);
    3677            0 : }
    3678              : 
    3679              : // Parses a binary right shift-assignment expression (with Pratt parsing).
    3680              : template <typename ManagedTokenSource>
    3681              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3682            0 : Parser<ManagedTokenSource>::parse_right_shift_assig_expr (
    3683              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3684              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3685              : {
    3686              :   // parse RHS (as tok has already been consumed in parse_expression)
    3687            0 :   auto right
    3688            0 :     = parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
    3689            0 :   if (!right)
    3690            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3691              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3692              : 
    3693              :   // TODO: check types. actually, do so during semantic analysis
    3694            0 :   location_t locus = left->get_locus ();
    3695              : 
    3696            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3697            0 :     std::move (left), std::move (right.value ()),
    3698            0 :     CompoundAssignmentOperator::RIGHT_SHIFT, locus);
    3699            0 : }
    3700              : 
    3701              : // Parses a postfix unary await expression (with Pratt parsing).
    3702              : template <typename ManagedTokenSource>
    3703              : tl::expected<std::unique_ptr<AST::AwaitExpr>, Parse::Error::Expr>
    3704            0 : Parser<ManagedTokenSource>::parse_await_expr (
    3705              :   const_TokenPtr tok, std::unique_ptr<AST::Expr> expr_to_await,
    3706              :   AST::AttrVec outer_attrs)
    3707              : {
    3708              :   /* skip "await" identifier (as "." has already been consumed in
    3709              :    * parse_expression) this assumes that the identifier was already identified
    3710              :    * as await */
    3711            0 :   if (!skip_token (IDENTIFIER))
    3712              :     {
    3713            0 :       Error error (tok->get_locus (), "failed to skip %<await%> in await expr "
    3714              :                                       "- this is probably a deep issue");
    3715            0 :       add_error (std::move (error));
    3716              : 
    3717              :       // skip somewhere?
    3718            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3719            0 :     }
    3720              : 
    3721              :   // TODO: check inside async block in semantic analysis
    3722            0 :   location_t locus = expr_to_await->get_locus ();
    3723              : 
    3724            0 :   return std::unique_ptr<AST::AwaitExpr> (
    3725            0 :     new AST::AwaitExpr (std::move (expr_to_await), std::move (outer_attrs),
    3726            0 :                         locus));
    3727              : }
    3728              : 
    3729              : /* Parses an exclusive range ('..') in left denotation position (i.e.
    3730              :  * RangeFromExpr or RangeFromToExpr). */
    3731              : template <typename ManagedTokenSource>
    3732              : tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
    3733           78 : Parser<ManagedTokenSource>::parse_led_range_exclusive_expr (
    3734              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3735              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3736              : {
    3737              :   // FIXME: this probably parses expressions accidently or whatever
    3738              :   // try parsing RHS (as tok has already been consumed in parse_expression)
    3739              :   // Can be nullptr, in which case it is a RangeFromExpr, otherwise a
    3740              :   // RangeFromToExpr.
    3741           78 :   restrictions.expr_can_be_null = true;
    3742           78 :   auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
    3743              : 
    3744           78 :   location_t locus = left->get_locus ();
    3745              : 
    3746           78 :   if (!right)
    3747              :     {
    3748              :       // range from expr
    3749            9 :       return std::make_unique<AST::RangeFromExpr> (std::move (left), locus);
    3750              :     }
    3751              :   else
    3752              :     {
    3753           69 :       return std::make_unique<AST::RangeFromToExpr> (std::move (left),
    3754           69 :                                                      std::move (right.value ()),
    3755           69 :                                                      locus);
    3756              :     }
    3757              :   // FIXME: make non-associative
    3758           78 : }
    3759              : 
    3760              : /* Parses an exclusive range ('..') in null denotation position (i.e.
    3761              :  * RangeToExpr or RangeFullExpr). */
    3762              : template <typename ManagedTokenSource>
    3763              : tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
    3764            9 : Parser<ManagedTokenSource>::parse_nud_range_exclusive_expr (
    3765              :   const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
    3766              : {
    3767            9 :   auto restrictions = ParseRestrictions ();
    3768            9 :   restrictions.expr_can_be_null = true;
    3769              : 
    3770              :   // FIXME: this probably parses expressions accidently or whatever
    3771              :   // try parsing RHS (as tok has already been consumed in parse_expression)
    3772            9 :   auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
    3773              : 
    3774            9 :   location_t locus = tok->get_locus ();
    3775              : 
    3776            9 :   if (!right)
    3777              :     {
    3778              :       // range from expr
    3779            1 :       return std::make_unique<AST::RangeFullExpr> (locus);
    3780              :     }
    3781              :   else
    3782              :     {
    3783            8 :       return std::make_unique<AST::RangeToExpr> (std::move (right.value ()),
    3784            8 :                                                  locus);
    3785              :     }
    3786              :   // FIXME: make non-associative
    3787            9 : }
    3788              : 
    3789              : // Parses a full binary range inclusive expression.
    3790              : template <typename ManagedTokenSource>
    3791              : tl::expected<std::unique_ptr<AST::RangeFromToInclExpr>, Parse::Error::Expr>
    3792            7 : Parser<ManagedTokenSource>::parse_range_inclusive_expr (
    3793              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3794              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3795              : {
    3796              :   // parse RHS (as tok has already been consumed in parse_expression)
    3797            7 :   auto right = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions);
    3798            7 :   if (!right)
    3799            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3800              :   // FIXME: make non-associative
    3801              : 
    3802              :   // TODO: check types. actually, do so during semantic analysis
    3803            7 :   location_t locus = left->get_locus ();
    3804              : 
    3805            7 :   return std::make_unique<AST::RangeFromToInclExpr> (std::move (left),
    3806            7 :                                                      std::move (right.value ()),
    3807            7 :                                                      locus);
    3808            7 : }
    3809              : 
    3810              : // Parses an inclusive range-to prefix unary expression.
    3811              : template <typename ManagedTokenSource>
    3812              : tl::expected<std::unique_ptr<AST::RangeToInclExpr>, Parse::Error::Expr>
    3813            0 : Parser<ManagedTokenSource>::parse_range_to_inclusive_expr (
    3814              :   const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
    3815              : {
    3816              :   // parse RHS (as tok has already been consumed in parse_expression)
    3817            0 :   auto right = parse_expr (LBP_DOT_DOT_EQ);
    3818            0 :   if (!right)
    3819            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3820              :   // FIXME: make non-associative
    3821              : 
    3822              :   // TODO: check types. actually, do so during semantic analysis
    3823              : 
    3824            0 :   return std::make_unique<AST::RangeToInclExpr> (std::move (right.value ()),
    3825            0 :                                                  tok->get_locus ());
    3826            0 : }
    3827              : 
    3828              : // Parses a pseudo-binary infix tuple index expression.
    3829              : template <typename ManagedTokenSource>
    3830              : tl::expected<std::unique_ptr<AST::TupleIndexExpr>, Parse::Error::Expr>
    3831          905 : Parser<ManagedTokenSource>::parse_tuple_index_expr (
    3832              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> tuple_expr,
    3833              :   AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
    3834              : {
    3835              :   // parse int literal (as token already skipped)
    3836          905 :   const_TokenPtr index_tok = expect_token (INT_LITERAL);
    3837          905 :   if (index_tok == nullptr)
    3838            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3839              : 
    3840         1810 :   std::string index = index_tok->get_str ();
    3841              : 
    3842              :   // convert to integer
    3843          905 :   if (!index_tok->is_pure_decimal ())
    3844              :     {
    3845           27 :       Error error (index_tok->get_locus (),
    3846              :                    "tuple index should be a pure decimal literal");
    3847           27 :       add_error (std::move (error));
    3848           27 :     }
    3849          905 :   int index_int = atoi (index.c_str ());
    3850              : 
    3851          905 :   location_t locus = tuple_expr->get_locus ();
    3852              : 
    3853          905 :   return std::make_unique<AST::TupleIndexExpr> (std::move (tuple_expr),
    3854              :                                                 index_int,
    3855          905 :                                                 std::move (outer_attrs), locus);
    3856          905 : }
    3857              : 
    3858              : // Parses a pseudo-binary infix array (or slice) index expression.
    3859              : template <typename ManagedTokenSource>
    3860              : tl::expected<std::unique_ptr<AST::ArrayIndexExpr>, Parse::Error::Expr>
    3861          303 : Parser<ManagedTokenSource>::parse_index_expr (
    3862              :   const_TokenPtr, std::unique_ptr<AST::Expr> array_expr,
    3863              :   AST::AttrVec outer_attrs, ParseRestrictions)
    3864              : {
    3865              :   // parse RHS (as tok has already been consumed in parse_expression)
    3866              :   /*std::unique_ptr<AST::Expr> index_expr
    3867              :     = parse_expr (LBP_ARRAY_REF, AST::AttrVec (),
    3868              :     restrictions);*/
    3869              :   // TODO: conceptually, should treat [] as brackets, so just parse all expr
    3870          303 :   auto index_expr = parse_expr ();
    3871          303 :   if (!index_expr)
    3872            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3873              : 
    3874              :   // skip ']' at end of array
    3875          303 :   if (!skip_token (RIGHT_SQUARE))
    3876              :     {
    3877              :       // skip somewhere?
    3878            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3879              :     }
    3880              : 
    3881              :   // TODO: check types. actually, do so during semantic analysis
    3882          303 :   location_t locus = array_expr->get_locus ();
    3883              : 
    3884          303 :   return std::make_unique<AST::ArrayIndexExpr> (std::move (array_expr),
    3885          303 :                                                 std::move (index_expr.value ()),
    3886          303 :                                                 std::move (outer_attrs), locus);
    3887          303 : }
    3888              : 
    3889              : // Parses a pseudo-binary infix struct field access expression.
    3890              : template <typename ManagedTokenSource>
    3891              : tl::expected<std::unique_ptr<AST::FieldAccessExpr>, Parse::Error::Expr>
    3892         4915 : Parser<ManagedTokenSource>::parse_field_access_expr (
    3893              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> struct_expr,
    3894              :   AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
    3895              : {
    3896              :   /* get field name identifier (assume that this is a field access expr and
    3897              :    * not await, for instance) */
    3898         4915 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    3899         4915 :   if (ident_tok == nullptr)
    3900            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3901              : 
    3902         9830 :   Identifier ident{ident_tok};
    3903              : 
    3904         4915 :   location_t locus = struct_expr->get_locus ();
    3905              : 
    3906              :   // TODO: check types. actually, do so during semantic analysis
    3907         4915 :   return std::make_unique<AST::FieldAccessExpr> (std::move (struct_expr),
    3908              :                                                  std::move (ident),
    3909              :                                                  std::move (outer_attrs),
    3910         4915 :                                                  locus);
    3911         4915 : }
    3912              : 
    3913              : // Parses a pseudo-binary infix method call expression.
    3914              : template <typename ManagedTokenSource>
    3915              : tl::expected<std::unique_ptr<AST::MethodCallExpr>, Parse::Error::Expr>
    3916         3020 : Parser<ManagedTokenSource>::parse_method_call_expr (
    3917              :   const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
    3918              :   AST::AttrVec outer_attrs, ParseRestrictions)
    3919              : {
    3920              :   // parse path expr segment
    3921         3020 :   AST::PathExprSegment segment = parse_path_expr_segment ();
    3922         3020 :   if (segment.is_error ())
    3923              :     {
    3924            0 :       Error error (tok->get_locus (),
    3925              :                    "failed to parse path expr segment of method call expr");
    3926            0 :       add_error (std::move (error));
    3927              : 
    3928              :       return tl::unexpected<Parse::Error::Expr> (
    3929            0 :         Parse::Error::Expr::CHILD_ERROR);
    3930            0 :     }
    3931              : 
    3932              :   // skip left parentheses
    3933         3020 :   if (!skip_token (LEFT_PAREN))
    3934              :     {
    3935            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3936              :     }
    3937              : 
    3938              :   // parse method params (if they exist)
    3939         3020 :   std::vector<std::unique_ptr<AST::Expr>> params;
    3940              : 
    3941         3020 :   const_TokenPtr t = lexer.peek_token ();
    3942         5058 :   while (t->get_id () != RIGHT_PAREN)
    3943              :     {
    3944         2038 :       auto param = parse_expr ();
    3945         2038 :       if (!param)
    3946              :         {
    3947            0 :           Error error (t->get_locus (),
    3948              :                        "failed to parse method param in method call");
    3949            0 :           add_error (std::move (error));
    3950              : 
    3951              :           return tl::unexpected<Parse::Error::Expr> (
    3952            0 :             Parse::Error::Expr::CHILD_ERROR);
    3953            0 :         }
    3954         2038 :       params.push_back (std::move (param.value ()));
    3955              : 
    3956         4076 :       if (lexer.peek_token ()->get_id () != COMMA)
    3957              :         break;
    3958              : 
    3959            0 :       lexer.skip_token ();
    3960            0 :       t = lexer.peek_token ();
    3961              :     }
    3962              : 
    3963              :   // skip right paren
    3964         3020 :   if (!skip_token (RIGHT_PAREN))
    3965              :     {
    3966            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3967              :     }
    3968              : 
    3969              :   // TODO: check types. actually do so in semantic analysis pass.
    3970         3020 :   location_t locus = receiver_expr->get_locus ();
    3971              : 
    3972         3020 :   return std::make_unique<AST::MethodCallExpr> (std::move (receiver_expr),
    3973              :                                                 std::move (segment),
    3974              :                                                 std::move (params),
    3975         3020 :                                                 std::move (outer_attrs), locus);
    3976         3020 : }
    3977              : 
    3978              : // Parses a pseudo-binary infix function call expression.
    3979              : template <typename ManagedTokenSource>
    3980              : tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
    3981          978 : Parser<ManagedTokenSource>::parse_function_call_expr (
    3982              :   const_TokenPtr, std::unique_ptr<AST::Expr> function_expr,
    3983              :   AST::AttrVec outer_attrs, ParseRestrictions)
    3984              : {
    3985              :   // parse function params (if they exist)
    3986          978 :   std::vector<std::unique_ptr<AST::Expr>> params;
    3987              : 
    3988          978 :   const_TokenPtr t = lexer.peek_token ();
    3989         1854 :   while (t->get_id () != RIGHT_PAREN)
    3990              :     {
    3991          876 :       auto param = parse_expr ();
    3992          876 :       if (!param)
    3993              :         {
    3994            0 :           Error error (t->get_locus (),
    3995              :                        "failed to parse function param in function call");
    3996            0 :           add_error (std::move (error));
    3997              : 
    3998              :           return tl::unexpected<Parse::Error::Expr> (
    3999            0 :             Parse::Error::Expr::CHILD_ERROR);
    4000            0 :         }
    4001          876 :       params.push_back (std::move (param.value ()));
    4002              : 
    4003         1752 :       if (lexer.peek_token ()->get_id () != COMMA)
    4004              :         break;
    4005              : 
    4006           24 :       lexer.skip_token ();
    4007           24 :       t = lexer.peek_token ();
    4008              :     }
    4009              : 
    4010              :   // skip ')' at end of param list
    4011          978 :   if (!skip_token (RIGHT_PAREN))
    4012              :     {
    4013              :       // skip somewhere?
    4014            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4015              :     }
    4016              : 
    4017              :   // TODO: check types. actually, do so during semantic analysis
    4018          978 :   location_t locus = function_expr->get_locus ();
    4019              : 
    4020          978 :   return std::make_unique<AST::CallExpr> (std::move (function_expr),
    4021              :                                           std::move (params),
    4022          978 :                                           std::move (outer_attrs), locus);
    4023          978 : }
    4024              : 
    4025              : /* Parses a struct expr struct with a path in expression already parsed (but
    4026              :  * not
    4027              :  * '{' token). */
    4028              : template <typename ManagedTokenSource>
    4029              : tl::expected<std::unique_ptr<AST::StructExprStruct>, Parse::Error::Expr>
    4030         1379 : Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
    4031              :   AST::PathInExpression path, AST::AttrVec outer_attrs)
    4032              : {
    4033              :   // assume struct expr struct (as struct-enum disambiguation requires name
    4034              :   // lookup) again, make statement if final ';'
    4035         1379 :   if (!skip_token (LEFT_CURLY))
    4036              :     {
    4037            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4038              :     }
    4039              : 
    4040              :   // parse inner attributes
    4041         1379 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    4042              : 
    4043              :   // branch based on next token
    4044         1379 :   const_TokenPtr t = lexer.peek_token ();
    4045         1379 :   location_t path_locus = path.get_locus ();
    4046         1379 :   switch (t->get_id ())
    4047              :     {
    4048           78 :     case RIGHT_CURLY:
    4049              :       // struct with no body
    4050           78 :       lexer.skip_token ();
    4051              : 
    4052           78 :       return std::make_unique<AST::StructExprStruct> (std::move (path),
    4053              :                                                       std::move (inner_attrs),
    4054              :                                                       std::move (outer_attrs),
    4055           78 :                                                       path_locus);
    4056         1301 :     case DOT_DOT:
    4057              :       /* technically this would give a struct base-only struct, but this
    4058              :        * algorithm should work too. As such, AST type not happening. */
    4059              :     case IDENTIFIER:
    4060              :     case HASH:
    4061              :     case INT_LITERAL:
    4062              :       {
    4063              :         // struct with struct expr fields
    4064              : 
    4065              :         // parse struct expr fields
    4066         1301 :         std::vector<std::unique_ptr<AST::StructExprField>> fields;
    4067              : 
    4068         3550 :         while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT)
    4069              :           {
    4070         2249 :             auto field = parse_struct_expr_field ();
    4071         2249 :             if (!field
    4072         2249 :                 && field.error () != Parse::Error::StructExprField::STRUCT_BASE)
    4073              :               {
    4074            0 :                 Error error (t->get_locus (),
    4075              :                              "failed to parse struct (or enum) expr field");
    4076            0 :                 add_error (std::move (error));
    4077              : 
    4078              :                 return tl::unexpected<Parse::Error::Expr> (
    4079            0 :                   Parse::Error::Expr::CHILD_ERROR);
    4080            0 :               }
    4081              : 
    4082              :             // DEBUG:
    4083         2249 :             rust_debug ("struct/enum expr field validated to not be null");
    4084              : 
    4085         2249 :             fields.push_back (std::move (field.value ()));
    4086              : 
    4087              :             // DEBUG:
    4088         2249 :             rust_debug ("struct/enum expr field pushed back");
    4089              : 
    4090         4498 :             if (lexer.peek_token ()->get_id () != COMMA)
    4091              :               {
    4092              :                 // DEBUG:
    4093         1062 :                 rust_debug ("lack of comma detected in struct/enum expr "
    4094              :                             "fields - break");
    4095              :                 break;
    4096              :               }
    4097         1187 :             lexer.skip_token ();
    4098              : 
    4099              :             // DEBUG:
    4100         1187 :             rust_debug ("struct/enum expr fields comma skipped ");
    4101              : 
    4102         1187 :             t = lexer.peek_token ();
    4103              :           }
    4104              : 
    4105              :         // DEBUG:
    4106         1301 :         rust_debug ("struct/enum expr about to parse struct base ");
    4107              : 
    4108              :         // parse struct base if it exists
    4109              :         AST::StructBase struct_base = AST::StructBase::error ();
    4110         2602 :         if (lexer.peek_token ()->get_id () == DOT_DOT)
    4111              :           {
    4112           63 :             location_t dot_dot_location = lexer.peek_token ()->get_locus ();
    4113           63 :             lexer.skip_token ();
    4114              : 
    4115              :             // parse required struct base expr
    4116           63 :             auto base_expr = parse_expr ();
    4117           63 :             if (!base_expr)
    4118              :               {
    4119            0 :                 Error error (lexer.peek_token ()->get_locus (),
    4120              :                              "failed to parse struct base expression in struct "
    4121              :                              "expression");
    4122            0 :                 add_error (std::move (error));
    4123              : 
    4124              :                 return tl::unexpected<Parse::Error::Expr> (
    4125            0 :                   Parse::Error::Expr::CHILD_ERROR);
    4126            0 :               }
    4127              : 
    4128              :             // DEBUG:
    4129           63 :             rust_debug ("struct/enum expr - parsed and validated base expr");
    4130              : 
    4131          126 :             struct_base = AST::StructBase (std::move (base_expr.value ()),
    4132           63 :                                            dot_dot_location);
    4133              : 
    4134              :             // DEBUG:
    4135           63 :             rust_debug ("assigned struct base to new struct base ");
    4136           63 :           }
    4137              : 
    4138         1301 :         if (!skip_token (RIGHT_CURLY))
    4139              :           {
    4140              :             return tl::unexpected<Parse::Error::Expr> (
    4141            0 :               Parse::Error::Expr::MALFORMED);
    4142              :           }
    4143              : 
    4144              :         // DEBUG:
    4145         1301 :         rust_debug (
    4146              :           "struct/enum expr skipped right curly - done and ready to return");
    4147              : 
    4148         1301 :         return std::make_unique<AST::StructExprStructFields> (
    4149              :           std::move (path), std::move (fields), path_locus,
    4150              :           std::move (struct_base), std::move (inner_attrs),
    4151         1301 :           std::move (outer_attrs));
    4152         1301 :       }
    4153            0 :     default:
    4154            0 :       add_error (
    4155            0 :         Error (t->get_locus (),
    4156              :                "unrecognised token %qs in struct (or enum) expression - "
    4157              :                "expected %<}%>, identifier, integer literal, or %<..%>",
    4158              :                t->get_token_description ()));
    4159              : 
    4160            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4161              :     }
    4162         1379 : }
    4163              : 
    4164              : /* Parses a struct expr tuple with a path in expression already parsed (but
    4165              :  * not
    4166              :  * '(' token).
    4167              :  * FIXME: this currently outputs a call expr, as they cannot be disambiguated.
    4168              :  * A better solution would be to just get this to call that function directly.
    4169              :  * */
    4170              : template <typename ManagedTokenSource>
    4171              : tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
    4172        11287 : Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
    4173              :   AST::PathInExpression path, AST::AttrVec outer_attrs)
    4174              : {
    4175        11287 :   if (!skip_token (LEFT_PAREN))
    4176              :     {
    4177            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4178              :     }
    4179              : 
    4180        11287 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    4181              : 
    4182        11287 :   std::vector<std::unique_ptr<AST::Expr>> exprs;
    4183              : 
    4184        11287 :   const_TokenPtr t = lexer.peek_token ();
    4185        23770 :   while (t->get_id () != RIGHT_PAREN)
    4186              :     {
    4187              :       // parse expression (required)
    4188        12483 :       auto expr = parse_expr ();
    4189        12483 :       if (!expr)
    4190              :         {
    4191            0 :           Error error (t->get_locus (), "failed to parse expression in "
    4192              :                                         "struct (or enum) expression tuple");
    4193            0 :           add_error (std::move (error));
    4194              : 
    4195              :           return tl::unexpected<Parse::Error::Expr> (
    4196            0 :             Parse::Error::Expr::CHILD_ERROR);
    4197            0 :         }
    4198        12483 :       exprs.push_back (std::move (expr.value ()));
    4199              : 
    4200        24966 :       if (lexer.peek_token ()->get_id () != COMMA)
    4201              :         break;
    4202              : 
    4203         4005 :       lexer.skip_token ();
    4204              : 
    4205         4005 :       t = lexer.peek_token ();
    4206              :     }
    4207              : 
    4208        11287 :   if (!skip_token (RIGHT_PAREN))
    4209              :     {
    4210            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4211              :     }
    4212              : 
    4213        11287 :   location_t path_locus = path.get_locus ();
    4214              : 
    4215        11287 :   auto pathExpr = std::make_unique<AST::PathInExpression> (std::move (path));
    4216              : 
    4217        11287 :   return std::make_unique<AST::CallExpr> (std::move (pathExpr),
    4218              :                                           std::move (exprs),
    4219        11287 :                                           std::move (outer_attrs), path_locus);
    4220        22574 : }
    4221              : 
    4222              : // Parses a closure expression with pratt parsing (from null denotation).
    4223              : template <typename ManagedTokenSource>
    4224              : tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Expr>
    4225           73 : Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
    4226              :                                                       AST::AttrVec outer_attrs)
    4227              : {
    4228              :   // TODO: does this need pratt parsing (for precedence)? probably not, but
    4229              :   // idk
    4230           73 :   location_t locus = tok->get_locus ();
    4231           73 :   bool has_move = false;
    4232           73 :   if (tok->get_id () == MOVE)
    4233              :     {
    4234            1 :       has_move = true;
    4235            1 :       tok = lexer.peek_token ();
    4236            1 :       lexer.skip_token ();
    4237              :       // skip token and reassign
    4238              :     }
    4239              : 
    4240              :   // handle parameter list
    4241           73 :   std::vector<AST::ClosureParam> params;
    4242              : 
    4243           73 :   switch (tok->get_id ())
    4244              :     {
    4245              :     case OR:
    4246              :       // no parameters, don't skip token
    4247              :       break;
    4248           61 :     case PIPE:
    4249              :       {
    4250              :         // actually may have parameters
    4251              :         // don't skip token
    4252           61 :         const_TokenPtr t = lexer.peek_token ();
    4253          133 :         while (t->get_id () != PIPE)
    4254              :           {
    4255           72 :             AST::ClosureParam param = parse_closure_param ();
    4256           72 :             if (param.is_error ())
    4257              :               {
    4258              :                 // TODO is this really an error?
    4259            0 :                 Error error (t->get_locus (), "could not parse closure param");
    4260            0 :                 add_error (std::move (error));
    4261              : 
    4262              :                 return tl::unexpected<Parse::Error::Expr> (
    4263            0 :                   Parse::Error::Expr::CHILD_ERROR);
    4264            0 :               }
    4265           72 :             params.push_back (std::move (param));
    4266              : 
    4267          144 :             if (lexer.peek_token ()->get_id () != COMMA)
    4268              :               {
    4269          122 :                 if (lexer.peek_token ()->get_id () == OR)
    4270            1 :                   lexer.split_current_token (PIPE, PIPE);
    4271              :                 // not an error but means param list is done
    4272              :                 break;
    4273              :               }
    4274              :             // skip comma
    4275           11 :             lexer.skip_token ();
    4276              : 
    4277           22 :             if (lexer.peek_token ()->get_id () == OR)
    4278            0 :               lexer.split_current_token (PIPE, PIPE);
    4279              : 
    4280           11 :             t = lexer.peek_token ();
    4281              :           }
    4282              : 
    4283           61 :         if (!skip_token (PIPE))
    4284              :           {
    4285              :             return tl::unexpected<Parse::Error::Expr> (
    4286            0 :               Parse::Error::Expr::MALFORMED);
    4287              :           }
    4288              :         break;
    4289           61 :       }
    4290            0 :     default:
    4291            0 :       add_error (Error (tok->get_locus (),
    4292              :                         "unexpected token %qs in closure expression - expected "
    4293              :                         "%<|%> or %<||%>",
    4294              :                         tok->get_token_description ()));
    4295              : 
    4296              :       // skip somewhere?
    4297            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4298              :     }
    4299              : 
    4300              :   // again branch based on next token
    4301           73 :   tok = lexer.peek_token ();
    4302           73 :   if (tok->get_id () == RETURN_TYPE)
    4303              :     {
    4304              :       // must be return type closure with block expr
    4305              : 
    4306              :       // skip "return type" token
    4307           31 :       lexer.skip_token ();
    4308              : 
    4309              :       // parse actual type, which is required
    4310           31 :       auto type = parse_type_no_bounds ();
    4311           31 :       if (!type)
    4312              :         {
    4313              :           // error
    4314            0 :           Error error (tok->get_locus (), "failed to parse type for closure");
    4315            0 :           add_error (std::move (error));
    4316              : 
    4317              :           // skip somewhere?
    4318              :           return tl::unexpected<Parse::Error::Expr> (
    4319            0 :             Parse::Error::Expr::CHILD_ERROR);
    4320            0 :         }
    4321              : 
    4322              :       // parse block expr, which is required
    4323           31 :       auto block = parse_block_expr ();
    4324           31 :       if (!block)
    4325              :         {
    4326              :           // error
    4327            0 :           Error error (lexer.peek_token ()->get_locus (),
    4328              :                        "failed to parse block expr in closure");
    4329            0 :           add_error (std::move (error));
    4330              : 
    4331              :           // skip somewhere?
    4332              :           return tl::unexpected<Parse::Error::Expr> (
    4333            0 :             Parse::Error::Expr::CHILD_ERROR);
    4334            0 :         }
    4335              : 
    4336           31 :       return std::make_unique<AST::ClosureExprInnerTyped> (
    4337           31 :         std::move (type), std::move (block.value ()), std::move (params), locus,
    4338           31 :         has_move, std::move (outer_attrs));
    4339           31 :     }
    4340              :   else
    4341              :     {
    4342              :       // must be expr-only closure
    4343              : 
    4344              :       // parse expr, which is required
    4345           42 :       auto expr = parse_expr ();
    4346           42 :       if (!expr)
    4347              :         {
    4348            0 :           Error error (tok->get_locus (),
    4349              :                        "failed to parse expression in closure");
    4350            0 :           add_error (std::move (error));
    4351              : 
    4352              :           // skip somewhere?
    4353              :           return tl::unexpected<Parse::Error::Expr> (
    4354            0 :             Parse::Error::Expr::CHILD_ERROR);
    4355            0 :         }
    4356              : 
    4357           42 :       return std::make_unique<AST::ClosureExprInner> (std::move (expr.value ()),
    4358              :                                                       std::move (params), locus,
    4359              :                                                       has_move,
    4360           42 :                                                       std::move (outer_attrs));
    4361           42 :     }
    4362           73 : }
    4363              : 
    4364              : } // namespace Rust
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.