Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Feature #19979] Method definition with &nil #11065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2023,7 +2023,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons

if (node_args) {
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
struct rb_args_info *args = &RNODE_ARGS(node_args)->nd_ainfo;
const struct rb_args_info *const args = &RNODE_ARGS(node_args)->nd_ainfo;
ID rest_id = 0;
int last_comma = 0;
ID block_id = 0;
Expand Down Expand Up @@ -2121,7 +2121,10 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
body->param.flags.accepts_no_kwarg = TRUE;
}

if (block_id) {
if (args->no_blockarg) {
body->param.flags.accepts_no_block = TRUE;
}
else if (block_id) {
body->param.block_start = arg_size++;
body->param.flags.has_block = TRUE;
iseq_set_use_block(iseq);
Expand Down Expand Up @@ -13118,7 +13121,8 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
(body->param.flags.anon_rest << 10) |
(body->param.flags.anon_kwrest << 11) |
(body->param.flags.use_block << 12) |
(body->param.flags.forwardable << 13) ;
(body->param.flags.forwardable << 13) |
(body->param.flags.accepts_no_block << 14);

#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
# define IBF_BODY_OFFSET(x) (x)
Expand Down Expand Up @@ -13336,6 +13340,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load_body->param.flags.anon_kwrest = (param_flags >> 11) & 1;
load_body->param.flags.use_block = (param_flags >> 12) & 1;
load_body->param.flags.forwardable = (param_flags >> 13) & 1;
load_body->param.flags.accepts_no_block = (param_flags >> 14) & 1;
load_body->param.size = param_size;
load_body->param.lead_num = param_lead_num;
load_body->param.opt_num = param_opt_num;
Expand Down
8 changes: 7 additions & 1 deletion iseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -3586,7 +3586,13 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
}
rb_ary_push(args, a);
}
if (body->param.flags.has_block) {
if (body->param.flags.accepts_no_block) {
ID noblock;
CONST_ID(noblock, "noblock");
PARAM_TYPE(noblock);
rb_ary_push(args, a);
}
else if (body->param.flags.has_block) {
CONST_ID(block, "block");
rb_ary_push(args, PARAM(body->param.block_start, block));
}
Expand Down
8 changes: 7 additions & 1 deletion lib/prism/node_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,13 @@ def signature
names << [:nokey]
end

names << [:block, block.name || :&] if block
case block
when BlockParameterNode
names << [:block, block.name || :&]
when NoBlockParameterNode
names << [:noblock]
end

names
end
end
Expand Down
9 changes: 9 additions & 0 deletions parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -6683,6 +6683,11 @@ f_block_arg : blkarg_mark tIDENTIFIER
$$ = $2;
/*% ripper: blockarg!($:2) %*/
}
| blkarg_mark keyword_nil
{
$$ = idNil;
/*% ripper: blockarg!(ID2VAL(idNil)) %*/
}
| blkarg_mark
{
arg_var(p, idFWD_BLOCK);
Expand Down Expand Up @@ -14442,6 +14447,10 @@ new_args_tail(struct parser_params *p, rb_node_kw_arg_t *kw_args, ID kw_rest_arg
struct rb_args_info *args = &node->nd_ainfo;
if (p->error_p) return node;

if (block == idNil) {
block = 0;
args->no_blockarg = TRUE;
}
args->block_arg = block;
args->kw_args = kw_args;

Expand Down
16 changes: 15 additions & 1 deletion prism/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3103,6 +3103,18 @@ nodes:

nil
^^^
- name: NoBlockParameterNode
fields:
- name: operator_loc
type: location
- name: keyword_loc
type: location
comment: |
Represents the use of `&nil` inside method arguments.

def a(&nil)
^^^^
end
- name: NoKeywordsParameterNode
fields:
- name: operator_loc
Expand Down Expand Up @@ -3249,7 +3261,9 @@ nodes:
- NoKeywordsParameterNode
- name: block
type: node?
kind: BlockParameterNode
kind:
- BlockParameterNode
- NoBlockParameterNode
comment: |
Represents the list of parameters on a method, block, or lambda definition.

Expand Down
63 changes: 47 additions & 16 deletions prism/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -6025,6 +6025,30 @@ pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) {
return node;
}

/**
* Allocate and initialize a new NoKeywordsParameterNode node.
*/
static pm_no_block_parameter_node_t *
pm_no_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
assert(operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND);
assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
pm_no_block_parameter_node_t *node = PM_ALLOC_NODE(parser, pm_no_block_parameter_node_t);

*node = (pm_no_block_parameter_node_t) {
{
.type = PM_NO_BLOCK_PARAMETER_NODE,
.location = {
.start = operator->start,
.end = keyword->end
}
},
.operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
.keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
};

return node;
}

