[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]