Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ArgNode for arg, kwarg, optarg, restarg, kwoptarg, `kwr…
Browse files Browse the repository at this point in the history
…estarg`, `blockarg`, `forward_arg` and `shadowarg` types.

- Add `ArgsNode#argument_list` to return all descendant argument type nodes.
- Added Procarg0Node for modernized compatibility with ArgNode.
- Expose `ArgsNode#argument_list` on `BlockNode`.
dvandersluis committed Nov 24, 2020
1 parent 6dad540 commit 67fdc91
Showing 14 changed files with 507 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#154](https://github.com/rubocop-hq/rubocop-ast/pull/154): Add `BlockNode#argument_list` and `BlockNode#argument_names`. ([@dvandersluis][])
2 changes: 2 additions & 0 deletions changelog/new_add_blocknodeargument_names.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* [#154](https://github.com/rubocop-hq/rubocop-ast/pull/154): Add `ArgNode` and `Procarg0Node` ("modern" mode), and add `ArgsNode#argument_list` to get only argument type nodes. ([@dvandersluis][])

20 changes: 11 additions & 9 deletions docs/modules/ROOT/pages/node_types.adoc
Original file line number Diff line number Diff line change
@@ -58,7 +58,7 @@ The following fields are given when relevant to nodes in the source code:

|and_asgn|And-assignment (AND the receiver with the argument and assign it back to receiver).|First child must be an assignment node, second child is the expression node.|a &&= b |N/A

|arg|Required positional argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar)|N/A
|arg|Required positional argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|args|Argument list. Must come inside a `def`, `defs`, `def_e`, `defs_e` or `block` node.|Children must be `arg`, `optarg`, `restarg`, `blockarg`, `kwarg`, `kwoptarg`, `kwrestarg`, `kwnilarg`, or `forwardarg`.|def whatever(foo, bar=1, baz: 5)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgsNode[ArgsNode]

@@ -70,7 +70,7 @@ The following fields are given when relevant to nodes in the source code:

|block_pass|Used when passing a block as an argument.|One child, an expression node representing the block to pass.|foo(a, &my_block)|N/A

|blockarg|Reference to block argument from a function definition. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(&bar)|N/A
|blockarg|Reference to block argument from a function definition. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(&bar)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|break|break keyword|One child with an expression node for the results to be passed through the break.|break 1|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/BreakNode[BreakNode]

@@ -114,7 +114,7 @@ The following fields are given when relevant to nodes in the source code:

|for|for..in looping condition|Three children. First child is a `lvasgn` or `mlhs` node with the variable(s), second child is an expression node with the array/range to loop over, third child is a body statement.|for a in arr do foo; end|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ForNode[ForNode]

|forward_arg|Forwarding argument, for Ruby 2.8 (when `emit_forward_arg` is true). Must come inside an `args` node.|None|def whatever(foo, ...)|N/A
|forward_arg|Forwarding argument, for Ruby 2.8 (when `emit_forward_arg` is true). Must come inside an `args` node.|None|def whatever(foo, ...)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|forward_args|Forwarding argument list, for Ruby 2.7 (when `emit_forward_arg` is false). Must come inside a `def`, `defs`, `def_e`, or `defs_e` node.|None|def (foo(...)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ForwardArgsNode[ForwardArgsNode]

@@ -136,25 +136,25 @@ The following fields are given when relevant to nodes in the source code:

|irange|Inclusive range literal.|Two children, the start and end nodes (including `nil` for beginless/endless)|1..2|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/RangeNode[RangeNode]

|kwarg|Required keyword argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar:)|N/A
|kwarg|Required keyword argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar:)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|kwbegin|Explicit `begin` block.|Child nodes are body statements.|begin,end|N/A

|kwnilarg|Double splat with nil in function definition, used to specify that the function does not accept keyword args. Must come inside an `args`.|None|def foo(**nil)|N/A

