diff --git a/prism/diagnostic.c b/prism/diagnostic.c index bf89ca781a..815a2b2a6c 100644 --- a/prism/diagnostic.c +++ b/prism/diagnostic.c @@ -261,6 +261,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = { [PM_ERR_RESCUE_TERM] = { "expected a closing delimiter for the `rescue` clause", PM_ERROR_LEVEL_FATAL }, [PM_ERR_RESCUE_VARIABLE] = { "expected an exception variable after `=>` in a rescue statement", PM_ERROR_LEVEL_FATAL }, [PM_ERR_RETURN_INVALID] = { "invalid `return` in a class or module body", PM_ERROR_LEVEL_FATAL }, + [PM_ERR_SINGLETON_FOR_LITERALS] = { "cannot define singleton method for literals", PM_ERROR_LEVEL_FATAL }, [PM_ERR_STATEMENT_ALIAS] = { "unexpected an `alias` at a non-statement position", PM_ERROR_LEVEL_FATAL }, [PM_ERR_STATEMENT_POSTEXE_END] = { "unexpected an `END` at a non-statement position", PM_ERROR_LEVEL_FATAL }, [PM_ERR_STATEMENT_PREEXE_BEGIN] = { "unexpected a `BEGIN` at a non-statement position", PM_ERROR_LEVEL_FATAL }, diff --git a/prism/diagnostic.h b/prism/diagnostic.h index 33123262b5..8f09fb9099 100644 --- a/prism/diagnostic.h +++ b/prism/diagnostic.h @@ -259,6 +259,7 @@ typedef enum { PM_ERR_RESCUE_TERM, PM_ERR_RESCUE_VARIABLE, PM_ERR_RETURN_INVALID, + PM_ERR_SINGLETON_FOR_LITERALS, PM_ERR_STATEMENT_ALIAS, PM_ERR_STATEMENT_POSTEXE_END, PM_ERR_STATEMENT_PREEXE_BEGIN, diff --git a/prism/prism.c b/prism/prism.c index aaa0035104..2f54e9ee93 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -2743,6 +2743,45 @@ pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *targ return node; } +/** + * Check if the receiver of a `def` node is allowed. + */ +static void +pm_check_def_receiver(pm_parser_t *parser, pm_node_t *receiver) { + switch (receiver->type) { + case PM_BEGIN_NODE: { + pm_begin_node_t *begin_node = (pm_begin_node_t *)receiver; + pm_check_def_receiver(parser, (pm_node_t *) begin_node->statements); + break; + } + case PM_PARENTHESES_NODE: + pm_check_def_receiver(parser, ((pm_parentheses_node_t *) receiver)->body); + break; + case PM_STATEMENTS_NODE: { + pm_statements_node_t *statements_node = (pm_statements_node_t *)receiver; + pm_check_def_receiver(parser, statements_node->body.nodes[statements_node->body.size - 1]); + break; + } + case PM_ARRAY_NODE: + case PM_FLOAT_NODE: + case PM_IMAGINARY_NODE: + case PM_INTEGER_NODE: + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + case PM_INTERPOLATED_STRING_NODE: + case PM_INTERPOLATED_SYMBOL_NODE: + case PM_INTERPOLATED_X_STRING_NODE: + case PM_RATIONAL_NODE: + case PM_REGULAR_EXPRESSION_NODE: + case PM_SOURCE_ENCODING_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_STRING_NODE: + case PM_SYMBOL_NODE: + case PM_X_STRING_NODE: + pm_parser_err_node(parser, receiver, PM_ERR_SINGLETON_FOR_LITERALS); + } +} + /** * Allocate and initialize a new DefNode node. */ @@ -2771,6 +2810,10 @@ pm_def_node_create( end = end_keyword->end; } + if ((receiver != NULL) && PM_NODE_TYPE_P(receiver, PM_PARENTHESES_NODE)) { + pm_check_def_receiver(parser, receiver); + } + *node = (pm_def_node_t) { { .type = PM_DEF_NODE, diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 62e72ea16a..c65fff5167 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -2087,6 +2087,52 @@ module Prism assert_errors expression(source), source, errors, compare_ripper: false end + def test_singleton_method_for_literals + source = <<~'RUBY' + def (1).g; end + def ((a; 1)).foo; end + def ((return; 1)).bar; end + def (((1))).foo; end + def (__FILE__).foo; end + def (__ENCODING__).foo; end + def (__LINE__).foo; end + def ("foo").foo; end + def (3.14).foo; end + def (3.14i).foo; end + def (:foo).foo; end + def (:'foo').foo; end + def (:'f{o}').foo; end + def ('foo').foo; end + def ("foo").foo; end + def ("#{fo}o").foo; end + def (/foo/).foo; end + def (/f#{oo}/).foo; end + def ([1]).foo; end + RUBY + errors = [ + ["cannot define singleton method for literals", 5..6], + ["cannot define singleton method for literals", 24..25], + ["cannot define singleton method for literals", 51..52], + ["cannot define singleton method for literals", 71..72], + ["cannot define singleton method for literals", 90..98], + ["cannot define singleton method for literals", 114..126], + ["cannot define singleton method for literals", 142..150], + ["cannot define singleton method for literals", 166..171], + ["cannot define singleton method for literals", 187..191], + ["cannot define singleton method for literals", 207..212], + ["cannot define singleton method for literals", 228..232], + ["cannot define singleton method for literals", 248..254], + ["cannot define singleton method for literals", 270..277], + ["cannot define singleton method for literals", 293..298], + ["cannot define singleton method for literals", 314..319], + ["cannot define singleton method for literals", 335..343], + ["cannot define singleton method for literals", 359..364], + ["cannot define singleton method for literals", 380..388], + ["cannot define singleton method for literals", 404..407] + ] + assert_errors expression(source), source, errors, compare_ripper: false + end + private def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")