ruby-changes:48674
From: nobu <ko1@a...>
Date: Thu, 16 Nov 2017 14:36:46 +0900 (JST)
Subject: [ruby-changes:48674] nobu:r60790 (trunk): parse.y: no return in class
nobu 2017-11-16 14:36:42 +0900 (Thu, 16 Nov 2017) New Revision: 60790 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=60790 Log: parse.y: no return in class * parse.y (k_return): prohibit return in class/module body except for singleton class. Modified files: trunk/parse.y trunk/spec/ruby/language/return_spec.rb trunk/test/ruby/test_class.rb trunk/test/ruby/test_syntax.rb Index: test/ruby/test_syntax.rb =================================================================== --- test/ruby/test_syntax.rb (revision 60789) +++ test/ruby/test_syntax.rb (revision 60790) @@ -1014,12 +1014,22 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L1014 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 Index: test/ruby/test_class.rb =================================================================== --- test/ruby/test_class.rb (revision 60789) +++ test/ruby/test_class.rb (revision 60790) @@ -241,15 +241,23 @@ class TestClass < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_class.rb#L241 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 Index: parse.y =================================================================== --- parse.y (revision 60789) +++ parse.y (revision 60790) @@ -243,6 +243,7 @@ struct parser_params { https://github.com/ruby/ruby/blob/trunk/parse.y#L243 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_ https://github.com/ruby/ruby/blob/trunk/parse.y#L300 #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 %pre https://github.com/ruby/ruby/blob/trunk/parse.y#L1702 $$ = dispatch1(yield, $2); %*/ } - | keyword_return call_args + | k_return call_args { /*%%%*/ $$ = NEW_RETURN(ret_args($2)); @@ -2737,7 +2739,7 @@ primary : literal https://github.com/ruby/ruby/blob/trunk/parse.y#L2739 $$ = dispatch1(hash, escape_Qundef($2)); %*/ } - | keyword_return + | k_return { /*%%%*/ $$ = NEW_RETURN(0); @@ -2937,6 +2939,8 @@ primary : literal https://github.com/ruby/ruby/blob/trunk/parse.y#L2939 { if (in_def) yyerror0("class definition in method body"); + $<num>1 = in_class; + in_class = 1; local_push(0); /*%%%*/ $<num>$ = ruby_sourceline; @@ -2956,11 +2960,13 @@ primary : literal https://github.com/ruby/ruby/blob/trunk/parse.y#L2960 $$ = dispatch3(class, $2, $3, $5); %*/ local_pop(); + in_class = $<num>1 & 1; } | k_class tLSHFT expr { - $<num>$ = in_def; + $<num>$ = (in_class << 1) | in_def; in_def = 0; + in_class = 0; local_push(0); } term @@ -2978,11 +2984,14 @@ primary : literal https://github.com/ruby/ruby/blob/trunk/parse.y#L2984 %*/ local_pop(); in_def = $<num>4 & 1; + in_class = ($<num>4 >> 1) & 1; } | k_module cpath { if (in_def) yyerror0("module definition in method body"); + $<num>1 = in_class; + in_class = 1; local_push(0); /*%%%*/ $<num>$ = ruby_sourceline; @@ -3002,6 +3011,7 @@ primary : literal https://github.com/ruby/ruby/blob/trunk/parse.y#L3011 $$ = dispatch2(module, $2, $4); %*/ local_pop(); + in_class = $<num>1 & 1; } | k_def fname { @@ -3184,6 +3194,13 @@ k_end : keyword_end https://github.com/ruby/ruby/blob/trunk/parse.y#L3194 } ; +k_return : keyword_return + { + if (in_class && !in_def && !dyna_in_block()) + yyerror0("Invalid return in class/module body"); + } + ; + then : term /*%c%*/ /*%c Index: spec/ruby/language/return_spec.rb =================================================================== --- spec/ruby/language/return_spec.rb (revision 60789) +++ spec/ruby/language/return_spec.rb (revision 60790) @@ -409,22 +409,6 @@ describe "The return keyword" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/language/return_spec.rb#L409 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) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/