diff --git a/config.yml b/config.yml index f6e99c66b1..0736f5a0bf 100644 --- a/config.yml +++ b/config.yml @@ -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 @@ -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. diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb index 469e54ca0c..1bb3ebcf5f 100644 --- a/lib/prism/node_ext.rb +++ b/lib/prism/node_ext.rb @@ -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 diff --git a/src/prism.c b/src/prism.c index ca0f2a55aa..81768024f2 100644 --- a/src/prism.c +++ b/src/prism.c @@ -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. */ @@ -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; } @@ -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; diff --git a/test/prism/result/source_location_test.rb b/test/prism/result/source_location_test.rb index 993150f581..dbda848211 100644 --- a/test/prism/result/source_location_test.rb +++ b/test/prism/result/source_location_test.rb @@ -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 diff --git a/test/prism/ruby/parameters_signature_test.rb b/test/prism/ruby/parameters_signature_test.rb index ea1eea106b..5a225862c3 100644 --- a/test/prism/ruby/parameters_signature_test.rb +++ b/test/prism/ruby/parameters_signature_test.rb @@ -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 @@ -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")