aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2023-09-11 01:02:34 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2023-09-27 21:53:01 +0900
commit29e5fca7181de5f0db372374d5f39704f81f2731 (patch)
tree1bd3d238ff89ec5c830acb543db80b55060fb53b
parentff8278e52c08e6b7bc33ba994b996595b1b65833 (diff)
downloadruby-29e5fca7181de5f0db372374d5f39704f81f2731.tar.gz
Syntax check of `retry` in the parser
-rw-r--r--parse.y96
-rw-r--r--test/ruby/test_ast.rb13
-rw-r--r--test/ruby/test_rubyoptions.rb2
-rw-r--r--test/yarp/parse_test.rb4
4 files changed, 86 insertions, 29 deletions
diff --git a/parse.y b/parse.y
index 8fd0e39ee9..a7c3e9b608 100644
--- a/parse.y
+++ b/parse.y
@@ -277,6 +277,13 @@ enum shareability {
shareable_everything,
};
+enum rescue_context {
+ before_rescue,
+ after_rescue,
+ after_else,
+ after_ensure,
+};
+
struct lex_context {
unsigned int in_defined: 1;
unsigned int in_kwarg: 1;
@@ -284,6 +291,7 @@ struct lex_context {
unsigned int in_def: 1;
unsigned int in_class: 1;
BITFIELD(enum shareability, shareable_constant_value, 2);
+ BITFIELD(enum rescue_context, in_rescue, 2);
};
#if defined(__GNUC__) && !defined(__clang__)
@@ -1358,6 +1366,7 @@ restore_defun(struct parser_params *p, NODE *name)
p->cur_arg = name->nd_vid;
p->ctxt.in_def = c.ctxt.in_def;
p->ctxt.shareable_constant_value = c.ctxt.shareable_constant_value;
+ p->ctxt.in_rescue = c.ctxt.in_rescue;
p->max_numparam = (int)save->nd_nth;
numparam_pop(p, save->nd_head);
clear_block_exit(p, true);
@@ -1712,7 +1721,7 @@ static int looking_at_eol_p(struct parser_params *p);
%type <id> f_kwrest f_label f_arg_asgn call_op call_op2 reswords relop dot_or_colon
%type <id> p_kwrest p_kwnorest p_any_kwrest p_kw_label
%type <id> f_no_kwarg f_any_kwrest args_forward excessed_comma nonlocal_var
-%type <ctxt> lex_ctxt begin_defined k_class k_module k_END
+%type <ctxt> lex_ctxt begin_defined k_class k_module k_END k_rescue k_ensure after_rescue
%type <tbl> p_lparen p_lbracket
%token END_OF_INPUT 0 "end-of-input"
%token <id> '.'
@@ -1888,7 +1897,11 @@ begin_block : block_open top_compstmt '}'
bodystmt : compstmt
opt_rescue
- k_else {if (!$2) {yyerror1(&@3, "else without rescue is useless");}}
+ k_else
+ {
+ if (!$2) yyerror1(&@3, "else without rescue is useless");
+ p->ctxt.in_rescue = after_else;
+ }
compstmt
opt_ensure
{
@@ -2038,19 +2051,21 @@ stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
/*% %*/
/*% ripper: until_mod!($3, $1) %*/
}
- | stmt modifier_rescue stmt
+ | stmt modifier_rescue after_rescue stmt
{
+ p->ctxt.in_rescue = $3.in_rescue;
/*%%%*/
NODE *resq;
- YYLTYPE loc = code_loc_gen(&@2, &@3);
- resq = NEW_RESBODY(0, remove_begin($3), 0, &loc);
+ YYLTYPE loc = code_loc_gen(&@2, &@4);
+ resq = NEW_RESBODY(0, remove_begin($4), 0, &loc);
$$ = NEW_RESCUE(remove_begin($1), resq, 0, &@$);
/*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ /*% ripper: rescue_mod!($1, $4) %*/
}
| k_END
{
$<node>$ = allow_block_exit(p);
+ p->ctxt.in_rescue = before_rescue;
}[exits]
'{' compstmt '}'
{
@@ -2084,16 +2099,18 @@ stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
/*% %*/
/*% ripper: assign!($1, $4) %*/
}
- | mlhs '=' lex_ctxt mrhs_arg modifier_rescue stmt
+ | mlhs '=' lex_ctxt mrhs_arg modifier_rescue
+ after_rescue stmt[resbody]
{
+ p->ctxt.in_rescue = $3.in_rescue;
/*%%%*/
- YYLTYPE loc = code_loc_gen(&@5, &@6);
- NODE *resbody = NEW_RESBODY(0, remove_begin($6), 0, &loc);
- loc.beg_pos = @4.beg_pos;
- NODE *rhs = NEW_RESCUE($4, resbody, 0, &loc);
- $$ = node_assign(p, $1, rhs, $3, &@$);
+ YYLTYPE loc = code_loc_gen(&@modifier_rescue, &@resbody);
+ $resbody = NEW_RESBODY(0, remove_begin($resbody), 0, &loc);
+ loc.beg_pos = @mrhs_arg.beg_pos;
+ $mrhs_arg = NEW_RESCUE($mrhs_arg, $resbody, 0, &loc);
+ $$ = node_assign(p, $mlhs, $mrhs_arg, $lex_ctxt, &@$);
/*% %*/
- /*% ripper: massign!($1, rescue_mod!($4, $6)) %*/
+ /*% ripper: massign!($1, rescue_mod!($4, $7)) %*/
}
| mlhs '=' lex_ctxt mrhs_arg
{
@@ -2198,12 +2215,13 @@ command_asgn : lhs '=' lex_ctxt command_rhs
;
endless_command : command
- | endless_command modifier_rescue arg
+ | endless_command modifier_rescue after_rescue arg
{
+ p->ctxt.in_rescue = $3.in_rescue;
/*%%%*/
- $$ = rescued_expr(p, $1, $3, &@1, &@2, &@3);
+ $$ = rescued_expr(p, $1, $4, &@1, &@2, &@4);
/*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ /*% ripper: rescue_mod!($1, $4) %*/
}
| keyword_not opt_nl endless_command
{
@@ -2216,14 +2234,15 @@ command_rhs : command_call %prec tOP_ASGN
value_expr($1);
$$ = $1;
}
- | command_call modifier_rescue stmt
+ | command_call modifier_rescue after_rescue stmt
{
+ p->ctxt.in_rescue = $3.in_rescue;
/*%%%*/
- YYLTYPE loc = code_loc_gen(&@2, &@3);
+ YYLTYPE loc = code_loc_gen(&@2, &@4);
value_expr($1);
- $$ = NEW_RESCUE($1, NEW_RESBODY(0, remove_begin($3), 0, &loc), 0, &@$);
+ $$ = NEW_RESCUE($1, NEW_RESBODY(0, remove_begin($4), 0, &loc), 0, &@$);
/*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ /*% ripper: rescue_mod!($1, $4) %*/
}
| command_asgn
;
@@ -2306,6 +2325,7 @@ def_name : fname
local_push(p, 0);
p->cur_arg = 0;
p->ctxt.in_def = 1;
+ p->ctxt.in_rescue = before_rescue;
$<node>$ = NEW_NODE(NODE_SELF, /*vid*/cur_arg, /*mid*/fname, /*args*/save, &@$);
/*%%%*/
/*%
@@ -3137,12 +3157,13 @@ arg : lhs '=' lex_ctxt arg_rhs
;
endless_arg : arg %prec modifier_rescue
- | endless_arg modifier_rescue arg
+ | endless_arg modifier_rescue after_rescue arg
{
+ p->ctxt.in_rescue = $3.in_rescue;
/*%%%*/
- $$ = rescued_expr(p, $1, $3, &@1, &@2, &@3);
+ $$ = rescued_expr(p, $1, $4, &@1, &@2, &@4);
/*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ /*% ripper: rescue_mod!($1, $4) %*/
}
| keyword_not opt_nl endless_arg
{
@@ -3180,6 +3201,13 @@ begin_defined : lex_ctxt
}
;
+after_rescue : lex_ctxt
+ {
+ p->ctxt.in_rescue = after_rescue;
+ $$ = $1;
+ }
+ ;
+
arg_value : arg
{
value_expr($1);
@@ -3213,13 +3241,14 @@ arg_rhs : arg %prec tOP_ASGN
value_expr($1);
$$ = $1;
}
- | arg modifier_rescue arg
+ | arg modifier_rescue after_rescue arg
{
+ p->ctxt.in_rescue = $3.in_rescue;
/*%%%*/
value_expr($1);
- $$ = rescued_expr(p, $1, $3, &@1, &@2, &@3);
+ $$ = rescued_expr(p, $1, $4, &@1, &@2, &@4);
/*% %*/
- /*% ripper: rescue_mod!($1, $3) %*/
+ /*% ripper: rescue_mod!($1, $4) %*/
}
;
@@ -3836,6 +3865,12 @@ primary : literal
}
| keyword_retry
{
+ switch (p->ctxt.in_rescue) {
+ case before_rescue: yyerror1(&@1, "Invalid retry without rescue"); break;
+ case after_rescue: /* ok */ break;
+ case after_else: yyerror1(&@1, "Invalid retry after else"); break;
+ case after_ensure: yyerror1(&@1, "Invalid retry after ensure"); break;
+ }
/*%%%*/
$$ = NEW_RETRY(&@$);
/*% %*/
@@ -3931,6 +3966,7 @@ k_class : keyword_class
{
token_info_push(p, "class", &@$);
$$ = p->ctxt;
+ p->ctxt.in_rescue = before_rescue;
/*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
/*% %*/
@@ -3941,6 +3977,7 @@ k_module : keyword_module
{
token_info_push(p, "module", &@$);
$$ = p->ctxt;
+ p->ctxt.in_rescue = before_rescue;
/*%%%*/
push_end_expect_token_locations(p, &@1.beg_pos);
/*% %*/
@@ -3975,12 +4012,16 @@ k_do_block : keyword_do_block
k_rescue : keyword_rescue
{
token_info_warn(p, "rescue", p->token_info, 1, &@$);
+ $$ = p->ctxt;
+ p->ctxt.in_rescue = after_rescue;
}
;
k_ensure : keyword_ensure
{
token_info_warn(p, "ensure", p->token_info, 1, &@$);
+ $$ = p->ctxt;
+ p->ctxt.in_rescue = after_ensure;
}
;
@@ -4576,7 +4617,7 @@ do_body : {$<vars>$ = dyna_push(p);}[dyna]
{
$<node>$ = allow_block_exit(p);
}[exits]
- opt_block_param bodystmt
+ opt_block_param[args] bodystmt
{
int max_numparam = p->max_numparam;
p->max_numparam = $<num>max_numparam;
@@ -5232,6 +5273,7 @@ exc_var : tASSOC lhs
opt_ensure : k_ensure compstmt
{
+ p->ctxt.in_rescue = $1.in_rescue;
/*%%%*/
$$ = $2;
/*% %*/
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index bb6f98c4a9..8515bc0780 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -247,6 +247,19 @@ class TestAst < Test::Unit::TestCase
end
end
+ def test_invalid_retry
+ msg = /Invalid retry/
+ assert_invalid_parse(msg, "retry")
+ assert_invalid_parse(msg, "def m; retry; end")
+ assert_invalid_parse(msg, "begin retry; end")
+ assert_parse("begin rescue; retry; end")
+ assert_invalid_parse(msg, "begin rescue; else; retry; end")
+ assert_invalid_parse(msg, "begin rescue; ensure; retry; end")
+ assert_parse("nil rescue retry")
+ assert_invalid_parse(msg, "END {retry}")
+ assert_invalid_parse(msg, "begin rescue; END {retry}; end")
+ end
+
def test_node_id_for_location
exception = begin
raise
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index fe104ecddc..43c8451235 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -372,9 +372,11 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(-c -e break), "", [], ["-e:1: Invalid break", :*])
assert_in_out_err(%w(-c -e next), "", [], ["-e:1: Invalid next", :*])
assert_in_out_err(%w(-c -e redo), "", [], ["-e:1: Invalid redo", :*])
+ assert_in_out_err(%w(-c -e retry), "", [], ["-e:1: Invalid retry", :*])
assert_in_out_err(%w(-c -e begin -e break -e end), "", [], ["-e:2: Invalid break", :*])
assert_in_out_err(%w(-c -e begin -e next -e end), "", [], ["-e:2: Invalid next", :*])
assert_in_out_err(%w(-c -e begin -e redo -e end), "", [], ["-e:2: Invalid redo", :*])
+ assert_in_out_err(%w(-c -e begin -e retry -e end), "", [], ["-e:2: Invalid retry", :*])
end
def test_invalid_option
diff --git a/test/yarp/parse_test.rb b/test/yarp/parse_test.rb
index 0baa099e94..aa3a76ad7c 100644
--- a/test/yarp/parse_test.rb
+++ b/test/yarp/parse_test.rb
@@ -89,9 +89,9 @@ module YARP
src = source
case relative
- when /break|next|redo|if|unless|rescue|control|keywords/
+ when /break|next|redo|if|unless|rescue|control|keywords|retry/
# Uncaught syntax errors: Invalid break, Invalid next
- src = "->{\n#{src}\n}"
+ src = "->do\nrescue\n#{src}\nend"
ripper_should_match = false
end