rubocop 1.41.1 → 1.45.1
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/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +97 -31
- data/lib/rubocop/cli.rb +55 -9
- data/lib/rubocop/config.rb +7 -7
- data/lib/rubocop/config_loader.rb +12 -15
- data/lib/rubocop/config_loader_resolver.rb +8 -5
- data/lib/rubocop/cop/base.rb +89 -70
- data/lib/rubocop/cop/commissioner.rb +8 -2
- data/lib/rubocop/cop/cop.rb +51 -31
- data/lib/rubocop/cop/corrector.rb +30 -10
- data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +1 -6
- data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
- data/lib/rubocop/cop/gemspec/development_dependencies.rb +107 -0
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +11 -3
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/block_end_newline.rb +7 -1
- data/lib/rubocop/cop/layout/class_structure.rb +31 -23
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +2 -6
- data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +6 -9
- data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
- data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +11 -13
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +4 -4
- data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +5 -4
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -2
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -0
- data/lib/rubocop/cop/lint/debugger.rb +8 -27
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +62 -112
- data/lib/rubocop/cop/lint/else_layout.rb +2 -6
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +14 -7
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +15 -17
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +8 -5
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +11 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
- data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
- data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +7 -4
- data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -3
- data/lib/rubocop/cop/lint/useless_rescue.rb +85 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +14 -4
- data/lib/rubocop/cop/lint/void.rb +19 -10
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
- data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +3 -6
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/allowed_methods.rb +3 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +5 -3
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +57 -23
- data/lib/rubocop/cop/mixin/line_length_help.rb +3 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -0
- data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -3
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +4 -0
- data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
- data/lib/rubocop/cop/registry.rb +34 -29
- data/lib/rubocop/cop/security/compound_hash.rb +2 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -11
- data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -0
- data/lib/rubocop/cop/style/block_comments.rb +1 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +8 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +3 -10
- data/lib/rubocop/cop/style/command_literal.rb +1 -1
- data/lib/rubocop/cop/style/comparable_clamp.rb +125 -0
- data/lib/rubocop/cop/style/concat_array_literals.rb +22 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +0 -6
- data/lib/rubocop/cop/style/documentation.rb +1 -1
- data/lib/rubocop/cop/style/documentation_method.rb +6 -0
- data/lib/rubocop/cop/style/guard_clause.rb +11 -7
- data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +11 -7
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
- data/lib/rubocop/cop/style/infinite_loop.rb +2 -5
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +114 -0
- data/lib/rubocop/cop/style/map_to_set.rb +61 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +23 -14
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
- data/lib/rubocop/cop/style/min_max_comparison.rb +83 -0
- data/lib/rubocop/cop/style/missing_else.rb +13 -1
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +0 -4
- data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +18 -3
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -5
- data/lib/rubocop/cop/style/numbered_parameters_limit.rb +11 -3
- data/lib/rubocop/cop/style/one_line_conditional.rb +3 -6
- data/lib/rubocop/cop/style/operator_method_call.rb +16 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +16 -1
- data/lib/rubocop/cop/style/redundant_conditional.rb +0 -4
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -10
- data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +58 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
- data/lib/rubocop/cop/style/redundant_string_escape.rb +4 -2
- data/lib/rubocop/cop/style/require_order.rb +6 -11
- data/lib/rubocop/cop/style/select_by_regexp.rb +6 -2
- data/lib/rubocop/cop/style/self_assignment.rb +2 -2
- data/lib/rubocop/cop/style/semicolon.rb +24 -2
- data/lib/rubocop/cop/style/signal_exception.rb +8 -6
- data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
- data/lib/rubocop/cop/style/word_array.rb +42 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +12 -5
- data/lib/rubocop/cop/style/yoda_expression.rb +90 -0
- data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
- data/lib/rubocop/cop/team.rb +48 -43
- data/lib/rubocop/cop/variable_force/scope.rb +3 -3
- data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
- data/lib/rubocop/cop/variable_force.rb +1 -4
- data/lib/rubocop/formatter.rb +0 -1
- data/lib/rubocop/options.rb +22 -1
- data/lib/rubocop/path_util.rb +17 -7
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/rspec/expect_offense.rb +6 -4
- data/lib/rubocop/runner.rb +50 -7
- data/lib/rubocop/server/cache.rb +10 -3
- data/lib/rubocop/server/cli.rb +37 -18
- data/lib/rubocop/server/client_command/exec.rb +1 -1
- data/lib/rubocop/server/client_command/start.rb +6 -1
- data/lib/rubocop/server/core.rb +23 -8
- data/lib/rubocop/target_ruby.rb +0 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +8 -0
- metadata +21 -33
@@ -299,8 +299,8 @@ module RuboCop
|
|
299
299
|
|
300
300
|
def move_comment_before_block(corrector, comment, block_node, closing_brace)
|
301
301
|
range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
|
302
|
-
|
303
|
-
corrector
|
302
|
+
corrector.remove(range_with_surrounding_space(comment.loc.expression, side: :right))
|
303
|
+
remove_trailing_whitespace(corrector, range, comment)
|
304
304
|
corrector.insert_after(range, "\n")
|
305
305
|
|
306
306
|
corrector.insert_before(block_node, "#{comment.text}\n")
|
@@ -313,6 +313,12 @@ module RuboCop
|
|
313
313
|
end_of_chain(node.parent)
|
314
314
|
end
|
315
315
|
|
316
|
+
def remove_trailing_whitespace(corrector, range, comment)
|
317
|
+
range_of_trailing = range.end.join(comment.loc.expression.begin)
|
318
|
+
|
319
|
+
corrector.remove(range_of_trailing) if range_of_trailing.source.match?(/\A\s+\z/)
|
320
|
+
end
|
321
|
+
|
316
322
|
def with_block?(node)
|
317
323
|
node.respond_to?(:block_node) && node.block_node
|
318
324
|
end
|
@@ -31,6 +31,7 @@ module RuboCop
|
|
31
31
|
#
|
32
32
|
# The compact style is only forced for classes/modules with one child.
|
33
33
|
class ClassAndModuleChildren < Base
|
34
|
+
include Alignment
|
34
35
|
include ConfigurableEnforcedStyle
|
35
36
|
include RangeHelp
|
36
37
|
extend AutoCorrector
|
@@ -59,7 +60,7 @@ module RuboCop
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def nest_definition(corrector, node)
|
62
|
-
padding = (
|
63
|
+
padding = indentation(node) + leading_spaces(node)
|
63
64
|
padding_for_trailing_end = padding.sub(' ' * node.loc.end.column, '')
|
64
65
|
|
65
66
|
replace_namespace_keyword(corrector, node)
|
@@ -124,10 +125,6 @@ module RuboCop
|
|
124
125
|
corrector.remove(range)
|
125
126
|
end
|
126
127
|
|
127
|
-
def configured_indentation_width
|
128
|
-
config.for_badge(Layout::IndentationWidth.badge).fetch('Width', 2)
|
129
|
-
end
|
130
|
-
|
131
128
|
def unindent(corrector, node)
|
132
129
|
return if node.body.children.last.nil?
|
133
130
|
|
@@ -141,10 +138,6 @@ module RuboCop
|
|
141
138
|
node.source_range.source_line[/\A\s*/]
|
142
139
|
end
|
143
140
|
|
144
|
-
def indent_width
|
145
|
-
@config.for_cop('Layout/IndentationWidth')['Width'] || 2
|
146
|
-
end
|
147
|
-
|
148
141
|
def check_style(node, body)
|
149
142
|
return if node.identifier.children[0]&.cbase_type?
|
150
143
|
|
@@ -185,7 +178,7 @@ module RuboCop
|
|
185
178
|
end
|
186
179
|
|
187
180
|
def compact_node_name?(node)
|
188
|
-
|
181
|
+
node.identifier.source.include?('::')
|
189
182
|
end
|
190
183
|
end
|
191
184
|
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Enforces the use of `Comparable#clamp` instead of comparison by minimum and maximum.
|
7
|
+
#
|
8
|
+
# This cop supports autocorrection for `if/elsif/else` bad style only.
|
9
|
+
# Because `ArgumentError` occurs if the minimum and maximum of `clamp` arguments are reversed.
|
10
|
+
# When these are variables, it is not possible to determine which is the minimum and maximum:
|
11
|
+
#
|
12
|
+
# [source,ruby]
|
13
|
+
# ----
|
14
|
+
# [1, [2, 3].max].min # => 1
|
15
|
+
# 1.clamp(3, 1) # => min argument must be smaller than max argument (ArgumentError)
|
16
|
+
# ----
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# [[x, low].max, high].min
|
22
|
+
#
|
23
|
+
# # bad
|
24
|
+
# if x < low
|
25
|
+
# low
|
26
|
+
# elsif high < x
|
27
|
+
# high
|
28
|
+
# else
|
29
|
+
# x
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# x.clamp(low, high)
|
34
|
+
#
|
35
|
+
class ComparableClamp < Base
|
36
|
+
include Alignment
|
37
|
+
extend AutoCorrector
|
38
|
+
extend TargetRubyVersion
|
39
|
+
|
40
|
+
minimum_target_ruby_version 2.4
|
41
|
+
|
42
|
+
MSG = 'Use `%<prefer>s` instead of `if/elsif/else`.'
|
43
|
+
MSG_MIN_MAX = 'Use `Comparable#clamp` instead.'
|
44
|
+
RESTRICT_ON_SEND = %i[min max].freeze
|
45
|
+
|
46
|
+
# @!method if_elsif_else_condition?(node)
|
47
|
+
def_node_matcher :if_elsif_else_condition?, <<~PATTERN
|
48
|
+
{
|
49
|
+
(if (send _x :< _min) _min (if (send _max :< _x) _max _x))
|
50
|
+
(if (send _min :> _x) _min (if (send _max :< _x) _max _x))
|
51
|
+
(if (send _x :< _min) _min (if (send _x :> _max) _max _x))
|
52
|
+
(if (send _min :> _x) _min (if (send _x :> _max) _max _x))
|
53
|
+
(if (send _max :< _x) _max (if (send _x :< _min) _min _x))
|
54
|
+
(if (send _x :> _max) _max (if (send _x :< _min) _min _x))
|
55
|
+
(if (send _max :< _x) _max (if (send _min :> _x) _min _x))
|
56
|
+
(if (send _x :> _max) _max (if (send _min :> _x) _min _x))
|
57
|
+
}
|
58
|
+
PATTERN
|
59
|
+
|
60
|
+
# @!method array_min_max?(node)
|
61
|
+
def_node_matcher :array_min_max?, <<~PATTERN
|
62
|
+
{
|
63
|
+
(send
|
64
|
+
(array
|
65
|
+
(send (array _ _) :max) _) :min)
|
66
|
+
(send
|
67
|
+
(array
|
68
|
+
_ (send (array _ _) :max)) :min)
|
69
|
+
(send
|
70
|
+
(array
|
71
|
+
(send (array _ _) :min) _) :max)
|
72
|
+
(send
|
73
|
+
(array
|
74
|
+
_ (send (array _ _) :min)) :max)
|
75
|
+
}
|
76
|
+
PATTERN
|
77
|
+
|
78
|
+
def on_if(node)
|
79
|
+
return unless if_elsif_else_condition?(node)
|
80
|
+
|
81
|
+
if_body, elsif_body, else_body = *node.branches
|
82
|
+
|
83
|
+
else_body_source = else_body.source
|
84
|
+
|
85
|
+
if min_condition?(node.condition, else_body_source)
|
86
|
+
min = if_body.source
|
87
|
+
max = elsif_body.source
|
88
|
+
else
|
89
|
+
min = elsif_body.source
|
90
|
+
max = if_body.source
|
91
|
+
end
|
92
|
+
|
93
|
+
prefer = "#{else_body_source}.clamp(#{min}, #{max})"
|
94
|
+
|
95
|
+
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
96
|
+
autocorrect(corrector, node, prefer)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def on_send(node)
|
101
|
+
return unless array_min_max?(node)
|
102
|
+
|
103
|
+
add_offense(node, message: MSG_MIN_MAX)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def autocorrect(corrector, node, prefer)
|
109
|
+
if node.elsif?
|
110
|
+
corrector.insert_before(node, "else\n")
|
111
|
+
corrector.replace(node, "#{indentation(node)}#{prefer}")
|
112
|
+
else
|
113
|
+
corrector.replace(node, prefer)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def min_condition?(if_condition, else_body)
|
118
|
+
lhs, op, rhs = *if_condition
|
119
|
+
|
120
|
+
(lhs.source == else_body && op == :<) || (rhs.source == else_body && op == :>)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -30,6 +30,7 @@ module RuboCop
|
|
30
30
|
'Use `push` with elements as arguments without array brackets instead of `%<current>s`.'
|
31
31
|
RESTRICT_ON_SEND = %i[concat].freeze
|
32
32
|
|
33
|
+
# rubocop:disable Metrics
|
33
34
|
def on_send(node)
|
34
35
|
return if node.arguments.empty?
|
35
36
|
return unless node.arguments.all?(&:array_type?)
|
@@ -38,7 +39,12 @@ module RuboCop
|
|
38
39
|
current = offense.source
|
39
40
|
|
40
41
|
if node.arguments.any?(&:percent_literal?)
|
41
|
-
|
42
|
+
if percent_literals_includes_only_basic_literals?(node)
|
43
|
+
prefer = preferred_method(node)
|
44
|
+
message = format(MSG, prefer: prefer, current: current)
|
45
|
+
else
|
46
|
+
message = format(MSG_FOR_PERCENT_LITERALS, current: current)
|
47
|
+
end
|
42
48
|
else
|
43
49
|
prefer = preferred_method(node)
|
44
50
|
message = format(MSG, prefer: prefer, current: current)
|
@@ -48,6 +54,7 @@ module RuboCop
|
|
48
54
|
corrector.replace(offense, prefer)
|
49
55
|
end
|
50
56
|
end
|
57
|
+
# rubocop:enable Metrics
|
51
58
|
|
52
59
|
private
|
53
60
|
|
@@ -56,10 +63,23 @@ module RuboCop
|
|
56
63
|
end
|
57
64
|
|
58
65
|
def preferred_method(node)
|
59
|
-
new_arguments =
|
66
|
+
new_arguments =
|
67
|
+
node.arguments.map do |arg|
|
68
|
+
if arg.percent_literal?
|
69
|
+
arg.children.map(&:value).map(&:inspect)
|
70
|
+
else
|
71
|
+
arg.children.map(&:source)
|
72
|
+
end
|
73
|
+
end.join(', ')
|
60
74
|
|
61
75
|
"push(#{new_arguments})"
|
62
76
|
end
|
77
|
+
|
78
|
+
def percent_literals_includes_only_basic_literals?(node)
|
79
|
+
node.arguments.select(&:percent_literal?).all? do |arg|
|
80
|
+
arg.children.all? { |child| child.str_type? || child.sym_type? }
|
81
|
+
end
|
82
|
+
end
|
63
83
|
end
|
64
84
|
end
|
65
85
|
end
|
@@ -218,11 +218,9 @@ module RuboCop
|
|
218
218
|
VARIABLE_ASSIGNMENT_TYPES = %i[casgn cvasgn gvasgn ivasgn lvasgn].freeze
|
219
219
|
ASSIGNMENT_TYPES = VARIABLE_ASSIGNMENT_TYPES + %i[and_asgn or_asgn op_asgn masgn].freeze
|
220
220
|
LINE_LENGTH = 'Layout/LineLength'
|
221
|
-
INDENTATION_WIDTH = 'Layout/IndentationWidth'
|
222
221
|
ENABLED = 'Enabled'
|
223
222
|
MAX = 'Max'
|
224
223
|
SINGLE_LINE_CONDITIONS_ONLY = 'SingleLineConditionsOnly'
|
225
|
-
WIDTH = 'Width'
|
226
224
|
|
227
225
|
# The shovel operator `<<` does not have its own type. It is a `send`
|
228
226
|
# type.
|
@@ -428,10 +426,6 @@ module RuboCop
|
|
428
426
|
config.for_cop(LINE_LENGTH)[MAX]
|
429
427
|
end
|
430
428
|
|
431
|
-
def indentation_width
|
432
|
-
config.for_cop(INDENTATION_WIDTH)[WIDTH] || 2
|
433
|
-
end
|
434
|
-
|
435
429
|
def single_line_conditions_only?
|
436
430
|
cop_config[SINGLE_LINE_CONDITIONS_ONLY]
|
437
431
|
end
|
@@ -7,6 +7,10 @@ module RuboCop
|
|
7
7
|
# It can optionally be configured to also require documentation for
|
8
8
|
# non-public methods.
|
9
9
|
#
|
10
|
+
# NOTE: This cop allows `initialize` method because `initialize` is
|
11
|
+
# a special method called from `new`. In some programming languages
|
12
|
+
# they are called constructor to distinguish it from method.
|
13
|
+
#
|
10
14
|
# @example
|
11
15
|
#
|
12
16
|
# # bad
|
@@ -103,6 +107,8 @@ module RuboCop
|
|
103
107
|
PATTERN
|
104
108
|
|
105
109
|
def on_def(node)
|
110
|
+
return if node.method?(:initialize)
|
111
|
+
|
106
112
|
parent = node.parent
|
107
113
|
module_function_node?(parent) ? check(parent) : check(node)
|
108
114
|
end
|
@@ -196,7 +196,7 @@ module RuboCop
|
|
196
196
|
return unless node.else?
|
197
197
|
|
198
198
|
corrector.remove(node.loc.else)
|
199
|
-
corrector.remove(
|
199
|
+
corrector.remove(range_of_branch_to_remove(node, guard))
|
200
200
|
end
|
201
201
|
end
|
202
202
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
@@ -206,20 +206,24 @@ module RuboCop
|
|
206
206
|
end
|
207
207
|
|
208
208
|
def autocorrect_heredoc_argument(corrector, node, heredoc_branch, leave_branch, guard)
|
209
|
+
return unless node.else?
|
210
|
+
|
209
211
|
remove_whole_lines(corrector, leave_branch.source_range)
|
210
212
|
remove_whole_lines(corrector, node.loc.else)
|
211
213
|
remove_whole_lines(corrector, node.loc.end)
|
212
|
-
remove_whole_lines(corrector,
|
214
|
+
remove_whole_lines(corrector, range_of_branch_to_remove(node, guard))
|
213
215
|
corrector.insert_after(
|
214
216
|
heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
|
215
217
|
)
|
216
218
|
end
|
217
219
|
|
218
|
-
def
|
219
|
-
case guard
|
220
|
-
|
221
|
-
|
222
|
-
|
220
|
+
def range_of_branch_to_remove(node, guard)
|
221
|
+
branch = case guard
|
222
|
+
when :if then node.if_branch
|
223
|
+
when :else then node.else_branch
|
224
|
+
end
|
225
|
+
|
226
|
+
branch.source_range
|
223
227
|
end
|
224
228
|
|
225
229
|
def guard_clause_source(guard_clause)
|
@@ -118,11 +118,23 @@ module RuboCop
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def allowed_receiver?(receiver)
|
121
|
-
receiver_name = receiver
|
121
|
+
receiver_name = receiver_name(receiver)
|
122
122
|
|
123
123
|
allowed_receivers.include?(receiver_name)
|
124
124
|
end
|
125
125
|
|
126
|
+
def receiver_name(receiver)
|
127
|
+
if receiver.send_type?
|
128
|
+
if receiver.receiver
|
129
|
+
"#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
|
130
|
+
else
|
131
|
+
receiver.method_name.to_s
|
132
|
+
end
|
133
|
+
else
|
134
|
+
receiver.source
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
126
138
|
def allowed_receivers
|
127
139
|
cop_config.fetch('AllowedReceivers', [])
|
128
140
|
end
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
# * always - forces use of the 3.1 syntax (e.g. {foo:})
|
29
29
|
# * never - forces use of explicit hash literal value
|
30
30
|
# * either - accepts both shorthand and explicit use of hash literal value
|
31
|
-
# * consistent -
|
31
|
+
# * consistent - forces use of the 3.1 syntax only if all values can be omitted in the hash
|
32
32
|
#
|
33
33
|
# @example EnforcedStyle: ruby19 (default)
|
34
34
|
# # bad
|
@@ -92,16 +92,19 @@ module RuboCop
|
|
92
92
|
#
|
93
93
|
# @example EnforcedShorthandSyntax: consistent
|
94
94
|
#
|
95
|
-
# # bad
|
96
|
-
# {foo: , bar: bar}
|
95
|
+
# # bad - `foo` and `bar` values can be omitted
|
96
|
+
# {foo: foo, bar: bar}
|
97
97
|
#
|
98
|
-
# #
|
99
|
-
# {foo:, bar:}
|
98
|
+
# # bad - `bar` value can be omitted
|
99
|
+
# {foo:, bar: bar}
|
100
100
|
#
|
101
|
-
# # bad
|
102
|
-
# {foo
|
101
|
+
# # bad - mixed syntaxes
|
102
|
+
# {foo:, bar: baz}
|
103
103
|
#
|
104
104
|
# # good
|
105
|
+
# {foo:, bar:}
|
106
|
+
#
|
107
|
+
# # good - can't omit `baz`
|
105
108
|
# {foo: foo, bar: baz}
|
106
109
|
#
|
107
110
|
class HashSyntax < Base
|
@@ -251,6 +254,7 @@ module RuboCop
|
|
251
254
|
op = pair_node.loc.operator
|
252
255
|
|
253
256
|
key_with_hash_rocket = ":#{pair_node.key.source}#{pair_node.inverse_delimiter(true)}"
|
257
|
+
key_with_hash_rocket += pair_node.key.source if pair_node.value_omission?
|
254
258
|
corrector.replace(pair_node.key, key_with_hash_rocket)
|
255
259
|
corrector.remove(range_with_surrounding_space(op))
|
256
260
|
end
|
@@ -136,6 +136,7 @@ module RuboCop
|
|
136
136
|
|
137
137
|
private
|
138
138
|
|
139
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
139
140
|
def check_branches(node, branches)
|
140
141
|
# return if any branch is empty. An empty branch can be an `if`
|
141
142
|
# without an `else` or a branch that contains only comments.
|
@@ -144,9 +145,13 @@ module RuboCop
|
|
144
145
|
tails = branches.map { |branch| tail(branch) }
|
145
146
|
check_expressions(node, tails, :after_condition) if duplicated_expressions?(node, tails)
|
146
147
|
|
148
|
+
return if last_child_of_parent?(node) &&
|
149
|
+
branches.any? { |branch| single_child_branch?(branch) }
|
150
|
+
|
147
151
|
heads = branches.map { |branch| head(branch) }
|
148
152
|
check_expressions(node, heads, :before_condition) if duplicated_expressions?(node, heads)
|
149
153
|
end
|
154
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
150
155
|
|
151
156
|
def duplicated_expressions?(node, expressions)
|
152
157
|
unique_expressions = expressions.uniq
|
@@ -180,6 +185,16 @@ module RuboCop
|
|
180
185
|
end
|
181
186
|
end
|
182
187
|
|
188
|
+
def last_child_of_parent?(node)
|
189
|
+
return true unless (parent = node.parent)
|
190
|
+
|
191
|
+
parent.child_nodes.last == node
|
192
|
+
end
|
193
|
+
|
194
|
+
def single_child_branch?(branch_node)
|
195
|
+
!branch_node.begin_type? || branch_node.children.size == 1
|
196
|
+
end
|
197
|
+
|
183
198
|
def message(node)
|
184
199
|
format(MSG, source: node.source)
|
185
200
|
end
|
@@ -21,6 +21,7 @@ module RuboCop
|
|
21
21
|
# work
|
22
22
|
# end
|
23
23
|
class InfiniteLoop < Base
|
24
|
+
include Alignment
|
24
25
|
extend AutoCorrector
|
25
26
|
|
26
27
|
LEADING_SPACE = /\A(\s*)/.freeze
|
@@ -106,7 +107,7 @@ module RuboCop
|
|
106
107
|
else
|
107
108
|
indentation = body.source_range.source_line[LEADING_SPACE]
|
108
109
|
|
109
|
-
['loop do', body.source.gsub(/^/,
|
110
|
+
['loop do', body.source.gsub(/^/, indentation(node)), 'end'].join("\n#{indentation}")
|
110
111
|
end
|
111
112
|
end
|
112
113
|
|
@@ -120,10 +121,6 @@ module RuboCop
|
|
120
121
|
|
121
122
|
start_range.join(end_range)
|
122
123
|
end
|
123
|
-
|
124
|
-
def configured_indent
|
125
|
-
' ' * config.for_cop('Layout/IndentationWidth')['Width']
|
126
|
-
end
|
127
124
|
end
|
128
125
|
end
|
129
126
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for usages of `unless` which can be replaced by `if` with inverted condition.
|
7
|
+
# Code without `unless` is easier to read, but that is subjective, so this cop
|
8
|
+
# is disabled by default.
|
9
|
+
#
|
10
|
+
# Methods that can be inverted should be defined in `InverseMethods`. Note that
|
11
|
+
# the relationship of inverse methods needs to be defined in both directions.
|
12
|
+
# For example,
|
13
|
+
# InverseMethods:
|
14
|
+
# :!=: :==
|
15
|
+
# :even?: :odd?
|
16
|
+
# :odd?: :even?
|
17
|
+
#
|
18
|
+
# will suggest both `even?` and `odd?` to be inverted, but only `!=` (and not `==`).
|
19
|
+
#
|
20
|
+
# @safety
|
21
|
+
# This cop is unsafe because it cannot be guaranteed that the method
|
22
|
+
# and its inverse method are both defined on receiver, and also are
|
23
|
+
# actually inverse of each other.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# # bad (simple condition)
|
27
|
+
# foo unless !bar
|
28
|
+
# foo unless x != y
|
29
|
+
# foo unless x >= 10
|
30
|
+
# foo unless x.even?
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# foo if bar
|
34
|
+
# foo if x == y
|
35
|
+
# foo if x < 10
|
36
|
+
# foo if x.odd?
|
37
|
+
#
|
38
|
+
# # bad (complex condition)
|
39
|
+
# foo unless x != y || x.even?
|
40
|
+
#
|
41
|
+
# # good
|
42
|
+
# foo if x == y && x.odd?
|
43
|
+
#
|
44
|
+
# # good (if)
|
45
|
+
# foo if !condition
|
46
|
+
#
|
47
|
+
class InvertibleUnlessCondition < Base
|
48
|
+
extend AutoCorrector
|
49
|
+
|
50
|
+
MSG = 'Favor `if` with inverted condition over `unless`.'
|
51
|
+
|
52
|
+
def on_if(node)
|
53
|
+
return unless node.unless?
|
54
|
+
|
55
|
+
condition = node.condition
|
56
|
+
return unless invertible?(condition)
|
57
|
+
|
58
|
+
add_offense(node) do |corrector|
|
59
|
+
corrector.replace(node.loc.keyword, node.inverse_keyword)
|
60
|
+
autocorrect(corrector, condition)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def invertible?(node)
|
67
|
+
case node.type
|
68
|
+
when :begin
|
69
|
+
invertible?(node.children.first)
|
70
|
+
when :send
|
71
|
+
return if inheritance_check?(node)
|
72
|
+
|
73
|
+
node.method?(:!) || inverse_methods.key?(node.method_name)
|
74
|
+
when :or, :and
|
75
|
+
invertible?(node.lhs) && invertible?(node.rhs)
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def inheritance_check?(node)
|
82
|
+
argument = node.first_argument
|
83
|
+
node.method?(:<) &&
|
84
|
+
(argument.const_type? && argument.short_name.to_s.upcase != argument.short_name.to_s)
|
85
|
+
end
|
86
|
+
|
87
|
+
def autocorrect(corrector, node)
|
88
|
+
case node.type
|
89
|
+
when :begin
|
90
|
+
autocorrect(corrector, node.children.first)
|
91
|
+
when :send
|
92
|
+
autocorrect_send_node(corrector, node)
|
93
|
+
when :or, :and
|
94
|
+
corrector.replace(node.loc.operator, node.inverse_operator)
|
95
|
+
autocorrect(corrector, node.lhs)
|
96
|
+
autocorrect(corrector, node.rhs)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def autocorrect_send_node(corrector, node)
|
101
|
+
if node.method?(:!)
|
102
|
+
corrector.remove(node.loc.selector)
|
103
|
+
else
|
104
|
+
corrector.replace(node.loc.selector, inverse_methods[node.method_name])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def inverse_methods
|
109
|
+
@inverse_methods ||= cop_config['InverseMethods']
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Looks for uses of `map.to_set` or `collect.to_set` that could be
|
7
|
+
# written with just `to_set`.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# This cop is unsafe, as it can produce false positives if the receiver
|
11
|
+
# is not an `Enumerable`.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# something.map { |i| i * 2 }.to_set
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# something.to_set { |i| i * 2 }
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# [1, 2, 3].collect { |i| i.to_s }.to_set
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# [1, 2, 3].to_set { |i| i.to_s }
|
25
|
+
#
|
26
|
+
class MapToSet < Base
|
27
|
+
extend AutoCorrector
|
28
|
+
include RangeHelp
|
29
|
+
|
30
|
+
MSG = 'Pass a block to `to_set` instead of calling `%<method>s.to_set`.'
|
31
|
+
RESTRICT_ON_SEND = %i[to_set].freeze
|
32
|
+
|
33
|
+
# @!method map_to_set?(node)
|
34
|
+
def_node_matcher :map_to_set?, <<~PATTERN
|
35
|
+
$(send (block $(send _ {:map :collect}) ...) :to_set)
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
return unless (to_set_node, map_node = map_to_set?(node))
|
40
|
+
|
41
|
+
message = format(MSG, method: map_node.loc.selector.source)
|
42
|
+
add_offense(map_node.loc.selector, message: message) do |corrector|
|
43
|
+
# If the `to_set` call already has a block, do not autocorrect.
|
44
|
+
next if to_set_node.block_node
|
45
|
+
|
46
|
+
autocorrect(corrector, to_set_node, map_node)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def autocorrect(corrector, to_set, map)
|
53
|
+
removal_range = range_between(to_set.loc.dot.begin_pos, to_set.loc.selector.end_pos)
|
54
|
+
|
55
|
+
corrector.remove(range_with_surrounding_space(removal_range, side: :left))
|
56
|
+
corrector.replace(map.loc.selector, 'to_set')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|