Remove circular parameter syntax error
https://bugs.ruby-lang.org/issues/20478
This commit is contained in:
parent
eb46b0924f
commit
cbc83c4a92
22
parse.y
22
parse.y
@ -539,8 +539,6 @@ struct parser_params {
|
|||||||
int end_col;
|
int end_col;
|
||||||
} delayed;
|
} delayed;
|
||||||
|
|
||||||
ID cur_arg;
|
|
||||||
|
|
||||||
rb_ast_t *ast;
|
rb_ast_t *ast;
|
||||||
int node_id;
|
int node_id;
|
||||||
|
|
||||||
@ -1305,7 +1303,6 @@ struct RNode_DEF_TEMP {
|
|||||||
ID nd_mid;
|
ID nd_mid;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
ID cur_arg;
|
|
||||||
int max_numparam;
|
int max_numparam;
|
||||||
NODE *numparam_save;
|
NODE *numparam_save;
|
||||||
struct lex_context ctxt;
|
struct lex_context ctxt;
|
||||||
@ -1671,7 +1668,6 @@ restore_defun(struct parser_params *p, rb_node_def_temp_t *temp)
|
|||||||
{
|
{
|
||||||
/* See: def_name action */
|
/* See: def_name action */
|
||||||
struct lex_context ctxt = temp->save.ctxt;
|
struct lex_context ctxt = temp->save.ctxt;
|
||||||
p->cur_arg = temp->save.cur_arg;
|
|
||||||
p->ctxt.in_def = ctxt.in_def;
|
p->ctxt.in_def = ctxt.in_def;
|
||||||
p->ctxt.shareable_constant_value = ctxt.shareable_constant_value;
|
p->ctxt.shareable_constant_value = ctxt.shareable_constant_value;
|
||||||
p->ctxt.in_rescue = ctxt.in_rescue;
|
p->ctxt.in_rescue = ctxt.in_rescue;
|
||||||
@ -2900,7 +2896,6 @@ rb_parser_ary_free(rb_parser_t *p, rb_parser_ary_t *ary)
|
|||||||
*/
|
*/
|
||||||
%rule f_opt(value) <node_opt_arg>: f_arg_asgn f_eq value
|
%rule f_opt(value) <node_opt_arg>: f_arg_asgn f_eq value
|
||||||
{
|
{
|
||||||
p->cur_arg = 0;
|
|
||||||
p->ctxt.in_argdef = 1;
|
p->ctxt.in_argdef = 1;
|
||||||
$$ = NEW_OPT_ARG(assignable(p, $1, $3, &@$), &@$);
|
$$ = NEW_OPT_ARG(assignable(p, $1, $3, &@$), &@$);
|
||||||
/*% ripper: [$:$, $:3] %*/
|
/*% ripper: [$:$, $:3] %*/
|
||||||
@ -3378,7 +3373,6 @@ def_name : fname
|
|||||||
ID fname = $1;
|
ID fname = $1;
|
||||||
numparam_name(p, fname);
|
numparam_name(p, fname);
|
||||||
local_push(p, 0);
|
local_push(p, 0);
|
||||||
p->cur_arg = 0;
|
|
||||||
p->ctxt.in_def = 1;
|
p->ctxt.in_def = 1;
|
||||||
p->ctxt.in_rescue = before_rescue;
|
p->ctxt.in_rescue = before_rescue;
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
@ -5113,7 +5107,6 @@ opt_block_param : none
|
|||||||
|
|
||||||
block_param_def : '|' opt_bv_decl '|'
|
block_param_def : '|' opt_bv_decl '|'
|
||||||
{
|
{
|
||||||
p->cur_arg = 0;
|
|
||||||
p->max_numparam = ORDINAL_PARAM;
|
p->max_numparam = ORDINAL_PARAM;
|
||||||
p->ctxt.in_argdef = 0;
|
p->ctxt.in_argdef = 0;
|
||||||
$$ = 0;
|
$$ = 0;
|
||||||
@ -5121,7 +5114,6 @@ block_param_def : '|' opt_bv_decl '|'
|
|||||||
}
|
}
|
||||||
| '|' block_param opt_bv_decl '|'
|
| '|' block_param opt_bv_decl '|'
|
||||||
{
|
{
|
||||||
p->cur_arg = 0;
|
|
||||||
p->max_numparam = ORDINAL_PARAM;
|
p->max_numparam = ORDINAL_PARAM;
|
||||||
p->ctxt.in_argdef = 0;
|
p->ctxt.in_argdef = 0;
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
@ -6560,14 +6552,12 @@ f_arg_asgn : f_norm_arg
|
|||||||
{
|
{
|
||||||
ID id = $1;
|
ID id = $1;
|
||||||
arg_var(p, id);
|
arg_var(p, id);
|
||||||
p->cur_arg = id;
|
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
f_arg_item : f_arg_asgn
|
f_arg_item : f_arg_asgn
|
||||||
{
|
{
|
||||||
p->cur_arg = 0;
|
|
||||||
$$ = NEW_ARGS_AUX($1, 1, &NULL_LOC);
|
$$ = NEW_ARGS_AUX($1, 1, &NULL_LOC);
|
||||||
/*% ripper: $:1 %*/
|
/*% ripper: $:1 %*/
|
||||||
}
|
}
|
||||||
@ -6606,7 +6596,6 @@ f_arg : f_arg_item
|
|||||||
f_label : tLABEL
|
f_label : tLABEL
|
||||||
{
|
{
|
||||||
arg_var(p, formal_argument(p, $1));
|
arg_var(p, formal_argument(p, $1));
|
||||||
p->cur_arg = $1;
|
|
||||||
p->max_numparam = ORDINAL_PARAM;
|
p->max_numparam = ORDINAL_PARAM;
|
||||||
p->ctxt.in_argdef = 0;
|
p->ctxt.in_argdef = 0;
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
@ -6616,14 +6605,12 @@ f_label : tLABEL
|
|||||||
|
|
||||||
f_kw : f_label arg_value
|
f_kw : f_label arg_value
|
||||||
{
|
{
|
||||||
p->cur_arg = 0;
|
|
||||||
p->ctxt.in_argdef = 1;
|
p->ctxt.in_argdef = 1;
|
||||||
$$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$);
|
$$ = new_kw_arg(p, assignable(p, $1, $2, &@$), &@$);
|
||||||
/*% ripper: [$:$, $:2] %*/
|
/*% ripper: [$:$, $:2] %*/
|
||||||
}
|
}
|
||||||
| f_label
|
| f_label
|
||||||
{
|
{
|
||||||
p->cur_arg = 0;
|
|
||||||
p->ctxt.in_argdef = 1;
|
p->ctxt.in_argdef = 1;
|
||||||
$$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$);
|
$$ = new_kw_arg(p, assignable(p, $1, NODE_SPECIAL_REQUIRED_KEYWORD, &@$), &@$);
|
||||||
/*% ripper: [$:$, 0] %*/
|
/*% ripper: [$:$, 0] %*/
|
||||||
@ -12500,7 +12487,6 @@ static rb_node_def_temp_t *
|
|||||||
rb_node_def_temp_new(struct parser_params *p, const YYLTYPE *loc)
|
rb_node_def_temp_new(struct parser_params *p, const YYLTYPE *loc)
|
||||||
{
|
{
|
||||||
rb_node_def_temp_t *n = NODE_NEWNODE((enum node_type)NODE_DEF_TEMP, rb_node_def_temp_t, loc);
|
rb_node_def_temp_t *n = NODE_NEWNODE((enum node_type)NODE_DEF_TEMP, rb_node_def_temp_t, loc);
|
||||||
n->save.cur_arg = p->cur_arg;
|
|
||||||
n->save.numparam_save = 0;
|
n->save.numparam_save = 0;
|
||||||
n->save.max_numparam = 0;
|
n->save.max_numparam = 0;
|
||||||
n->save.ctxt = p->ctxt;
|
n->save.ctxt = p->ctxt;
|
||||||
@ -13031,19 +13017,11 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
|
|||||||
case ID_LOCAL:
|
case ID_LOCAL:
|
||||||
if (dyna_in_block(p) && dvar_defined_ref(p, id, &vidp)) {
|
if (dyna_in_block(p) && dvar_defined_ref(p, id, &vidp)) {
|
||||||
if (NUMPARAM_ID_P(id) && (numparam_nested_p(p) || it_used_p(p))) return 0;
|
if (NUMPARAM_ID_P(id) && (numparam_nested_p(p) || it_used_p(p))) return 0;
|
||||||
if (id == p->cur_arg) {
|
|
||||||
compile_error(p, "circular argument reference - %"PRIsWARN, rb_id2str(id));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (vidp) *vidp |= LVAR_USED;
|
if (vidp) *vidp |= LVAR_USED;
|
||||||
node = NEW_DVAR(id, loc);
|
node = NEW_DVAR(id, loc);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (local_id_ref(p, id, &vidp)) {
|
if (local_id_ref(p, id, &vidp)) {
|
||||||
if (id == p->cur_arg) {
|
|
||||||
compile_error(p, "circular argument reference - %"PRIsWARN, rb_id2str(id));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (vidp) *vidp |= LVAR_USED;
|
if (vidp) *vidp |= LVAR_USED;
|
||||||
node = NEW_LVAR(id, loc);
|
node = NEW_LVAR(id, loc);
|
||||||
return node;
|
return node;
|
||||||
|
@ -14394,7 +14394,7 @@ parse_parameters(
|
|||||||
context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
|
context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
|
||||||
|
|
||||||
pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
|
pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
|
||||||
uint32_t reads = pm_locals_reads(&parser->current_scope->locals, name_id);
|
uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
|
||||||
|
|
||||||
pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT);
|
pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT);
|
||||||
pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
|
pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
|
||||||
@ -14407,7 +14407,7 @@ parse_parameters(
|
|||||||
// If the value of the parameter increased the number of
|
// If the value of the parameter increased the number of
|
||||||
// reads of that parameter, then we need to warn that we
|
// reads of that parameter, then we need to warn that we
|
||||||
// have a circular definition.
|
// have a circular definition.
|
||||||
if (pm_locals_reads(&parser->current_scope->locals, name_id) != reads) {
|
if ((parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
|
||||||
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR);
|
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14486,10 +14486,10 @@ parse_parameters(
|
|||||||
context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
|
context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
|
||||||
|
|
||||||
pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
|
pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
|
||||||
uint32_t reads = pm_locals_reads(&parser->current_scope->locals, name_id);
|
uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
|
||||||
pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT_KW);
|
pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT_KW);
|
||||||
|
|
||||||
if (pm_locals_reads(&parser->current_scope->locals, name_id) != reads) {
|
if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
|
||||||
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR);
|
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -960,24 +960,27 @@ describe "Post-args" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "with a circular argument reference" do
|
describe "with a circular argument reference" do
|
||||||
it "raises a SyntaxError if using an existing local with the same name as the argument" do
|
ruby_version_is ""..."3.4" do
|
||||||
a = 1
|
it "raises a SyntaxError if using the argument in its default value" do
|
||||||
-> {
|
a = 1
|
||||||
@proc = eval "proc { |a=a| a }"
|
-> {
|
||||||
}.should raise_error(SyntaxError)
|
eval "proc { |a=a| a }"
|
||||||
|
}.should raise_error(SyntaxError)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises a SyntaxError if there is an existing method with the same name as the argument" do
|
ruby_version_is "3.4" do
|
||||||
def a; 1; end
|
it "is nil if using the argument in its default value" do
|
||||||
-> {
|
-> {
|
||||||
@proc = eval "proc { |a=a| a }"
|
eval "proc { |a=a| a }.call"
|
||||||
}.should raise_error(SyntaxError)
|
}.call.should == nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "calls an existing method with the same name as the argument if explicitly using ()" do
|
it "calls an existing method with the same name as the argument if explicitly using ()" do
|
||||||
def a; 1; end
|
def a; 1; end
|
||||||
proc { |a=a()| a }.call.should == 1
|
proc { |a=a()| a }.call.should == 1
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -197,15 +197,25 @@ describe "An instance method with a default argument" do
|
|||||||
foo(2,3,3).should == [2,3,[3]]
|
foo(2,3,3).should == [2,3,[3]]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises a SyntaxError when there is an existing method with the same name as the local variable" do
|
ruby_version_is ""..."3.4" do
|
||||||
def bar
|
it "raises a SyntaxError if using the argument in its default value" do
|
||||||
1
|
-> {
|
||||||
|
eval "def foo(bar = bar)
|
||||||
|
bar
|
||||||
|
end"
|
||||||
|
}.should raise_error(SyntaxError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.4" do
|
||||||
|
it "is nil if using the argument in its default value" do
|
||||||
|
-> {
|
||||||
|
eval "def foo(bar = bar)
|
||||||
|
bar
|
||||||
|
end
|
||||||
|
foo"
|
||||||
|
}.call.should == nil
|
||||||
end
|
end
|
||||||
-> {
|
|
||||||
eval "def foo(bar = bar)
|
|
||||||
bar
|
|
||||||
end"
|
|
||||||
}.should raise_error(SyntaxError)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "calls a method with the same name as the local when explicitly using ()" do
|
it "calls a method with the same name as the local when explicitly using ()" do
|
||||||
|
@ -263,18 +263,21 @@ describe "A lambda literal -> () { }" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "with circular optional argument reference" do
|
describe "with circular optional argument reference" do
|
||||||
it "raises a SyntaxError if using an existing local with the same name as the argument" do
|
ruby_version_is ""..."3.4" do
|
||||||
a = 1
|
it "raises a SyntaxError if using the argument in its default value" do
|
||||||
-> {
|
a = 1
|
||||||
@proc = eval "-> (a=a) { a }"
|
-> {
|
||||||
}.should raise_error(SyntaxError)
|
eval "-> (a=a) { a }"
|
||||||
|
}.should raise_error(SyntaxError)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises a SyntaxError if there is an existing method with the same name as the argument" do
|
ruby_version_is "3.4" do
|
||||||
def a; 1; end
|
it "is nil if using the argument in its default value" do
|
||||||
-> {
|
-> {
|
||||||
@proc = eval "-> (a=a) { a }"
|
eval "-> (a=a) { a }.call"
|
||||||
}.should raise_error(SyntaxError)
|
}.call.should == nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "calls an existing method with the same name as the argument if explicitly using ()" do
|
it "calls an existing method with the same name as the argument if explicitly using ()" do
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
exclude(:test_optional_self_reference, "https://bugs.ruby-lang.org/issues/20478")
|
|
||||||
exclude(:test_keyword_self_reference, "https://bugs.ruby-lang.org/issues/20478")
|
|
@ -1990,12 +1990,18 @@ module Prism
|
|||||||
proc { |foo: foo| }
|
proc { |foo: foo| }
|
||||||
RUBY
|
RUBY
|
||||||
|
|
||||||
assert_errors expression(source), source, [
|
assert_errors(
|
||||||
["circular argument reference - bar", 8..11],
|
expression(source),
|
||||||
["circular argument reference - bar", 32..35],
|
source,
|
||||||
["circular argument reference - foo", 55..58],
|
[
|
||||||
["circular argument reference - foo", 76..79]
|
["circular argument reference - bar", 8..11],
|
||||||
]
|
["circular argument reference - bar", 32..35],
|
||||||
|
["circular argument reference - foo", 55..58],
|
||||||
|
["circular argument reference - foo", 76..79]
|
||||||
|
],
|
||||||
|
check_valid_syntax: false,
|
||||||
|
version: "3.3.0"
|
||||||
|
)
|
||||||
|
|
||||||
refute_error_messages("def foo(bar: bar = 1); end")
|
refute_error_messages("def foo(bar: bar = 1); end")
|
||||||
end
|
end
|
||||||
@ -2244,10 +2250,10 @@ module Prism
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assert_errors(expected, source, errors, check_valid_syntax: true)
|
def assert_errors(expected, source, errors, check_valid_syntax: true, **options)
|
||||||
refute_valid_syntax(source) if check_valid_syntax
|
refute_valid_syntax(source) if check_valid_syntax
|
||||||
|
|
||||||
result = Prism.parse(source)
|
result = Prism.parse(source, **options)
|
||||||
node = result.value.statements.body.last
|
node = result.value.statements.body.last
|
||||||
|
|
||||||
assert_equal_nodes(expected, node, compare_location: false)
|
assert_equal_nodes(expected, node, compare_location: false)
|
||||||
|
@ -392,12 +392,11 @@ class TestSyntax < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_keyword_self_reference
|
def test_keyword_self_reference
|
||||||
message = /circular argument reference - var/
|
assert_valid_syntax("def foo(var: defined?(var)) var end")
|
||||||
assert_syntax_error("def foo(var: defined?(var)) var end", message)
|
assert_valid_syntax("def foo(var: var) var end")
|
||||||
assert_syntax_error("def foo(var: var) var end", message)
|
assert_valid_syntax("def foo(var: bar(var)) var end")
|
||||||
assert_syntax_error("def foo(var: bar(var)) var end", message)
|
assert_valid_syntax("def foo(var: bar {var}) var end")
|
||||||
assert_syntax_error("def foo(var: bar {var}) var end", message)
|
assert_valid_syntax("def foo(var: (1 in ^var)); end")
|
||||||
assert_syntax_error("def foo(var: (1 in ^var)); end", message)
|
|
||||||
|
|
||||||
o = Object.new
|
o = Object.new
|
||||||
assert_warn("") do
|
assert_warn("") do
|
||||||
@ -423,6 +422,9 @@ class TestSyntax < Test::Unit::TestCase
|
|||||||
assert_warn("") do
|
assert_warn("") do
|
||||||
o.instance_eval("proc {|var: 1| var}")
|
o.instance_eval("proc {|var: 1| var}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
o = Object.new
|
||||||
|
assert_nil(o.instance_eval("def foo(bar: bar) = bar; foo"))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_keyword_invalid_name
|
def test_keyword_invalid_name
|
||||||
@ -456,14 +458,13 @@ class TestSyntax < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_optional_self_reference
|
def test_optional_self_reference
|
||||||
message = /circular argument reference - var/
|
assert_valid_syntax("def foo(var = defined?(var)) var end")
|
||||||
assert_syntax_error("def foo(var = defined?(var)) var end", message)
|
assert_valid_syntax("def foo(var = var) var end")
|
||||||
assert_syntax_error("def foo(var = var) var end", message)
|
assert_valid_syntax("def foo(var = bar(var)) var end")
|
||||||
assert_syntax_error("def foo(var = bar(var)) var end", message)
|
assert_valid_syntax("def foo(var = bar {var}) var end")
|
||||||
assert_syntax_error("def foo(var = bar {var}) var end", message)
|
assert_valid_syntax("def foo(var = (def bar;end; var)) var end")
|
||||||
assert_syntax_error("def foo(var = (def bar;end; var)) var end", message)
|
assert_valid_syntax("def foo(var = (def self.bar;end; var)) var end")
|
||||||
assert_syntax_error("def foo(var = (def self.bar;end; var)) var end", message)
|
assert_valid_syntax("def foo(var = (1 in ^var)); end")
|
||||||
assert_syntax_error("def foo(var = (1 in ^var)); end", message)
|
|
||||||
|
|
||||||
o = Object.new
|
o = Object.new
|
||||||
assert_warn("") do
|
assert_warn("") do
|
||||||
@ -489,6 +490,9 @@ class TestSyntax < Test::Unit::TestCase
|
|||||||
assert_warn("") do
|
assert_warn("") do
|
||||||
o.instance_eval("proc {|var = 1| var}")
|
o.instance_eval("proc {|var = 1| var}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
o = Object.new
|
||||||
|
assert_nil(o.instance_eval("def foo(bar: bar) = bar; foo"))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_warn_grouped_expression
|
def test_warn_grouped_expression
|
||||||
|
Loading…
x
Reference in New Issue
Block a user