|kwoptarg|Optional keyword argument. Must come inside an `args`.|Two children - a symbol, representing the argument name, and an expression node for the value.|def foo(bar: 5)|N/A
|kwoptarg|Optional keyword argument. Must come inside an `args`.|Two children - a symbol, representing the argument name, and an expression node for the value.|def foo(bar: 5)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|kwsplat|Double splat used for keyword arguments inside a function call (as opposed to a function definition).|One child, an expression.|foo(bar, **kwargs)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/KeywordSplatNode[KeywordSplatNode]

|kwrestargs|Double splat used for keyword arguments inside a function definition (as opposed to a function call). Must come inside an `args`.|One child - a symbol, representing the argument name, if a name is given. If no name given, it has no children..|def foo(**kwargs)|N/A
|kwrestargs|Double splat used for keyword arguments inside a function definition (as opposed to a function call). Must come inside an `args`.|One child - a symbol, representing the argument name, if a name is given. If no name given, it has no children..|def foo(**kwargs)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|lvar|Local variable access|One child, the variable name|foo|N/A

|lvasgn|Local variable assignment|Two children: The variable name (symbol) and the expression.|a = some_thing|N/A

|masgn|Multiple assigment.|First set of children are all `mlhs` nodes, and the rest of the children must be expression nodes corresponding to the values in the `mlhs` nodes.|a, b, = [1, 2]|N/A

|mlhs|Multiple left-hand side. Only used inside a `masgn`.|Children must all be assignment nodes. Represents the left side of a multiple assignment (`a, b` in the example).|a, b = 5, 6|N/A
|mlhs|Multiple left-hand side. Used inside a `masgn` and block argument destructuring.|Children must all be assignment nodes. Represents the left side of a multiple assignment (`a, b` in the example).|a, b = 5, 6|N/A

|module|Module definition|Two children. First child is a `const` node for the module name. Second child is a body statement.|module Foo < Bar; end|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ModuleNode[ModuleNode]

@@ -168,7 +168,7 @@ The following fields are given when relevant to nodes in the source code:

|op_asgn|Operator-assignment - perform an operation and assign the value.|Three children. First child must be an assignment node, second child is the operator (e.g. `:+`) and the third child is the expression node.|a += b|N/A

|opt_arg|Optional positional argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar=1)|N/A
|optarg|Optional positional argument. Must come inside an `args`.|One child - a symbol, representing the argument name.|def foo(bar=1)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|or|Or operator|Two children are both expression nodes representing the operands.|a or b|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/OrNode[OrNode]

@@ -189,7 +189,7 @@ The following fields are given when relevant to nodes in the source code:

|rescue|A rescue statement.May be "top-level" or may be nested inside an `ensure` block (if both rescue and ensure are in the block).|First node is a body statement. Last child is the "else" body statement, or `nil`. Remaining children are `resbody` nodes.|begin; rescue Exception, A => bar; 1; end|

|restarg|Positional splat argument. Must come inside an `args`.|One child - a symbol, representing the argument name (if given). If no name given, there are no children.|def foo(*rest)|N/A
|restarg|Positional splat argument. Must come inside an `args`.|One child - a symbol, representing the argument name (if given). If no name given, there are no children.|def foo(*rest)|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|return|Return statement|Zero or one child, an expression node for the value to return.|return|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ReturnNode[ReturnNode]

@@ -200,6 +200,8 @@ The following fields are given when relevant to nodes in the source code:
|send|Non-safe method invocation (i.e. top-level or using a dot)|First child is the receiver node (e.g. `self`), second child is the method name (e.g. `:foo=`) and the remaining children (if any) are the arguments (expression nodes).
a|`foo` or `foo.bar`|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/SendNode[SendNode]

|shadowarg|Shadow argument, aka block-local variable. Must come inside an `args`.|One child - a symbol, representing the argument name.|foo { \|a; b\| b }|https://rubydoc.info/github/rubocop-hq/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

|splat|Array or function argument * operator|One child, an expression.|*foo|N/A

|str|Non-interpolated string literal. The heredoc version works very differently from the regular version and the location info is totally separate.|One child, the String content.
2 changes: 2 additions & 0 deletions lib/rubocop/ast.rb
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@
require_relative 'ast/node/mixin/basic_literal_node'
require_relative 'ast/node/alias_node'
require_relative 'ast/node/and_node'
require_relative 'ast/node/arg_node'
require_relative 'ast/node/args_node'
require_relative 'ast/node/array_node'
require_relative 'ast/node/block_node'
@@ -62,6 +63,7 @@
require_relative 'ast/node/next_node'
require_relative 'ast/node/or_node'
require_relative 'ast/node/pair_node'
require_relative 'ast/node/procarg0_node'
require_relative 'ast/node/range_node'
require_relative 'ast/node/regexp_node'
require_relative 'ast/node/rescue_node'
10 changes: 10 additions & 0 deletions lib/rubocop/ast/builder.rb
Original file line number Diff line number Diff line change
@@ -20,6 +20,15 @@ class Builder < Parser::Builders::Default
NODE_MAP = {
and: AndNode,
alias: AliasNode,
arg: ArgNode,
blockarg: ArgNode,
forward_arg: ArgNode,
kwarg: ArgNode,
kwoptarg: ArgNode,
kwrestarg: ArgNode,
optarg: ArgNode,
restarg: ArgNode,
shadowarg: ArgNode,
args: ArgsNode,
array: ArrayNode,
block: BlockNode,
@@ -49,6 +58,7 @@ class Builder < Parser::Builders::Default
next: NextNode,
or: OrNode,
pair: PairNode,
procarg0: Procarg0Node,
regexp: RegexpNode,
rescue: RescueNode,
resbody: ResbodyNode,
3 changes: 2 additions & 1 deletion lib/rubocop/ast/node.rb
Original file line number Diff line number Diff line change
@@ -77,7 +77,8 @@ class Node < Parser::AST::Node # rubocop:disable Metrics/ClassLength
# @api private
SPECIAL_KEYWORDS = %w[__FILE__ __LINE__ __ENCODING__].to_set.freeze
# @api private
ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg].to_set.freeze
ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg
blockarg forward_arg shadowarg].to_set.freeze

LITERAL_RECURSIVE_METHODS = (COMPARISON_OPERATORS + %i[* ! <=>]).freeze
LITERAL_RECURSIVE_TYPES = (OPERATOR_KEYWORDS + COMPOSITE_LITERALS + %i[begin pair]).freeze
34 changes: 34 additions & 0 deletions lib/rubocop/ast/node/arg_node.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module RuboCop
module AST
# A node extension for `arg`, `optarg`, `restarg`, `kwarg`, `kwoptarg`,
# `kwrestarg`, `blockarg`, `shadowarg` and `forward_arg` nodes.
# This will be used in place of a plain node when the builder constructs
# the AST, making its methods available to all `arg` nodes within RuboCop.
class ArgNode < Node
# Returns the name of an argument.
#
# @return [Symbol, nil] the name of the argument
def name
node_parts[0]
end

# Returns the default value of the argument, if any.
#
# @return [Node, nil] the default value of the argument
def default_value
return unless default?

node_parts[1]
end

# Checks whether the argument has a default value
#
# @return [Boolean] whether the argument has a default value
def default?
optarg_type? || kwoptarg_type?
end
end
end
end
10 changes: 10 additions & 0 deletions lib/rubocop/ast/node/args_node.rb
Original file line number Diff line number Diff line change
@@ -24,6 +24,16 @@ class ArgsNode < Node
def empty_and_without_delimiters?
loc.expression.nil?
end

