Skip to content

Commit 2d7829f

Browse files
Report missing end errors at opening token
This commit adds an `expect1_opening` function that expects a token and attaches the error to the opening token location rather than the current position. This is useful for errors about missing closing tokens, where we want to point to the line with the opening token rather than the end of the file. For example: ```ruby def foo def bar def baz ^ expected an `end` to close the `def` statement ^ expected an `end` to close the `def` statement ^ expected an `end` to close the `def` statement ``` This would previously produce three identical errors at the end of the file. After this commit, they would be reported at the opening token location: ```ruby def foo ^~~ expected an `end` to close the `def` statement def bar ^~~ expected an `end` to close the `def` statement def baz ^~~ expected an `end` to close the `def` statement ``` I considered using the end of the line where the opening token is located, but in some cases that would be less useful than the opening token location itself. For example: ```ruby def foo def bar def baz ``` Here the end of the line where the opening token is located would be the same for each of the unclosed `def` nodes.
1 parent 166764f commit 2d7829f

26 files changed

+69
-52
lines changed

src/prism.c

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12422,6 +12422,22 @@ expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ide
1242212422
}
1242312423
}
1242412424

12425+
/**
12426+
* A special expect1 that attaches the error to the opening token location
12427+
* rather than the current position. This is useful for errors about missing
12428+
* closing tokens, where we want to point to the line with the opening token
12429+
* (e.g., `def`, `class`, `if`, `{`) rather than the end of the file.
12430+
*/
12431+
static void
12432+
expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) {
12433+
if (accept1(parser, type)) return;
12434+
12435+
pm_parser_err(parser, opening->start, opening->end, diag_id);
12436+
12437+
parser->previous.start = opening->end;
12438+
parser->previous.type = PM_TOKEN_MISSING;
12439+
}
12440+
1242512441
static pm_node_t *
1242612442
parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth);
1242712443

@@ -14764,7 +14780,7 @@ parse_block(pm_parser_t *parser, uint16_t depth) {
1476414780
statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)));
1476514781
}
1476614782

14767-
expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE);
14783+
expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening);
1476814784
} else {
1476914785
if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
1477014786
if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) {
@@ -14779,7 +14795,7 @@ parse_block(pm_parser_t *parser, uint16_t depth) {
1477914795
}
1478014796
}
1478114797

14782-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END);
14798+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening);
1478314799
}
1478414800

1478514801
pm_constant_id_list_t locals;
@@ -15204,7 +15220,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl
1520415220

1520515221
accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
1520615222
parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false);
15207-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE);
15223+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword);
1520815224

1520915225
pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous);
1521015226

@@ -15221,7 +15237,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl
1522115237
}
1522215238
} else {
1522315239
parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false);
15224-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM);
15240+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword);
1522515241
}
1522615242

1522715243
// Set the appropriate end location for all of the nodes in the subtree.
@@ -16202,7 +16218,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures
1620216218
if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
1620316219
inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
1620416220
accept1(parser, PM_TOKEN_NEWLINE);
16205-
expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET);
16221+
expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
1620616222
}
1620716223

1620816224
closing = parser->previous;
@@ -16214,7 +16230,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures
1621416230
if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
1621516231
inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
1621616232
accept1(parser, PM_TOKEN_NEWLINE);
16217-
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN);
16233+
expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
1621816234
}
1621916235

1622016236
closing = parser->previous;
@@ -16594,7 +16610,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
1659416610
pm_node_t *inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
1659516611

1659616612
accept1(parser, PM_TOKEN_NEWLINE);
16597-
expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET);
16613+
expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
1659816614
pm_token_t closing = parser->previous;
1659916615

1660016616
switch (PM_NODE_TYPE(inner)) {
@@ -16672,7 +16688,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
1667216688
node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1));
1667316689

1667416690
accept1(parser, PM_TOKEN_NEWLINE);
16675-
expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE);
16691+
expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening);
1667616692
pm_token_t closing = parser->previous;
1667716693