/**
* Allocate and initialize a new NoKeywordsParameterNode node.
*/
Expand Down Expand Up @@ -6282,9 +6306,9 @@ pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *par
* Set the block parameter on a ParametersNode node.
*/
static void
pm_parameters_node_block_set(pm_parameters_node_t *params, pm_block_parameter_node_t *param) {
pm_parameters_node_block_set(pm_parameters_node_t *params, pm_node_t *param) {
assert(params->block == NULL);
pm_parameters_node_location_set(params, (pm_node_t *) param);
pm_parameters_node_location_set(params, param);
params->block = param;
}

Expand Down Expand Up @@ -14230,27 +14254,34 @@ parse_parameters(
parser_lex(parser);

pm_token_t operator = parser->previous;
pm_token_t name;
pm_node_t *param;

bool repeated = false;
if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
name = parser->previous;
repeated = pm_parser_parameter_name_check(parser, &name);
pm_parser_local_add_token(parser, &name, 1);
if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous);
} else {
name = not_provided(parser);
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
}
pm_token_t name;

pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator);
if (repeated) {
pm_node_flag_set_repeated_parameter((pm_node_t *)param);
bool repeated = false;
if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
name = parser->previous;
repeated = pm_parser_parameter_name_check(parser, &name);
pm_parser_local_add_token(parser, &name, 1);
} else {
name = not_provided(parser);
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
}

param = (pm_node_t *) pm_block_parameter_node_create(parser, &name, &operator);
if (repeated) {
pm_node_flag_set_repeated_parameter(param);
}
}

if (params->block == NULL) {
pm_parameters_node_block_set(params, param);
} else {
pm_parser_err_node(parser, (pm_node_t *) param, PM_ERR_PARAMETER_BLOCK_MULTI);
pm_parameters_node_posts_append(params, (pm_node_t *) param);
pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI);
pm_parameters_node_posts_append(params, param);
}

break;
Expand Down
37 changes: 24 additions & 13 deletions prism_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -7774,6 +7774,12 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,

return;
}
case PM_NO_BLOCK_PARAMETER_NODE: {
// def foo(&nil); end
// ^^^^
ISEQ_BODY(iseq)->param.flags.accepts_no_block = TRUE;
return;
}
case PM_NO_KEYWORDS_PARAMETER_NODE: {
// def foo(**nil); end
// ^^^^^
Expand Down Expand Up @@ -8779,25 +8785,30 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
// ^^
if (parameters_node->block) {
body->param.block_start = local_index;
body->param.flags.has_block = true;
if (PM_NODE_TYPE(parameters_node->block) == PM_NO_BLOCK_PARAMETER_NODE) {
body->param.flags.accepts_no_block = true;
}
else {
body->param.flags.has_block = true;
body->param.block_start = local_index;

pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;
pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;

if (name) {
if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
ID local = pm_constant_id_lookup(scope_node, name);
local_table_for_iseq->ids[local_index] = local;
if (name) {
if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
ID local = pm_constant_id_lookup(scope_node, name);
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
}
}
else {
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq);
}
}
else {
pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq);
}

local_index++;
local_index++;
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3177,6 +3177,7 @@ method_inspect(VALUE method)
const VALUE keyrest = ID2SYM(rb_intern("keyrest"));
const VALUE block = ID2SYM(rb_intern("block"));
const VALUE nokey = ID2SYM(rb_intern("nokey"));
const VALUE noblock = ID2SYM(rb_intern("noblock"));
int forwarding = 0;