# Yield each argument from the collection.
# Arguments can be inside `mlhs` nodes in the case of destructuring, so this
# flattens the collection to just `arg`, `optarg`, `restarg`, `kwarg`,
# `kwoptarg`, `kwrestarg`, `blockarg`, `forward_arg` and `shadowarg`.
#
# @return [Array<Node>] array of argument nodes.
def argument_list
each_descendant(*ARGUMENT_TYPES).to_a
end
end
end
end
13 changes: 13 additions & 0 deletions lib/rubocop/ast/node/block_node.rb
Original file line number Diff line number Diff line change
@@ -22,6 +22,9 @@ def send_node
end

# The arguments of this block.
# Note that if the block has destructured arguments, `arguments` will
# return a `mlhs` node, whereas `argument_list` will return only
# actual argument nodes.
#
# @return [Array<Node>]
def arguments
@@ -32,6 +35,16 @@ def arguments
end
end

# Returns a collection of all descendants of this node that are
# argument type nodes. See `ArgsNode#argument_list` for details.
#
# @return [Array<Node>]
def argument_list
return [] unless arguments?

arguments.argument_list
end

# The body of this block.
#
# @return [Node, nil] the body of the `block` node or `nil`
17 changes: 17 additions & 0 deletions lib/rubocop/ast/node/procarg0_node.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module RuboCop
module AST
# A node extension for `procarg0` nodes.
# This will be used in place of a plain node when the builder constructs
# the AST, making its methods available to all `arg` nodes within RuboCop.
class Procarg0Node < ArgNode
# Returns the name of an argument.
#
# @return [Symbol, nil] the name of the argument
def name
node_parts[0].name
end
end
end
end
312 changes: 312 additions & 0 deletions spec/rubocop/ast/arg_node_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
# frozen_string_literal: true

RSpec.describe RuboCop::AST::ArgNode do
let(:args_node) { parse_source(source).ast.arguments }
let(:arg_node) { args_node.first }

