[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_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_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_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_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_PIPEPIPEEQ] = { "expected an expression after `||=`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA] = { "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_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_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_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_CLASS] = { "`%.*s' is not allowed as an class variable name", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "incomplete instance variable", 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_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", 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 }, [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_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_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_FATAL },
[PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_FATAL }, // TODO WHAT? [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_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_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 }, [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_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_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_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_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_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 }, [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_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_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_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_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_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 }, [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_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_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_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_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 }, [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_NAME,
PM_ERR_MODULE_TERM, PM_ERR_MODULE_TERM,
PM_ERR_MULTI_ASSIGN_MULTI_SPLATS, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS,
PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST,
PM_ERR_NOT_EXPRESSION, PM_ERR_NOT_EXPRESSION,
PM_ERR_NO_LOCAL_VARIABLE, PM_ERR_NO_LOCAL_VARIABLE,
PM_ERR_NUMBER_LITERAL_UNDERSCORE, PM_ERR_NUMBER_LITERAL_UNDERSCORE,
@ -272,6 +273,7 @@ typedef enum {
PM_ERR_STATEMENT_UNDEF, PM_ERR_STATEMENT_UNDEF,
PM_ERR_STRING_CONCATENATION, PM_ERR_STRING_CONCATENATION,
PM_ERR_STRING_INTERPOLATED_TERM, PM_ERR_STRING_INTERPOLATED_TERM,
PM_ERR_STRING_LITERAL_EOF,
PM_ERR_STRING_LITERAL_TERM, PM_ERR_STRING_LITERAL_TERM,
PM_ERR_SYMBOL_INVALID, PM_ERR_SYMBOL_INVALID,
PM_ERR_SYMBOL_TERM_DYNAMIC, 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. * 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 * 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 * Append an error to the list of errors on the parser using the given location
* using a format string. * 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 * 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 * Append an error to the list of errors on the parser using the location of the
* given node and a format string. * 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 * 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 * Append an error to the list of errors on the parser using the location of the
* given token and a format string. * 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. * 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 static void
pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) { 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) { if (node->rest == NULL) {
node->rest = target; node->rest = target;
} else { } else {
pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS); pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
pm_node_list_append(&node->rights, target); 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) { } else if (node->rest == NULL) {
pm_node_list_append(&node->lefts, target); pm_node_list_append(&node->lefts, target);
} else { } else {
@ -7173,7 +7198,7 @@ lex_numeric(pm_parser_t *parser) {
static pm_token_type_t static pm_token_type_t
lex_global_variable(pm_parser_t *parser) { lex_global_variable(pm_parser_t *parser) {
if (parser->current.end >= parser->end) { 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; 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); } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
// $0 isn't allowed to be followed by anything. // $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; return PM_TOKEN_GLOBAL_VARIABLE;
@ -7245,7 +7270,7 @@ lex_global_variable(pm_parser_t *parser) {
} else { } else {
// If we get here, then we have a $ followed by something that isn't // If we get here, then we have a $ followed by something that isn't
// recognized as a global variable. // 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; 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) { while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) {
parser->current.end += width; parser->current.end += width;
} }
} else if (type == PM_TOKEN_CLASS_VARIABLE) {
pm_parser_err_current(parser, PM_ERR_INCOMPLETE_VARIABLE_CLASS);
} else { } 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 // 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; return target;
case PM_BACK_REFERENCE_READ_NODE: case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_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; return target;
case PM_GLOBAL_VARIABLE_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE:
assert(sizeof(pm_global_variable_target_node_t) == sizeof(pm_global_variable_read_node_t)); 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_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_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 */ /* fallthrough */
case PM_GLOBAL_VARIABLE_READ_NODE: { case PM_GLOBAL_VARIABLE_READ_NODE: {
pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value); 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); pm_multi_target_node_targets_append(parser, result, target);
} else if (!match1(parser, PM_TOKEN_EOF)) { } else if (!match1(parser, PM_TOKEN_EOF)) {
// If we get here, then we have a trailing , in a multi target node. // 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_node_t *rest = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
pm_multi_target_node_targets_append(parser, result, rest); pm_multi_target_node_targets_append(parser, result, rest);
break; break;
@ -11458,8 +11483,13 @@ parse_statements(pm_parser_t *parser, pm_context_t context) {
while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
if (context_terminator(context, &parser->current)) break; if (context_terminator(context, &parser->current)) break;
} else { } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
expect1(parser, PM_TOKEN_NEWLINE, PM_ERR_EXPECT_EOL_AFTER_STATEMENT); // 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); 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); variable = (pm_node_t *) pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0);
} else { } 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); 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); parser_lex(parser);
if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) { 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 // If we get here, then we have an end immediately after a
// start. In that case we'll create an empty content token and // start. In that case we'll create an empty content token and
// return an uninterpolated string. // return an uninterpolated string.
@ -14219,15 +14249,19 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) {
parser_lex(parser); parser_lex(parser);
} while (match1(parser, PM_TOKEN_STRING_CONTENT)); } 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); 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) { } 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)); 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)) { } 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); 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 { } 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); node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
} }
} else if (match1(parser, PM_TOKEN_STRING_CONTENT)) { } 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)) { 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); node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped);
pm_node_flag_set(node, parse_unescaped_encoding(parser)); 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)) { } 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)); node = (pm_node_t *) pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &unescaped));
} else { } 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 // If we didn't find a terminator and we didn't find a right
// parenthesis, then this is a syntax error. // parenthesis, then this is a syntax error.
if (!terminator_found) { 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. // 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)) { } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
break; break;
} else { } 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)) { switch (PM_NODE_TYPE(node)) {
case PM_BACK_REFERENCE_READ_NODE: case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_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 */ /* fallthrough */
case PM_GLOBAL_VARIABLE_READ_NODE: { case PM_GLOBAL_VARIABLE_READ_NODE: {
parser_lex(parser); 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)) { switch (PM_NODE_TYPE(node)) {
case PM_BACK_REFERENCE_READ_NODE: case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_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 */ /* fallthrough */
case PM_GLOBAL_VARIABLE_READ_NODE: { case PM_GLOBAL_VARIABLE_READ_NODE: {
parser_lex(parser); 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)) { switch (PM_NODE_TYPE(node)) {
case PM_BACK_REFERENCE_READ_NODE: case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_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 */ /* fallthrough */
case PM_GLOBAL_VARIABLE_READ_NODE: { case PM_GLOBAL_VARIABLE_READ_NODE: {
parser_lex(parser); parser_lex(parser);

View File

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

View File

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

View File

@ -15,9 +15,9 @@ module Prism
assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false) assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false)
> 1 | "%W"\u" > 1 | "%W"\u"
| ^ expected a newline or semicolon after the statement
| ^ invalid character `\` | ^ 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 ERROR
end end
end end

View File

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