rubocop 1.0.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +36 -16
- data/config/default.yml +141 -14
- data/exe/rubocop +1 -1
- data/lib/rubocop.rb +16 -0
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/config_loader.rb +7 -6
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
- data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
- data/lib/rubocop/cop/commissioner.rb +10 -10
- data/lib/rubocop/cop/corrector.rb +3 -1
- data/lib/rubocop/cop/force.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/line_length.rb +8 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
- data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +23 -2
- data/lib/rubocop/cop/lint/debugger.rb +17 -28
- data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
- data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
- data/lib/rubocop/cop/lint/else_layout.rb +29 -3
- data/lib/rubocop/cop/lint/empty_block.rb +82 -0
- data/lib/rubocop/cop/lint/empty_class.rb +93 -0
- data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +38 -6
- data/lib/rubocop/cop/lint/loop.rb +4 -4
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
- data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +95 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
- data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
- data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
- data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +11 -1
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
- data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
- data/lib/rubocop/cop/naming/variable_number.rb +98 -8
- data/lib/rubocop/cop/offense.rb +3 -3
- data/lib/rubocop/cop/style/and_or.rb +1 -3
- data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
- data/lib/rubocop/cop/style/case_like_if.rb +0 -4
- data/lib/rubocop/cop/style/collection_compact.rb +91 -0
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +162 -0
- data/lib/rubocop/cop/style/double_negation.rb +6 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -3
- data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
- data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
- data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +104 -0
- data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
- data/lib/rubocop/cop/style/raise_args.rb +21 -6
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +3 -0
- data/lib/rubocop/cop/style/static_class.rb +97 -0
- data/lib/rubocop/cop/style/swap_values.rb +108 -0
- data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
- data/lib/rubocop/cop/team.rb +6 -1
- data/lib/rubocop/cop/util.rb +5 -1
- data/lib/rubocop/ext/regexp_node.rb +17 -9
- data/lib/rubocop/ext/regexp_parser.rb +84 -0
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
- data/lib/rubocop/magic_comment.rb +2 -2
- data/lib/rubocop/options.rb +2 -0
- data/lib/rubocop/rspec/shared_contexts.rb +4 -0
- data/lib/rubocop/target_ruby.rb +57 -1
- data/lib/rubocop/version.rb +1 -1
- metadata +21 -5
@@ -137,10 +137,10 @@ module RuboCop
|
|
137
137
|
def asgn_variable_align_with(outer_node, inner_node)
|
138
138
|
expr = outer_node.source_range
|
139
139
|
|
140
|
-
if
|
141
|
-
range_between(expr.begin_pos, inner_node.loc.keyword.end_pos)
|
142
|
-
else
|
140
|
+
if line_break_before_keyword?(expr, inner_node)
|
143
141
|
inner_node.loc.keyword
|
142
|
+
else
|
143
|
+
range_between(expr.begin_pos, inner_node.loc.keyword.end_pos)
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
@@ -56,8 +56,7 @@ module RuboCop
|
|
56
56
|
aligned = Set[locs.first.line, locs.last.line]
|
57
57
|
locs.each_cons(3) do |before, loc, after|
|
58
58
|
col = loc.column
|
59
|
-
aligned << loc.line if col == before.column ||
|
60
|
-
col == after.column
|
59
|
+
aligned << loc.line if col == before.column || col == after.column
|
61
60
|
end
|
62
61
|
aligned
|
63
62
|
end
|
@@ -296,13 +296,13 @@ module RuboCop
|
|
296
296
|
# just give each lambda the same reference and they would all get the
|
297
297
|
# last value of each. A local variable fixes the problem.
|
298
298
|
|
299
|
-
if
|
300
|
-
delta_value = delta[:key] || 0
|
301
|
-
correct_no_value(corrector, delta_value, node.source_range)
|
302
|
-
else
|
299
|
+
if node.value
|
303
300
|
correct_key_value(corrector, delta, node.key.source_range,
|
304
301
|
node.value.source_range,
|
305
302
|
node.loc.operator)
|
303
|
+
else
|
304
|
+
delta_value = delta[:key] || 0
|
305
|
+
correct_no_value(corrector, delta_value, node.source_range)
|
306
306
|
end
|
307
307
|
end
|
308
308
|
|
@@ -88,6 +88,10 @@ module RuboCop
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
def correctable?
|
92
|
+
super && !breakable_range.nil?
|
93
|
+
end
|
94
|
+
|
91
95
|
def autocorrect(range)
|
92
96
|
return if range.nil?
|
93
97
|
|
@@ -98,6 +102,8 @@ module RuboCop
|
|
98
102
|
|
99
103
|
private
|
100
104
|
|
105
|
+
attr_accessor :breakable_range
|
106
|
+
|
101
107
|
def check_for_breakable_node(node)
|
102
108
|
breakable_node = extract_breakable_node(node, max)
|
103
109
|
return if breakable_node.nil?
|
@@ -195,7 +201,8 @@ module RuboCop
|
|
195
201
|
def register_offense(loc, line, line_index)
|
196
202
|
message = format(MSG, length: line_length(line), max: max)
|
197
203
|
|
198
|
-
breakable_range = breakable_range_by_line_index[line_index]
|
204
|
+
self.breakable_range = breakable_range_by_line_index[line_index]
|
205
|
+
|
199
206
|
add_offense(breakable_range, location: loc, message: message) do
|
200
207
|
self.max = line_length(line)
|
201
208
|
end
|
@@ -54,17 +54,11 @@ module RuboCop
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def check_inside_pipes(arguments)
|
57
|
-
opening_pipe, closing_pipe = pipes(arguments)
|
58
|
-
|
59
57
|
case style
|
60
58
|
when :no_space
|
61
|
-
check_no_space_style_inside_pipes(arguments
|
62
|
-
opening_pipe,
|
63
|
-
closing_pipe)
|
59
|
+
check_no_space_style_inside_pipes(arguments)
|
64
60
|
when :space
|
65
|
-
check_space_style_inside_pipes(arguments
|
66
|
-
opening_pipe,
|
67
|
-
closing_pipe)
|
61
|
+
check_space_style_inside_pipes(arguments)
|
68
62
|
end
|
69
63
|
end
|
70
64
|
|
@@ -76,22 +70,29 @@ module RuboCop
|
|
76
70
|
closing_pipe, 'after closing `|`')
|
77
71
|
end
|
78
72
|
|
79
|
-
def check_no_space_style_inside_pipes(
|
73
|
+
def check_no_space_style_inside_pipes(arguments)
|
74
|
+
args = arguments.children
|
75
|
+
opening_pipe, closing_pipe = pipes(arguments)
|
76
|
+
|
80
77
|
first = args.first.source_range
|
81
78
|
last = args.last.source_range
|
82
79
|
|
83
80
|
check_no_space(opening_pipe.end_pos, first.begin_pos,
|
84
81
|
'Space before first')
|
85
|
-
check_no_space(last_end_pos_inside_pipes(last),
|
82
|
+
check_no_space(last_end_pos_inside_pipes(arguments, last),
|
86
83
|
closing_pipe.begin_pos, 'Space after last')
|
87
84
|
end
|
88
85
|
|
89
|
-
def check_space_style_inside_pipes(
|
90
|
-
|
91
|
-
|
86
|
+
def check_space_style_inside_pipes(arguments)
|
87
|
+
opening_pipe, closing_pipe = pipes(arguments)
|
88
|
+
|
89
|
+
check_opening_pipe_space(arguments, opening_pipe)
|
90
|
+
check_closing_pipe_space(arguments, closing_pipe)
|
92
91
|
end
|
93
92
|
|
94
|
-
def check_opening_pipe_space(
|
93
|
+
def check_opening_pipe_space(arguments, opening_pipe)
|
94
|
+
args = arguments.children
|
95
|
+
|
95
96
|
first_arg = args.first
|
96
97
|
range = first_arg.source_range
|
97
98
|
|
@@ -101,9 +102,11 @@ module RuboCop
|
|
101
102
|
'Extra space before first')
|
102
103
|
end
|
103
104
|
|
104
|
-
def check_closing_pipe_space(
|
105
|
+
def check_closing_pipe_space(arguments, closing_pipe)
|
106
|
+
args = arguments.children
|
107
|
+
|
105
108
|
last = args.last.source_range
|
106
|
-
last_end_pos = last_end_pos_inside_pipes(last)
|
109
|
+
last_end_pos = last_end_pos_inside_pipes(arguments, last)
|
107
110
|
|
108
111
|
check_space(last_end_pos, closing_pipe.begin_pos, last,
|
109
112
|
'after last block parameter')
|
@@ -111,9 +114,12 @@ module RuboCop
|
|
111
114
|
'Extra space after last')
|
112
115
|
end
|
113
116
|
|
114
|
-
def last_end_pos_inside_pipes(range)
|
117
|
+
def last_end_pos_inside_pipes(arguments, range)
|
115
118
|
pos = range.end_pos
|
116
|
-
|
119
|
+
num = pos - arguments.source_range.begin_pos
|
120
|
+
trailing_comma_index = arguments.source[num..-1].index(',')
|
121
|
+
|
122
|
+
trailing_comma_index ? pos + trailing_comma_index + 1 : pos
|
117
123
|
end
|
118
124
|
|
119
125
|
def check_each_arg(args)
|
@@ -11,10 +11,12 @@ module RuboCop
|
|
11
11
|
# # bad
|
12
12
|
# f( 3)
|
13
13
|
# g = (a + 3 )
|
14
|
+
# f( )
|
14
15
|
#
|
15
16
|
# # good
|
16
17
|
# f(3)
|
17
18
|
# g = (a + 3)
|
19
|
+
# f()
|
18
20
|
#
|
19
21
|
# @example EnforcedStyle: space
|
20
22
|
# # The `space` style enforces that parentheses have a space at the
|
@@ -44,11 +46,7 @@ module RuboCop
|
|
44
46
|
@processed_source = processed_source
|
45
47
|
|
46
48
|
if style == :space
|
47
|
-
|
48
|
-
add_offense(range, message: MSG_SPACE) do |corrector|
|
49
|
-
corrector.insert_before(range, ' ')
|
50
|
-
end
|
51
|
-
end
|
49
|
+
process_with_space_style(processed_source)
|
52
50
|
else
|
53
51
|
each_extraneous_space(processed_source.tokens) do |range|
|
54
52
|
add_offense(range) do |corrector|
|
@@ -60,6 +58,21 @@ module RuboCop
|
|
60
58
|
|
61
59
|
private
|
62
60
|
|
61
|
+
def process_with_space_style(processed_source)
|
62
|
+
processed_source.tokens.each_cons(2) do |token1, token2|
|
63
|
+
each_extraneous_space_in_empty_parens(token1, token2) do |range|
|
64
|
+
add_offense(range) do |corrector|
|
65
|
+
corrector.remove(range)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
each_missing_space(token1, token2) do |range|
|
69
|
+
add_offense(range, message: MSG_SPACE) do |corrector|
|
70
|
+
corrector.insert_before(range, ' ')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
63
76
|
def each_extraneous_space(tokens)
|
64
77
|
tokens.each_cons(2) do |token1, token2|
|
65
78
|
next unless parens?(token1, token2)
|
@@ -73,15 +86,21 @@ module RuboCop
|
|
73
86
|
end
|
74
87
|
end
|
75
88
|
|
76
|
-
def
|
77
|
-
|
78
|
-
next if can_be_ignored?(token1, token2)
|
89
|
+
def each_extraneous_space_in_empty_parens(token1, token2)
|
90
|
+
return unless token1.left_parens? && token2.right_parens?
|
79
91
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
92
|
+
return if range_between(token1.begin_pos, token2.end_pos).source == '()'
|
93
|
+
|
94
|
+
yield range_between(token1.end_pos, token2.begin_pos)
|
95
|
+
end
|
96
|
+
|
97
|
+
def each_missing_space(token1, token2)
|
98
|
+
return if can_be_ignored?(token1, token2)
|
99
|
+
|
100
|
+
if token1.left_parens?
|
101
|
+
yield range_between(token2.begin_pos, token2.begin_pos + 1)
|
102
|
+
elsif token2.right_parens?
|
103
|
+
yield range_between(token2.begin_pos, token2.end_pos)
|
85
104
|
end
|
86
105
|
end
|
87
106
|
|
@@ -96,6 +115,9 @@ module RuboCop
|
|
96
115
|
def can_be_ignored?(token1, token2)
|
97
116
|
return true unless parens?(token1, token2)
|
98
117
|
|
118
|
+
# Ignore empty parentheses.
|
119
|
+
return true if range_between(token1.begin_pos, token2.end_pos).source == '()'
|
120
|
+
|
99
121
|
# If the second token is a comment, that means that a line break
|
100
122
|
# follows, and that the rules for space inside don't apply.
|
101
123
|
return true if token2.comment?
|
@@ -63,7 +63,7 @@ module RuboCop
|
|
63
63
|
range = offense_range(lineno, line)
|
64
64
|
add_offense(range) do |corrector|
|
65
65
|
if heredoc
|
66
|
-
corrector.
|
66
|
+
corrector.wrap(range, "\#{'", "'}") unless static?(heredoc)
|
67
67
|
else
|
68
68
|
corrector.remove(range)
|
69
69
|
end
|
@@ -49,7 +49,18 @@ module RuboCop
|
|
49
49
|
# const_set(:LIST, [])
|
50
50
|
# end
|
51
51
|
# end
|
52
|
+
#
|
53
|
+
# @example AllowedMethods: ['enums']
|
54
|
+
# # good
|
55
|
+
# class TestEnum < T::Enum
|
56
|
+
# enums do
|
57
|
+
# Foo = new("foo")
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
52
61
|
class ConstantDefinitionInBlock < Base
|
62
|
+
include AllowedMethods
|
63
|
+
|
53
64
|
MSG = 'Do not define constants this way within a block.'
|
54
65
|
|
55
66
|
def_node_matcher :constant_assigned_in_block?, <<~PATTERN
|
@@ -61,13 +72,23 @@ module RuboCop
|
|
61
72
|
PATTERN
|
62
73
|
|
63
74
|
def on_casgn(node)
|
64
|
-
|
75
|
+
return if !constant_assigned_in_block?(node) || allowed_method?(method_name(node))
|
76
|
+
|
77
|
+
add_offense(node)
|
65
78
|
end
|
66
79
|
|
67
80
|
def on_class(node)
|
68
|
-
|
81
|
+
return if !module_defined_in_block?(node) || allowed_method?(method_name(node))
|
82
|
+
|
83
|
+
add_offense(node)
|
69
84
|
end
|
70
85
|
alias on_module on_class
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def method_name(node)
|
90
|
+
node.ancestors.find(&:block_type?).send_node.method_name
|
91
|
+
end
|
71
92
|
end
|
72
93
|
end
|
73
94
|
end
|
@@ -4,6 +4,7 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
6
|
# This cop checks for calls to debugger or pry.
|
7
|
+
# The cop can be configured to define which methods and receivers must be fixed.
|
7
8
|
#
|
8
9
|
# @example
|
9
10
|
#
|
@@ -35,34 +36,11 @@ module RuboCop
|
|
35
36
|
class Debugger < Base
|
36
37
|
MSG = 'Remove debugger entry point `%<source>s`.'
|
37
38
|
|
38
|
-
RESTRICT_ON_SEND =
|
39
|
-
debugger byebug remote_byebug pry remote_pry pry_remote console rescue
|
40
|
-
save_and_open_page save_and_open_screenshot save_screenshot irb
|
41
|
-
].freeze
|
42
|
-
|
43
|
-
def_node_matcher :kernel?, <<~PATTERN
|
44
|
-
{
|
45
|
-
(const nil? :Kernel)
|
46
|
-
(const (cbase) :Kernel)
|
47
|
-
}
|
48
|
-
PATTERN
|
49
|
-
|
50
|
-
def_node_matcher :debugger_call?, <<~PATTERN
|
51
|
-
{(send {nil? #kernel?} {:debugger :byebug :remote_byebug} ...)
|
52
|
-
(send (send {#kernel? nil?} :binding)
|
53
|
-
{:pry :remote_pry :pry_remote :console} ...)
|
54
|
-
(send (const {nil? (cbase)} :Pry) :rescue ...)
|
55
|
-
(send nil? {:save_and_open_page
|
56
|
-
:save_and_open_screenshot
|
57
|
-
:save_screenshot} ...)}
|
58
|
-
PATTERN
|
59
|
-
|
60
|
-
def_node_matcher :binding_irb_call?, <<~PATTERN
|
61
|
-
(send (send {#kernel? nil?} :binding) :irb ...)
|
62
|
-
PATTERN
|
39
|
+
RESTRICT_ON_SEND = [].freeze
|
63
40
|
|
64
41
|
def on_send(node)
|
65
|
-
return unless
|
42
|
+
return unless debugger_method?(node.method_name)
|
43
|
+
return if !node.receiver.nil? && !debugger_receiver?(node)
|
66
44
|
|
67
45
|
add_offense(node)
|
68
46
|
end
|
@@ -73,8 +51,19 @@ module RuboCop
|
|
73
51
|
format(MSG, source: node.source)
|
74
52
|
end
|
75
53
|
|
76
|
-
def
|
77
|
-
|
54
|
+
def debugger_method?(name)
|
55
|
+
cop_config.fetch('DebuggerMethods', []).include?(name.to_s)
|
56
|
+
end
|
57
|
+
|
58
|
+
def debugger_receiver?(node)
|
59
|
+
receiver = case node.receiver
|
60
|
+
when RuboCop::AST::SendNode
|
61
|
+
node.receiver.method_name
|
62
|
+
when RuboCop::AST::ConstNode
|
63
|
+
node.receiver.const_name
|
64
|
+
end
|
65
|
+
|
66
|
+
cop_config.fetch('DebuggerReceivers', []).include?(receiver.to_s)
|
78
67
|
end
|
79
68
|
end
|
80
69
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks that there are no repeated bodies
|
7
|
+
# within `if/unless`, `case-when` and `rescue` constructs.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# if foo
|
12
|
+
# do_foo
|
13
|
+
# do_something_else
|
14
|
+
# elsif bar
|
15
|
+
# do_foo
|
16
|
+
# do_something_else
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# if foo || bar
|
21
|
+
# do_foo
|
22
|
+
# do_something_else
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # bad
|
26
|
+
# case x
|
27
|
+
# when foo
|
28
|
+
# do_foo
|
29
|
+
# when bar
|
30
|
+
# do_foo
|
31
|
+
# else
|
32
|
+
# do_something_else
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# case x
|
37
|
+
# when foo, bar
|
38
|
+
# do_foo
|
39
|
+
# else
|
40
|
+
# do_something_else
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # bad
|
44
|
+
# begin
|
45
|
+
# do_something
|
46
|
+
# rescue FooError
|
47
|
+
# handle_error
|
48
|
+
# rescue BarError
|
49
|
+
# handle_error
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# # good
|
53
|
+
# begin
|
54
|
+
# do_something
|
55
|
+
# rescue FooError, BarError
|
56
|
+
# handle_error
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
class DuplicateBranch < Base
|
60
|
+
include RescueNode
|
61
|
+
|
62
|
+
MSG = 'Duplicate branch body detected.'
|
63
|
+
|
64
|
+
def on_branching_statement(node)
|
65
|
+
branches = node.branches.compact
|
66
|
+
branches.each_with_object(Set.new) do |branch, previous|
|
67
|
+
add_offense(offense_range(branch)) unless previous.add?(branch)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
alias on_if on_branching_statement
|
71
|
+
alias on_case on_branching_statement
|
72
|
+
alias on_rescue on_branching_statement
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def offense_range(duplicate_branch)
|
77
|
+
parent = duplicate_branch.parent
|
78
|
+
|
79
|
+
if parent.respond_to?(:else_branch) &&
|
80
|
+
parent.else_branch.equal?(duplicate_branch)
|
81
|
+
if parent.if_type? && parent.ternary?
|
82
|
+
duplicate_branch.source_range
|
83
|
+
else
|
84
|
+
parent.loc.else
|
85
|
+
end
|
86
|
+
else
|
87
|
+
parent.source_range
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|