describe '.new' do
context 'with a method definition' do
let(:source) { 'def foo(x) end' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with a block' do
let(:source) { 'foo { |x| bar }' }

if RuboCop::AST::Builder.emit_procarg0
it { expect(arg_node).to be_a(RuboCop::AST::Procarg0Node) }
else
it { expect(arg_node).to be_a(described_class) }
end
end

context 'with a lambda literal' do
let(:source) { '-> (x) { bar }' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with a keyword argument' do
let(:source) { 'def foo(x:) end' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with an optional argument' do
let(:source) { 'def foo(x = 42) end' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with an optional keyword argument' do
let(:source) { 'def foo(x: 42) end' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with a splatted argument' do
let(:source) { 'def foo(*x) end' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with a double splatted argument' do
let(:source) { 'def foo(**x) end' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with a block argument' do
let(:source) { 'def foo(&x) end' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with a shadow argument' do
let(:source) { 'foo { |; x| }' }

it { expect(arg_node).to be_a(described_class) }
end

context 'with argument forwarding' do
context 'with Ruby >= 2.7', :ruby27 do
let(:source) { 'def foo(...); end' }

if RuboCop::AST::Builder.emit_forward_arg
it { expect(arg_node).to be_a(described_class) }
else
it { expect(arg_node).to be_forward_args_type }
end
end

context 'with Ruby >= 3.0', :ruby30 do
let(:source) { 'def foo(x, ...); end' }
let(:arg_node) { args_node.last }

it { expect(arg_node).to be_a(described_class) }
end
end
end

describe '#name' do
subject { arg_node.name }

context 'with a regular argument' do
let(:source) { 'def foo(x) end' }

it { is_expected.to eq(:x) }
end

context 'with a block' do
let(:source) { 'foo { |x| x }' }

it { is_expected.to eq(:x) }
end

context 'with a keyword argument' do
let(:source) { 'def foo(x:) end' }

it { is_expected.to eq(:x) }
end

context 'with an optional argument' do
let(:source) { 'def foo(x = 42) end' }

it { is_expected.to eq(:x) }
end

context 'with an optional keyword argument' do
let(:source) { 'def foo(x: 42) end' }

it { is_expected.to eq(:x) }
end

context 'with a splatted argument' do
let(:source) { 'def foo(*x) end' }

it { is_expected.to eq(:x) }
end

context 'with a nameless splatted argument' do
let(:source) { 'def foo(*) end' }

it { is_expected.to be_nil }
end

context 'with a double splatted argument' do
let(:source) { 'def foo(**x) end' }

it { is_expected.to eq(:x) }
end

context 'with a nameless double splatted argument' do
let(:source) { 'def foo(**) end' }

it { is_expected.to be_nil }
end

context 'with a block argument' do
let(:source) { 'def foo(&x) end' }

it { is_expected.to eq(:x) }
end

context 'with a shadow argument' do
let(:source) { 'foo { |; x| x = 5 }' }

it { is_expected.to eq(:x) }
end

context 'with argument forwarding' do
context 'with Ruby >= 2.7', :ruby27 do
let(:source) { 'def foo(...); end' }

it { is_expected.to be_nil } if RuboCop::AST::Builder.emit_forward_arg
end

context 'with Ruby >= 3.0', :ruby30 do
let(:source) { 'def foo(x, ...); end' }
let(:arg_node) { args_node.last }

it { is_expected.to be_nil }
end
end
end

describe '#default_value' do
include AST::Sexp

subject { arg_node.default_value }

context 'with a regular argument' do
let(:source) { 'def foo(x) end' }

it { is_expected.to be_nil }
end

context 'with a block' do
let(:source) { 'foo { |x| x }' }

it { is_expected.to be_nil }
end

context 'with an optional argument' do
let(:source) { 'def foo(x = 42) end' }

it { is_expected.to eq(s(:int, 42)) }
end

context 'with an optional keyword argument' do
let(:source) { 'def foo(x: 42) end' }

it { is_expected.to eq(s(:int, 42)) }
end

context 'with a splatted argument' do
let(:source) { 'def foo(*x) end' }

it { is_expected.to be_nil }
end

context 'with a double splatted argument' do
let(:source) { 'def foo(**x) end' }

it { is_expected.to be_nil }
end

context 'with a block argument' do
let(:source) { 'def foo(&x) end' }

it { is_expected.to be_nil }
end

context 'with a shadow argument' do
let(:source) { 'foo { |; x| x = 5 }' }

it { is_expected.to be_nil }
end

context 'with argument forwarding' do
context 'with Ruby >= 2.7', :ruby27 do
let(:source) { 'def foo(...); end' }

it { is_expected.to be_nil } if RuboCop::AST::Builder.emit_forward_arg
end

context 'with Ruby >= 3.0', :ruby30 do
let(:source) { 'def foo(x, ...); end' }
let(:arg_node) { args_node.last }

it { is_expected.to be_nil }
end
end
end

describe '#default?' do
subject { arg_node.default? }

context 'with a regular argument' do
let(:source) { 'def foo(x) end' }

it { is_expected.to eq(false) }
end

context 'with a block' do
let(:source) { 'foo { |x| x }' }

it { is_expected.to eq(false) }
end

context 'with an optional argument' do
let(:source) { 'def foo(x = 42) end' }

it { is_expected.to eq(true) }
end

context 'with an optional keyword argument' do
let(:source) { 'def foo(x: 42) end' }

it { is_expected.to eq(true) }
end

context 'with a splatted argument' do
let(:source) { 'def foo(*x) end' }

it { is_expected.to eq(false) }
end

context 'with a double splatted argument' do
let(:source) { 'def foo(**x) end' }

it { is_expected.to eq(false) }
end

context 'with a block argument' do
let(:source) { 'def foo(&x) end' }

it { is_expected.to eq(false) }
end

context 'with a shadow argument' do
let(:source) { 'foo { |; x| x = 5 }' }

it { is_expected.to eq(false) }
end

context 'with argument forwarding' do
context 'with Ruby >= 2.7', :ruby27 do
let(:source) { 'def foo(...); end' }

it { is_expected.to eq(false) } if RuboCop::AST::Builder.emit_forward_arg
end

context 'with Ruby >= 3.0', :ruby30 do
let(:source) { 'def foo(x, ...); end' }
let(:arg_node) { args_node.last }

it { is_expected.to eq(false) }
end
end
end
end
32 changes: 32 additions & 0 deletions spec/rubocop/ast/args_node_spec.rb
Original file line number Diff line number Diff line change
@@ -80,4 +80,36 @@
end
end
end

describe '#argument_list' do
include AST::Sexp

subject { args_node.argument_list }

let(:source) { 'foo { |a, b = 42, (c, *d), e:, f: 42, **g, &h; i| nil }' }
let(:arguments) do
[
s(:arg, :a),
s(:optarg, :b, s(:int, 42)),
s(:arg, :c),
s(:restarg, :d),
s(:kwarg, :e),
s(:kwoptarg, :f, s(:int, 42)),
s(:kwrestarg, :g),
s(:blockarg, :h),
s(:shadowarg, :i)
]
end

it { is_expected.to eq(arguments) }

context 'when using Ruby 2.7 or newer', :ruby27 do
context 'with argument forwarding' do
let(:source) { 'def foo(...); end' }
let(:arguments) { [s(:forward_arg)] }

it { is_expected.to eq(arguments) } if RuboCop::AST::Builder.emit_forward_arg
end
end
end
end
36 changes: 36 additions & 0 deletions spec/rubocop/ast/block_node_spec.rb
Original file line number Diff line number Diff line change
@@ -34,6 +34,12 @@
it { expect(block_node.arguments.size).to eq(2) }
end

context 'with destructured arguments' do
let(:source) { 'foo { |q, (r, s)| bar(q, r, s) }' }

it { expect(block_node.arguments.size).to eq(2) }
end

context '>= Ruby 2.7', :ruby27 do
context 'using numbered parameters' do
let(:source) { 'foo { _1 }' }
@@ -43,6 +49,30 @@
end
end

describe '#argument_list' do
subject(:argument_list) { block_node.argument_list }

context 'with no arguments' do
let(:source) { 'foo { bar }' }

it { is_expected.to be_empty }
end

context 'all argument types' do
let(:source) { 'foo { |a, b = 42, (c, *d), e:, f: 42, **g, &h| nil }' }

it { expect(argument_list.size).to eq(8) }
end

context '>= Ruby 2.7', :ruby27 do
context 'using numbered parameters' do
let(:source) { 'foo { _1 }' }

it { is_expected.to be_empty }
end
end
end

describe '#arguments?' do
context 'with no arguments' do
let(:source) { 'foo { bar }' }
@@ -68,6 +98,12 @@
it { is_expected.to be_arguments }
end

context 'with destructuring arguments' do
let(:source) { 'foo { |(q, r)| bar(q, r) }' }

it { is_expected.to be_arguments }
end

context '>= Ruby 2.7', :ruby27 do
context 'using numbered parameters' do
let(:source) { 'foo { _1 }' }
25 changes: 25 additions & 0 deletions spec/rubocop/ast/procarg0_node_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

RSpec.describe RuboCop::AST::Procarg0Node, :ruby27 do
let(:procarg0_node) { parse_source(source).ast.arguments.first }

describe '.new' do
context 'with a block' do
let(:source) { 'foo { |x| x }' }

if RuboCop::AST::Builder.emit_procarg0
it { expect(procarg0_node).to be_a(described_class) }
else
it { expect(procarg0_node).to be_a(RuboCop::AST::ArgNode) }
end
end
end

describe '#name' do
subject { procarg0_node.name }

let(:source) { 'foo { |x| x }' }

it { is_expected.to eq(:x) }
end
end

0 comments on commit 67fdc91

Please sign in to comment.