rubocop 1.38.0 → 1.40.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +51 -11
- data/exe/rubocop +1 -1
- data/lib/rubocop/comment_config.rb +5 -0
- data/lib/rubocop/config.rb +5 -4
- data/lib/rubocop/config_loader.rb +5 -5
- data/lib/rubocop/config_loader_resolver.rb +1 -1
- data/lib/rubocop/cop/base.rb +2 -9
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
- data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +29 -8
- data/lib/rubocop/cop/layout/space_inside_array_percent_literal.rb +3 -0
- data/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb +34 -0
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
- data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +17 -8
- data/lib/rubocop/cop/lint/empty_block.rb +1 -5
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +13 -3
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +15 -6
- data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +4 -3
- data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/void.rb +6 -6
- data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
- data/lib/rubocop/cop/metrics/block_length.rb +9 -4
- data/lib/rubocop/cop/metrics/class_length.rb +9 -4
- data/lib/rubocop/cop/metrics/method_length.rb +9 -4
- data/lib/rubocop/cop/metrics/module_length.rb +9 -4
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -2
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +24 -5
- data/lib/rubocop/cop/mixin/range_help.rb +23 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +15 -1
- data/lib/rubocop/cop/mixin/visibility_help.rb +40 -5
- data/lib/rubocop/cop/registry.rb +23 -11
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -25
- data/lib/rubocop/cop/style/array_intersect.rb +111 -0
- data/lib/rubocop/cop/style/class_equality_comparison.rb +7 -5
- data/lib/rubocop/cop/style/collection_compact.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +32 -5
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -0
- data/lib/rubocop/cop/style/hash_each_methods.rb +32 -10
- data/lib/rubocop/cop/style/hash_except.rb +4 -0
- data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -1
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/object_then.rb +3 -0
- data/lib/rubocop/cop/style/operator_method_call.rb +13 -0
- data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
- data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
- data/lib/rubocop/cop/style/redundant_constant_base.rb +72 -0
- data/lib/rubocop/cop/style/redundant_each.rb +13 -8
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +12 -3
- data/lib/rubocop/cop/style/redundant_return.rb +7 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
- data/lib/rubocop/cop/style/require_order.rb +88 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
- data/lib/rubocop/cop/style/select_by_regexp.rb +8 -4
- data/lib/rubocop/cop/style/string_literals.rb +1 -5
- data/lib/rubocop/cop/style/symbol_proc.rb +2 -4
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/util.rb +2 -2
- data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +20 -29
- data/lib/rubocop/cops_documentation_generator.rb +3 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +3 -1
- data/lib/rubocop/optimized_patterns.rb +38 -0
- data/lib/rubocop/options.rb +9 -1
- data/lib/rubocop/path_util.rb +14 -2
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/rspec/cop_helper.rb +4 -1
- data/lib/rubocop/rspec/support.rb +2 -2
- data/lib/rubocop/server/core.rb +16 -1
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +14 -6
- metadata +8 -3
data/lib/rubocop/cop/registry.rb
CHANGED
@@ -72,27 +72,27 @@ module RuboCop
|
|
72
72
|
#
|
73
73
|
# @example gives back a correctly qualified cop name
|
74
74
|
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
# qualified_cop_name('Layout/EndOfLine') # => 'Layout/EndOfLine'
|
75
|
+
# registry = RuboCop::Cop::Registry
|
76
|
+
# registry.qualified_cop_name('Layout/EndOfLine', '') # => 'Layout/EndOfLine'
|
78
77
|
#
|
79
78
|
# @example fixes incorrect namespaces
|
80
79
|
#
|
81
|
-
#
|
82
|
-
#
|
80
|
+
# registry = RuboCop::Cop::Registry
|
81
|
+
# registry.qualified_cop_name('Lint/EndOfLine', '') # => 'Layout/EndOfLine'
|
83
82
|
#
|
84
83
|
# @example namespaces bare cop identifiers
|
85
84
|
#
|
86
|
-
#
|
87
|
-
#
|
85
|
+
# registry = RuboCop::Cop::Registry
|
86
|
+
# registry.qualified_cop_name('EndOfLine', '') # => 'Layout/EndOfLine'
|
88
87
|
#
|
89
88
|
# @example passes back unrecognized cop names
|
90
89
|
#
|
91
|
-
#
|
92
|
-
#
|
90
|
+
# registry = RuboCop::Cop::Registry
|
91
|
+
# registry.qualified_cop_name('NotACop', '') # => 'NotACop'
|
93
92
|
#
|
94
93
|
# @param name [String] Cop name extracted from config
|
95
94
|
# @param path [String, nil] Path of file that `name` was extracted from
|
95
|
+
# @param warn [Boolean] Print a warning if no department given for `name`
|
96
96
|
#
|
97
97
|
# @raise [AmbiguousCopName]
|
98
98
|
# if a bare identifier with two possible namespaces is provided
|
@@ -158,7 +158,7 @@ module RuboCop
|
|
158
158
|
end
|
159
159
|
|
160
160
|
def enabled?(cop, config)
|
161
|
-
return true if options
|
161
|
+
return true if options[:only]&.include?(cop.cop_name)
|
162
162
|
|
163
163
|
cfg = config.for_cop(cop)
|
164
164
|
|
@@ -182,8 +182,12 @@ module RuboCop
|
|
182
182
|
cops.map(&:cop_name)
|
183
183
|
end
|
184
184
|
|
185
|
+
def cops_for_department(department)
|
186
|
+
cops.select { |cop| cop.department == department.to_sym }
|
187
|
+
end
|
188
|
+
|
185
189
|
def names_for_department(department)
|
186
|
-
|
190
|
+
cops_for_department(department).map(&:cop_name)
|
187
191
|
end
|
188
192
|
|
189
193
|
def ==(other)
|
@@ -211,6 +215,14 @@ module RuboCop
|
|
211
215
|
to_h[cop_name].first
|
212
216
|
end
|
213
217
|
|
218
|
+
# When a cop name is given returns a single-element array with the cop class.
|
219
|
+
# When a department name is given returns an array with all the cop classes
|
220
|
+
# for that department.
|
221
|
+
def find_cops_by_directive(directive)
|
222
|
+
cop = find_by_cop_name(directive)
|
223
|
+
cop ? [cop] : cops_for_department(directive)
|
224
|
+
end
|
225
|
+
|
214
226
|
def freeze
|
215
227
|
clear_enrollment_queue
|
216
228
|
unqualified_cop_names # build cache
|
@@ -202,31 +202,7 @@ module RuboCop
|
|
202
202
|
end
|
203
203
|
|
204
204
|
def remove_node(corrector, node)
|
205
|
-
corrector.remove(
|
206
|
-
range_by_whole_lines(
|
207
|
-
range_with_comments(node),
|
208
|
-
include_final_newline: true
|
209
|
-
)
|
210
|
-
)
|
211
|
-
end
|
212
|
-
|
213
|
-
def range_with_comments(node)
|
214
|
-
ranges = [
|
215
|
-
node,
|
216
|
-
*processed_source.ast_with_comments[node]
|
217
|
-
].map do |element|
|
218
|
-
element.location.expression
|
219
|
-
end
|
220
|
-
ranges.reduce do |result, range|
|
221
|
-
add_range(result, range)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def add_range(range1, range2)
|
226
|
-
range1.with(
|
227
|
-
begin_pos: [range1.begin_pos, range2.begin_pos].min,
|
228
|
-
end_pos: [range1.end_pos, range2.end_pos].max
|
229
|
-
)
|
205
|
+
corrector.remove(range_with_comments_and_lines(node))
|
230
206
|
end
|
231
207
|
end
|
232
208
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# In Ruby 3.1, `Array#intersect?` has been added.
|
7
|
+
#
|
8
|
+
# This cop identifies places where `(array1 & array2).any?`
|
9
|
+
# can be replaced by `array1.intersect?(array2)`.
|
10
|
+
#
|
11
|
+
# The `array1.intersect?(array2)` method is faster than
|
12
|
+
# `(array1 & array2).any?` and is more readable.
|
13
|
+
#
|
14
|
+
# @safety
|
15
|
+
# This cop cannot guarantee that array1 and array2 are
|
16
|
+
# actually arrays while method `intersect?` is for arrays only.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# # bad
|
20
|
+
# (array1 & array2).any?
|
21
|
+
# (array1 & array2).empty?
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# array1.intersect?(array2)
|
25
|
+
# !array1.intersect?(array2)
|
26
|
+
#
|
27
|
+
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
28
|
+
# # good
|
29
|
+
# (array1 & array2).present?
|
30
|
+
# (array1 & array2).blank?
|
31
|
+
#
|
32
|
+
# @example AllCops:ActiveSupportExtensionsEnabled: true
|
33
|
+
# # bad
|
34
|
+
# (array1 & array2).present?
|
35
|
+
# (array1 & array2).blank?
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# array1.intersect?(array2)
|
39
|
+
# !array1.intersect?(array2)
|
40
|
+
class ArrayIntersect < Base
|
41
|
+
extend AutoCorrector
|
42
|
+
extend TargetRubyVersion
|
43
|
+
|
44
|
+
minimum_target_ruby_version 3.1
|
45
|
+
|
46
|
+
# @!method regular_bad_intersection_check?(node)
|
47
|
+
def_node_matcher :regular_bad_intersection_check?, <<~PATTERN
|
48
|
+
(send
|
49
|
+
(begin
|
50
|
+
(send $(...) :& $(...))
|
51
|
+
) ${:any? :empty?}
|
52
|
+
)
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
# @!method active_support_bad_intersection_check?(node)
|
56
|
+
def_node_matcher :active_support_bad_intersection_check?, <<~PATTERN
|
57
|
+
(send
|
58
|
+
(begin
|
59
|
+
(send $(...) :& $(...))
|
60
|
+
) ${:present? :any? :blank? :empty?}
|
61
|
+
)
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
MSG = 'Use `%<negated>s%<receiver>s.intersect?(%<argument>s)` ' \
|
65
|
+
'instead of `(%<receiver>s & %<argument>s).%<method_name>s`.'
|
66
|
+
STRAIGHT_METHODS = %i[present? any?].freeze
|
67
|
+
NEGATED_METHODS = %i[blank? empty?].freeze
|
68
|
+
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
69
|
+
|
70
|
+
def on_send(node)
|
71
|
+
return unless (receiver, argument, method_name = bad_intersection_check?(node))
|
72
|
+
|
73
|
+
message = message(receiver.source, argument.source, method_name)
|
74
|
+
|
75
|
+
add_offense(node, message: message) do |corrector|
|
76
|
+
if straight?(method_name)
|
77
|
+
corrector.replace(node, "#{receiver.source}.intersect?(#{argument.source})")
|
78
|
+
else
|
79
|
+
corrector.replace(node, "!#{receiver.source}.intersect?(#{argument.source})")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def bad_intersection_check?(node)
|
87
|
+
if active_support_extensions_enabled?
|
88
|
+
active_support_bad_intersection_check?(node)
|
89
|
+
else
|
90
|
+
regular_bad_intersection_check?(node)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def straight?(method_name)
|
95
|
+
STRAIGHT_METHODS.include?(method_name.to_sym)
|
96
|
+
end
|
97
|
+
|
98
|
+
def message(receiver, argument, method_name)
|
99
|
+
negated = straight?(method_name) ? '' : '!'
|
100
|
+
format(
|
101
|
+
MSG,
|
102
|
+
negated: negated,
|
103
|
+
receiver: receiver,
|
104
|
+
argument: argument,
|
105
|
+
method_name: method_name
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -97,12 +97,14 @@ module RuboCop
|
|
97
97
|
if node.children.first.method?(:name)
|
98
98
|
return class_node.receiver.source if class_node.receiver
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
100
|
+
if class_node.str_type?
|
101
|
+
value = class_node.source.delete('"').delete("'")
|
102
|
+
value.prepend('::') if class_node.each_ancestor(:class, :module).any?
|
103
|
+
return value
|
104
|
+
end
|
105
105
|
end
|
106
|
+
|
107
|
+
class_node.source
|
106
108
|
end
|
107
109
|
|
108
110
|
def offense_range(receiver_node, node)
|
@@ -112,7 +112,7 @@ module RuboCop
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def range(begin_pos_node, end_pos_node)
|
115
|
-
range_between(begin_pos_node.loc.selector.begin_pos, end_pos_node.loc.
|
115
|
+
range_between(begin_pos_node.loc.selector.begin_pos, end_pos_node.loc.expression.end_pos)
|
116
116
|
end
|
117
117
|
end
|
118
118
|
end
|
@@ -94,6 +94,7 @@ module RuboCop
|
|
94
94
|
#
|
95
95
|
class GuardClause < Base
|
96
96
|
extend AutoCorrector
|
97
|
+
include RangeHelp
|
97
98
|
include MinBodyLength
|
98
99
|
include StatementModifier
|
99
100
|
|
@@ -179,13 +180,35 @@ module RuboCop
|
|
179
180
|
end
|
180
181
|
end
|
181
182
|
|
183
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
182
184
|
def autocorrect(corrector, node, condition, replacement, guard)
|
183
185
|
corrector.replace(node.loc.keyword.join(condition.loc.expression), replacement)
|
184
|
-
corrector.remove(node.loc.end)
|
185
|
-
return unless node.else?
|
186
186
|
|
187
|
-
|
188
|
-
|
187
|
+
if_branch = node.if_branch
|
188
|
+
else_branch = node.else_branch
|
189
|
+
|
190
|
+
if if_branch&.send_type? && if_branch.last_argument&.heredoc?
|
191
|
+
autocorrect_heredoc_argument(corrector, node, if_branch, else_branch, guard)
|
192
|
+
elsif else_branch&.send_type? && else_branch.last_argument&.heredoc?
|
193
|
+
autocorrect_heredoc_argument(corrector, node, else_branch, if_branch, guard)
|
194
|
+
else
|
195
|
+
corrector.remove(node.loc.end)
|
196
|
+
return unless node.else?
|
197
|
+
|
198
|
+
corrector.remove(node.loc.else)
|
199
|
+
corrector.remove(branch_to_remove(node, guard))
|
200
|
+
end
|
201
|
+
end
|
202
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
203
|
+
|
204
|
+
def autocorrect_heredoc_argument(corrector, node, heredoc_branch, leave_branch, guard)
|
205
|
+
remove_whole_lines(corrector, leave_branch.source_range)
|
206
|
+
remove_whole_lines(corrector, node.loc.else)
|
207
|
+
remove_whole_lines(corrector, node.loc.end)
|
208
|
+
remove_whole_lines(corrector, branch_to_remove(node, guard).source_range)
|
209
|
+
corrector.insert_after(
|
210
|
+
heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
|
211
|
+
)
|
189
212
|
end
|
190
213
|
|
191
214
|
def branch_to_remove(node, guard)
|
@@ -222,7 +245,7 @@ module RuboCop
|
|
222
245
|
end
|
223
246
|
|
224
247
|
def accepted_if?(node, ending)
|
225
|
-
return true if node.modifier_form? || node.ternary?
|
248
|
+
return true if node.modifier_form? || node.ternary? || node.elsif_conditional?
|
226
249
|
|
227
250
|
if ending
|
228
251
|
node.else?
|
@@ -231,6 +254,10 @@ module RuboCop
|
|
231
254
|
end
|
232
255
|
end
|
233
256
|
|
257
|
+
def remove_whole_lines(corrector, range)
|
258
|
+
corrector.remove(range_by_whole_lines(range, include_final_newline: true))
|
259
|
+
end
|
260
|
+
|
234
261
|
def allowed_consecutive_conditionals?
|
235
262
|
cop_config.fetch('AllowConsecutiveConditionals', false)
|
236
263
|
end
|
@@ -38,28 +38,50 @@ module RuboCop
|
|
38
38
|
({block numblock} $(send (send _ ${:keys :values}) :each) ...)
|
39
39
|
PATTERN
|
40
40
|
|
41
|
+
# @!method kv_each_with_block_pass(node)
|
42
|
+
def_node_matcher :kv_each_with_block_pass, <<~PATTERN
|
43
|
+
(send $(send _ ${:keys :values}) :each (block_pass (sym _)))
|
44
|
+
PATTERN
|
45
|
+
|
41
46
|
def on_block(node)
|
42
|
-
|
47
|
+
kv_each(node) do |target, method|
|
48
|
+
register_kv_offense(target, method)
|
49
|
+
end
|
43
50
|
end
|
44
51
|
|
45
52
|
alias on_numblock on_block
|
46
53
|
|
54
|
+
def on_block_pass(node)
|
55
|
+
kv_each_with_block_pass(node.parent) do |target, method|
|
56
|
+
register_kv_with_block_pass_offense(node, target, method)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
47
60
|
private
|
48
61
|
|
49
|
-
def register_kv_offense(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
62
|
+
def register_kv_offense(target, method)
|
63
|
+
return unless (parent_receiver = target.receiver.receiver)
|
64
|
+
return if allowed_receiver?(parent_receiver)
|
65
|
+
|
66
|
+
add_offense(kv_range(target), message: format_message(method)) do |corrector|
|
67
|
+
correct_key_value_each(target, corrector)
|
68
|
+
end
|
69
|
+
end
|
54
70
|
|
55
|
-
|
71
|
+
def register_kv_with_block_pass_offense(node, target, method)
|
72
|
+
return unless (parent_receiver = node.parent.receiver.receiver)
|
73
|
+
return if allowed_receiver?(parent_receiver)
|
56
74
|
|
57
|
-
|
58
|
-
|
59
|
-
|
75
|
+
range = target.loc.selector.with(end_pos: node.parent.loc.selector.end_pos)
|
76
|
+
add_offense(range, message: format_message(method)) do |corrector|
|
77
|
+
corrector.replace(range, "each_#{method[0..-2]}")
|
60
78
|
end
|
61
79
|
end
|
62
80
|
|
81
|
+
def format_message(method_name)
|
82
|
+
format(MSG, prefer: "each_#{method_name[0..-2]}", current: "#{method_name}.each")
|
83
|
+
end
|
84
|
+
|
63
85
|
def check_argument(variable)
|
64
86
|
return unless variable.block_argument?
|
65
87
|
|
@@ -13,6 +13,10 @@ module RuboCop
|
|
13
13
|
# when used `==`.
|
14
14
|
# And do not check `Hash#delete_if` and `Hash#keep_if` to change receiver object.
|
15
15
|
#
|
16
|
+
# @safety
|
17
|
+
# This cop is unsafe because it cannot be guaranteed that the receiver
|
18
|
+
# is a `Hash` or responds to the replacement method.
|
19
|
+
#
|
16
20
|
# @example
|
17
21
|
#
|
18
22
|
# # bad
|
@@ -39,9 +39,10 @@ module RuboCop
|
|
39
39
|
def autocorrect(node)
|
40
40
|
return correct_elsif(node) if node.else_branch.if_type?
|
41
41
|
|
42
|
+
then_code = node.if_branch ? node.if_branch.source : 'nil'
|
42
43
|
else_code = node.else_branch ? node.else_branch.source : 'nil'
|
43
44
|
|
44
|
-
"#{node.condition.source} ? #{
|
45
|
+
"#{node.condition.source} ? #{then_code} : #{else_code}"
|
45
46
|
end
|
46
47
|
|
47
48
|
def correct_elsif(node)
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
44
44
|
PATTERN
|
45
45
|
|
46
46
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
47
|
-
return unless node.
|
47
|
+
return unless node.lambda_or_proc?
|
48
48
|
return unless nil_return?(node.body)
|
49
49
|
|
50
50
|
message = format(MSG, type: node.lambda? ? 'lambda' : 'proc')
|
@@ -31,9 +31,22 @@ module RuboCop
|
|
31
31
|
return if rhs.nil? || rhs.children.first
|
32
32
|
|
33
33
|
add_offense(dot) do |corrector|
|
34
|
+
wrap_in_parentheses_if_chained(corrector, node)
|
34
35
|
corrector.replace(dot, ' ')
|
35
36
|
end
|
36
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def wrap_in_parentheses_if_chained(corrector, node)
|
42
|
+
return unless node.parent&.call_type?
|
43
|
+
|
44
|
+
operator = node.loc.selector
|
45
|
+
|
46
|
+
ParenthesesCorrector.correct(corrector, node)
|
47
|
+
corrector.insert_after(operator, ' ')
|
48
|
+
corrector.wrap(node, '(', ')')
|
49
|
+
end
|
37
50
|
end
|
38
51
|
end
|
39
52
|
end
|
@@ -13,6 +13,7 @@ module RuboCop
|
|
13
13
|
# ----
|
14
14
|
# Methods:
|
15
15
|
# join: ''
|
16
|
+
# sum: 0
|
16
17
|
# split: ' '
|
17
18
|
# chomp: "\n"
|
18
19
|
# chomp!: "\n"
|
@@ -33,6 +34,7 @@ module RuboCop
|
|
33
34
|
# # bad
|
34
35
|
# array.join('')
|
35
36
|
# [1, 2, 3].join("")
|
37
|
+
# array.sum(0)
|
36
38
|
# string.split(" ")
|
37
39
|
# "first\nsecond".split(" ")
|
38
40
|
# string.chomp("\n")
|
@@ -42,6 +44,7 @@ module RuboCop
|
|
42
44
|
# # good
|
43
45
|
# array.join
|
44
46
|
# [1, 2, 3].join
|
47
|
+
# array.sum
|
45
48
|
# string.split
|
46
49
|
# "first second".split
|
47
50
|
# string.chomp
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Avoid redundant `::` prefix on constant.
|
7
|
+
#
|
8
|
+
# How Ruby searches constant is a bit complicated, and it can often be difficult to
|
9
|
+
# understand from the code whether the `::` is intended or not. Where `Module.nesting`
|
10
|
+
# is empty, there is no need to prepend `::`, so it would be nice to consistently
|
11
|
+
# avoid such meaningless `::` prefix to avoid confusion.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# ::Const
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# Const
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# class << self
|
22
|
+
# ::Const
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# class << self
|
27
|
+
# Const
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# class A
|
32
|
+
# ::Const
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# module A
|
37
|
+
# ::Const
|
38
|
+
# end
|
39
|
+
class RedundantConstantBase < Base
|
40
|
+
extend AutoCorrector
|
41
|
+
|
42
|
+
MSG = 'Remove redundant `::`.'
|
43
|
+
|
44
|
+
def on_cbase(node)
|
45
|
+
return unless bad?(node)
|
46
|
+
|
47
|
+
add_offense(node) do |corrector|
|
48
|
+
corrector.remove(node)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def bad?(node)
|
55
|
+
module_nesting_ancestors_of(node).none?
|
56
|
+
end
|
57
|
+
|
58
|
+
def module_nesting_ancestors_of(node)
|
59
|
+
node.each_ancestor(:class, :module).reject do |ancestor|
|
60
|
+
ancestor.class_type? && used_in_super_class_part?(node, class_node: ancestor)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def used_in_super_class_part?(node, class_node:)
|
65
|
+
class_node.parent_class&.each_descendant(:cbase)&.any? do |descendant|
|
66
|
+
descendant.equal?(node)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
#
|
8
8
|
# @safety
|
9
9
|
# This cop is unsafe, as it can produce false positives if the receiver
|
10
|
-
# is not an `
|
10
|
+
# is not an `Enumerator`.
|
11
11
|
#
|
12
12
|
# @example
|
13
13
|
#
|
@@ -61,19 +61,24 @@ module RuboCop
|
|
61
61
|
|
62
62
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
63
63
|
def redundant_each_method(node)
|
64
|
-
if node.
|
64
|
+
return if node.last_argument&.block_pass_type?
|
65
|
+
|
66
|
+
if node.method?(:each) && !node.parent&.block_type?
|
65
67
|
ancestor_node = node.each_ancestor(:send).detect do |ancestor|
|
66
|
-
|
68
|
+
ancestor.receiver == node &&
|
69
|
+
(RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each))
|
67
70
|
end
|
71
|
+
|
72
|
+
return ancestor_node if ancestor_node
|
68
73
|
end
|
69
74
|
|
70
|
-
|
71
|
-
|
75
|
+
return unless (prev_method = node.children.first)
|
76
|
+
return if !prev_method.send_type? ||
|
77
|
+
prev_method.parent.block_type? || prev_method.last_argument&.block_pass_type?
|
72
78
|
|
73
|
-
|
79
|
+
detected = prev_method.method_name.to_s.start_with?('each_') unless node.method?(:each)
|
74
80
|
|
75
|
-
|
76
|
-
end
|
81
|
+
prev_method if detected || prev_method.method?(:reverse_each)
|
77
82
|
end
|
78
83
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
79
84
|
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
44
44
|
|
45
45
|
def on_regexp(node)
|
46
46
|
each_escape(node) do |char, index, within_character_class|
|
47
|
-
next if allowed_escape?(node, char, within_character_class)
|
47
|
+
next if allowed_escape?(node, char, index, within_character_class)
|
48
48
|
|
49
49
|
location = escape_range_at_index(node, index)
|
50
50
|
|
@@ -56,7 +56,7 @@ module RuboCop
|
|
56
56
|
|
57
57
|
private
|
58
58
|
|
59
|
-
def allowed_escape?(node, char, within_character_class)
|
59
|
+
def allowed_escape?(node, char, index, within_character_class)
|
60
60
|
# Strictly speaking a few single-letter metachars are currently
|
61
61
|
# unnecessary to "escape", e.g. i, E, F, but enumerating them is
|
62
62
|
# rather difficult, and their behavior could change over time with
|
@@ -65,12 +65,21 @@ module RuboCop
|
|
65
65
|
return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
|
66
66
|
|
67
67
|
if within_character_class
|
68
|
-
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char)
|
68
|
+
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char) &&
|
69
|
+
!char_class_begins_or_ends_with_escaped_hyphen?(node, index)
|
69
70
|
else
|
70
71
|
ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES.include?(char)
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
75
|
+
def char_class_begins_or_ends_with_escaped_hyphen?(node, index)
|
76
|
+
# The hyphen character is allowed to be escaped within a character class
|
77
|
+
# but it's not necessry to escape hyphen if it's the first or last character
|
78
|
+
# within the character class. This method checks if that's the case.
|
79
|
+
# e.g. "[0-9\\-]" or "[\\-0-9]" would return true
|
80
|
+
node.source[index] == '[' || node.source[index + 3] == ']'
|
81
|
+
end
|
82
|
+
|
74
83
|
def delimiter?(node, char)
|
75
84
|
delimiters = [node.loc.begin.source[-1], node.loc.end.source[0]]
|
76
85
|
|
@@ -53,6 +53,13 @@ module RuboCop
|
|
53
53
|
|
54
54
|
MSG = 'Redundant `return` detected.'
|
55
55
|
MULTI_RETURN_MSG = 'To return multiple values, use an array.'
|
56
|
+
RESTRICT_ON_SEND = %i[define_method define_singleton_method].freeze
|
57
|
+
|
58
|
+
def on_send(node)
|
59
|
+
return unless (parent = node.parent) && parent.block_type?
|
60
|
+
|
61
|
+
check_branch(parent.body)
|
62
|
+
end
|
56
63
|
|
57
64
|
def on_def(node)
|
58
65
|
check_branch(node.body)
|