From e8896a31d48e5797df3878696dcb50aed85b87c2 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 13 Sep 2023 21:55:12 +0900 Subject: [PATCH] [Bug #19877] Literals cannot have singleton methods even in blocks --- parse.y | 15 +++++++++++++-- test/ruby/test_parse.rb | 25 ++++++++++++++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/parse.y b/parse.y index d48d74db0f..e675793271 100644 --- a/parse.y +++ b/parse.y @@ -917,6 +917,15 @@ set_embraced_location(NODE *node, const rb_code_location_t *beg, const rb_code_l nd_set_line(node, beg->end_pos.lineno); } +static NODE * +last_expr_node(NODE *expr) +{ + if (nd_type_p(expr, NODE_BLOCK)) { + expr = expr->nd_end->nd_head; + } + return expr; +} + #define yyparse ruby_yyparse static NODE* cond(struct parser_params *p, NODE *node, const YYLTYPE *loc); @@ -6050,16 +6059,18 @@ singleton : var_ref | '(' {SET_LEX_STATE(EXPR_BEG);} expr rparen { /*%%%*/ - switch (nd_type($3)) { + NODE *expr = last_expr_node($3); + switch (nd_type(expr)) { case NODE_STR: case NODE_DSTR: case NODE_XSTR: case NODE_DXSTR: case NODE_DREGX: case NODE_LIT: + case NODE_DSYM: case NODE_LIST: case NODE_ZLIST: - yyerror1(&@3, "can't define singleton method for literals"); + yyerror1(&expr->nd_loc, "can't define singleton method for literals"); break; default: value_expr($3); diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 957a37eb81..5209a4839f 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -453,11 +453,16 @@ class TestParse < Test::Unit::TestCase end def test_define_singleton_error - assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /singleton method for literals/) do - begin; - def ("foo").foo; end - end; - end + msg = /singleton method for literals/ + assert_parse_error(%q[def ("foo").foo; end], msg) + assert_parse_error(%q[def (1).foo; end], msg) + assert_parse_error(%q[def ((1;1)).foo; end], msg) + assert_parse_error(%q[def ((;1)).foo; end], msg) + assert_parse_error(%q[def ((1+1;1)).foo; end], msg) + assert_parse_error(%q[def ((%s();1)).foo; end], msg) + assert_parse_error(%q[def ((%w();1)).foo; end], msg) + assert_parse_error(%q[def ("#{42}").foo; end], msg) + assert_parse_error(%q[def (:"#{42}").foo; end], msg) end def test_op_asgn1_with_block @@ -1453,4 +1458,14 @@ x = __ENCODING__ assert_warning(/past scope/) {catch {|tag| eval("BEGIN{throw tag}; tap {a = 1}; a")}} end =end + + def assert_parse(code) + assert_kind_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.parse(code)) + end + + def assert_parse_error(code, message) + assert_raise_with_message(SyntaxError, message) do + RubyVM::AbstractSyntaxTree.parse(code) + end + end end