[ruby/prism] Error messages closer to CRuby

https://github.com/ruby/prism/commit/19ffa0b980
This commit is contained in:
Kevin Newton 2024-02-10 10:46:39 -05:00 committed by git
parent e4d3e652ff
commit e08c128417
7 changed files with 137 additions and 97 deletions

View File

@ -159,7 +159,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
[PM_ERR_ESCAPE_INVALID_UNICODE_LONG] = { "invalid Unicode escape sequence; maximum length is 6 digits", PM_ERROR_LEVEL_FATAL },
[PM_ERR_ESCAPE_INVALID_UNICODE_TERM] = { "invalid Unicode escape sequence; needs closing `}`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_EXPECT_ARGUMENT] = { "expected an argument", PM_ERROR_LEVEL_FATAL },
[PM_ERR_EXPECT_EOL_AFTER_STATEMENT] = { "expected a newline or semicolon after the statement", PM_ERROR_LEVEL_FATAL },
[PM_ERR_EXPECT_EOL_AFTER_STATEMENT] = { "unexpected %s, expecting end-of-input", PM_ERROR_LEVEL_FATAL },
[PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ] = { "expected an expression after `&&=`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ] = { "expected an expression after `||=`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA] = { "expected an expression after `,`", PM_ERROR_LEVEL_FATAL },
@ -190,8 +190,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
[PM_ERR_HASH_VALUE] = { "expected a value in the hash literal", PM_ERROR_LEVEL_FATAL },
[PM_ERR_HEREDOC_TERM] = { "could not find a terminator for the heredoc", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INCOMPLETE_QUESTION_MARK] = { "incomplete expression at `?`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INCOMPLETE_VARIABLE_CLASS] = { "incomplete class variable", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "incomplete instance variable", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INCOMPLETE_VARIABLE_CLASS] = { "`%.*s' is not allowed as an class variable name", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number", PM_ERROR_LEVEL_FATAL },
@ -202,7 +202,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
[PM_ERR_INVALID_MULTIBYTE_CHARACTER] = { "invalid multibyte character 0x%X", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_FATAL }, // TODO WHAT?
[PM_ERR_INVALID_VARIABLE_GLOBAL] = { "invalid global variable", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INVALID_VARIABLE_GLOBAL] = { "`%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_FATAL },
[PM_ERR_IT_NOT_ALLOWED] = { "`it` is not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_FATAL },
[PM_ERR_LAMBDA_OPEN] = { "expected a `do` keyword or a `{` to open the lambda block", PM_ERROR_LEVEL_FATAL },
[PM_ERR_LAMBDA_TERM_BRACE] = { "expected a lambda block beginning with `{` to end with `}`", PM_ERROR_LEVEL_FATAL },
@ -221,6 +221,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
[PM_ERR_MODULE_NAME] = { "expected a constant name after `module`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_MODULE_TERM] = { "expected an `end` to close the `module` statement", PM_ERROR_LEVEL_FATAL },
[PM_ERR_MULTI_ASSIGN_MULTI_SPLATS] = { "multiple splats in multiple assignment", PM_ERROR_LEVEL_FATAL },
[PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST] = { "unexpected '%.*s' resulting in multiple splats in multiple assignment", PM_ERROR_LEVEL_FATAL },
[PM_ERR_NOT_EXPRESSION] = { "expected an expression after `not`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_NO_LOCAL_VARIABLE] = { "%.*s: no such local variable", PM_ERROR_LEVEL_FATAL },
[PM_ERR_NUMBER_LITERAL_UNDERSCORE] = { "number literal ending with a `_`", PM_ERROR_LEVEL_FATAL },
@ -274,7 +275,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
[PM_ERR_STATEMENT_UNDEF] = { "unexpected an `undef` at a non-statement position", PM_ERROR_LEVEL_FATAL },
[PM_ERR_STRING_CONCATENATION] = { "expected a string for concatenation", PM_ERROR_LEVEL_FATAL },
[PM_ERR_STRING_INTERPOLATED_TERM] = { "expected a closing delimiter for the interpolated string", PM_ERROR_LEVEL_FATAL },
[PM_ERR_STRING_LITERAL_TERM] = { "expected a closing delimiter for the string literal", PM_ERROR_LEVEL_FATAL },
[PM_ERR_STRING_LITERAL_EOF] = { "unterminated string meets end of file", PM_ERROR_LEVEL_FATAL },
[PM_ERR_STRING_LITERAL_TERM] = { "unexpected %s, expected a string literal terminator", PM_ERROR_LEVEL_FATAL },
[PM_ERR_SYMBOL_INVALID] = { "invalid symbol", PM_ERROR_LEVEL_FATAL }, // TODO expected symbol? prism.c ~9719
[PM_ERR_SYMBOL_TERM_DYNAMIC] = { "expected a closing delimiter for the dynamic symbol", PM_ERROR_LEVEL_FATAL },
[PM_ERR_SYMBOL_TERM_INTERPOLATED] = { "expected a closing delimiter for the interpolated symbol", PM_ERROR_LEVEL_FATAL },
@ -292,7 +294,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
[PM_ERR_VOID_EXPRESSION] = { "unexpected void value expression", PM_ERROR_LEVEL_FATAL },
[PM_ERR_WHILE_TERM] = { "expected an `end` to close the `while` statement", PM_ERROR_LEVEL_FATAL },
[PM_ERR_WRITE_TARGET_IN_METHOD] = { "dynamic constant assignment", PM_ERROR_LEVEL_FATAL },
[PM_ERR_WRITE_TARGET_READONLY] = { "immutable variable as a write target", PM_ERROR_LEVEL_FATAL },
[PM_ERR_WRITE_TARGET_READONLY] = { "Can't set variable %.*s", PM_ERROR_LEVEL_FATAL },
[PM_ERR_WRITE_TARGET_UNEXPECTED] = { "unexpected write target", PM_ERROR_LEVEL_FATAL },
[PM_ERR_XSTRING_TERM] = { "expected a closing delimiter for the `%x` or backtick string", PM_ERROR_LEVEL_FATAL },

View File

@ -219,6 +219,7 @@ typedef enum {
PM_ERR_MODULE_NAME,
PM_ERR_MODULE_TERM,
PM_ERR_MULTI_ASSIGN_MULTI_SPLATS,
PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST,
PM_ERR_NOT_EXPRESSION,
PM_ERR_NO_LOCAL_VARIABLE,
PM_ERR_NUMBER_LITERAL_UNDERSCORE,
@ -272,6 +273,7 @@ typedef enum {
PM_ERR_STATEMENT_UNDEF,
PM_ERR_STRING_CONCATENATION,
PM_ERR_STRING_INTERPOLATED_TERM,
PM_ERR_STRING_LITERAL_EOF,
PM_ERR_STRING_LITERAL_TERM,
PM_ERR_SYMBOL_INVALID,
PM_ERR_SYMBOL_TERM_DYNAMIC,

View File

@ -492,7 +492,8 @@ pm_parser_err(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_
/**
* Append an error to the list of errors on the parser using a format string.
*/
#define PM_PARSER_ERR_FORMAT(parser, start, end, diag_id, ...) pm_diagnostic_list_append_format(&parser->error_list, start, end, diag_id, __VA_ARGS__)
#define PM_PARSER_ERR_FORMAT(parser, start, end, diag_id, ...) \
pm_diagnostic_list_append_format(&parser->error_list, start, end, diag_id, __VA_ARGS__)
/**
* Append an error to the list of errors on the parser using the location of the
@ -507,7 +508,8 @@ pm_parser_err_current(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
* Append an error to the list of errors on the parser using the given location
* using a format string.
*/
#define PM_PARSER_ERR_LOCATION_FORMAT(parser, location, diag_id, ...) pm_diagnostic_list_append_format(&parser->error_list, (location)->start, (location)->end, diag_id, __VA_ARGS__)
#define PM_PARSER_ERR_LOCATION_FORMAT(parser, location, diag_id, ...) \
PM_PARSER_ERR_FORMAT(parser, (location)->start, (location)->end, diag_id, __VA_ARGS__)
/**
* Append an error to the list of errors on the parser using the location of the
@ -522,7 +524,15 @@ pm_parser_err_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_
* Append an error to the list of errors on the parser using the location of the
* given node and a format string.
*/
#define PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, ...) pm_diagnostic_list_append_format(&parser->error_list, node->location.start, node->location.end, diag_id, __VA_ARGS__)
#define PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, ...) \
PM_PARSER_ERR_FORMAT(parser, (node)->location.start, (node)->location.end, diag_id, __VA_ARGS__)
/**
* Append an error to the list of errors on the parser using the location of the
* given node and a format string, and add on the content of the node.
*/
#define PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, diag_id) \
PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, (int) ((node)->location.end - (node)->location.start), (const char *) (node)->location.start)
/**
* Append an error to the list of errors on the parser using the location of the
@ -546,7 +556,15 @@ pm_parser_err_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_
* Append an error to the list of errors on the parser using the location of the
* given token and a format string.
*/
#define PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, ...) pm_diagnostic_list_append_format(&parser->error_list, (token).start, (token).end, diag_id, __VA_ARGS__)
#define PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, ...) \
PM_PARSER_ERR_FORMAT(parser, (token).start, (token).end, diag_id, __VA_ARGS__)
/**
* Append an error to the list of errors on the parser using the location of the
* given token and a format string, and add on the content of the token.
*/
#define PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, diag_id) \
PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, (int) ((token).end - (token).start), (const char *) (token).start)
/**
* Append a warning to the list of warnings on the parser.
@ -4643,13 +4661,20 @@ pm_multi_target_node_create(pm_parser_t *parser) {
*/
static void
pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) {
if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE) || PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE)) {
if (node->rest == NULL) {
node->rest = target;
} else {
pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
pm_node_list_append(&node->rights, target);
}
} else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
if (node->rest == NULL) {
node->rest = target;
} else {
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
pm_node_list_append(&node->rights, target);
}
} else if (node->rest == NULL) {
pm_node_list_append(&node->lefts, target);
} else {
@ -7173,7 +7198,7 @@ lex_numeric(pm_parser_t *parser) {
static pm_token_type_t
lex_global_variable(pm_parser_t *parser) {
if (parser->current.end >= parser->end) {
pm_parser_err_current(parser, PM_ERR_INVALID_VARIABLE_GLOBAL);
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_ERR_INVALID_VARIABLE_GLOBAL);
return PM_TOKEN_GLOBAL_VARIABLE;
}
@ -7214,7 +7239,7 @@ lex_global_variable(pm_parser_t *parser) {
} while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
// $0 isn't allowed to be followed by anything.
pm_parser_err_current(parser, PM_ERR_INVALID_VARIABLE_GLOBAL);
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_ERR_INVALID_VARIABLE_GLOBAL);
}
return PM_TOKEN_GLOBAL_VARIABLE;
@ -7245,7 +7270,7 @@ lex_global_variable(pm_parser_t *parser) {
} else {
// If we get here, then we have a $ followed by something that isn't
// recognized as a global variable.
pm_parser_err_current(parser, PM_ERR_INVALID_VARIABLE_GLOBAL);
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_ERR_INVALID_VARIABLE_GLOBAL);
}
return PM_TOKEN_GLOBAL_VARIABLE;
@ -8149,10 +8174,10 @@ lex_at_variable(pm_parser_t *parser) {
while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) {
parser->current.end += width;
}
} else if (type == PM_TOKEN_CLASS_VARIABLE) {
pm_parser_err_current(parser, PM_ERR_INCOMPLETE_VARIABLE_CLASS);
} else {
pm_parser_err_current(parser, PM_ERR_INCOMPLETE_VARIABLE_INSTANCE);
pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
}
// If we're lexing an embedded variable, then we need to pop back into the
@ -11055,7 +11080,7 @@ parse_target(pm_parser_t *parser, pm_node_t *target) {
return target;
case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_REFERENCE_READ_NODE:
pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_READONLY);
PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
return target;
case PM_GLOBAL_VARIABLE_READ_NODE:
assert(sizeof(pm_global_variable_target_node_t) == sizeof(pm_global_variable_read_node_t));
@ -11193,7 +11218,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod
}
case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_REFERENCE_READ_NODE:
pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_READONLY);
PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
/* fallthrough */
case PM_GLOBAL_VARIABLE_READ_NODE: {
pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value);
@ -11368,7 +11393,7 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b
pm_multi_target_node_targets_append(parser, result, target);
} else if (!match1(parser, PM_TOKEN_EOF)) {
// If we get here, then we have a trailing , in a multi target node.
// We'll set the implicit rest flag to indicate this.
// We'll add an implicit rest node to represent this.
pm_node_t *rest = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
pm_multi_target_node_targets_append(parser, result, rest);
break;
@ -11458,8 +11483,13 @@ parse_statements(pm_parser_t *parser, pm_context_t context) {
while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
if (context_terminator(context, &parser->current)) break;
} else {
expect1(parser, PM_TOKEN_NEWLINE, PM_ERR_EXPECT_EOL_AFTER_STATEMENT);
} else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
// This is an inlined version of accept1 because the error that we
// want to add has varargs. If this happens again, we should
// probably extract a helper function.
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
parser->previous.start = parser->previous.end;
parser->previous.type = PM_TOKEN_MISSING;
}
}
@ -13853,7 +13883,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
pm_constant_id_t name_id = pm_parser_constant_id_constant(parser, "0it", 3);
variable = (pm_node_t *) pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0);
} else {
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE, (int) (parser->previous.end - parser->previous.start), parser->previous.start);
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE);
variable = (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, 0);
}
}
@ -14162,7 +14192,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) {
parser_lex(parser);
if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_TERM);
expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
// If we get here, then we have an end immediately after a
// start. In that case we'll create an empty content token and
// return an uninterpolated string.
@ -14219,15 +14249,19 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) {
parser_lex(parser);
} while (match1(parser, PM_TOKEN_STRING_CONTENT));
expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_TERM);
expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
node = (pm_node_t *) pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous);
} else if (accept1(parser, PM_TOKEN_LABEL_END) && !state_is_arg_labeled) {
node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped));
} else if (match1(parser, PM_TOKEN_EOF)) {
pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_TERM);
pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF);
node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped);
} else if (accept1(parser, PM_TOKEN_STRING_END)) {
node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
} else {
expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_TERM);
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_type_human(parser->previous.type));
parser->previous.start = parser->previous.end;
parser->previous.type = PM_TOKEN_MISSING;
node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
}
} else if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
@ -14242,7 +14276,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) {
if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped);
pm_node_flag_set(node, parse_unescaped_encoding(parser));
expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_TERM);
expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
} else if (accept1(parser, PM_TOKEN_LABEL_END)) {
node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped));
} else {
@ -14517,7 +14551,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
// If we didn't find a terminator and we didn't find a right
// parenthesis, then this is a syntax error.
if (!terminator_found) {
pm_parser_err(parser, parser->current.start, parser->current.start, PM_ERR_EXPECT_EOL_AFTER_STATEMENT);
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
}
// Parse each statement within the parentheses.
@ -14546,7 +14580,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
} else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
break;
} else {
pm_parser_err(parser, parser->current.start, parser->current.start, PM_ERR_EXPECT_EOL_AFTER_STATEMENT);
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
}
}
@ -16897,7 +16931,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
switch (PM_NODE_TYPE(node)) {
case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_REFERENCE_READ_NODE:
pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_READONLY);
PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
/* fallthrough */
case PM_GLOBAL_VARIABLE_READ_NODE: {
parser_lex(parser);
@ -17008,7 +17042,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
switch (PM_NODE_TYPE(node)) {
case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_REFERENCE_READ_NODE:
pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_READONLY);
PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
/* fallthrough */
case PM_GLOBAL_VARIABLE_READ_NODE: {
parser_lex(parser);
@ -17129,7 +17163,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
switch (PM_NODE_TYPE(node)) {
case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_REFERENCE_READ_NODE:
pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_READONLY);
PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
/* fallthrough */
case PM_GLOBAL_VARIABLE_READ_NODE: {
parser_lex(parser);

View File

@ -138,7 +138,7 @@ pm_token_type_human(pm_token_type_t token_type) {
case PM_TOKEN_HEREDOC_START:
return "heredoc beginning";
case PM_TOKEN_IDENTIFIER:
return "local variable or method identifier";
return "local variable or method";
case PM_TOKEN_IGNORED_NEWLINE:
return "ignored newline";
case PM_TOKEN_INSTANCE_VARIABLE:
@ -248,7 +248,7 @@ pm_token_type_human(pm_token_type_t token_type) {
case PM_TOKEN_LABEL:
return "label";
case PM_TOKEN_LABEL_END:
return "':'";
return "label terminator";
case PM_TOKEN_LAMBDA_BEGIN:
return "'{'";
case PM_TOKEN_LESS:

View File

@ -9,7 +9,7 @@ module Prism
def test_constant_path_with_invalid_token_after
assert_error_messages "A::$b", [
"expected a constant after the `::` operator",
"expected a newline or semicolon after the statement"
"unexpected global variable, expecting end-of-input"
]
end
@ -151,7 +151,7 @@ module Prism
def test_unterminated_interpolated_string
expr = expression('"hello')
assert_errors expr, '"hello', [
["expected a closing delimiter for the string literal", 6..6]
["unterminated string meets end of file", 6..6]
]
assert_equal expr.unescaped, "hello"
assert_equal expr.closing, ""
@ -160,7 +160,7 @@ module Prism
def test_unterminated_string
expr = expression("'hello")
assert_errors expr, "'hello", [
["expected a closing delimiter for the string literal", 0..1]
["unterminated string meets end of file", 0..1]
]
assert_equal expr.unescaped, "hello"
assert_equal expr.closing, ""
@ -169,7 +169,7 @@ module Prism
def test_unterminated_empty_string
expr = expression('"')
assert_errors expr, '"', [
["expected a closing delimiter for the string literal", 1..1]
["unterminated string meets end of file", 1..1]
]
assert_equal expr.unescaped, ""
assert_equal expr.closing, ""
@ -177,8 +177,8 @@ module Prism
def test_incomplete_instance_var_string
assert_errors expression('%@#@@#'), '%@#@@#', [
["incomplete instance variable", 4..5],
["expected a newline or semicolon after the statement", 4..4]
["`@#' is not allowed as an instance variable name", 4..5],
["unexpected instance variable, expecting end-of-input", 4..5]
]
end
@ -190,7 +190,7 @@ module Prism
def test_unterminated_parenthesized_expression
assert_errors expression('(1 + 2'), '(1 + 2', [
["expected a newline or semicolon after the statement", 6..6],
["unexpected end of file, expecting end-of-input", 6..6],
["unexpected end of file, assuming it is closing the parent top level context", 6..6],
["expected a matching `)`", 6..6]
]
@ -198,7 +198,7 @@ module Prism
def test_missing_terminator_in_parentheses
assert_error_messages "(0 0)", [
"expected a newline or semicolon after the statement"
"unexpected integer, expecting end-of-input"
]
end
@ -224,24 +224,24 @@ module Prism
def test_1_2_3
assert_errors expression("(1, 2, 3)"), "(1, 2, 3)", [
["expected a newline or semicolon after the statement", 2..2],
["unexpected ',', expecting end-of-input", 2..3],
["unexpected ',', ignoring it", 2..3],
["expected a matching `)`", 2..2],
["expected a newline or semicolon after the statement", 2..2],
["unexpected ',', expecting end-of-input", 2..3],
["unexpected ',', ignoring it", 2..3],
["expected a newline or semicolon after the statement", 5..5],
["unexpected ',', expecting end-of-input", 5..6],
["unexpected ',', ignoring it", 5..6],
["expected a newline or semicolon after the statement", 8..8],
["unexpected ')', expecting end-of-input", 8..9],
["unexpected ')', ignoring it", 8..9]
]
end
def test_return_1_2_3
assert_error_messages "return(1, 2, 3)", [
"expected a newline or semicolon after the statement",
"unexpected ',', expecting end-of-input",
"unexpected ',', ignoring it",
"expected a matching `)`",
"expected a newline or semicolon after the statement",
"unexpected ')', expecting end-of-input",
"unexpected ')', ignoring it"
]
end
@ -254,10 +254,10 @@ module Prism
def test_next_1_2_3
assert_errors expression("next(1, 2, 3)"), "next(1, 2, 3)", [
["expected a newline or semicolon after the statement", 6..6],
["unexpected ',', expecting end-of-input", 6..7],
["unexpected ',', ignoring it", 6..7],
["expected a matching `)`", 6..6],
["expected a newline or semicolon after the statement", 12..12],
["unexpected ')', expecting end-of-input", 12..13],
["unexpected ')', ignoring it", 12..13]
]
end
@ -270,10 +270,10 @@ module Prism
def test_break_1_2_3
assert_errors expression("break(1, 2, 3)"), "break(1, 2, 3)", [
["expected a newline or semicolon after the statement", 7..7],
["unexpected ',', expecting end-of-input", 7..8],
["unexpected ',', ignoring it", 7..8],
["expected a matching `)`", 7..7],
["expected a newline or semicolon after the statement", 13..13],
["unexpected ')', expecting end-of-input", 13..14],
["unexpected ')', ignoring it", 13..14]
]
end
@ -300,14 +300,14 @@ module Prism
def test_top_level_constant_with_downcased_identifier
assert_error_messages "::foo", [
"expected a constant after the `::` operator",
"expected a newline or semicolon after the statement"
"unexpected local variable or method, expecting end-of-input"
]
end
def test_top_level_constant_starting_with_downcased_identifier
assert_error_messages "::foo::A", [
"expected a constant after the `::` operator",
"expected a newline or semicolon after the statement"
"unexpected local variable or method, expecting end-of-input"
]
end
@ -354,7 +354,7 @@ module Prism
def test_block_beginning_with_brace_and_ending_with_end
assert_error_messages "x.each { x end", [
"expected a newline or semicolon after the statement",
"unexpected 'end', expecting end-of-input",
"unexpected 'end', ignoring it",
"unexpected end of file, assuming it is closing the parent top level context",
"expected a block beginning with `{` to end with `}`"
@ -403,7 +403,7 @@ module Prism
def test_arguments_binding_power_for_and
assert_error_messages "foo(*bar and baz)", [
"expected a `)` to close the arguments",
"expected a newline or semicolon after the statement",
"unexpected ')', expecting end-of-input",
"unexpected ')', ignoring it"
]
end
@ -1124,8 +1124,8 @@ module Prism
)
assert_errors expected, "begin\n$+ = nil\n$1466 = nil\nend", [
["immutable variable as a write target", 6..8],
["immutable variable as a write target", 15..20]
["Can't set variable $+", 6..8],
["Can't set variable $1466", 15..20]
]
end
@ -1258,20 +1258,19 @@ module Prism
def test_unterminated_global_variable
assert_errors expression("$"), "$", [
["invalid global variable", 0..1]
["`$' is not allowed as a global variable name", 0..1]
]
end
def test_invalid_global_variable_write
assert_errors expression("$',"), "$',", [
["immutable variable as a write target", 0..2],
["Can't set variable $'", 0..2],
["unexpected write target", 0..2]
]
end
def test_invalid_multi_target
error_messages = ["unexpected write target"]
immutable = "immutable variable as a write target"
assert_error_messages "foo,", error_messages
assert_error_messages "foo = 1; foo,", error_messages
@ -1280,8 +1279,8 @@ module Prism
assert_error_messages "@foo,", error_messages
assert_error_messages "@@foo,", error_messages
assert_error_messages "$foo,", error_messages
assert_error_messages "$1,", [immutable, *error_messages]
assert_error_messages "$+,", [immutable, *error_messages]
assert_error_messages "$1,", ["Can't set variable $1", *error_messages]
assert_error_messages "$+,", ["Can't set variable $+", *error_messages]
assert_error_messages "Foo,", error_messages
assert_error_messages "::Foo,", error_messages
assert_error_messages "Foo::Foo,", error_messages
@ -1471,9 +1470,10 @@ module Prism
def test_shadow_args_in_lambda
source = "->a;b{}"
assert_errors expression(source), source, [
["expected a `do` keyword or a `{` to open the lambda block", 3..3],
["expected a newline or semicolon after the statement", 7..7],
["unexpected end of file, expecting end-of-input", 7..7],
["unexpected end of file, assuming it is closing the parent top level context", 7..7],
["expected a lambda block beginning with `do` to end with `end`", 7..7]
]
@ -1517,14 +1517,14 @@ module Prism
def test_symbol_in_keyword_parameter
source = "def foo(x:'y':); end"
assert_errors expression(source), source, [
["expected a closing delimiter for the string literal", 14..14],
["unexpected label terminator, expected a string literal terminator", 12..14]
]
end
def test_symbol_in_hash
source = "{x:'y':}"
assert_errors expression(source), source, [
["expected a closing delimiter for the string literal", 7..7],
["unexpected label terminator, expected a string literal terminator", 5..7]
]
end
@ -1545,19 +1545,19 @@ module Prism
RUBY
assert_errors expression(source), source, [
["expected a newline or semicolon after the statement", 6..6],
["unexpected '+', expecting end-of-input", 7..8],
["unexpected '+', ignoring it", 7..8],
["expected a newline or semicolon after the statement", 17..17],
["unexpected '+', expecting end-of-input", 18..19],
["unexpected '+', ignoring it", 18..19]
]
end
def test_rational_number_with_exponential_portion
source = '1e1r; 1e1ri'
message = 'expected a newline or semicolon after the statement'
assert_errors expression(source), source, [
[message, 3..3],
[message, 9..9]
["unexpected local variable or method, expecting end-of-input", 3..4],
["unexpected local variable or method, expecting end-of-input", 9..11]
]
end
@ -1845,7 +1845,7 @@ module Prism
source = '1....2'
assert_errors expression(source), source, [
["expected a newline or semicolon after the statement", 4..4],
["unexpected '.', expecting end-of-input", 4..5],
["unexpected '.', ignoring it", 4..5]
]
end
@ -1879,21 +1879,21 @@ module Prism
RUBY
assert_errors expression(source), source, [
["expected a newline or semicolon after the statement", 9..9],
["unexpected '+', expecting end-of-input", 10..11],
["unexpected '+', ignoring it", 10..11],
["expected a newline or semicolon after the statement", 23..23],
["unexpected '.', expecting end-of-input", 23..24],
["unexpected '.', ignoring it", 23..24],
["expected a newline or semicolon after the statement", 39..39],
["unexpected '+', expecting end-of-input", 40..41],
["unexpected '+', ignoring it", 40..41],
["expected a newline or semicolon after the statement", 57..57],
["unexpected '.', expecting end-of-input", 57..58],
["unexpected '.', ignoring it", 57..58],
["expected a newline or semicolon after the statement", 71..71],
["unexpected '+', expecting end-of-input", 72..73],
["unexpected '+', ignoring it", 72..73],
["expected a newline or semicolon after the statement", 87..87],
["unexpected '.', expecting end-of-input", 87..88],
["unexpected '.', ignoring it", 87..88],
["expected a newline or semicolon after the statement", 97..97],
["unexpected '+', expecting end-of-input", 98..99],
["unexpected '+', ignoring it", 98..99],
["expected a newline or semicolon after the statement", 109..109],
["unexpected '.', expecting end-of-input", 109..110],
["unexpected '.', ignoring it", 109..110]
]
end
@ -1920,9 +1920,9 @@ module Prism
RUBY
assert_errors expression(source), source, [
["expected a newline or semicolon after the statement", 3..3],
["unexpected '..', expecting end-of-input", 3..5],
["unexpected '..', ignoring it", 3..5],
["expected a newline or semicolon after the statement", 10..10],
["unexpected '..', expecting end-of-input", 10..12],
["unexpected '..', ignoring it", 10..12]
]
end
@ -2004,13 +2004,12 @@ module Prism
foo 1 in a
a = foo 2 in b
RUBY
message1 = 'unexpected `in` keyword in arguments'
message2 = 'expected a newline or semicolon after the statement'
assert_errors expression(source), source, [
[message1, 9..10],
[message2, 8..8],
[message1, 24..25],
[message2, 23..23],
["unexpected `in` keyword in arguments", 9..10],
["unexpected local variable or method, expecting end-of-input", 9..10],
["unexpected `in` keyword in arguments", 24..25],
["unexpected local variable or method, expecting end-of-input", 24..25]
]
end
@ -2032,17 +2031,17 @@ module Prism
RUBY
assert_errors expression(source), source, [
["expected a newline or semicolon after the statement", 6..6],
["unexpected '==', expecting end-of-input", 7..9],
["unexpected '==', ignoring it", 7..9],
["expected a newline or semicolon after the statement", 18..18],
["unexpected '!=', expecting end-of-input", 19..21],
["unexpected '!=', ignoring it", 19..21],
["expected a newline or semicolon after the statement", 31..31],
["unexpected '===', expecting end-of-input", 32..35],
["unexpected '===', ignoring it", 32..35],
["expected a newline or semicolon after the statement", 44..44],
["unexpected '=~', expecting end-of-input", 45..47],
["unexpected '=~', ignoring it", 45..47],
["expected a newline or semicolon after the statement", 56..56],
["unexpected '!~', expecting end-of-input", 57..59],
["unexpected '!~', ignoring it", 57..59],
["expected a newline or semicolon after the statement", 69..69],
["unexpected '<=>', expecting end-of-input", 70..73],
["unexpected '<=>', ignoring it", 70..73]
]
end

View File

@ -15,9 +15,9 @@ module Prism
assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false)
> 1 | "%W"\u"
| ^ expected a newline or semicolon after the statement
| ^ invalid character `\`
| ^ expected a closing delimiter for the string literal
| ^ unexpected local variable or method, expecting end-of-input
| ^ unterminated string meets end of file
ERROR
end
end

View File

@ -3,11 +3,14 @@
require_relative "test_helper"
begin
verbose, $VERBOSE = $VERBOSE, nil
require "parser/current"
rescue LoadError
# In CRuby's CI, we're not going to test against the parser gem because we
# don't want to have to install it. So in this case we'll just skip this test.
return
ensure
$VERBOSE = verbose
end
# First, opt in to every AST feature.