rubocop 1.38.0 → 1.40.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 +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)
|