Skip to content
Merged
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
16 changes: 15 additions & 1 deletion config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3920,6 +3920,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 @@ -4066,7 +4078,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
8 changes: 7 additions & 1 deletion lib/prism/node_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,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
55 changes: 40 additions & 15 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -5558,6 +5558,24 @@ 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_AMPERSAND || operator->type == PM_TOKEN_UAMPERSAND);
assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
pm_no_block_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_no_block_parameter_node_t);

*node = (pm_no_block_parameter_node_t) {
.base = PM_NODE_INIT(parser, PM_NO_BLOCK_PARAMETER_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, operator, keyword)),
.operator_loc = TOK2LOC(parser, operator),
.keyword_loc = TOK2LOC(parser, keyword)
};

return node;
}

/**
* Allocate and initialize a new NoKeywordsParameterNode node.
*/
Expand Down Expand Up @@ -5787,9 +5805,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, UP(param));
pm_parameters_node_location_set(params, param);
params->block = param;
}

Expand Down Expand Up @@ -13929,26 +13947,33 @@ parse_parameters(
parser_lex(parser);

pm_token_t operator = parser->previous;
pm_token_t name = { 0 };
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 {
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
}
pm_token_t name = {0};

pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator);
if (repeated) {
pm_node_flag_set_repeated_parameter(UP(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 {
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
}

param = (pm_node_t *) pm_block_parameter_node_create(parser, NTOK2PTR(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, UP(param), PM_ERR_PARAMETER_BLOCK_MULTI);
pm_parameters_node_posts_append(params, UP(param));
pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI);
pm_parameters_node_posts_append(params, param);
}

break;
Expand Down
4 changes: 4 additions & 0 deletions test/prism/result/source_location_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,10 @@ def test_NilNode
assert_location(NilNode, "nil")
end

def test_NoBlockParameterNode
assert_location(NoBlockParameterNode, "def foo(&nil); end", 8...12) { |node| node.parameters.block }
end

def test_NoKeywordsParameterNode
assert_location(NoKeywordsParameterNode, "def foo(**nil); end", 8...13) { |node| node.parameters.keyword_rest }
end
Expand Down
8 changes: 7 additions & 1 deletion test/prism/ruby/parameters_signature_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ def test_nokey
assert_parameters([[:nokey]], "**nil")
end

def test_noblock
# FIXME: `compare: RUBY_VERSION >= "4.1"` once builds are available
assert_parameters([[:noblock]], "&nil", compare: false)
end

def test_keyrest_anonymous
assert_parameters([[:keyrest, :**]], "**")
end
Expand All @@ -74,10 +79,11 @@ def test_forwarding

private

def assert_parameters(expected, source)
def assert_parameters(expected, source, compare: true)
# Compare against our expectation.
assert_equal(expected, signature(source))

return unless compare
# Compare against Ruby's expectation.
object = Object.new
eval("def object.m(#{source}); end")
Expand Down