Skip to content

Commit

Permalink
[Fix #4843] Fix shadow rescued Exceptions of same error code (#4844)
Browse files Browse the repository at this point in the history
* [Fix #4843] Fix shadow rescued Exceptions of same error code

Note: System dependent error code depends on runtime environment.
  • Loading branch information
koic authored and bbatsov committed Oct 9, 2017
1 parent 1317b89 commit 18c74f5
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -29,6 +29,7 @@
* [#4830](https://github.com/bbatsov/rubocop/issues/4830): Prevent `Lint/BooleanSymbol` from truncating symbol's value in the message when offense is located in the new syntax hash. ([@akhramov][])
* [#4747](https://github.com/bbatsov/rubocop/issues/4747): Fix `Rails/HasManyOrHasOneDependent` cop incorrectly flags `with_options` blocks. ([@koic][])
* [#4836](https://github.com/bbatsov/rubocop/issues/4836): Make `Rails/OutputSafety` aware of safe navigation operator. ([@drenmi][])
* [#4843](https://github.com/bbatsov/rubocop/issues/4843): Make `Lint/ShadowedException` cop aware of same system error code. ([@koic][])

### Changes

Expand Down
39 changes: 36 additions & 3 deletions lib/rubocop/cop/lint/shadowed_exception.rb
Expand Up @@ -19,8 +19,6 @@ module Lint
# handle_standard_error
# end
#
# @example
#
# # good
#
# begin
Expand All @@ -30,6 +28,21 @@ module Lint
# rescue Exception
# handle_exception
# end
#
# # good, however depending on runtime environment.
# #
# # This is a special case for system call errors.
# # System dependent error code depends on runtime environment.
# # For example, whether `Errno::EAGAIN` and `Errno::EWOULDBLOCK` are
# # the same error code or different error code depends on environment.
# # This good case is for `Errno::EAGAIN` and `Errno::EWOULDBLOCK` with
# # the same error code.
# begin
# something
# rescue Errno::EAGAIN, Errno::EWOULDBLOCK
# handle_standard_error
# end
#
class ShadowedException < Cop
include RescueNode

Expand Down Expand Up @@ -77,7 +90,27 @@ def contains_multiple_levels_of_exceptions?(group)
return !(group.size == 2 && group.include?(NilClass))
end

group.combination(2).any? { |a, b| a && b && a <=> b }
group.combination(2).any? do |a, b|
compare_exceptions(a, b)
end
end

def compare_exceptions(exception, other_exception)
if system_call_err?(exception) && system_call_err?(other_exception)
# This condition logic is for special case.
# System dependent error code depends on runtime environment.
# For example, whether `Errno::EAGAIN` and `Errno::EWOULDBLOCK` are
# the same error code or different error code depends on runtime
# environment. This checks the error code for that.
exception.const_get(:Errno) != other_exception.const_get(:Errno) &&
exception <=> other_exception
else
exception && other_exception && exception <=> other_exception
end
end

def system_call_err?(error)
error && error.ancestors[1] == SystemCallError
end

def evaluate_exceptions(rescue_group)
Expand Down
17 changes: 15 additions & 2 deletions manual/cops_lint.md
Expand Up @@ -1704,8 +1704,7 @@ rescue Exception
rescue StandardError
handle_standard_error
end
```
```ruby

# good

begin
Expand All @@ -1715,6 +1714,20 @@ rescue StandardError
rescue Exception
handle_exception
end

# good, however depending on runtime environment.
#
# This is a special case for system call errors.
# System dependent error code depends on runtime environment.
# For example, whether `Errno::EAGAIN` and `Errno::EWOULDBLOCK` are
# the same error code or different error code depends on environment.
# This good case is for `Errno::EAGAIN` and `Errno::EWOULDBLOCK` with
# the same error code.
begin
something
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
handle_standard_error
end
```

## Lint/ShadowingOuterLocalVariable
Expand Down
15 changes: 15 additions & 0 deletions spec/rubocop/cop/lint/shadowed_exception_spec.rb
Expand Up @@ -60,6 +60,21 @@
RUBY
end

it 'accepts rescue containing multiple same error code exceptions' do
# System dependent error code depends on runtime environment.
stub_const('Errno::EAGAIN::Errno', 35)
stub_const('Errno::EWOULDBLOCK::Errno', 35)
stub_const('Errno::ECONNABORTED::Errno', 53)

expect_no_offenses(<<-RUBY.strip_indent)
begin
something
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED
handle_exception
end
RUBY
end

it 'registers an offense rescuing exceptions that are ' \
'ancestors of each other ' do
inspect_source(<<-RUBY.strip_indent)
Expand Down

0 comments on commit 18c74f5

Please sign in to comment.