From 8c15f40acfd4171419d15b2516674a067d7d999f Mon Sep 17 00:00:00 2001 From: nobu Date: Thu, 16 Nov 2017 05:36:42 +0000 Subject: [PATCH] parse.y: no return in class * parse.y (k_return): prohibit return in class/module body except for singleton class. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60790 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- parse.y | 23 ++++++++++++++++++++--- spec/ruby/language/return_spec.rb | 16 ---------------- test/ruby/test_class.rb | 12 ++++++++++-- test/ruby/test_syntax.rb | 20 +++++++++++++++----- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/parse.y b/parse.y index 120b935d39..c3756c52a3 100644 --- a/parse.y +++ b/parse.y @@ -243,6 +243,7 @@ struct parser_params { unsigned int in_main: 1; unsigned int in_kwarg: 1; unsigned int in_def: 1; + unsigned int in_class: 1; unsigned int token_seen: 1; unsigned int token_info_enabled: 1; # if WARN_PAST_SCOPE @@ -299,6 +300,7 @@ static int parser_yyerror(struct parser_params*, const char*); #define lpar_beg (parser->lex.lpar_beg) #define brace_nest (parser->lex.brace_nest) #define in_def (parser->in_def) +#define in_class (parser->in_class) #define in_main (parser->in_main) #define in_defined (parser->in_defined) #define tokenbuf (parser->tokenbuf) @@ -1700,7 +1702,7 @@ command : fcall command_args %prec tLOWEST $$ = dispatch1(yield, $2); %*/ } - | keyword_return call_args + | k_return call_args { /*%%%*/ $$ = NEW_RETURN(ret_args($2)); @@ -2737,7 +2739,7 @@ primary : literal $$ = dispatch1(hash, escape_Qundef($2)); %*/ } - | keyword_return + | k_return { /*%%%*/ $$ = NEW_RETURN(0); @@ -2937,6 +2939,8 @@ primary : literal { if (in_def) yyerror0("class definition in method body"); + $1 = in_class; + in_class = 1; local_push(0); /*%%%*/ $$ = ruby_sourceline; @@ -2956,11 +2960,13 @@ primary : literal $$ = dispatch3(class, $2, $3, $5); %*/ local_pop(); + in_class = $1 & 1; } | k_class tLSHFT expr { - $$ = in_def; + $$ = (in_class << 1) | in_def; in_def = 0; + in_class = 0; local_push(0); } term @@ -2978,11 +2984,14 @@ primary : literal %*/ local_pop(); in_def = $4 & 1; + in_class = ($4 >> 1) & 1; } | k_module cpath { if (in_def) yyerror0("module definition in method body"); + $1 = in_class; + in_class = 1; local_push(0); /*%%%*/ $$ = ruby_sourceline; @@ -3002,6 +3011,7 @@ primary : literal $$ = dispatch2(module, $2, $4); %*/ local_pop(); + in_class = $1 & 1; } | k_def fname { @@ -3184,6 +3194,13 @@ k_end : keyword_end } ; +k_return : keyword_return + { + if (in_class && !in_def && !dyna_in_block()) + yyerror0("Invalid return in class/module body"); + } + ; + then : term /*%c%*/ /*%c diff --git a/spec/ruby/language/return_spec.rb b/spec/ruby/language/return_spec.rb index 6a98fa6d12..a203b79078 100644 --- a/spec/ruby/language/return_spec.rb +++ b/spec/ruby/language/return_spec.rb @@ -409,22 +409,6 @@ describe "The return keyword" do end end - describe "within a class" do - it "is allowed" do - File.write(@filename, <<-END_OF_CODE) - class A - ScratchPad << "before return" - return - - ScratchPad << "after return" - end - END_OF_CODE - - load @filename - ScratchPad.recorded.should == ["before return"] - end - end - describe "file loading" do it "stops file loading and execution" do File.write(@filename, <<-END_OF_CODE) diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 0986e64fbb..bb8a3c574d 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -241,15 +241,23 @@ class TestClass < Test::Unit::TestCase assert_equal("TestClass::C\u{df}", c.name, '[ruby-core:24600]') end - def test_invalid_jump_from_class_definition + def test_invalid_next_from_class_definition assert_raise(SyntaxError) { eval("class C; next; end") } + end + + def test_invalid_break_from_class_definition assert_raise(SyntaxError) { eval("class C; break; end") } + end + + def test_invalid_redo_from_class_definition assert_raise(SyntaxError) { eval("class C; redo; end") } + end + + def test_invalid_retry_from_class_definition assert_raise(SyntaxError) { eval("class C; retry; end") } end def test_invalid_return_from_class_definition - skip "Wrongly return from this method" assert_raise(SyntaxError) { eval("class C; return; end") } end diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 2014e289d3..bb03dad999 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -1014,12 +1014,22 @@ eom Tempfile.create(%w"test_return_ .rb") do |lib| lib.close args = %W[-W0 -r#{lib.path}] - all_assertions_foreach(feature4840, *[true, false].product(code)) do |main, (n, s, *ex)| - if main - assert_in_out_err(%[-W0], s, ex, [], proc {failed[n, s]}, success: true) + all_assertions_foreach(feature4840, *[:main, :lib].product([:class, :top], code)) do |main, klass, (n, s, *ex)| + if klass == :class + s = "class X; #{s}; end" + if main == :main + assert_in_out_err(%[-W0], s, [], /return/, proc {failed[n, s]}, success: false) + else + File.write(lib, s) + assert_in_out_err(args, "", [], /return/, proc {failed[n, s]}, success: false) + end else - File.write(lib, s) - assert_in_out_err(args, "", ex, [], proc {failed[n, s]}, success: true) + if main == :main + assert_in_out_err(%[-W0], s, ex, [], proc {failed[n, s]}, success: true) + else + File.write(lib, s) + assert_in_out_err(args, "", ex, [], proc {failed[n, s]}, success: true) + end end end end