Skip to content

Commit

Permalink
Syntax check of retry in the parser
Browse files Browse the repository at this point in the history
  • Loading branch information
nobu committed Sep 27, 2023
1 parent ff8278e commit 29e5fca
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 29 deletions.
96 changes: 69 additions & 27 deletions parse.y
Expand Up @@ -277,13 +277,21 @@ 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;
unsigned int in_argdef: 1;
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__)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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> '.'
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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 '}'
{
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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
{
Expand All @@ -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
;
Expand Down Expand Up @@ -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, &@$);
/*%%%*/
/*%
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) %*/
}
;

Expand Down Expand Up @@ -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(&@$);
/*% %*/
Expand Down Expand Up @@ -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);
/*% %*/
Expand All @@ -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);
/*% %*/
Expand Down Expand Up @@ -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;
}
;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -5232,6 +5273,7 @@ exc_var : tASSOC lhs

opt_ensure : k_ensure compstmt
{
p->ctxt.in_rescue = $1.in_rescue;
/*%%%*/
$$ = $2;
/*% %*/
Expand Down
13 changes: 13 additions & 0 deletions test/ruby/test_ast.rb
Expand Up @@ -247,6 +247,19 @@ def test_invalid_exit
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
Expand Down
2 changes: 2 additions & 0 deletions test/ruby/test_rubyoptions.rb
Expand Up @@ -372,9 +372,11 @@ def test_syntax_check
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
Expand Down
4 changes: 2 additions & 2 deletions test/yarp/parse_test.rb
Expand Up @@ -89,9 +89,9 @@ def test_parse_lex_file
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

Expand Down

0 comments on commit 29e5fca

Please sign in to comment.