1667816694
node->base.location.start = opening.start;
@@ -16798,7 +16814,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
1679816814
parser->pattern_matching_newlines = previous_pattern_matching_newlines;
1679916815

1680016816
accept1(parser, PM_TOKEN_NEWLINE);
16801-
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN);
16817+
expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen);
1680216818
return UP(pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous));
1680316819
}
1680416820
default: {
@@ -16896,7 +16912,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p
1689616912

1689716913
pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
1689816914
accept1(parser, PM_TOKEN_NEWLINE);
16899-
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN);
16915+
expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
1690016916
pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0));
1690116917

1690216918
if (!alternation) {
@@ -17748,7 +17764,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1774817764
pm_accepts_block_stack_push(parser, true);
1774917765
parser_lex(parser);
1775017766

17751-
pm_hash_node_t *node = pm_hash_node_create(parser, &parser->previous);
17767+
pm_token_t opening = parser->previous;
17768+
pm_hash_node_t *node = pm_hash_node_create(parser, &opening);
1775217769

1775317770
if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
1775417771
if (current_hash_keys != NULL) {
@@ -17763,7 +17780,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1776317780
}
1776417781

1776517782
pm_accepts_block_stack_pop(parser);
17766-
expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM);
17783+
expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening);
1776717784
pm_hash_node_closing_loc_set(node, &parser->previous);
1776817785

1776917786
return UP(node);
@@ -18380,7 +18397,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1838018397
}
1838118398

1838218399
parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18383-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM);
18400+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword);
1838418401

1838518402
if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
1838618403
pm_case_node_end_keyword_loc_set((pm_case_node_t *) node, &parser->previous);
@@ -18413,7 +18430,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1841318430

1841418431
pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
1841518432
parse_rescues(parser, opening_newline_index, &begin_keyword, begin_node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1));
18416-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM);
18433+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword);
1841718434

1841818435
begin_node->base.location.end = parser->previous.end;
1841918436
pm_begin_node_end_keyword_set(begin_node, &parser->previous);
@@ -18438,7 +18455,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1843818455
pm_token_t opening = parser->previous;
1843918456
pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE, (uint16_t) (depth + 1));
1844018457

18441-
expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM);
18458+
expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening);
1844218459
pm_context_t context = parser->current_context->context;
1844318460
if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
1844418461
pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
@@ -18568,7 +18585,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1856818585
parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
1856918586
}
1857018587

18571-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM);
18588+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
1857218589

1857318590
pm_constant_id_list_t locals;
1857418591
pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
@@ -18626,7 +18643,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1862618643
parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
1862718644
}
1862818645

18629-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM);
18646+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
1863018647

1863118648
if (context_def_p(parser)) {
1863218649
pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
@@ -18936,7 +18953,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1893618953
pm_accepts_block_stack_pop(parser);
1893718954
pm_do_loop_stack_pop(parser);
1893818955

18939-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM);
18956+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword);
1894018957
end_keyword = parser->previous;
1894118958
}
1894218959

@@ -19030,7 +19047,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1903019047
pm_token_t opening = parser->previous;
1903119048
pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1));
1903219049

19033-
expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM);
19050+
expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening);
1903419051
return UP(pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
1903519052
}
1903619053
case PM_TOKEN_KEYWORD_FALSE:
@@ -19094,7 +19111,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1909419111
}
1909519112

1909619113
parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false);
19097-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM);
19114+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword);
1909819115

1909919116
return UP(pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous));
1910019117
}
@@ -19245,7 +19262,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1924519262
pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
1924619263

1924719264
pm_parser_scope_pop(parser);
19248-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM);
19265+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword);
1924919266

1925019267
if (context_def_p(parser)) {
1925119268
pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
@@ -19311,7 +19328,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1931119328
}
1931219329

1931319330
parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
19314-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM);
19331+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword);
1931519332

1931619333
return UP(pm_until_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0));
1931719334
}
@@ -19345,7 +19362,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1934519362
}
1934619363

1934719364
parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
19348-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM);
19365+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword);
1934919366

