Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match many more CRuby error messages #2656

Merged
merged 8 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
errors:
- ALIAS_ARGUMENT
- ALIAS_ARGUMENT_NUMBERED_REFERENCE
- AMPAMPEQ_MULTI_ASSIGN
- ARGUMENT_AFTER_BLOCK
- ARGUMENT_AFTER_FORWARDING_ELLIPSES
Expand Down Expand Up @@ -46,6 +47,7 @@ errors:
- CLASS_SUPERCLASS
- CLASS_TERM
- CLASS_UNEXPECTED_END
- CLASS_VARIABLE_BARE
- CONDITIONAL_ELSIF_PREDICATE
- CONDITIONAL_IF_PREDICATE
- CONDITIONAL_PREDICATE_TERM
Expand Down Expand Up @@ -107,6 +109,7 @@ errors:
- FOR_IN
- FOR_INDEX
- FOR_TERM
- GLOBAL_VARIABLE_BARE
- HASH_EXPRESSION_AFTER_LABEL
- HASH_KEY
- HASH_ROCKET
Expand All @@ -118,6 +121,7 @@ errors:
- INCOMPLETE_VARIABLE_CLASS_3_3_0
- INCOMPLETE_VARIABLE_INSTANCE
- INCOMPLETE_VARIABLE_INSTANCE_3_3_0
- INSTANCE_VARIABLE_BARE
- INVALID_CHARACTER
- INVALID_ENCODING_MAGIC_COMMENT
- INVALID_FLOAT_EXPONENT
Expand Down
41 changes: 32 additions & 9 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -1988,7 +1988,6 @@ pm_block_parameters_node_closing_set(pm_block_parameters_node_t *node, const pm_
*/
static pm_block_local_variable_node_t *
pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) {
assert(name->type == PM_TOKEN_IDENTIFIER || name->type == PM_TOKEN_MISSING);
pm_block_local_variable_node_t *node = PM_ALLOC_NODE(parser, pm_block_local_variable_node_t);

*node = (pm_block_local_variable_node_t) {
Expand Down Expand Up @@ -7995,8 +7994,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_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0 : PM_ERR_INVALID_VARIABLE_GLOBAL;
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id);
pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
return PM_TOKEN_GLOBAL_VARIABLE;
}

Expand Down Expand Up @@ -8067,10 +8065,11 @@ lex_global_variable(pm_parser_t *parser) {
parser->current.end += width;
} while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
} else {
// If we get here, then we have a $ followed by something that isn't
// recognized as a global variable.
// If we get here, then we have a $ followed by something that
// isn't recognized as a global variable.
pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0 : PM_ERR_INVALID_VARIABLE_GLOBAL;
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id);
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);
}

return PM_TOKEN_GLOBAL_VARIABLE;
Expand Down Expand Up @@ -9023,14 +9022,17 @@ 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 {
} else if (parser->current.end < parser->end && pm_char_is_decimal_digit(*parser->current.end)) {
pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3_0) {
diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3_0 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0;
}

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);
} else {
pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE;
pm_parser_err_token(parser, &parser->current, diag_id);
}