rb_str_buf_cat2(str, "(");
Expand Down Expand Up @@ -3210,6 +3211,9 @@ method_inspect(VALUE method)
else if (kind == nokey) {
name = rb_str_new2("nil");
}
else if (kind == noblock) {
name = rb_str_new2("nil");
}
}

if (kind == req) {
Expand Down Expand Up @@ -3259,6 +3263,9 @@ method_inspect(VALUE method)
else if (kind == nokey) {
rb_str_buf_cat2(str, "**nil");
}
else if (kind == noblock) {
rb_str_buf_cat2(str, "&nil");
}

if (i < RARRAY_LEN(params) - 1) {
rb_str_buf_cat2(str, ", ");
Expand Down
1 change: 1 addition & 0 deletions rjit_c.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,7 @@ def C.rb_iseq_constant_body
anon_kwrest: [CType::BitField.new(1, 3), 11],
use_block: [CType::BitField.new(1, 4), 12],
forwardable: [CType::BitField.new(1, 5), 13],
accepts_no_block: [CType::BitField.new(1, 6), 14],
), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, flags)")],
size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, size)")],
lead_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF(((struct rb_iseq_constant_body *)NULL)->param, lead_num)")],
Expand Down
1 change: 1 addition & 0 deletions rubyparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,7 @@ struct rb_args_info {

struct RNode_OPT_ARG *opt_args;
unsigned int no_kwarg: 1;
unsigned int no_blockarg: 1;
unsigned int ruby2_keywords: 1;
unsigned int forwarding: 1;
};
Expand Down
13 changes: 13 additions & 0 deletions spec/ruby/core/method/parameters_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ def one_splat_one_block(*args, &block)
local_is_not_parameter = {}
end

ruby_version_is "3.4" do
eval <<-RUBY
def one_noblock(&nil); end
RUBY
end

def forward_parameters(...) end

def underscore_parameters(_, _, _ = 1, *_, _:, _: 2, **_, &_); end
Expand Down Expand Up @@ -187,6 +193,13 @@ def underscore_parameters(_, _, _ = 1, *_, _:, _: 2, **_, &_); end
m.parameters.should == [[:nokey]]
end

ruby_version_is "3.4" do
it "returns [[:noblock]] for a method with a single &nil parameter" do
m = MethodSpecs::Methods.instance_method(:one_noblock)
m.parameters.should == [[:noblock]]
end
end

it "works with ->(){} as the value of an optional argument" do
m = MethodSpecs::Methods.instance_method(:one_opt_with_stabby)
m.parameters.should == [[:opt,:a]]
Expand Down
8 changes: 8 additions & 0 deletions spec/ruby/core/proc/parameters_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,12 @@
it "returns :nokey for **nil parameter" do
proc { |**nil| }.parameters.should == [[:nokey]]
end

ruby_version_is "3.4" do
it "returns :noblock for &nil parameter" do
eval <<~RUBY
proc { |&nil| }.parameters.should == [[:noblock]]
RUBY
end
end
end
11 changes: 11 additions & 0 deletions spec/ruby/language/block_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1071,4 +1071,15 @@ def all_kwrest(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, **
all_kwrest(:a, :b, :c, :d, :e, okw1: 'x', okw2: 'y') { 1 }.should == 1
end
end

ruby_version_is "3.4" do
it "works alongside disallowed block argument" do
no_block = eval <<-EOF
proc {|arg1, &nil| arg1}
EOF

no_block.call(:a).should == :a
-> { no_block.call(:a) {} }.should raise_error(ArgumentError, 'no block accepted')
end
end
end
12 changes: 12 additions & 0 deletions spec/ruby/language/method_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,18 @@ def m(a, b = nil, c = nil, d, e: nil, **f)
result = m(1, {foo: :bar})
result.should == [1, nil, nil, {foo: :bar}, nil, {}]
end

ruby_version_is "3.4" do
evaluate <<-ruby do
def m(a, &nil); a end;
ruby

m(1).should == 1

-> { m(1) {} }.should raise_error(ArgumentError, 'no block accepted')
-> { m(1, &proc {}) }.should raise_error(ArgumentError, 'no block accepted')
end
end
end

context 'when passing an empty keyword splat to a method that does not accept keywords' do
Expand Down
Loading
Loading