Skip to content

Commit

Permalink
Merge pull request #1940 from r7kamura/unspecified-exception-chain
Browse files Browse the repository at this point in the history
Fix false-negative for `UnspecifiedException` when matcher is chained
  • Loading branch information
pirj committed Jul 20, 2024
2 parents ce4cd77 + 60199ba commit 9eb23a6
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 15 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Master (Unreleased)

- Fix false-negative for `UnspecifiedException` when matcher is chained. ([@r7kamura])

## 3.0.3 (2024-07-12)

- Add support for Unicode RIGHT SINGLE QUOTATION MARK in `RSpec/ExampleWording`. ([@jdufresne])
Expand Down
35 changes: 20 additions & 15 deletions lib/rubocop/cop/rspec/unspecified_exception.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,39 @@ module RSpec
#
class UnspecifiedException < Base
MSG = 'Specify the exception being captured'
RESTRICT_ON_SEND = %i[to].freeze

# @!method empty_raise_error_or_exception(node)
def_node_matcher :empty_raise_error_or_exception, <<~PATTERN
(send
(block
(send nil? :expect) ...)
:to
(send nil? {:raise_error :raise_exception})
)
RESTRICT_ON_SEND = %i[
raise_exception
raise_error
].freeze

# @!method expect_to?(node)
def_node_matcher :expect_to?, <<~PATTERN
(send (block (send nil? :expect) ...) :to ...)
PATTERN

def on_send(node)
return unless empty_exception_matcher?(node)

add_offense(node.children.last)
add_offense(node)
end

private

def empty_exception_matcher?(node)
empty_raise_error_or_exception(node) && !block_with_args?(node.parent)
end
return false if node.arguments? || node.block_literal?

def block_with_args?(node)
return false unless node&.block_type?
expect_to = find_expect_to(node)
return false unless expect_to
return false if expect_to.block_node&.arguments?

true
end

node.arguments?
def find_expect_to(node)
node.each_ancestor(:send).find do |ancestor|
expect_to?(ancestor)
end
end
end
end
Expand Down
27 changes: 27 additions & 0 deletions spec/rubocop/cop/rspec/unspecified_exception_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,5 +171,32 @@
}.to raise_exception(my_exception)
RUBY
end

it 'detects chained offenses' do
expect_offense(<<~RUBY)
expect {
foo
}.to raise_exception.and change { bar }
^^^^^^^^^^^^^^^ Specify the exception being captured
RUBY
end

it 'detects more chained offenses' do
expect_offense(<<~RUBY)
expect {
foo
}.to raise_exception.and change { bar }.and change { baz }
^^^^^^^^^^^^^^^ Specify the exception being captured
RUBY
end

it 'detects more complex chained offenses' do
expect_offense(<<~RUBY)
expect {
foo
}.to change { bar }.and raise_exception.and change { baz }
^^^^^^^^^^^^^^^ Specify the exception being captured
RUBY
end
end
end

0 comments on commit 9eb23a6

Please sign in to comment.