mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Ripper: Refined error callbacks [Bug #17345]
This commit is contained in:
parent
2e436982ee
commit
e0bdd54348
Notes:
git
2020-12-15 21:36:48 +09:00
4 changed files with 68 additions and 47 deletions
|
@ -190,8 +190,8 @@ class Ripper
|
||||||
e
|
e
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_error(mesg)
|
def on_error(mesg, tok = token())
|
||||||
@errors.push Elem.new([lineno(), column()], __callee__, token(), state(), mesg)
|
@errors.push Elem.new([lineno(), column()], __callee__, tok, state(), mesg)
|
||||||
end
|
end
|
||||||
PARSER_EVENTS.grep(/_error\z/) do |e|
|
PARSER_EVENTS.grep(/_error\z/) do |e|
|
||||||
alias_method "on_#{e}", :on_error
|
alias_method "on_#{e}", :on_error
|
||||||
|
|
87
parse.y
87
parse.y
|
@ -623,10 +623,11 @@ static VALUE new_regexp(struct parser_params *, VALUE, VALUE, const YYLTYPE *);
|
||||||
static VALUE const_decl(struct parser_params *p, VALUE path);
|
static VALUE const_decl(struct parser_params *p, VALUE path);
|
||||||
|
|
||||||
static VALUE var_field(struct parser_params *p, VALUE a);
|
static VALUE var_field(struct parser_params *p, VALUE a);
|
||||||
static VALUE assign_error(struct parser_params *p, VALUE a);
|
static VALUE assign_error(struct parser_params *p, const char *mesg, VALUE a);
|
||||||
|
|
||||||
static VALUE parser_reg_compile(struct parser_params*, VALUE, int, VALUE *);
|
static VALUE parser_reg_compile(struct parser_params*, VALUE, int, VALUE *);
|
||||||
|
|
||||||
|
static VALUE backref_error(struct parser_params*, NODE *, VALUE);
|
||||||
#endif /* !RIPPER */
|
#endif /* !RIPPER */
|
||||||
|
|
||||||
/* forward declaration */
|
/* forward declaration */
|
||||||
|
@ -1019,6 +1020,7 @@ endless_method_name(struct parser_params *p, NODE *defn, const YYLTYPE *loc)
|
||||||
# define rb_warning4L(l,fmt,a,b,c,d) WARNING_CALL(WARNING_ARGS_L(l, fmt, 5), (a), (b), (c), (d))
|
# define rb_warning4L(l,fmt,a,b,c,d) WARNING_CALL(WARNING_ARGS_L(l, fmt, 5), (a), (b), (c), (d))
|
||||||
#ifdef RIPPER
|
#ifdef RIPPER
|
||||||
static ID id_warn, id_warning, id_gets, id_assoc;
|
static ID id_warn, id_warning, id_gets, id_assoc;
|
||||||
|
# define ERR_MESG() STR_NEW2(mesg) /* to bypass Ripper DSL */
|
||||||
# define WARN_S_L(s,l) STR_NEW(s,l)
|
# define WARN_S_L(s,l) STR_NEW(s,l)
|
||||||
# define WARN_S(s) STR_NEW2(s)
|
# define WARN_S(s) STR_NEW2(s)
|
||||||
# define WARN_I(i) INT2NUM(i)
|
# define WARN_I(i) INT2NUM(i)
|
||||||
|
@ -1454,11 +1456,12 @@ stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
|
||||||
}
|
}
|
||||||
| keyword_alias tGVAR tNTH_REF
|
| keyword_alias tGVAR tNTH_REF
|
||||||
{
|
{
|
||||||
|
static const char mesg[] = "can't make alias for the number variables";
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
yyerror1(&@3, "can't make alias for the number variables");
|
yyerror1(&@3, mesg);
|
||||||
$$ = NEW_BEGIN(0, &@$);
|
$$ = NEW_BEGIN(0, &@$);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: alias_error!(var_alias!($2, $3)) %*/
|
/*% ripper[error]: alias_error!(ERR_MESG(), var_alias!($2, $3)) %*/
|
||||||
}
|
}
|
||||||
| keyword_undef undef_list
|
| keyword_undef undef_list
|
||||||
{
|
{
|
||||||
|
@ -1624,7 +1627,7 @@ command_asgn : lhs '=' lex_ctxt command_rhs
|
||||||
rb_backref_error(p, $1);
|
rb_backref_error(p, $1);
|
||||||
$$ = NEW_BEGIN(0, &@$);
|
$$ = NEW_BEGIN(0, &@$);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: assign_error!(assign!(var_field(p, $1), $4)) %*/
|
/*% ripper[error]: backref_error(p, RNODE($1), assign!(var_field(p, $1), $4)) %*/
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -2079,7 +2082,7 @@ mlhs_node : user_variable
|
||||||
rb_backref_error(p, $1);
|
rb_backref_error(p, $1);
|
||||||
$$ = NEW_BEGIN(0, &@$);
|
$$ = NEW_BEGIN(0, &@$);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: assign_error!(var_field(p, $1)) %*/
|
/*% ripper[error]: backref_error(p, RNODE($1), var_field(p, $1)) %*/
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -2145,16 +2148,17 @@ lhs : user_variable
|
||||||
rb_backref_error(p, $1);
|
rb_backref_error(p, $1);
|
||||||
$$ = NEW_BEGIN(0, &@$);
|
$$ = NEW_BEGIN(0, &@$);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: assign_error!(var_field(p, $1)) %*/
|
/*% ripper[error]: backref_error(p, RNODE($1), var_field(p, $1)) %*/
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
cname : tIDENTIFIER
|
cname : tIDENTIFIER
|
||||||
{
|
{
|
||||||
|
static const char mesg[] = "class/module name must be CONSTANT";
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
yyerror1(&@1, "class/module name must be CONSTANT");
|
yyerror1(&@1, mesg);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: class_name_error!($1) %*/
|
/*% ripper[error]: class_name_error!(ERR_MESG(), $1) %*/
|
||||||
}
|
}
|
||||||
| tCONSTANT
|
| tCONSTANT
|
||||||
;
|
;
|
||||||
|
@ -2329,7 +2333,7 @@ arg : lhs '=' lex_ctxt arg_rhs
|
||||||
rb_backref_error(p, $1);
|
rb_backref_error(p, $1);
|
||||||
$$ = NEW_BEGIN(0, &@$);
|
$$ = NEW_BEGIN(0, &@$);
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: assign_error!(opassign!(var_field(p, $1), $2, $4)) %*/
|
/*% ripper[error]: backref_error(p, RNODE($1), opassign!(var_field(p, $1), $2, $4)) %*/
|
||||||
}
|
}
|
||||||
| arg tDOT2 arg
|
| arg tDOT2 arg
|
||||||
{
|
{
|
||||||
|
@ -5143,35 +5147,39 @@ args_forward : tBDOT3
|
||||||
|
|
||||||
f_bad_arg : tCONSTANT
|
f_bad_arg : tCONSTANT
|
||||||
{
|
{
|
||||||
|
static const char mesg[] = "formal argument cannot be a constant";
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
yyerror1(&@1, "formal argument cannot be a constant");
|
yyerror1(&@1, mesg);
|
||||||
$$ = 0;
|
$$ = 0;
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: param_error!($1) %*/
|
/*% ripper[error]: param_error!(ERR_MESG(), $1) %*/
|
||||||
}
|
}
|
||||||
| tIVAR
|
| tIVAR
|
||||||
{
|
{
|
||||||
|
static const char mesg[] = "formal argument cannot be an instance variable";
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
yyerror1(&@1, "formal argument cannot be an instance variable");
|
yyerror1(&@1, mesg);
|
||||||
$$ = 0;
|
$$ = 0;
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: param_error!($1) %*/
|
/*% ripper[error]: param_error!(ERR_MESG(), $1) %*/
|
||||||
}
|
}
|
||||||
| tGVAR
|
| tGVAR
|
||||||
{
|
{
|
||||||
|
static const char mesg[] = "formal argument cannot be a global variable";
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
yyerror1(&@1, "formal argument cannot be a global variable");
|
yyerror1(&@1, mesg);
|
||||||
$$ = 0;
|
$$ = 0;
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: param_error!($1) %*/
|
/*% ripper[error]: param_error!(ERR_MESG(), $1) %*/
|
||||||
}
|
}
|
||||||
| tCVAR
|
| tCVAR
|
||||||
{
|
{
|
||||||
|
static const char mesg[] = "formal argument cannot be a class variable";
|
||||||
/*%%%*/
|
/*%%%*/
|
||||||
yyerror1(&@1, "formal argument cannot be a class variable");
|
yyerror1(&@1, mesg);
|
||||||
$$ = 0;
|
$$ = 0;
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper[error]: param_error!($1) %*/
|
/*% ripper[error]: param_error!(ERR_MESG(), $1) %*/
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -5657,7 +5665,7 @@ ripper_yylval_id(struct parser_params *p, ID x)
|
||||||
# define set_yylval_id(x) (void)(x)
|
# define set_yylval_id(x) (void)(x)
|
||||||
# define set_yylval_name(x) (void)(yylval.val = ripper_yylval_id(p, x))
|
# define set_yylval_name(x) (void)(yylval.val = ripper_yylval_id(p, x))
|
||||||
# define set_yylval_literal(x) add_mark_object(p, (x))
|
# define set_yylval_literal(x) add_mark_object(p, (x))
|
||||||
# define set_yylval_node(x) (void)(x)
|
# define set_yylval_node(x) (yylval.val = ripper_new_yylval(p, 0, 0, STR_NEW(p->lex.ptok, p->lex.pcur-p->lex.ptok)))
|
||||||
# define yylval_id() yylval.id
|
# define yylval_id() yylval.id
|
||||||
# define _cur_loc NULL_LOC /* dummy */
|
# define _cur_loc NULL_LOC /* dummy */
|
||||||
#endif
|
#endif
|
||||||
|
@ -7852,27 +7860,26 @@ formal_argument(struct parser_params *p, ID lhs)
|
||||||
case ID_LOCAL:
|
case ID_LOCAL:
|
||||||
break;
|
break;
|
||||||
#ifndef RIPPER
|
#ifndef RIPPER
|
||||||
|
# define ERR(mesg) yyerror0(mesg)
|
||||||
|
#else
|
||||||
|
# define ERR(mesg) (dispatch2(param_error, WARN_S(mesg), lhs), ripper_error(p))
|
||||||
|
#endif
|
||||||
case ID_CONST:
|
case ID_CONST:
|
||||||
yyerror0("formal argument cannot be a constant");
|
ERR("formal argument cannot be a constant");
|
||||||
return 0;
|
return 0;
|
||||||
case ID_INSTANCE:
|
case ID_INSTANCE:
|
||||||
yyerror0("formal argument cannot be an instance variable");
|
ERR("formal argument cannot be an instance variable");
|
||||||
return 0;
|
return 0;
|
||||||
case ID_GLOBAL:
|
case ID_GLOBAL:
|
||||||
yyerror0("formal argument cannot be a global variable");
|
ERR("formal argument cannot be a global variable");
|
||||||
return 0;
|
return 0;
|
||||||
case ID_CLASS:
|
case ID_CLASS:
|
||||||
yyerror0("formal argument cannot be a class variable");
|
ERR("formal argument cannot be a class variable");
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
yyerror0("formal argument must be local variable");
|
ERR("formal argument must be local variable");
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#undef ERR
|
||||||
default:
|
|
||||||
lhs = dispatch1(param_error, lhs);
|
|
||||||
ripper_error(p);
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
shadowing_lvar(p, lhs);
|
shadowing_lvar(p, lhs);
|
||||||
return lhs;
|
return lhs;
|
||||||
|
@ -8840,7 +8847,8 @@ parse_gvar(struct parser_params *p, const enum lex_state_e last_state)
|
||||||
pushback(p, c);
|
pushback(p, c);
|
||||||
if (IS_lex_state_for(last_state, EXPR_FNAME)) goto gvar;
|
if (IS_lex_state_for(last_state, EXPR_FNAME)) goto gvar;
|
||||||
tokfix(p);
|
tokfix(p);
|
||||||
set_yylval_node(NEW_NTH_REF(parse_numvar(p), &_cur_loc));
|
c = parse_numvar(p);
|
||||||
|
set_yylval_node(NEW_NTH_REF(c, &_cur_loc));
|
||||||
return tNTH_REF;
|
return tNTH_REF;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -10782,7 +10790,7 @@ assignable(struct parser_params *p, VALUE lhs)
|
||||||
{
|
{
|
||||||
const char *err = 0;
|
const char *err = 0;
|
||||||
assignable0(p, get_id(lhs), &err);
|
assignable0(p, get_id(lhs), &err);
|
||||||
if (err) lhs = assign_error(p, lhs);
|
if (err) lhs = assign_error(p, err, lhs);
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -10876,7 +10884,17 @@ rb_backref_error(struct parser_params *p, NODE *node)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static VALUE
|
||||||
|
backref_error(struct parser_params *p, NODE *ref, VALUE expr)
|
||||||
|
{
|
||||||
|
VALUE mesg = rb_str_new_cstr("Can't set variable ");
|
||||||
|
rb_str_append(mesg, ref->nd_cval);
|
||||||
|
return dispatch2(assign_error, mesg, expr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RIPPER
|
||||||
static NODE *
|
static NODE *
|
||||||
arg_append(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *loc)
|
arg_append(struct parser_params *p, NODE *node1, NODE *node2, const YYLTYPE *loc)
|
||||||
{
|
{
|
||||||
|
@ -12176,16 +12194,15 @@ static VALUE
|
||||||
const_decl(struct parser_params *p, VALUE path)
|
const_decl(struct parser_params *p, VALUE path)
|
||||||
{
|
{
|
||||||
if (p->ctxt.in_def) {
|
if (p->ctxt.in_def) {
|
||||||
path = dispatch1(assign_error, path);
|
path = assign_error(p, "dynamic constant assignment", path);
|
||||||
ripper_error(p);
|
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
assign_error(struct parser_params *p, VALUE a)
|
assign_error(struct parser_params *p, const char *mesg, VALUE a)
|
||||||
{
|
{
|
||||||
a = dispatch1(assign_error, a);
|
a = dispatch2(assign_error, ERR_MESG(), a);
|
||||||
ripper_error(p);
|
ripper_error(p);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,11 @@ class DummyParser < Ripper
|
||||||
Node.new('valias', a, b)
|
Node.new('valias', a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_alias_error(a)
|
def on_assign_error(mesg = nil, a)
|
||||||
|
Node.new('assign_error', a)
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_alias_error(mesg = nil, a)
|
||||||
Node.new('aliaserr', a)
|
Node.new('aliaserr', a)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -147,18 +147,18 @@ class TestRipper::Lexer < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
BAD_CODE = {
|
BAD_CODE = {
|
||||||
parse_error: 'def req(true) end',
|
parse_error: ['def req(true) end', %r[unexpected `true']],
|
||||||
assign_error: 'begin; nil = 1; end',
|
assign_error: ['begin; nil = 1; end', %r[assign to nil]],
|
||||||
alias_error: 'begin; alias $x $1; end',
|
alias_error: ['begin; alias $x $1; end', %r[number variables]],
|
||||||
class_name_error: 'class bad; end',
|
class_name_error: ['class bad; end', %r[class/module name]],
|
||||||
param_error: 'def req(@a) end',
|
param_error: ['def req(@a) end', %r[formal argument.*instance]],
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_raise_errors_keyword
|
def test_raise_errors_keyword
|
||||||
all_assertions do |all|
|
all_assertions do |all|
|
||||||
BAD_CODE.each do |err, code|
|
BAD_CODE.each do |err, (code, message)|
|
||||||
all.for(err) do
|
all.for(err) do
|
||||||
assert_raise(SyntaxError) { Ripper.tokenize(code, raise_errors: true) }
|
assert_raise_with_message(SyntaxError, message) { Ripper.tokenize(code, raise_errors: true) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -166,7 +166,7 @@ class TestRipper::Lexer < Test::Unit::TestCase
|
||||||
|
|
||||||
def test_tokenize_with_syntax_error
|
def test_tokenize_with_syntax_error
|
||||||
all_assertions do |all|
|
all_assertions do |all|
|
||||||
BAD_CODE.each do |err, code|
|
BAD_CODE.each do |err, (code)|
|
||||||
all.for(err) do
|
all.for(err) do
|
||||||
assert_equal "end", Ripper.tokenize(code).last
|
assert_equal "end", Ripper.tokenize(code).last
|
||||||
end
|
end
|
||||||
|
@ -176,7 +176,7 @@ class TestRipper::Lexer < Test::Unit::TestCase
|
||||||
|
|
||||||
def test_lex_with_syntax_error
|
def test_lex_with_syntax_error
|
||||||
all_assertions do |all|
|
all_assertions do |all|
|
||||||
BAD_CODE.each do |err, code|
|
BAD_CODE.each do |err, (code)|
|
||||||
all.for(err) do
|
all.for(err) do
|
||||||
assert_equal [[1, code.size-3], :on_kw, "end", state(:EXPR_END)], Ripper.lex(code).last
|
assert_equal [[1, code.size-3], :on_kw, "end", state(:EXPR_END)], Ripper.lex(code).last
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue