Skip to content

Commit 7c71213

Browse files
authored
Add support for itblock node for Ruby 3.4 (#365)
In tests, `parser` will be capped to Ruby 3.3 and tests that don't pass with get marked as broken.
1 parent be16c46 commit 7c71213

File tree

13 files changed

+156
-13
lines changed

13 files changed

+156
-13
lines changed

changelog/new_support_itblock.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#365](https://github.com/rubocop/rubocop-ast/pull/365): Add support for `itblock` node for Ruby 3.4. ([@earlopain][])

docs/modules/ROOT/pages/node_types.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ The following fields are given when relevant to nodes in the source code:
170170

171171
|numblock|Block that has numbered arguments (`_1`) referenced inside it.|Three children. First child is a `send`/`csend` node representing the way the block is created, second child is an `int` (the number of numeric arguments) and the third child is a body statement.|proc { _1 + _3 }|https://rubydoc.info/github/rubocop/rubocop-ast/RuboCop/AST/BlockNode[BlockNode]
172172

173+
|itblock|Block that has the `it` block parameter referenced inside it.|Three children. First child is a `send`/`csend` node representing the way the block is created, second child is the symbol `it` and the third child is a body statement.|proc { it }|N/A
174+
173175
|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|https://rubydoc.info/github/rubocop/rubocop-ast/RuboCop/AST/OpAsgnNode[OpAsgnNode]
174176

175177
|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/rubocop-ast/RuboCop/AST/ArgNode[ArgNode]

lib/rubocop/ast/builder.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def self.included(base)
3232
gvasgn: AsgnNode,
3333
block: BlockNode,
3434
numblock: BlockNode,
35+
itblock: BlockNode,
3536
break: BreakNode,
3637
case_match: CaseMatchNode,
3738
casgn: CasgnNode,

lib/rubocop/ast/node.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class Node < Parser::AST::Node # rubocop:disable Metrics/ClassLength
112112
csend: :call,
113113

114114
block: :any_block,
115-
numblock: :any_block
115+
numblock: :any_block,
116+
itblock: :any_block
116117
}.freeze
117118
private_constant :GROUP_FOR_TYPE
118119

lib/rubocop/ast/node/block_node.rb

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ module AST
1111
class BlockNode < Node
1212
include MethodIdentifierPredicates
1313

14+
IT_BLOCK_ARGUMENT = [ArgNode.new(:arg, [:it])].freeze
15+
private_constant :IT_BLOCK_ARGUMENT
1416
VOID_CONTEXT_METHODS = %i[each tap].freeze
1517
private_constant :VOID_CONTEXT_METHODS
1618

@@ -46,10 +48,10 @@ def last_argument
4648
#
4749
# @return [Array<Node>]
4850
def arguments
49-
if numblock_type?
50-
[].freeze # Numbered parameters have no block arguments.
51-
else
51+
if block_type?
5252
node_parts[1]
53+
else
54+
[].freeze # Numblocks and itblocks have no explicit block arguments.
5355
end
5456
end
5557

@@ -60,6 +62,8 @@ def arguments
6062
def argument_list
6163
if numblock_type?
6264
numbered_arguments
65+
elsif itblock_type?
66+
IT_BLOCK_ARGUMENT
6367
else
6468
arguments.argument_list
6569
end
@@ -153,10 +157,7 @@ def void_context?
153157

154158
private
155159

156-
# Numbered arguments of this `numblock`.
157160
def numbered_arguments
158-
return [].freeze unless numblock_type?
159-
160161
max_param = children[1]
161162
1.upto(max_param).map do |i|
162163
ArgNode.new(:arg, [:"_#{i}"])

lib/rubocop/ast/node/mixin/method_dispatch_node.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ def selector
3939
end
4040
end
4141

42-
# The `block` or `numblock` node associated with this method dispatch, if any.
42+
# The `block`, `numblock`, or `itblock` node associated with this method dispatch, if any.
4343
#
44-
# @return [BlockNode, nil] the `block` or `numblock` node associated with this method
45-
# call or `nil`
44+
# @return [BlockNode, nil] the `block`, `numblock`, or `itblock` node associated with this
45+
# method call or `nil`
4646
def block_node
4747
parent if block_literal?
4848
end

