mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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
This commit is contained in:
parent
7a666c6766
commit
8c15f40acf
4 changed files with 45 additions and 26 deletions
23
parse.y
23
parse.y
|
@ -243,6 +243,7 @@ struct parser_params {
|
||||||
unsigned int in_main: 1;
|
unsigned int in_main: 1;
|
||||||
unsigned int in_kwarg: 1;
|
unsigned int in_kwarg: 1;
|
||||||
unsigned int in_def: 1;
|
unsigned int in_def: 1;
|
||||||
|
unsigned int in_class: 1;
|
||||||
unsigned int token_seen: 1;
|
unsigned int token_seen: 1;
|
||||||
unsigned int token_info_enabled: 1;
|
unsigned int token_info_enabled: 1;
|
||||||
# if WARN_PAST_SCOPE
|
# 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 lpar_beg (parser->lex.lpar_beg)
|
||||||
#define brace_nest (parser->lex.brace_nest)
|
#define brace_nest (parser->lex.brace_nest)
|
||||||
#define in_def (parser->in_def)
|
#define in_def (parser->in_def)
|
||||||
|
#define in_class (parser->in_class)
|
||||||
#define in_main (parser->in_main)
|
#define in_main (parser->in_main)
|
||||||
#define in_defined (parser->in_defined)
|
#define in_defined (parser->in_defined)
|
||||||
#define tokenbuf (parser->tokenbuf)
|
#define tokenbuf (parser->tokenbuf)
|
||||||
|
@ -1700,7 +1702,7 @@ command : fcall command_args %prec tLOWEST
|
||||||
$$ = dispatch1(yield, $2);
|
$$ = dispatch1(yield, $2);
|
||||||
%*/
|
%*/
|
||||||
}
|
}
|
||||||
| keyword_return call_args
|
| k_return call_args
|
||||||
{
|
{
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
$$ = NEW_RETURN(ret_args($2));
|
$$ = NEW_RETURN(ret_args($2));
|
||||||
|
@ -2737,7 +2739,7 @@ primary : literal
|
||||||
$$ = dispatch1(hash, escape_Qundef($2));
|
$$ = dispatch1(hash, escape_Qundef($2));
|
||||||
%*/
|
%*/
|
||||||
}
|
}
|
||||||
| keyword_return
|
| k_return
|
||||||
{
|
{
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
$$ = NEW_RETURN(0);
|
$$ = NEW_RETURN(0);
|
||||||
|
@ -2937,6 +2939,8 @@ primary : literal
|
||||||
{
|
{
|
||||||
if (in_def)
|
if (in_def)
|
||||||
yyerror0("class definition in method body");
|
yyerror0("class definition in method body");
|
||||||
|
$<num>1 = in_class;
|
||||||
|
in_class = 1;
|
||||||
local_push(0);
|
local_push(0);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
$<num>$ = ruby_sourceline;
|
$<num>$ = ruby_sourceline;
|
||||||
|
@ -2956,11 +2960,13 @@ primary : literal
|
||||||
$$ = dispatch3(class, $2, $3, $5);
|
$$ = dispatch3(class, $2, $3, $5);
|
||||||
%*/
|
%*/
|
||||||
local_pop();
|
local_pop();
|
||||||
|
in_class = $<num>1 & 1;
|
||||||
}
|
}
|
||||||
| k_class tLSHFT expr
|
| k_class tLSHFT expr
|
||||||
{
|
{
|
||||||
$<num>$ = in_def;
|
$<num>$ = (in_class << 1) | in_def;
|
||||||
in_def = 0;
|
in_def = 0;
|
||||||
|
in_class = 0;
|
||||||
local_push(0);
|
local_push(0);
|
||||||
}
|
}
|
||||||
term
|
term
|
||||||
|
@ -2978,11 +2984,14 @@ primary : literal
|
||||||
%*/
|
%*/
|
||||||
local_pop();
|
local_pop();
|
||||||
in_def = $<num>4 & 1;
|
in_def = $<num>4 & 1;
|
||||||
|
in_class = ($<num>4 >> 1) & 1;
|
||||||
}
|
}
|
||||||
| k_module cpath
|
| k_module cpath
|
||||||
{
|
{
|
||||||
if (in_def)
|
if (in_def)
|
||||||
yyerror0("module definition in method body");
|
yyerror0("module definition in method body");
|
||||||
|
$<num>1 = in_class;
|
||||||
|
in_class = 1;
|
||||||
local_push(0);
|
local_push(0);
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
$<num>$ = ruby_sourceline;
|
$<num>$ = ruby_sourceline;
|
||||||
|
@ -3002,6 +3011,7 @@ primary : literal
|
||||||
$$ = dispatch2(module, $2, $4);
|
$$ = dispatch2(module, $2, $4);
|
||||||
%*/
|
%*/
|
||||||
local_pop();
|
local_pop();
|
||||||
|
in_class = $<num>1 & 1;
|
||||||
}
|
}
|
||||||
| k_def fname
|
| 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
|
then : term
|
||||||
/*%c%*/
|
/*%c%*/
|
||||||
/*%c
|
/*%c
|
||||||
|
|
|
@ -409,22 +409,6 @@ describe "The return keyword" do
|
||||||
end
|
end
|
||||||
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
|
describe "file loading" do
|
||||||
it "stops file loading and execution" do
|
it "stops file loading and execution" do
|
||||||
File.write(@filename, <<-END_OF_CODE)
|
File.write(@filename, <<-END_OF_CODE)
|
||||||
|
|
|
@ -241,15 +241,23 @@ class TestClass < Test::Unit::TestCase
|
||||||
assert_equal("TestClass::C\u{df}", c.name, '[ruby-core:24600]')
|
assert_equal("TestClass::C\u{df}", c.name, '[ruby-core:24600]')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_invalid_jump_from_class_definition
|
def test_invalid_next_from_class_definition
|
||||||
assert_raise(SyntaxError) { eval("class C; next; end") }
|
assert_raise(SyntaxError) { eval("class C; next; end") }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_invalid_break_from_class_definition
|
||||||
assert_raise(SyntaxError) { eval("class C; break; end") }
|
assert_raise(SyntaxError) { eval("class C; break; end") }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_invalid_redo_from_class_definition
|
||||||
assert_raise(SyntaxError) { eval("class C; redo; end") }
|
assert_raise(SyntaxError) { eval("class C; redo; end") }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_invalid_retry_from_class_definition
|
||||||
assert_raise(SyntaxError) { eval("class C; retry; end") }
|
assert_raise(SyntaxError) { eval("class C; retry; end") }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_invalid_return_from_class_definition
|
def test_invalid_return_from_class_definition
|
||||||
skip "Wrongly return from this method"
|
|
||||||
assert_raise(SyntaxError) { eval("class C; return; end") }
|
assert_raise(SyntaxError) { eval("class C; return; end") }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1014,12 +1014,22 @@ eom
|
||||||
Tempfile.create(%w"test_return_ .rb") do |lib|
|
Tempfile.create(%w"test_return_ .rb") do |lib|
|
||||||
lib.close
|
lib.close
|
||||||
args = %W[-W0 -r#{lib.path}]
|
args = %W[-W0 -r#{lib.path}]
|
||||||
all_assertions_foreach(feature4840, *[true, false].product(code)) do |main, (n, s, *ex)|
|
all_assertions_foreach(feature4840, *[:main, :lib].product([:class, :top], code)) do |main, klass, (n, s, *ex)|
|
||||||
if main
|
if klass == :class
|
||||||
assert_in_out_err(%[-W0], s, ex, [], proc {failed[n, s]}, success: true)
|
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
|
else
|
||||||
File.write(lib, s)
|
if main == :main
|
||||||
assert_in_out_err(args, "", ex, [], proc {failed[n, s]}, success: true)
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue