Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new Lint/ItWithoutArgumentsInBlock cop #12518

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#12516](https://github.com/rubocop/rubocop/pull/12516): Add new `Lint/ItWithoutArgumentsInBlock` cop. ([@koic][])
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1963,6 +1963,12 @@ Lint/InterpolationCheck:
VersionAdded: '0.50'
VersionChanged: '1.40'

Lint/ItWithoutArgumentsInBlock:
Description: 'Checks uses of `it` calls without arguments in block.'
Reference: 'https://bugs.ruby-lang.org/issues/18980'
Enabled: pending
VersionAdded: '<<next>>'

Lint/LambdaWithoutLiteralBlock:
Description: 'Checks uses of lambda without a literal block.'
Enabled: pending
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@
require_relative 'rubocop/cop/lint/ineffective_access_modifier'
require_relative 'rubocop/cop/lint/inherit_exception'
require_relative 'rubocop/cop/lint/interpolation_check'
require_relative 'rubocop/cop/lint/it_without_arguments_in_block'
require_relative 'rubocop/cop/lint/lambda_without_literal_block'
require_relative 'rubocop/cop/lint/literal_as_condition'
require_relative 'rubocop/cop/lint/literal_assignment_in_condition'
Expand Down
56 changes: 56 additions & 0 deletions lib/rubocop/cop/lint/it_without_arguments_in_block.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Lint
# Emulates the following Ruby warning in Ruby 3.3.
#
# [source,ruby]
# ----
# $ ruby -e '0.times { it }'
# -e:1: warning: `it` calls without arguments will refer to the first block param in Ruby 3.4;
# use it() or self.it
# ----
#
# `it` calls without arguments will refer to the first block param in Ruby 3.4.
# So use `it()` or `self.it` to ensure compatibility.
#
# @example
#
# # bad
# do_something { it }
#
# # good
# do_something { it() }
# do_something { self.it }
#
class ItWithoutArgumentsInBlock < Base
include NodePattern::Macros

MSG = '`it` calls without arguments will refer to the first block param in Ruby 3.4; ' \
'use `it()` or `self.it`.'

def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
return unless (body = node.body)
return unless node.arguments.empty_and_without_delimiters?

if body.send_type? && deprecated_it_method?(body)
add_offense(body)
else
body.each_descendant(:send).each do |send_node|
next unless deprecated_it_method?(send_node)

add_offense(send_node)
end
end
end

def deprecated_it_method?(node)
return false unless node.method?(:it)

!node.receiver && node.arguments.empty? && !node.parenthesized? && !node.block_literal?
end
end
end
end
end
139 changes: 139 additions & 0 deletions spec/rubocop/cop/lint/it_without_arguments_in_block_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Lint::ItWithoutArgumentsInBlock, :config do
it 'registers an offense when using `it` without arguments in the single line block' do
expect_offense(<<~RUBY)
0.times { it }
^^ `it` calls without arguments will refer to the first block param in Ruby 3.4; use `it()` or `self.it`.
RUBY
end

it 'registers an offense when using `it` without arguments in the multiline block' do
expect_offense(<<~RUBY)
0.times do
it
^^ `it` calls without arguments will refer to the first block param in Ruby 3.4; use `it()` or `self.it`.
it = 1
it
end
RUBY
end

it 'does not register an offense when using `it` with arguments in the single line block' do
expect_no_offenses(<<~RUBY)
0.times { it(42) }
RUBY
end

it 'does not register an offense when using `it` with block argument in the single line block' do
expect_no_offenses(<<~RUBY)
0.times { it { do_something } }
RUBY
end

it 'does not register an offense when using `it()` in the single line block' do
expect_no_offenses(<<~RUBY)
0.times { it() }
RUBY
end

it 'does not register an offense when using `self.it` in the single line block' do
expect_no_offenses(<<~RUBY)
0.times { self.it }
RUBY
end

it 'does not register an offense when using `it` with arguments in the multiline block' do
expect_no_offenses(<<~RUBY)
0.times do
it(42)
it = 1
it
end
RUBY
end

it 'does not register an offense when using `it` with block argument in the multiline block' do
expect_no_offenses(<<~RUBY)
0.times do
it { do_something }
it = 1
it
end
RUBY
end

it 'does not register an offense when using `it()` in the multiline block' do
expect_no_offenses(<<~RUBY)
0.times do
it()
it = 1
it
end
RUBY
end

it 'does not register an offense when using `self.it` without arguments in the multiline block' do
expect_no_offenses(<<~RUBY)
0.times do
self.it
it = 1
it
end
RUBY
end

it 'does not register an offense when using `it` without arguments in `if` body' do
expect_no_offenses(<<~RUBY)
if false
it
end
RUBY
end

it 'does not register an offense when using `it` without arguments in `def` body' do
expect_no_offenses(<<~RUBY)
def foo
it
end
RUBY
end

it 'does not register an offense when using `it` without arguments in the block with empty block parameter' do
expect_no_offenses(<<~RUBY)
0.times { ||
it
}
RUBY
end

it 'does not register an offense when using `it` without arguments in the block with useless block parameter' do
expect_no_offenses(<<~RUBY)
0.times { |_n|
it
}
RUBY
end

it 'does not register an offense when using `it` inner local variable in block' do
expect_no_offenses(<<~RUBY)
0.times do
it = 1
it
end
RUBY
end

it 'does not register an offense when using `it` outer local variable in block' do
expect_no_offenses(<<~RUBY)
it = 1
0.times { it }
RUBY
end

it 'does not register an offense when using empty block' do
expect_no_offenses(<<~RUBY)
0.times {}
RUBY
end
end