Skip to content

Commit

Permalink
Merge pull request #2656 from ruby/error-messages
Browse files Browse the repository at this point in the history
Match many more CRuby error messages
  • Loading branch information
kddnewton authored Apr 1, 2024
2 parents 7fcbfda + 0cc3a9d commit b36d1c7
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 25 deletions.
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

0 comments on commit b36d1c7

Please sign in to comment.