1935019367
return UP(pm_while_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0));
1935119368
}
@@ -20091,7 +20108,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
2009120108
}
2009220109

2009320110
parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20094-
expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE);
20111+
expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
2009520112
} else {
2009620113
expect1(parser, PM_TOKEN_KEYWORD_DO, PM_ERR_LAMBDA_OPEN);
2009720114
opening = parser->previous;
@@ -20109,7 +20126,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
2010920126
parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
2011020127
}
2011120128

20112-
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END);
20129+
expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator);
2011320130
}
2011420131

2011520132
pm_constant_id_list_t locals;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
x.each { x end
22
^~~ unexpected 'end', expecting end-of-input
33
^~~ unexpected 'end', ignoring it
4-
^ expected a block beginning with `{` to end with `}`
4+
^ expected a block beginning with `{` to end with `}`
55

test/prism/errors/command_calls_2.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{a: b c}
2-
^ expected a `}` to close the hash literal
2+
^ expected a `}` to close the hash literal
33
^ unexpected local variable or method, expecting end-of-input
44
^ unexpected '}', expecting end-of-input
55
^ unexpected '}', ignoring it
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
->a=b c{}
22
^ expected a `do` keyword or a `{` to open the lambda block
33
^ unexpected end-of-input, assuming it is closing the parent top level context
4-
^ expected a lambda block beginning with `do` to end with `end`
4+
^~ expected a lambda block beginning with `do` to end with `end`
55

test/prism/errors/command_calls_25.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
^ unexpected ')', expecting end-of-input
55
^ unexpected ')', ignoring it
66
^ unexpected end-of-input, assuming it is closing the parent top level context
7-
^ expected a lambda block beginning with `do` to end with `end`
7+
^~ expected a lambda block beginning with `do` to end with `end`
88

test/prism/errors/heredoc_unterminated.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ a=>{<<b
33
^~~ unexpected heredoc beginning; expected a key in the hash pattern
44
^ unterminated heredoc; can't find string "b" anywhere before EOF
55
^~~ expected a label as the key in the hash pattern
6-
^ expected a `}` to close the pattern expression
6+
^ expected a `}` to close the pattern expression
77
^ unexpected heredoc ending, expecting end-of-input
88
^ unexpected heredoc ending, ignoring it
99

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{ 'a':.upcase => 1 }
22
^ unexpected '.'; expected a value in the hash literal
3-
^ expected a `}` to close the hash literal
3+
^ expected a `}` to close the hash literal
44
^ unexpected '}', expecting end-of-input
55
^ unexpected '}', ignoring it
66

test/prism/errors/label_in_interpolated_string.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ case in el""Q
22
^~~~ expected a predicate for a case matching statement
33
^ expected a delimiter after the patterns of an `in` clause
44
^ unexpected constant, expecting end-of-input
5+
^~~~ expected an `end` to close the `case` statement
56
!"""#{in el"":Q
67
^~ unexpected 'in', assuming it is closing the parent 'in' clause
78
^ expected a `}` to close the embedded expression
@@ -10,5 +11,4 @@ case in el""Q
1011
^ cannot parse the string part
1112
^~~~~~~~~~~ unexpected label
1213
^~~~~~~~~~~ expected a string for concatenation
13-
^ expected an `end` to close the `case` statement
1414

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
case:a
2+
^~~~ expected an `end` to close the `case` statement
23
in b:"","#{}"
34
^~~~~ expected a label after the `,` in the hash pattern
45
^ expected a pattern expression after the key
56
^ expected a delimiter after the patterns of an `in` clause
67
^ unexpected end-of-input, assuming it is closing the parent top level context
7-
^ expected an `end` to close the `case` statement
88

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
->a;b{}
22
^ expected a `do` keyword or a `{` to open the lambda block
33
^ unexpected end-of-input, assuming it is closing the parent top level context
4-
^ expected a lambda block beginning with `do` to end with `end`
4+
^~ expected a lambda block beginning with `do` to end with `end`
55

0 commit comments

Comments
 (0)