prism 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/prism.c CHANGED
@@ -544,10 +544,7 @@ pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id
544
544
  * token.
545
545
  */
546
546
  static void
547
- pm_parser_err_heredoc_term(pm_parser_t *parser, pm_lex_mode_t *lex_mode) {
548
- const uint8_t *ident_start = lex_mode->as.heredoc.ident_start;
549
- size_t ident_length = lex_mode->as.heredoc.ident_length;
550
-
547
+ pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
551
548
  PM_PARSER_ERR_FORMAT(
552
549
  parser,
553
550
  ident_start,
@@ -964,7 +961,7 @@ pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals,
964
961
  if (local->name != PM_CONSTANT_ID_UNSET) {
965
962
  pm_constant_id_list_insert(list, (size_t) local->index, local->name);
966
963
 
967
- if (warn_unused && local->reads == 0) {
964
+ if (warn_unused && local->reads == 0 && ((parser->start_line >= 0) || (pm_newline_list_line(&parser->newline_list, local->location.start, parser->start_line) >= 0))) {
968
965
  pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name);
969
966
 
970
967
  if (constant->length >= 1 && *constant->start != '_') {
@@ -2110,14 +2107,6 @@ pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) {
2110
2107
  return node;
2111
2108
  }
2112
2109
 
2113
- /**
2114
- * Return the size of the given array node.
2115
- */
2116
- static inline size_t
2117
- pm_array_node_size(pm_array_node_t *node) {
2118
- return node->elements.size;
2119
- }
2120
-
2121
2110
  /**
2122
2111
  * Append an argument to an array node.
2123
2112
  */
@@ -8573,6 +8562,7 @@ context_terminator(pm_context_t context, pm_token_t *token) {
8573
8562
  case PM_CONTEXT_MAIN:
8574
8563
  case PM_CONTEXT_DEF_PARAMS:
8575
8564
  case PM_CONTEXT_DEFINED:
8565
+ case PM_CONTEXT_MULTI_TARGET:
8576
8566
  case PM_CONTEXT_TERNARY:
8577
8567
  case PM_CONTEXT_RESCUE_MODIFIER:
8578
8568
  return token->type == PM_TOKEN_EOF;
@@ -8777,6 +8767,7 @@ context_human(pm_context_t context) {
8777
8767
  case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
8778
8768
  case PM_CONTEXT_MAIN: return "top level context";
8779
8769
  case PM_CONTEXT_MODULE: return "module definition";
8770
+ case PM_CONTEXT_MULTI_TARGET: return "multiple targets";
8780
8771
  case PM_CONTEXT_PARENS: return "parentheses";
8781
8772
  case PM_CONTEXT_POSTEXE: return "'END' block";
8782
8773
  case PM_CONTEXT_PREDICATE: return "predicate";
@@ -9051,6 +9042,10 @@ lex_global_variable(pm_parser_t *parser) {
9051
9042
  return PM_TOKEN_GLOBAL_VARIABLE;
9052
9043
  }
9053
9044
 
9045
+ // True if multiple characters are allowed after the declaration of the
9046
+ // global variable. Not true when it starts with "$-".
9047
+ bool allow_multiple = true;
9048
+
9054
9049
  switch (*parser->current.end) {
9055
9050
  case '~': // $~: match-data
9056
9051
  case '*': // $*: argv
@@ -9109,6 +9104,7 @@ lex_global_variable(pm_parser_t *parser) {
9109
9104
 
9110
9105
  case '-':
9111
9106
  parser->current.end++;
9107
+ allow_multiple = false;
9112
9108
  /* fallthrough */
9113
9109
  default: {
9114
9110
  size_t width;
@@ -9116,7 +9112,7 @@ lex_global_variable(pm_parser_t *parser) {
9116
9112
  if ((width = char_is_identifier(parser, parser->current.end)) > 0) {
9117
9113
  do {
9118
9114
  parser->current.end += width;
9119
- } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
9115
+ } while (allow_multiple && parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
9120
9116
  } else if (pm_char_is_whitespace(peek(parser))) {
9121
9117
  // If we get here, then we have a $ followed by whitespace,
9122
9118
  // which is not allowed.
@@ -9881,6 +9877,10 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
9881
9877
  }
9882
9878
  case 'c': {
9883
9879
  parser->current.end++;
9880
+ if (flags & PM_ESCAPE_FLAG_CONTROL) {
9881
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9882
+ }
9883
+
9884
9884
  if (parser->current.end == parser->end) {
9885
9885
  pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9886
9886
  return;
@@ -9894,10 +9894,6 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
9894
9894
  return;
9895
9895
  }
9896
9896
  case '\\':
9897
- if (flags & PM_ESCAPE_FLAG_CONTROL) {
9898
- pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9899
- return;
9900
- }
9901
9897
  parser->current.end++;
9902
9898
 
9903
9899
  if (match(parser, 'u') || match(parser, 'U')) {
@@ -9931,6 +9927,10 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
9931
9927
  }
9932
9928
  case 'C': {
9933
9929
  parser->current.end++;
9930
+ if (flags & PM_ESCAPE_FLAG_CONTROL) {
9931
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9932
+ }
9933
+
9934
9934
  if (peek(parser) != '-') {
9935
9935
  size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9936
9936
  pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL);
@@ -9951,10 +9951,6 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
9951
9951
  return;
9952
9952
  }
9953
9953
  case '\\':
9954
- if (flags & PM_ESCAPE_FLAG_CONTROL) {
9955
- pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9956
- return;
9957
- }
9958
9954
  parser->current.end++;
9959
9955
 
9960
9956
  if (match(parser, 'u') || match(parser, 'U')) {
@@ -9989,6 +9985,10 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
9989
9985
  }
9990
9986
  case 'M': {
9991
9987
  parser->current.end++;
9988
+ if (flags & PM_ESCAPE_FLAG_META) {
9989
+ pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
9990
+ }
9991
+
9992
9992
  if (peek(parser) != '-') {
9993
9993
  size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9994
9994
  pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META);
@@ -10004,10 +10004,6 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
10004
10004
  uint8_t peeked = peek(parser);
10005
10005
  switch (peeked) {
10006
10006
  case '\\':
10007
- if (flags & PM_ESCAPE_FLAG_META) {
10008
- pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
10009
- return;
10010
- }
10011
10007
  parser->current.end++;
10012
10008
 
10013
10009
  if (match(parser, 'u') || match(parser, 'U')) {
@@ -10050,6 +10046,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
10050
10046
  default: {
10051
10047
  if (parser->current.end < parser->end) {
10052
10048
  escape_write_escape_encoded(parser, buffer);
10049
+ } else {
10050
+ pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER);
10053
10051
  }
10054
10052
  return;
10055
10053
  }
@@ -11153,12 +11151,14 @@ parser_lex(pm_parser_t *parser) {
11153
11151
  lex_mode_push(parser, (pm_lex_mode_t) {
11154
11152
  .mode = PM_LEX_HEREDOC,
11155
11153
  .as.heredoc = {
11156
- .ident_start = ident_start,
11157
- .ident_length = ident_length,
11154
+ .base = {
11155
+ .ident_start = ident_start,
11156
+ .ident_length = ident_length,
11157
+ .quote = quote,
11158
+ .indent = indent
11159
+ },
11158
11160
  .next_start = parser->current.end,
11159
- .quote = quote,
11160
- .indent = indent,
11161
- .common_whitespace = (size_t) -1,
11161
+ .common_whitespace = NULL,
11162
11162
  .line_continuation = false
11163
11163
  }
11164
11164
  });
@@ -11171,7 +11171,7 @@ parser_lex(pm_parser_t *parser) {
11171
11171
  // this is not a valid heredoc declaration. In this case we
11172
11172
  // will add an error, but we will still return a heredoc
11173
11173
  // start.
11174
- if (!ident_error) pm_parser_err_heredoc_term(parser, parser->lex_modes.current);
11174
+ if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length);
11175
11175
  body_start = parser->end;
11176
11176
  } else {
11177
11177
  // Otherwise, we want to indicate that the body of the
@@ -12514,6 +12514,7 @@ parser_lex(pm_parser_t *parser) {
12514
12514
  // Now let's grab the information about the identifier off of the
12515
12515
  // current lex mode.
12516
12516
  pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12517
+ pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base;
12517
12518
 
12518
12519
  bool line_continuation = lex_mode->as.heredoc.line_continuation;
12519
12520
  lex_mode->as.heredoc.line_continuation = false;
@@ -12523,15 +12524,16 @@ parser_lex(pm_parser_t *parser) {
12523
12524
  // terminator) but still continue parsing so that content after the
12524
12525
  // declaration of the heredoc can be parsed.
12525
12526
  if (parser->current.end >= parser->end) {
12526
- pm_parser_err_heredoc_term(parser, lex_mode);
12527
+ pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length);
12527
12528
  parser->next_start = lex_mode->as.heredoc.next_start;
12528
12529
  parser->heredoc_end = parser->current.end;
12529
12530
  lex_state_set(parser, PM_LEX_STATE_END);
12531
+ lex_mode_pop(parser);
12530
12532
  LEX(PM_TOKEN_HEREDOC_END);
12531
12533
  }
12532
12534
 
12533
- const uint8_t *ident_start = lex_mode->as.heredoc.ident_start;
12534
- size_t ident_length = lex_mode->as.heredoc.ident_length;
12535
+ const uint8_t *ident_start = heredoc_lex_mode->ident_start;
12536
+ size_t ident_length = heredoc_lex_mode->ident_length;
12535
12537
 
12536
12538
  // If we are immediately following a newline and we have hit the
12537
12539
  // terminator, then we need to return the ending of the heredoc.
@@ -12556,10 +12558,7 @@ parser_lex(pm_parser_t *parser) {
12556
12558
  const uint8_t *terminator_start = ident_end - ident_length;
12557
12559
  const uint8_t *cursor = start;
12558
12560
 
12559
- if (
12560
- lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_DASH ||
12561
- lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE
12562
- ) {
12561
+ if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12563
12562
  while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12564
12563
  cursor++;
12565
12564
  }
@@ -12582,17 +12581,19 @@ parser_lex(pm_parser_t *parser) {
12582
12581
  }
12583
12582
 
12584
12583
  lex_state_set(parser, PM_LEX_STATE_END);
12584
+ lex_mode_pop(parser);
12585
12585
  LEX(PM_TOKEN_HEREDOC_END);
12586
12586
  }
12587
12587
  }
12588
12588
 
12589
- size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.indent);
12589
+ size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent);
12590
12590
  if (
12591
- lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE &&
12592
- (lex_mode->as.heredoc.common_whitespace > whitespace) &&
12591
+ heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE &&
12592
+ lex_mode->as.heredoc.common_whitespace != NULL &&
12593
+ (*lex_mode->as.heredoc.common_whitespace > whitespace) &&
12593
12594
  peek_at(parser, start) != '\n'
12594
12595
  ) {
12595
- lex_mode->as.heredoc.common_whitespace = whitespace;
12596
+ *lex_mode->as.heredoc.common_whitespace = whitespace;
12596
12597
  }
12597
12598
  }
12598
12599
 
@@ -12601,7 +12602,7 @@ parser_lex(pm_parser_t *parser) {
12601
12602
  // strpbrk to find the first of these characters.
12602
12603
  uint8_t breakpoints[] = "\r\n\\#";
12603
12604
 
12604
- pm_heredoc_quote_t quote = lex_mode->as.heredoc.quote;
12605
+ pm_heredoc_quote_t quote = heredoc_lex_mode->quote;
12605
12606
  if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12606
12607
  breakpoints[3] = '\0';
12607
12608
  }
@@ -12664,8 +12665,7 @@ parser_lex(pm_parser_t *parser) {
12664
12665
  // leading whitespace if we have a - or ~ heredoc.
12665
12666
  const uint8_t *cursor = start;
12666
12667
 
12667
- if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_DASH ||
12668
- lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
12668
+ if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12669
12669
  while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12670
12670
  cursor++;
12671
12671
  }
@@ -12681,16 +12681,16 @@ parser_lex(pm_parser_t *parser) {
12681
12681
  }
12682
12682
  }
12683
12683
 
12684
- size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.indent);
12684
+ size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.indent);
12685
12685
 
12686
12686
  // If we have hit a newline that is followed by a valid
12687
12687
  // terminator, then we need to return the content of the
12688
12688
  // heredoc here as string content. Then, the next time a
12689
12689
  // token is lexed, it will match again and return the
12690
12690
  // end of the heredoc.
12691
- if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
12692
- if ((lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
12693
- lex_mode->as.heredoc.common_whitespace = whitespace;
12691
+ if (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) {
12692
+ if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
12693
+ *lex_mode->as.heredoc.common_whitespace = whitespace;
12694
12694
  }
12695
12695
 
12696
12696
  parser->current.end = breakpoint + 1;
@@ -12757,7 +12757,7 @@ parser_lex(pm_parser_t *parser) {
12757
12757
  // If we are in a tilde here, we should
12758
12758
  // break out of the loop and return the
12759
12759
  // string content.
12760
- if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
12760
+ if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12761
12761
  const uint8_t *end = parser->current.end;
12762
12762
  pm_newline_list_append(&parser->newline_list, end);
12763
12763
 
@@ -12983,7 +12983,7 @@ pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
12983
12983
  [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12984
12984
  [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12985
12985
  [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12986
- [PM_TOKEN_USTAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12986
+ [PM_TOKEN_USTAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_FACTOR),
12987
12987
 
12988
12988
  // -@
12989
12989
  [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
@@ -13165,13 +13165,11 @@ expect3(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_to
13165
13165
  * lex mode accordingly.
13166
13166
  */
13167
13167
  static void
13168
- expect1_heredoc_term(pm_parser_t *parser, pm_lex_mode_t *lex_mode) {
13168
+ expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
13169
13169
  if (match1(parser, PM_TOKEN_HEREDOC_END)) {
13170
- lex_mode_pop(parser);
13171
13170
  parser_lex(parser);
13172
13171
  } else {
13173
- pm_parser_err_heredoc_term(parser, lex_mode);
13174
- lex_mode_pop(parser);
13172
+ pm_parser_err_heredoc_term(parser, ident_start, ident_length);
13175
13173
  parser->previous.start = parser->previous.end;
13176
13174
  parser->previous.type = PM_TOKEN_MISSING;
13177
13175
  }
@@ -13797,6 +13795,13 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b
13797
13795
  pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name);
13798
13796
  pm_multi_target_node_targets_append(parser, result, splat);
13799
13797
  has_rest = true;
13798
+ } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
13799
+ context_push(parser, PM_CONTEXT_MULTI_TARGET);
13800
+ pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13801
+ target = parse_target(parser, target, true, false);
13802
+
13803
+ pm_multi_target_node_targets_append(parser, result, target);
13804
+ context_pop(parser);
13800
13805
  } else if (token_begins_expression_p(parser->current.type)) {
13801
13806
  pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13802
13807
  target = parse_target(parser, target, true, false);
@@ -14108,8 +14113,8 @@ static void
14108
14113
  parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint16_t depth) {
14109
14114
  pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
14110
14115
 
14111
- // First we need to check if the next token is one that could be the start of
14112
- // an argument. If it's not, then we can just return.
14116
+ // First we need to check if the next token is one that could be the start
14117
+ // of an argument. If it's not, then we can just return.
14113
14118
  if (
14114
14119
  match2(parser, terminator, PM_TOKEN_EOF) ||
14115
14120
  (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
@@ -14297,23 +14302,32 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
14297
14302
  // If parsing the argument failed, we need to stop parsing arguments.
14298
14303
  if (PM_NODE_TYPE_P(argument, PM_MISSING_NODE) || parser->recovering) break;
14299
14304
 
14300
- // If the terminator of these arguments is not EOF, then we have a specific
14301
- // token we're looking for. In that case we can accept a newline here
14302
- // because it is not functioning as a statement terminator.
14303
- if (terminator != PM_TOKEN_EOF) accept1(parser, PM_TOKEN_NEWLINE);
14305
+ // If the terminator of these arguments is not EOF, then we have a
14306
+ // specific token we're looking for. In that case we can accept a
14307
+ // newline here because it is not functioning as a statement terminator.
14308
+ bool accepted_newline = false;
14309
+ if (terminator != PM_TOKEN_EOF) {
14310
+ accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14311
+ }
14304
14312
 
14305
14313
  if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
14306
- // If we previously were on a comma and we just parsed a bare hash, then
14307
- // we want to continue parsing arguments. This is because the comma was
14308
- // grabbed up by the hash parser.
14314
+ // If we previously were on a comma and we just parsed a bare hash,
14315
+ // then we want to continue parsing arguments. This is because the
14316
+ // comma was grabbed up by the hash parser.
14317
+ } else if (accept1(parser, PM_TOKEN_COMMA)) {
14318
+ // If there was a comma, then we need to check if we also accepted a
14319
+ // newline. If we did, then this is a syntax error.
14320
+ if (accepted_newline) {
14321
+ pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14322
+ }
14309
14323
  } else {
14310
- // If there is no comma at the end of the argument list then we're done
14311
- // parsing arguments and can break out of this loop.
14312
- if (!accept1(parser, PM_TOKEN_COMMA)) break;
14324
+ // If there is no comma at the end of the argument list then we're
14325
+ // done parsing arguments and can break out of this loop.
14326
+ break;
14313
14327
  }
14314
14328
 
14315
- // If we hit the terminator, then that means we have a trailing comma so we
14316
- // can accept that output as well.
14329
+ // If we hit the terminator, then that means we have a trailing comma so
14330
+ // we can accept that output as well.
14317
14331
  if (match1(parser, terminator)) break;
14318
14332
  }
14319
14333
  }
@@ -14470,13 +14484,14 @@ parse_parameters(
14470
14484
  bool accepts_blocks_in_defaults,
14471
14485
  uint16_t depth
14472
14486
  ) {
14473
- pm_parameters_node_t *params = pm_parameters_node_create(parser);
14474
- bool looping = true;
14475
-
14476
14487
  pm_do_loop_stack_push(parser, false);
14488
+
14489
+ pm_parameters_node_t *params = pm_parameters_node_create(parser);
14477
14490
  pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
14478
14491
 
14479
- do {
14492
+ while (true) {
14493
+ bool parsing = true;
14494
+
14480
14495
  switch (parser->current.type) {
14481
14496
  case PM_TOKEN_PARENTHESIS_LEFT: {
14482
14497
  update_parameter_state(parser, &parser->current, &order);
@@ -14611,7 +14626,7 @@ parse_parameters(
14611
14626
  // then we can put a missing node in its place and stop parsing the
14612
14627
  // parameters entirely now.
14613
14628
  if (parser->recovering) {
14614
- looping = false;
14629
+ parsing = false;
14615
14630
  break;
14616
14631
  }
14617
14632
  } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
@@ -14669,7 +14684,7 @@ parse_parameters(
14669
14684
  context_pop(parser);
14670
14685
 
14671
14686
  if (uses_parentheses) {
14672
- looping = false;
14687
+ parsing = false;
14673
14688
  break;
14674
14689
  }
14675
14690
 
@@ -14713,7 +14728,7 @@ parse_parameters(
14713
14728
  // then we can put a missing node in its place and stop parsing the
14714
14729
  // parameters entirely now.
14715
14730
  if (parser->recovering) {
14716
- looping = false;
14731
+ parsing = false;
14717
14732
  break;
14718
14733
  }
14719
14734
  }
@@ -14815,14 +14830,31 @@ parse_parameters(
14815
14830
  }
14816
14831
  }
14817
14832
 
14818
- looping = false;
14833
+ parsing = false;
14819
14834
  break;
14820
14835
  }
14821
14836
 
14822
- if (looping && uses_parentheses) {
14823
- accept1(parser, PM_TOKEN_NEWLINE);
14837
+ // If we hit some kind of issue while parsing the parameter, this would
14838
+ // have been set to false. In that case, we need to break out of the
14839
+ // loop.
14840
+ if (!parsing) break;
14841
+
14842
+ bool accepted_newline = false;
14843
+ if (uses_parentheses) {
14844
+ accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14824
14845
  }
14825
- } while (looping && accept1(parser, PM_TOKEN_COMMA));
14846
+
14847
+ if (accept1(parser, PM_TOKEN_COMMA)) {
14848
+ // If there was a comma, but we also accepted a newline, then this
14849
+ // is a syntax error.
14850
+ if (accepted_newline) {
14851
+ pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14852
+ }
14853
+ } else {
14854
+ // If there was no comma, then we're done parsing parameters.
14855
+ break;
14856
+ }
14857
+ }
14826
14858
 
14827
14859
  pm_do_loop_stack_pop(parser);
14828
14860
 
@@ -15500,6 +15532,7 @@ parse_return(pm_parser_t *parser, pm_node_t *node) {
15500
15532
  case PM_CONTEXT_IF:
15501
15533
  case PM_CONTEXT_LOOP_PREDICATE:
15502
15534
  case PM_CONTEXT_MAIN:
15535
+ case PM_CONTEXT_MULTI_TARGET:
15503
15536
  case PM_CONTEXT_PARENS:
15504
15537
  case PM_CONTEXT_POSTEXE:
15505
15538
  case PM_CONTEXT_PREDICATE:
@@ -15628,6 +15661,7 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node) {
15628
15661
  case PM_CONTEXT_MODULE_ENSURE:
15629
15662
  case PM_CONTEXT_MODULE_RESCUE:
15630
15663
  case PM_CONTEXT_MODULE:
15664
+ case PM_CONTEXT_MULTI_TARGET:
15631
15665
  case PM_CONTEXT_PARENS:
15632
15666
  case PM_CONTEXT_PREDICATE:
15633
15667
  case PM_CONTEXT_RESCUE_MODIFIER:
@@ -17045,7 +17079,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node
17045
17079
  parse_pattern_hash_key(parser, &keys, first_node);
17046
17080
  pm_node_t *value;
17047
17081
 
17048
- if (match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
17082
+ if (match8(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) {
17049
17083
  // Otherwise, we will create an implicit local variable
17050
17084
  // target for the value.
17051
17085
  value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
@@ -17082,7 +17116,12 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node
17082
17116
  // If there are any other assocs, then we'll parse them now.
17083
17117
  while (accept1(parser, PM_TOKEN_COMMA)) {
17084
17118
  // Here we need to break to support trailing commas.
17085
- if (match6(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
17119
+ if (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) {
17120
+ // Trailing commas are not allowed to follow a rest pattern.
17121
+ if (rest != NULL) {
17122
+ pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17123
+ }
17124
+
17086
17125
  break;
17087
17126
  }
17088
17127
 
@@ -17575,9 +17614,10 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
17575
17614
  // Gather up all of the patterns into the list.
17576
17615
  while (accept1(parser, PM_TOKEN_COMMA)) {
17577
17616
  // Break early here in case we have a trailing comma.
17578
- if (match4(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_SEMICOLON)) {
17617
+ if (match6(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) {
17579
17618
  node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
17580
17619
  pm_node_list_append(&nodes, node);
17620
+ trailing_rest = true;
17581
17621
  break;
17582
17622
  }
17583
17623
 
@@ -17779,6 +17819,7 @@ parse_retry(pm_parser_t *parser, const pm_node_t *node) {
17779
17819
  case PM_CONTEXT_LAMBDA_BRACES:
17780
17820
  case PM_CONTEXT_LAMBDA_DO_END:
17781
17821
  case PM_CONTEXT_LOOP_PREDICATE:
17822
+ case PM_CONTEXT_MULTI_TARGET:
17782
17823
  case PM_CONTEXT_PARENS:
17783
17824
  case PM_CONTEXT_POSTEXE:
17784
17825
  case PM_CONTEXT_PREDICATE:
@@ -17862,6 +17903,7 @@ parse_yield(pm_parser_t *parser, const pm_node_t *node) {
17862
17903
  case PM_CONTEXT_LAMBDA_ENSURE:
17863
17904
  case PM_CONTEXT_LAMBDA_RESCUE:
17864
17905
  case PM_CONTEXT_LOOP_PREDICATE:
17906
+ case PM_CONTEXT_MULTI_TARGET:
17865
17907
  case PM_CONTEXT_PARENS:
17866
17908
  case PM_CONTEXT_POSTEXE:
17867
17909
  case PM_CONTEXT_PREDICATE:
@@ -17951,19 +17993,31 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
17951
17993
  bool parsed_bare_hash = false;
17952
17994
 
17953
17995
  while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
17996
+ bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
17997
+
17954
17998
  // Handle the case where we don't have a comma and we have a
17955
17999
  // newline followed by a right bracket.
17956
- if (accept1(parser, PM_TOKEN_NEWLINE) && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
18000
+ if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17957
18001
  break;
17958
18002
  }
17959
18003
 
17960
18004
  // Ensure that we have a comma between elements in the array.
17961
- if ((pm_array_node_size(array) != 0) && !accept1(parser, PM_TOKEN_COMMA)) {
17962
- const uint8_t *location = parser->previous.end;
17963
- PM_PARSER_ERR_FORMAT(parser, location, location, PM_ERR_ARRAY_SEPARATOR, pm_token_type_human(parser->current.type));
18005
+ if (array->elements.size > 0) {
18006
+ if (accept1(parser, PM_TOKEN_COMMA)) {
18007
+ // If there was a comma but we also accepts a newline,
18008
+ // then this is a syntax error.
18009
+ if (accepted_newline) {
18010
+ pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
18011
+ }
18012
+ } else {
18013
+ // If there was no comma, then we need to add a syntax
18014
+ // error.
18015
+ const uint8_t *location = parser->previous.end;
18016
+ PM_PARSER_ERR_FORMAT(parser, location, location, PM_ERR_ARRAY_SEPARATOR, pm_token_type_human(parser->current.type));
17964
18017
 
17965
- parser->previous.start = location;
17966
- parser->previous.type = PM_TOKEN_MISSING;
18018
+ parser->previous.start = location;
18019
+ parser->previous.type = PM_TOKEN_MISSING;
18020
+ }
17967
18021
  }
17968
18022
 
17969
18023
  // If we have a right bracket immediately following a comma,
@@ -18119,14 +18173,32 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18119
18173
  multi_target->base.location.start = lparen_loc.start;
18120
18174
  multi_target->base.location.end = rparen_loc.end;
18121
18175
 
18122
- if (match1(parser, PM_TOKEN_COMMA)) {
18123
- if (binding_power == PM_BINDING_POWER_STATEMENT) {
18124
- return parse_targets_validate(parser, (pm_node_t *) multi_target, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
18125
- }
18126
- return (pm_node_t *) multi_target;
18176
+ pm_node_t *result;
18177
+ if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
18178
+ result = parse_targets(parser, (pm_node_t *) multi_target, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
18179
+ accept1(parser, PM_TOKEN_NEWLINE);
18180
+ } else {
18181
+ result = (pm_node_t *) multi_target;
18127
18182
  }
18128
18183
 
18129
- return parse_target_validate(parser, (pm_node_t *) multi_target, false);
18184
+ if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
18185
+ // All set, this is explicitly allowed by the parent
18186
+ // context.
18187
+ } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) {
18188
+ // All set, we're inside a for loop and we're parsing
18189
+ // multiple targets.
18190
+ } else if (binding_power != PM_BINDING_POWER_STATEMENT) {
18191
+ // Multi targets are not allowed when it's not a
18192
+ // statement level.
18193
+ pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
18194
+ } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
18195
+ // Multi targets must be followed by an equal sign in
18196
+ // order to be valid (or a right parenthesis if they are
18197
+ // nested).
18198
+ pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
18199
+ }
18200
+
18201
+ return result;
18130
18202
  }
18131
18203
 
18132
18204
  // If we have a single statement and are ending on a right parenthesis
@@ -18187,6 +18259,33 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18187
18259
  pm_accepts_block_stack_pop(parser);
18188
18260
  expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
18189
18261
 
18262
+ // When we're parsing multi targets, we allow them to be followed by
18263
+ // a right parenthesis if they are at the statement level. This is
18264
+ // only possible if they are the final statement in a parentheses.
18265
+ // We need to explicitly reject that here.
18266
+ {
18267
+ pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
18268
+
18269
+ if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
18270
+ pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
18271
+ pm_multi_target_node_targets_append(parser, multi_target, statement);
18272
+
18273
+ statement = (pm_node_t *) multi_target;
18274
+ statements->body.nodes[statements->body.size - 1] = statement;
18275
+ }
18276
+
18277
+ if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
18278
+ const uint8_t *offset = statement->location.end;
18279
+ pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
18280
+ pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, offset, offset);
18281
+
18282
+ statement = (pm_node_t *) pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value);
18283
+ statements->body.nodes[statements->body.size - 1] = statement;
18284
+
18285
+ pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
18286
+ }
18287
+ }
18288
+
18190
18289
  pop_block_exits(parser, previous_block_exits);
18191
18290
  pm_node_list_free(&current_block_exits);
18192
18291
 
@@ -18442,10 +18541,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18442
18541
  case PM_TOKEN_HEREDOC_START: {
18443
18542
  // Here we have found a heredoc. We'll parse it and add it to the
18444
18543
  // list of strings.
18445
- pm_lex_mode_t *lex_mode = parser->lex_modes.current;
18446
- assert(lex_mode->mode == PM_LEX_HEREDOC);
18447
- pm_heredoc_quote_t quote = lex_mode->as.heredoc.quote;
18448
- pm_heredoc_indent_t indent = lex_mode->as.heredoc.indent;
18544
+ assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
18545
+ pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
18546
+
18547
+ size_t common_whitespace = (size_t) -1;
18548
+ parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
18449
18549
 
18450
18550
  parser_lex(parser);
18451
18551
  pm_token_t opening = parser->previous;
@@ -18456,10 +18556,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18456
18556
  if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
18457
18557
  // If we get here, then we have an empty heredoc. We'll create
18458
18558
  // an empty content token and return an empty string node.
18459
- expect1_heredoc_term(parser, lex_mode);
18559
+ expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
18460
18560
  pm_token_t content = parse_strings_empty_content(parser->previous.start);
18461
18561
 
18462
- if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
18562
+ if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
18463
18563
  node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY);
18464
18564
  } else {
18465
18565
  node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY);
@@ -18486,18 +18586,17 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18486
18586
  cast->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->current);
18487
18587
  cast->base.location = cast->opening_loc;
18488
18588
 
18489
- if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
18589
+ if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
18490
18590
  assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
18491
18591
  cast->base.type = PM_X_STRING_NODE;
18492
18592
  }
18493
18593
 
18494
- size_t common_whitespace = lex_mode->as.heredoc.common_whitespace;
18495
- if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
18594
+ if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
18496
18595
  parse_heredoc_dedent_string(&cast->unescaped, common_whitespace);
18497
18596
  }
18498
18597
 
18499
18598
  node = (pm_node_t *) cast;
18500
- expect1_heredoc_term(parser, lex_mode);
18599
+ expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
18501
18600
  } else {
18502
18601
  // If we get here, then we have multiple parts in the heredoc,
18503
18602
  // so we'll need to create an interpolated string node to hold
@@ -18511,15 +18610,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18511
18610
  }
18512
18611
  }
18513
18612
 
18514
- size_t common_whitespace = lex_mode->as.heredoc.common_whitespace;
18515
-
18516
18613
  // Now that we have all of the parts, create the correct type of
18517
18614
  // interpolated node.
18518
- if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
18615
+ if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
18519
18616
  pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
18520
18617
  cast->parts = parts;
18521
18618
 
18522
- expect1_heredoc_term(parser, lex_mode);
18619
+ expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
18523
18620
  pm_interpolated_xstring_node_closing_set(cast, &parser->previous);
18524
18621
 
18525
18622
  cast->base.location = cast->opening_loc;
@@ -18528,7 +18625,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18528
18625
  pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
18529
18626
  pm_node_list_free(&parts);
18530
18627
 
18531
- expect1_heredoc_term(parser, lex_mode);
18628
+ expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
18532
18629
  pm_interpolated_string_node_closing_set(cast, &parser->previous);
18533
18630
 
18534
18631
  cast->base.location = cast->opening_loc;
@@ -18537,9 +18634,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18537
18634
 
18538
18635
  // If this is a heredoc that is indented with a ~, then we need
18539
18636
  // to dedent each line by the common leading whitespace.
18540
- if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
18637
+ if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
18541
18638
  pm_node_list_t *nodes;
18542
- if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
18639
+ if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
18543
18640
  nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
18544
18641
  } else {
18545
18642
  nodes = &((pm_interpolated_string_node_t *) node)->parts;
@@ -21684,8 +21781,11 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
21684
21781
  // Otherwise we'll look and see if the next token can be parsed as an infix
21685
21782
  // operator. If it can, then we'll parse it using parse_expression_infix.
21686
21783
  pm_binding_powers_t current_binding_powers;
21784
+ pm_token_type_t current_token_type;
21785
+
21687
21786
  while (
21688
- current_binding_powers = pm_binding_powers[parser->current.type],
21787
+ current_token_type = parser->current.type,
21788
+ current_binding_powers = pm_binding_powers[current_token_type],
21689
21789
  binding_power <= current_binding_powers.left &&
21690
21790
  current_binding_powers.binary
21691
21791
  ) {
@@ -21726,6 +21826,13 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
21726
21826
  // If the operator is nonassoc and we should not be able to parse the
21727
21827
  // upcoming infix operator, break.
21728
21828
  if (current_binding_powers.nonassoc) {
21829
+ // If this is a non-assoc operator and we are about to parse the
21830
+ // exact same operator, then we need to add an error.
21831
+ if (match1(parser, current_token_type)) {
21832
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type));
21833
+ break;
21834
+ }
21835
+
21729
21836
  // If this is an endless range, then we need to reject a couple of
21730
21837
  // additional operators because it violates the normal operator
21731
21838
  // precedence rules. Those patterns are:
@@ -21735,7 +21842,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
21735
21842
  //
21736
21843
  if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
21737
21844
  if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
21738
- PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(parser->previous.type));
21845
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type));
21739
21846
  break;
21740
21847
  }
21741
21848
 
@@ -21857,6 +21964,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
21857
21964
  ));
21858
21965
 
21859
21966
  pm_arguments_node_arguments_append(arguments, (pm_node_t *) keywords);
21967
+ pm_node_flag_set((pm_node_t *) arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
21860
21968
  }
21861
21969
 
21862
21970
  pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);