Skip to content

Commit bde56f2

Browse files
committed
Add support for itblock node for Ruby 3.4
In tests, prism will always be used for Ruby 3.4+ and tests that don't pass with `parser` get marked as broken.
1 parent dc102aa commit bde56f2

File tree

12 files changed

+157
-12
lines changed

12 files changed

+157
-12
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 implicit it parameter (`it`) 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 { _1 + _3 }|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: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ def last_argument
4646
#
4747
# @return [Array<Node>]
4848
def arguments
49-
if numblock_type?
50-
[].freeze # Numbered parameters have no block arguments.
51-
else
49+
if block_type?
5250
node_parts[1]
51+
else
52+
[].freeze # Numblocks and itblocks have no explicit block arguments.
5353
end
5454
end
5555

@@ -60,6 +60,8 @@ def arguments
6060
def argument_list
6161
if numblock_type?
6262
numbered_arguments
63+
elsif itblock_type?
64+
it_arguments
6365
else
6466
arguments.argument_list
6567
end
@@ -153,15 +155,16 @@ def void_context?
153155

154156
private
155157

156-
# Numbered arguments of this `numblock`.
157158
def numbered_arguments
158-
return [].freeze unless numblock_type?
159-
160159
max_param = children[1]
161160
1.upto(max_param).map do |i|
162161
ArgNode.new(:arg, [:"_#{i}"])
163162
end.freeze
164163
end
164+
165+
def it_arguments
166+
[ArgNode.new(:arg, [:it])].freeze
167+
end
165168
end
166169
end
167170
end

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

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 implicit `it` 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 implicit `it` 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 implicit `it` 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 implicit `it` 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

spec/rubocop/ast/send_node_spec.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,21 @@ module Foo
206206
it { expect(send_node).to be_bare_access_modifier }
207207
end
208208
end
209+
210+
context 'with Ruby >= 3.4', :ruby34 do
211+
context 'when node is access modifier in itblock' do
212+
let(:source) do
213+
<<~RUBY
214+
included do
215+
it
216+
>> module_function <<
217+
end
218+
RUBY
219+
end
220+
221+
it { expect(send_node).to be_bare_access_modifier }
222+
end
223+
end
209224
end
210225

211226
describe '#non_bare_access_modifier?' do
@@ -339,6 +354,19 @@ module Foo
339354
end
340355
end
341356

357+
context 'with Ruby >= 3.4', :ruby27 do
358+
context 'when parent is an itblock in a macro scope' do
359+
let(:source) do
360+
['concern :Auth do',
361+
'>>bar :baz<<',
362+
' bar it',
363+
'end'].join("\n")
364+
end
365+
366+
it { expect(send_node).to be_macro }
367+
end
368+
end
369+
342370
context 'when parent is a block not in a macro scope' do
343371
let(:source) { <<~RUBY }
344372
class Foo
@@ -1106,6 +1134,14 @@ def bar
11061134
it { expect(send_node).to be_block_literal }
11071135
end
11081136
end
1137+
1138+
context 'with Ruby >= 3.4', :ruby27 do
1139+
context 'with an itblock literal' do
1140+
let(:source) { '>> foo.bar << { baz(it) }' }
1141+
1142+
it { expect(send_node).to be_block_literal }
1143+
end
1144+
end
11091145
end
11101146

11111147
describe '#arithmetic_operation?' do
@@ -1154,6 +1190,14 @@ def bar
11541190
it { expect(send_node.block_node).to be_numblock_type }
11551191
end
11561192
end
1193+
1194+
context 'with Ruby >= 3.4', :ruby34, broken_on: :parser do
1195+
context 'with an itblock literal' do
1196+
let(:source) { '>>foo.bar<< { baz(it) }' }
1197+
1198+
it { expect(send_node.block_node).to be_itblock_type }
1199+
end
1200+
end
11571201
end
11581202

11591203
describe '#splat_argument?' do

0 commit comments

Comments
 (0)