Skip to content

Commit

Permalink
Add BlockNode#{first,last}_argument helpers
Browse files Browse the repository at this point in the history
These methods allow more consistent use of `first_argument` and
`last_argument` across more node types.
  • Loading branch information
sambostock authored and marcandre committed Oct 26, 2023
1 parent 69aa13b commit 737c624
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/new_add_blocknodefirstlast_argument_helpers.md
@@ -0,0 +1 @@
* [#x](https://github.com/rubocop/rubocop-ast/pull/x): Add `BlockNode#{first,last}_argument` helpers. ([@sambostock][])
18 changes: 18 additions & 0 deletions lib/rubocop/ast/node/block_node.rb
Expand Up @@ -21,6 +21,24 @@ def send_node
node_parts[0]
end

# A shorthand for getting the first argument of this block.
# Equivalent to `arguments.first`.
#
# @return [Node, nil] the first argument of this block,
# or `nil` if there are no arguments
def first_argument
arguments[0]
end

# A shorthand for getting the last argument of this block.
# Equivalent to `arguments.last`.
#
# @return [Node, nil] the last argument of this block,
# or `nil` if there are no arguments
def last_argument
arguments[-1]
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
Expand Down
80 changes: 80 additions & 0 deletions spec/rubocop/ast/block_node_spec.rb
Expand Up @@ -302,4 +302,84 @@
it { expect(block_node.receiver.source).to eq('foo') }
end
end

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

it { expect(block_node.first_argument).to be_nil }
end

context 'with a single literal argument' do
let(:source) { 'foo { |q| bar(q) }' }

it { expect(block_node.first_argument.source).to eq('q') }
end

context 'with a single splat argument' do
let(:source) { 'foo { |*q| bar(q) }' }

it { expect(block_node.first_argument.source).to eq('*q') }
end

context 'with multiple mixed arguments' do
let(:source) { 'foo { |q, *z| bar(q, z) }' }

it { expect(block_node.first_argument.source).to eq('q') }
end

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

it { expect(block_node.first_argument.source).to eq('(q, r)') }
end

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

it { expect(block_node.first_argument).to be_nil }
end
end
end

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

it { expect(block_node.last_argument).to be_nil }
end

context 'with a single literal argument' do
let(:source) { 'foo { |q| bar(q) }' }

it { expect(block_node.last_argument.source).to eq('q') }
end

context 'with a single splat argument' do
let(:source) { 'foo { |*q| bar(q) }' }

it { expect(block_node.last_argument.source).to eq('*q') }
end

context 'with multiple mixed arguments' do
let(:source) { 'foo { |q, *z| bar(q, z) }' }

it { expect(block_node.last_argument.source).to eq('*z') }
end

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

it { expect(block_node.last_argument.source).to eq('(r, s)') }
end

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

it { expect(block_node.last_argument).to be_nil }
end
end
end
end

0 comments on commit 737c624

Please sign in to comment.