// If we're lexing an embedded variable, then we need to pop back into the
Expand Down Expand Up @@ -13587,7 +13589,28 @@ parse_block_parameters(

if (accept1(parser, PM_TOKEN_SEMICOLON)) {
do {
expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
switch (parser->current.type) {
case PM_TOKEN_CONSTANT:
pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
parser_lex(parser);
break;
case PM_TOKEN_INSTANCE_VARIABLE:
pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
parser_lex(parser);
break;
case PM_TOKEN_GLOBAL_VARIABLE:
pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
parser_lex(parser);
break;
case PM_TOKEN_CLASS_VARIABLE:
pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
parser_lex(parser);
break;
default:
expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
break;
}

bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
pm_parser_local_add_token(parser, &parser->previous);

Expand Down Expand Up @@ -16300,7 +16323,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
case PM_GLOBAL_VARIABLE_READ_NODE: {
if (PM_NODE_TYPE_P(old_name, PM_BACK_REFERENCE_READ_NODE) || PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE) || PM_NODE_TYPE_P(old_name, PM_GLOBAL_VARIABLE_READ_NODE)) {
if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
}
} else {
pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
Expand Down
14 changes: 9 additions & 5 deletions templates/src/diagnostic.c.erb
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,13 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {

// Errors that should raise syntax errors
[PM_ERR_ALIAS_ARGUMENT] = { "invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE] = { "invalid argument being passed to `alias`; can't make alias for the number variables", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_AMPAMPEQ_MULTI_ASSIGN] = { "unexpected `&&=` in a multiple assignment", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_AFTER_BLOCK] = { "unexpected argument after a block argument", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES] = { "unexpected argument after `...`", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_BARE_HASH] = { "unexpected bare hash argument", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_BLOCK_FORWARDING] = { "both a block argument and a forwarding argument; only one block is allowed", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_BLOCK_MULTI] = { "multiple block arguments; only one block is allowed", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_BLOCK_MULTI] = { "both block arg and actual block given; only one block is allowed", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_FORMAL_CLASS] = { "invalid formal argument; formal argument cannot be a class variable", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_FORMAL_CONSTANT] = { "invalid formal argument; formal argument cannot be a constant", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_FORMAL_GLOBAL] = { "invalid formal argument; formal argument cannot be a global variable", PM_ERROR_LEVEL_SYNTAX },
Expand Down Expand Up @@ -126,11 +127,12 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_CASE_MATCH_MISSING_PREDICATE] = { "expected a predicate for a case matching statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CASE_MISSING_CONDITIONS] = { "expected a `when` or `in` clause after `case`", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CASE_TERM] = { "expected an `end` to close the `case` statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CLASS_IN_METHOD] = { "unexpected class definition in a method definition", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CLASS_NAME] = { "expected a constant name after `class`", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CLASS_IN_METHOD] = { "unexpected class definition in method body", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CLASS_NAME] = { "unexpected constant path after `class`; class/module name must be CONSTANT", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CLASS_SUPERCLASS] = { "expected a superclass after `<`", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CLASS_TERM] = { "expected an `end` to close the `class` statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CLASS_UNEXPECTED_END] = { "unexpected `end`, expecting ';' or '\\n'", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CLASS_VARIABLE_BARE] = { "'@@' without identifiers is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CONDITIONAL_ELSIF_PREDICATE] = { "expected a predicate expression for the `elsif` statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CONDITIONAL_IF_PREDICATE] = { "expected a predicate expression for the `if` statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CONDITIONAL_PREDICATE_TERM] = { "expected `then` or `;` or '\\n'", PM_ERROR_LEVEL_SYNTAX },
Expand Down Expand Up @@ -191,6 +193,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_FOR_INDEX] = { "expected an index after `for`", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_FOR_IN] = { "expected an `in` after the index in a `for` statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_FOR_TERM] = { "expected an `end` to close the `for` loop", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_GLOBAL_VARIABLE_BARE] = { "'$' without identifiers is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_HASH_EXPRESSION_AFTER_LABEL] = { "expected an expression after the label in a hash", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_HASH_KEY] = { "unexpected %s, expecting '}' or a key in the hash literal", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_HASH_ROCKET] = { "expected a `=>` between the hash key and value", PM_ERROR_LEVEL_SYNTAX },
Expand All @@ -202,6 +205,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_INCOMPLETE_VARIABLE_CLASS] = { "'%.*s' is not allowed as a class variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "'%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INSTANCE_VARIABLE_BARE] = { "'@' without identifiers is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number", PM_ERROR_LEVEL_SYNTAX },
Expand Down Expand Up @@ -231,8 +235,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_LIST_W_UPPER_TERM] = { "expected a closing delimiter for the `%W` list", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MALLOC_FAILED] = { "failed to allocate memory", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MIXED_ENCODING] = { "UTF-8 mixed within %s source", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MODULE_IN_METHOD] = { "unexpected module definition in a method definition", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MODULE_NAME] = { "expected a constant name after `module`", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MODULE_IN_METHOD] = { "unexpected module definition in method body", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MODULE_NAME] = { "unexpected constant path after `module`; class/module name must be CONSTANT", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MODULE_TERM] = { "expected an `end` to close the `module` statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MULTI_ASSIGN_MULTI_SPLATS] = { "multiple splats in multiple assignment", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST] = { "unexpected '%.*s' resulting in multiple splats in multiple assignment", PM_ERROR_LEVEL_SYNTAX },
Expand Down
22 changes: 11 additions & 11 deletions test/prism/errors_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_module_name_recoverable
)

assert_errors expected, "module Parent module end", [
["expected a constant name after `module`", 14..20],
["unexpected constant path after `module`; class/module name must be CONSTANT", 14..20],
["unexpected 'end', assuming it is closing the parent module definition", 21..24]
]
end
Expand Down Expand Up @@ -177,7 +177,7 @@ def test_unterminated_empty_string

def test_incomplete_instance_var_string
assert_errors expression('%@#@@#'), '%@#@@#', [
["'@#' is not allowed as an instance variable name", 4..5],
["'@' without identifiers is not allowed as an instance variable name", 4..5],
["unexpected instance variable, expecting end-of-input", 4..5]
]
end
Expand Down Expand Up @@ -325,7 +325,7 @@ def test_aliasing_non_global_variable_with_global_variable

def test_aliasing_global_variable_with_global_number_variable
assert_errors expression("alias $a $1"), "alias $a $1", [
["invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", 9..11]
["invalid argument being passed to `alias`; can't make alias for the number variables", 9..11]
]
end

Expand Down Expand Up @@ -452,7 +452,7 @@ def test_module_definition_in_method_body
)

assert_errors expected, "def foo;module A;end;end", [
["unexpected module definition in a method definition", 8..14]
["unexpected module definition in method body", 8..14]
]
end

Expand Down Expand Up @@ -490,7 +490,7 @@ def test_module_definition_in_method_body_within_block
Location()
)