lib/rubocop/ast/traversal.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ def children_count_check_code(range)
159159
def_callback :if, :always, :nil?, :nil?
160160
def_callback :block, :always, :always, :nil?
161161
def_callback :numblock, :always, :skip, :nil?
162+
def_callback :itblock, :always, :skip, :nil?
162163
def_callback :defs, :always, :skip, :always, :nil?
163164

164165
def_callback %i[send csend], body: <<~RUBY
@@ -176,7 +177,7 @@ def children_count_check_code(range)
176177

177178
to_define = ::Parser::Meta::NODE_TYPES.to_a
178179
to_define -= defined
179-
to_define -= %i[numargs ident] # transient
180+
to_define -= %i[numargs itarg ident] # transient
180181
to_define -= %i[blockarg_expr restarg_expr] # obsolete
181182
to_define -= %i[objc_kwarg objc_restarg objc_varargs] # mac_ruby
182183
def_callback to_define, body: <<~RUBY

rubocop-ast.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Gem::Specification.new do |s|
3333
'rubygems_mfa_required' => 'true'
3434
}
3535

36-
s.add_dependency('parser', '>= 3.3.1.0')
36+
s.add_dependency('parser', '>= 3.3.7.2')
3737

3838
##### Do NOT add `rubocop` (or anything depending on `rubocop`) here. See Gemfile
3939
end

spec/rubocop/ast/block_node_spec.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
it { expect(block_node.arguments).to be_empty }
4848
end
4949
end
50+
51+
context '>= Ruby 3.4', :ruby34, broken_on: :parser do
52+
context 'using `it` block parameter' do
53+
let(:source) { 'foo { it }' }
54+
55+
it { expect(block_node.arguments).to be_empty }
56+
end
57+
end
5058
end
5159

5260
describe '#argument_list' do
@@ -81,6 +89,14 @@
8189
end
8290
end
8391
end
92+
93+
context '>= Ruby 3.4', :ruby34, broken_on: :parser do
94+
context 'using `it` block parameter' do
95+
let(:source) { 'foo { it }' }
96+
97+
it { expect(names).to eq(%i[it]) }
98+
end
99+
end
84100
end
85101

86102
describe '#arguments?' do
@@ -121,6 +137,14 @@
121137
it { is_expected.not_to be_arguments }
122138
end
123139
end
140+
141+
context '>= Ruby 3.4', :ruby34 do
142+
context 'using `it` block parameter' do
143+
let(:source) { 'foo { it }' }
144+
145+
it { is_expected.not_to be_arguments }
146+
end
147+
end
124148
end
125149

126150
describe '#braces?' do
@@ -341,6 +365,14 @@
341365
it { expect(block_node.first_argument).to be_nil }
342366
end
343367
end
368+
369+
context '>= Ruby 3.4', :ruby34 do
370+
context 'using `it` block parameter' do
371+
let(:source) { 'foo { it }' }
372+
373+
it { expect(block_node.first_argument).to be_nil }
374+
end
375+
end
344376
end
345377

346378
describe '#last_argument' do

spec/rubocop/ast/node_spec.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,40 @@ def used?
491491
end
492492
end
493493
end
494+
495+
context 'using Ruby >= 3.4', :ruby34 do
496+
context 'class definition with an itblock' do
497+
let(:src) { 'Class.new { do_something(it) }' }
498+
499+
it 'matches' do
500+
expect(node).to be_class_constructor
501+
end
502+
end
503+
504+
context 'module definition with an itblock' do
505+
let(:src) { 'Module.new { do_something(it) }' }
506+
507+
it 'matches' do
508+
expect(node).to be_class_constructor
509+
end
510+
end
511+
512+
context 'Struct definition with an itblock' do
513+
let(:src) { 'Struct.new(:foo, :bar) { do_something(it) }' }
514+
515+
it 'matches' do
516+
expect(node).to be_class_constructor
517+
end
518+
end
519+
520+
context 'Data definition with an itblock' do
521+
let(:src) { 'Data.define(:foo, :bar) { do_something(it) }' }
522+
523+
it 'matches' do
524+
expect(node).to be_class_constructor
525+
end
526+
end
527+
end
494528
end
495529

496530
describe '#struct_constructor?' do

0 commit comments

Comments
 (0)