diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb index 1bb3ebcf5f921b..a05123d1bb3aa5 100644 --- a/lib/prism/node_ext.rb +++ b/lib/prism/node_ext.rb @@ -286,7 +286,9 @@ def signature case param when MultiTargetNode names << [:req] - when NoKeywordsParameterNode, KeywordRestParameterNode, ForwardingParameterNode + when NoKeywordsParameterNode, KeywordRestParameterNode, + NoBlockParameterNode, BlockParameterNode, + ForwardingParameterNode # Invalid syntax, e.g. "def f(**nil, ...)" moves the NoKeywordsParameterNode to posts raise "Invalid syntax" else diff --git a/lib/prism/translation/parser/builder.rb b/lib/prism/translation/parser/builder.rb index 6b620c25bc9658..7fc3bba6b74718 100644 --- a/lib/prism/translation/parser/builder.rb +++ b/lib/prism/translation/parser/builder.rb @@ -7,12 +7,14 @@ class Parser # A builder that knows how to convert more modern Ruby syntax # into whitequark/parser gem's syntax tree. class Builder < ::Parser::Builders::Default - # It represents the `it` block argument, which is not yet implemented in the Parser gem. + # It represents the `it` block argument, which is not yet implemented in + # the Parser gem. def itarg n(:itarg, [:it], nil) end - # The following three lines have been added to support the `it` block parameter syntax in the source code below. + # The following three lines have been added to support the `it` block + # parameter syntax in the source code below. # # if args.type == :itarg # block_type = :itblock @@ -56,6 +58,12 @@ def block(method_call, begin_t, args, body, end_t) method_call.loc.with_expression(join_exprs(method_call, block))) end end + + # def foo(&nil); end + # ^^^^ + def blocknilarg(amper_t, nil_t) + n0(:blocknilarg, arg_prefix_map(amper_t, nil_t)) + end end end end diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb index 37951a1d400159..21aa7796c3c9d4 100644 --- a/lib/prism/translation/parser/compiler.rb +++ b/lib/prism/translation/parser/compiler.rb @@ -1390,6 +1390,12 @@ def visit_nil_node(node) builder.nil(token(node.location)) end + # def foo(&nil); end + # ^^^^ + def visit_no_block_parameter_node(node) + builder.blocknilarg(token(node.operator_loc), token(node.keyword_loc)) + end + # def foo(**nil); end # ^^^^^ def visit_no_keywords_parameter_node(node) diff --git a/prism/config.yml b/prism/config.yml index 0736f5a0bf2dfb..7527e01070b2a5 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -4065,6 +4065,9 @@ nodes: - on error: NoKeywordsParameterNode # On parsing error of `f(..., ...)`, the first forwarding parameter is moved here: - on error: ForwardingParameterNode + # On parsing error of `f(&nil, &foo)`/`f(&foo, &nil)`, the first forwarding parameter is moved here: + - on error: BlockParameterNode + - on error: NoBlockParameterNode - name: keywords type: node[] kind: diff --git a/prism/prism.c b/prism/prism.c index 81768024f2abf6..56cb9e3fb3f771 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -13949,7 +13949,7 @@ parse_parameters( pm_token_t operator = parser->previous; pm_node_t *param; - if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) { + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1 && accept1(parser, PM_TOKEN_KEYWORD_NIL)) { param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous); } else { pm_token_t name = {0}; diff --git a/test/prism/errors/3.3-4.0/noblock.txt b/test/prism/errors/3.3-4.0/noblock.txt new file mode 100644 index 00000000000000..07939041bb0d7a --- /dev/null +++ b/test/prism/errors/3.3-4.0/noblock.txt @@ -0,0 +1,6 @@ +def foo(&nil) + ^~~ unexpected 'nil'; expected a `)` to close the parameters + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it +end + diff --git a/test/prism/errors/4.1/multiple_blocks.txt b/test/prism/errors/4.1/multiple_blocks.txt new file mode 100644 index 00000000000000..7e8433cf82abd2 --- /dev/null +++ b/test/prism/errors/4.1/multiple_blocks.txt @@ -0,0 +1,12 @@ +def foo(&nil, &nil); end + ^ unexpected parameter order + ^~~~ multiple block parameters; only one block is allowed + +def foo(&foo, &nil); end + ^ unexpected parameter order + ^~~~ multiple block parameters; only one block is allowed + +def foo(&nil, &foo); end + ^ unexpected parameter order + ^~~~ multiple block parameters; only one block is allowed + diff --git a/test/prism/fixtures/4.1/noblock.txt b/test/prism/fixtures/4.1/noblock.txt new file mode 100644 index 00000000000000..2395393e228f10 --- /dev/null +++ b/test/prism/fixtures/4.1/noblock.txt @@ -0,0 +1,4 @@ +def foo(&nil) +end + +-> (&nil) {} diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index 48449018041dcf..bcb964aff67568 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -30,7 +30,7 @@ class LocalsTest < TestCase "command_method_call_2.txt", # https://bugs.ruby-lang.org/issues/21669 - "4.1/void_value.txt" + "4.1/void_value.txt", ] Fixture.each_for_current_ruby(except: except) do |fixture| @@ -207,7 +207,7 @@ def prism_locals(source) end end - if params.block + if params.block.is_a?(BlockParameterNode) sorted << (params.block.name || :&) end diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 39cb9395ab680e..00cf470e0ec6e5 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -40,6 +40,9 @@ class RipperTest < TestCase # https://bugs.ruby-lang.org/issues/21669 incorrect << "4.1/void_value.txt" + # https://bugs.ruby-lang.org/issues/19979 + incorrect << "4.1/noblock.txt" + # Skip these tests that we haven't implemented yet. omitted_sexp_raw = [ "bom_leading_space.txt",