Skip to content

Commit 6f91603

Browse files
authored
Merge pull request #3827 from thomasmarshall/unterminated-error-locations
Report unterminated construct errors at opening token
2 parents 801d982 + 2d7829f commit 6f91603

26 files changed

Lines changed: 120 additions & 36 deletions

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)