assert_errors expected, <<~RUBY, [["unexpected module definition in a method definition", 21..27]]
assert_errors expected, <<~RUBY, [["unexpected module definition in method body", 21..27]]
def foo
bar do
module Foo;end
Expand All @@ -505,7 +505,7 @@ def foo(bar = module A;end);end
def foo;rescue;module A;end;end
def foo;ensure;module A;end;end
RUBY
message = "unexpected module definition in a method definition"
message = "unexpected module definition in method body"
assert_errors expression(source), source, [
[message, 14..20],
[message, 47..53],
Expand Down Expand Up @@ -541,7 +541,7 @@ def test_class_definition_in_method_body
)

assert_errors expected, "def foo;class A;end;end", [
["unexpected class definition in a method definition", 8..13]
["unexpected class definition in method body", 8..13]
]
end

Expand All @@ -551,7 +551,7 @@ def foo(bar = class A;end);end
def foo;rescue;class A;end;end
def foo;ensure;class A;end;end
RUBY
message = "unexpected class definition in a method definition"
message = "unexpected class definition in method body"
assert_errors expression(source), source, [
[message, 14..19],
[message, 46..51],
Expand Down Expand Up @@ -1254,7 +1254,7 @@ def test_invalid_operator_write_dot

def test_unterminated_global_variable
assert_errors expression("$"), "$", [
["'$' is not allowed as a global variable name", 0..1]
["'$' without identifiers is not allowed as a global variable name", 0..1]
]
end

Expand Down Expand Up @@ -1438,7 +1438,7 @@ def test_parameter_name_ending_with_bang_or_question_mark
def test_class_name
source = "class 0.X end"
assert_errors expression(source), source, [
["expected a constant name after `class`", 6..9],
["unexpected constant path after `class`; class/module name must be CONSTANT", 6..9],
]
end

Expand Down Expand Up @@ -2060,7 +2060,7 @@ def test_non_assoc_equality
def test_block_arg_and_block
source = 'foo(&1) { }'
assert_errors expression(source), source, [
['multiple block arguments; only one block is allowed', 8..11]
["both block arg and actual block given; only one block is allowed", 8..11]
]
end

Expand Down
Loading