rubocop 1.75.8 → 1.80.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/README.md +20 -16
- data/config/default.yml +107 -26
- data/config/obsoletion.yml +6 -3
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli.rb +17 -1
- data/lib/rubocop/config_loader.rb +1 -38
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +6 -3
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
- data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +26 -5
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -0
- data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
- data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +30 -4
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
- data/lib/rubocop/cop/lint/useless_or.rb +98 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/method_name.rb +127 -13
- data/lib/rubocop/cop/naming/predicate_method.rb +306 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
- data/lib/rubocop/cop/security/eval.rb +2 -1
- data/lib/rubocop/cop/security/open.rb +1 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
- data/lib/rubocop/cop/style/array_intersect.rb +98 -34
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/case_like_if.rb +1 -1
- data/lib/rubocop/cop/style/collection_querying.rb +167 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +4 -2
- data/lib/rubocop/cop/style/dig_chain.rb +1 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
- data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
- data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
- data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
- data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
- data/lib/rubocop/cop/style/it_assignment.rb +69 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
- data/lib/rubocop/cop/style/map_to_set.rb +1 -3
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
- data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
- data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
- data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +42 -6
- data/lib/rubocop/cop/style/redundant_self.rb +8 -5
- data/lib/rubocop/cop/style/safe_navigation.rb +38 -12
- data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +32 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +25 -8
- data/lib/rubocop/cops_documentation_generator.rb +1 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
- data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
- data/lib/rubocop/lsp/diagnostic.rb +4 -4
- data/lib/rubocop/lsp/routes.rb +35 -6
- data/lib/rubocop/pending_cops_reporter.rb +56 -0
- data/lib/rubocop/result_cache.rb +14 -12
- data/lib/rubocop/rspec/expect_offense.rb +9 -3
- data/lib/rubocop/runner.rb +6 -4
- data/lib/rubocop/server/cache.rb +4 -2
- data/lib/rubocop/server/client_command/base.rb +10 -0
- data/lib/rubocop/server/client_command/exec.rb +2 -1
- data/lib/rubocop/server/client_command/start.rb +11 -1
- data/lib/rubocop/target_finder.rb +9 -9
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +11 -1
- data/lib/ruby_lsp/rubocop/addon.rb +2 -2
- metadata +17 -7
@@ -5,12 +5,17 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# In Ruby 3.1, `Array#intersect?` has been added.
|
7
7
|
#
|
8
|
-
# This cop identifies places where
|
9
|
-
# or `(array1.intersection(array2)).any?` can be replaced by
|
10
|
-
# `array1.intersect?(array2)`.
|
8
|
+
# This cop identifies places where:
|
11
9
|
#
|
12
|
-
#
|
13
|
-
# `(array1
|
10
|
+
# * `(array1 & array2).any?`
|
11
|
+
# * `(array1.intersection(array2)).any?`
|
12
|
+
# * `array1.any? { |elem| array2.member?(elem) }`
|
13
|
+
# * `(array1 & array2).count > 0`
|
14
|
+
# * `(array1 & array2).size > 0`
|
15
|
+
#
|
16
|
+
# can be replaced with `array1.intersect?(array2)`.
|
17
|
+
#
|
18
|
+
# `array1.intersect?(array2)` is faster and more readable.
|
14
19
|
#
|
15
20
|
# In cases like the following, compatibility is not ensured,
|
16
21
|
# so it will not be detected when using block argument.
|
@@ -40,10 +45,27 @@ module RuboCop
|
|
40
45
|
# array1.intersection(array2).empty?
|
41
46
|
# array1.intersection(array2).none?
|
42
47
|
#
|
48
|
+
# # bad
|
49
|
+
# array1.any? { |elem| array2.member?(elem) }
|
50
|
+
# array1.none? { |elem| array2.member?(elem) }
|
51
|
+
#
|
43
52
|
# # good
|
44
53
|
# array1.intersect?(array2)
|
45
54
|
# !array1.intersect?(array2)
|
46
55
|
#
|
56
|
+
# # bad
|
57
|
+
# (array1 & array2).count > 0
|
58
|
+
# (array1 & array2).count.positive?
|
59
|
+
# (array1 & array2).count != 0
|
60
|
+
#
|
61
|
+
# (array1 & array2).count == 0
|
62
|
+
# (array1 & array2).count.zero?
|
63
|
+
#
|
64
|
+
# # good
|
65
|
+
# array1.intersect?(array2)
|
66
|
+
#
|
67
|
+
# !array1.intersect?(array2)
|
68
|
+
#
|
47
69
|
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
48
70
|
# # good
|
49
71
|
# (array1 & array2).present?
|
@@ -66,9 +88,11 @@ module RuboCop
|
|
66
88
|
PREDICATES = %i[any? empty? none?].to_set.freeze
|
67
89
|
ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
|
68
90
|
|
91
|
+
ARRAY_SIZE_METHODS = %i[count length size].to_set.freeze
|
92
|
+
|
69
93
|
# @!method bad_intersection_check?(node, predicates)
|
70
94
|
def_node_matcher :bad_intersection_check?, <<~PATTERN
|
71
|
-
(call
|
95
|
+
$(call
|
72
96
|
{
|
73
97
|
(begin (send $_ :& $_))
|
74
98
|
(call $_ :intersection $_)
|
@@ -77,53 +101,93 @@ module RuboCop
|
|
77
101
|
)
|
78
102
|
PATTERN
|
79
103
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
104
|
+
# @!method intersection_size_check?(node, predicates)
|
105
|
+
def_node_matcher :intersection_size_check?, <<~PATTERN
|
106
|
+
(call
|
107
|
+
$(call
|
108
|
+
{
|
109
|
+
(begin (send $_ :& $_))
|
110
|
+
(call $_ :intersection $_)
|
111
|
+
}
|
112
|
+
%ARRAY_SIZE_METHODS
|
113
|
+
)
|
114
|
+
{$:> (int 0) | $:positive? | $:!= (int 0) | $:== (int 0) | $:zero?}
|
115
|
+
)
|
116
|
+
PATTERN
|
117
|
+
|
118
|
+
# @!method any_none_block_intersection(node)
|
119
|
+
def_node_matcher :any_none_block_intersection, <<~PATTERN
|
120
|
+
{
|
121
|
+
(block
|
122
|
+
(call $_receiver ${:any? :none?})
|
123
|
+
(args (arg _key))
|
124
|
+
(send $_argument :member? (lvar _key))
|
125
|
+
)
|
126
|
+
(numblock
|
127
|
+
(call $_receiver ${:any? :none?}) 1
|
128
|
+
(send $_argument :member? (lvar :_1))
|
129
|
+
)
|
130
|
+
(itblock
|
131
|
+
(call $_receiver ${:any? :none?}) :it
|
132
|
+
(send $_argument :member? (lvar :it))
|
133
|
+
)
|
134
|
+
}
|
135
|
+
PATTERN
|
136
|
+
|
137
|
+
MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
|
138
|
+
STRAIGHT_METHODS = %i[present? any? > positive? !=].freeze
|
139
|
+
NEGATED_METHODS = %i[blank? empty? none? == zero?].freeze
|
84
140
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
85
141
|
|
86
142
|
def on_send(node)
|
87
143
|
return if node.block_literal?
|
88
|
-
return unless (receiver, argument, method_name = bad_intersection?(node))
|
89
|
-
|
90
|
-
dot = node.loc.dot.source
|
91
|
-
message = message(receiver.source, argument.source, method_name, dot, node.source)
|
144
|
+
return unless (dot_node, receiver, argument, method_name = bad_intersection?(node))
|
92
145
|
|
93
|
-
|
94
|
-
|
146
|
+
dot = dot_node.loc.dot.source
|
147
|
+
bang = straight?(method_name) ? '' : '!'
|
148
|
+
replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
|
95
149
|
|
96
|
-
|
97
|
-
end
|
150
|
+
register_offense(node, replacement)
|
98
151
|
end
|
99
152
|
alias on_csend on_send
|
100
153
|
|
154
|
+
def on_block(node)
|
155
|
+
return unless (receiver, method_name, argument = any_none_block_intersection(node))
|
156
|
+
|
157
|
+
dot = node.send_node.loc.dot.source
|
158
|
+
bang = method_name == :any? ? '' : '!'
|
159
|
+
replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
|
160
|
+
|
161
|
+
register_offense(node, replacement)
|
162
|
+
end
|
163
|
+
alias on_numblock on_block
|
164
|
+
alias on_itblock on_block
|
165
|
+
|
101
166
|
private
|
102
167
|
|
103
168
|
def bad_intersection?(node)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
PREDICATES
|
108
|
-
end
|
169
|
+
bad_intersection_check?(node, bad_intersection_predicates) ||
|
170
|
+
intersection_size_check?(node)
|
171
|
+
end
|
109
172
|
|
110
|
-
|
173
|
+
def bad_intersection_predicates
|
174
|
+
if active_support_extensions_enabled?
|
175
|
+
ACTIVE_SUPPORT_PREDICATES
|
176
|
+
else
|
177
|
+
PREDICATES
|
178
|
+
end
|
111
179
|
end
|
112
180
|
|
113
181
|
def straight?(method_name)
|
114
182
|
STRAIGHT_METHODS.include?(method_name.to_sym)
|
115
183
|
end
|
116
184
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
argument: argument,
|
124
|
-
dot: dot,
|
125
|
-
existing: existing
|
126
|
-
)
|
185
|
+
def register_offense(node, replacement)
|
186
|
+
message = format(MSG, replacement: replacement, existing: node.source)
|
187
|
+
|
188
|
+
add_offense(node, message: message) do |corrector|
|
189
|
+
corrector.replace(node, replacement)
|
190
|
+
end
|
127
191
|
end
|
128
192
|
end
|
129
193
|
end
|
@@ -70,18 +70,25 @@ module RuboCop
|
|
70
70
|
(send _ :& _))
|
71
71
|
PATTERN
|
72
72
|
|
73
|
+
# rubocop:disable Metrics/AbcSize
|
73
74
|
def on_send(node)
|
74
75
|
return unless node.receiver&.begin_type?
|
75
76
|
return unless (preferred_method = preferred_method(node))
|
76
77
|
|
77
78
|
bit_operation = node.receiver.children.first
|
78
79
|
lhs, _operator, rhs = *bit_operation
|
79
|
-
|
80
|
+
|
81
|
+
preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
|
82
|
+
"#{rhs.source}.allbits?(#{lhs.source})"
|
83
|
+
else
|
84
|
+
"#{lhs.source}.#{preferred_method}(#{rhs.source})"
|
85
|
+
end
|
80
86
|
|
81
87
|
add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
|
82
88
|
corrector.replace(node, preferred)
|
83
89
|
end
|
84
90
|
end
|
91
|
+
# rubocop:enable Metrics/AbcSize
|
85
92
|
|
86
93
|
private
|
87
94
|
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Prefer `Enumerable` predicate methods over expressions with `count`.
|
7
|
+
#
|
8
|
+
# The cop checks calls to `count` without arguments, or with a
|
9
|
+
# block. It doesn't register offenses for `count` with a positional
|
10
|
+
# argument because its behavior differs from predicate methods (`count`
|
11
|
+
# matches the argument using `==`, while `any?`, `none?` and `one?` use
|
12
|
+
# `===`).
|
13
|
+
#
|
14
|
+
# NOTE: This cop doesn't check `length` and `size` methods because they
|
15
|
+
# would yield false positives. For example, `String` implements `length`
|
16
|
+
# and `size`, but it doesn't include `Enumerable`.
|
17
|
+
#
|
18
|
+
# @safety
|
19
|
+
# The cop is unsafe because receiver might not include `Enumerable`, or
|
20
|
+
# it has nonstandard implementation of `count` or any replacement
|
21
|
+
# methods.
|
22
|
+
#
|
23
|
+
# It's also unsafe because for collections with falsey values, expressions
|
24
|
+
# with `count` without a block return a different result than methods `any?`,
|
25
|
+
# `none?` and `one?`:
|
26
|
+
#
|
27
|
+
# [source,ruby]
|
28
|
+
# ----
|
29
|
+
# [nil, false].count.positive?
|
30
|
+
# [nil].count == 1
|
31
|
+
# # => true
|
32
|
+
#
|
33
|
+
# [nil, false].any?
|
34
|
+
# [nil].one?
|
35
|
+
# # => false
|
36
|
+
#
|
37
|
+
# [nil].count == 0
|
38
|
+
# # => false
|
39
|
+
#
|
40
|
+
# [nil].none?
|
41
|
+
# # => true
|
42
|
+
# ----
|
43
|
+
#
|
44
|
+
# Autocorrection is unsafe when replacement methods don't iterate over
|
45
|
+
# every element in collection and the given block runs side effects:
|
46
|
+
#
|
47
|
+
# [source,ruby]
|
48
|
+
# ----
|
49
|
+
# x.count(&:method_with_side_effects).positive?
|
50
|
+
# # calls `method_with_side_effects` on every element
|
51
|
+
#
|
52
|
+
# x.any?(&:method_with_side_effects)
|
53
|
+
# # calls `method_with_side_effects` until first element returns a truthy value
|
54
|
+
# ----
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
#
|
58
|
+
# # bad
|
59
|
+
# x.count.positive?
|
60
|
+
# x.count > 0
|
61
|
+
# x.count != 0
|
62
|
+
#
|
63
|
+
# x.count(&:foo?).positive?
|
64
|
+
# x.count { |item| item.foo? }.positive?
|
65
|
+
#
|
66
|
+
# # good
|
67
|
+
# x.any?
|
68
|
+
#
|
69
|
+
# x.any?(&:foo?)
|
70
|
+
# x.any? { |item| item.foo? }
|
71
|
+
#
|
72
|
+
# # bad
|
73
|
+
# x.count.zero?
|
74
|
+
# x.count == 0
|
75
|
+
#
|
76
|
+
# # good
|
77
|
+
# x.none?
|
78
|
+
#
|
79
|
+
# # bad
|
80
|
+
# x.count == 1
|
81
|
+
# x.one?
|
82
|
+
#
|
83
|
+
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
84
|
+
#
|
85
|
+
# # good
|
86
|
+
# x.count > 1
|
87
|
+
#
|
88
|
+
# @example AllCops:ActiveSupportExtensionsEnabled: true
|
89
|
+
#
|
90
|
+
# # bad
|
91
|
+
# x.count > 1
|
92
|
+
#
|
93
|
+
# # good
|
94
|
+
# x.many?
|
95
|
+
#
|
96
|
+
class CollectionQuerying < Base
|
97
|
+
include RangeHelp
|
98
|
+
extend AutoCorrector
|
99
|
+
|
100
|
+
MSG = 'Use `%<prefer>s` instead.'
|
101
|
+
|
102
|
+
RESTRICT_ON_SEND = %i[positive? > != zero? ==].freeze
|
103
|
+
|
104
|
+
REPLACEMENTS = {
|
105
|
+
[:positive?, nil] => :any?,
|
106
|
+
[:>, 0] => :any?,
|
107
|
+
[:!=, 0] => :any?,
|
108
|
+
[:zero?, nil] => :none?,
|
109
|
+
[:==, 0] => :none?,
|
110
|
+
[:==, 1] => :one?,
|
111
|
+
[:>, 1] => :many?
|
112
|
+
}.freeze
|
113
|
+
|
114
|
+
# @!method count_predicate(node)
|
115
|
+
def_node_matcher :count_predicate, <<~PATTERN
|
116
|
+
(send
|
117
|
+
{
|
118
|
+
(any_block $(call !nil? :count) _ _)
|
119
|
+
$(call !nil? :count (block-pass _)?)
|
120
|
+
}
|
121
|
+
{
|
122
|
+
:positive? |
|
123
|
+
:> (int 0) |
|
124
|
+
:!= (int 0) |
|
125
|
+
:zero? |
|
126
|
+
:== (int 0) |
|
127
|
+
:== (int 1) |
|
128
|
+
:> (int 1)
|
129
|
+
})
|
130
|
+
PATTERN
|
131
|
+
|
132
|
+
def on_send(node)
|
133
|
+
return unless (count_node = count_predicate(node))
|
134
|
+
|
135
|
+
replacement_method = replacement_method(node)
|
136
|
+
|
137
|
+
return unless replacement_supported?(replacement_method)
|
138
|
+
|
139
|
+
offense_range = count_node.loc.selector.join(node.source_range.end)
|
140
|
+
add_offense(offense_range,
|
141
|
+
message: format(MSG, prefer: replacement_method)) do |corrector|
|
142
|
+
corrector.replace(count_node.loc.selector, replacement_method)
|
143
|
+
corrector.remove(removal_range(node))
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def replacement_method(node)
|
150
|
+
REPLACEMENTS.fetch([node.method_name, node.first_argument&.value])
|
151
|
+
end
|
152
|
+
|
153
|
+
def replacement_supported?(method_name)
|
154
|
+
return true if active_support_extensions_enabled?
|
155
|
+
|
156
|
+
method_name != :many?
|
157
|
+
end
|
158
|
+
|
159
|
+
def removal_range(node)
|
160
|
+
range = (node.loc.dot || node.loc.selector).join(node.source_range.end)
|
161
|
+
|
162
|
+
range_with_surrounding_space(range, side: :left)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -111,7 +111,7 @@ module RuboCop
|
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
|
-
#
|
114
|
+
# Checks for `if` and `case` statements where each branch is used for
|
115
115
|
# both the assignment and comparison of the same variable
|
116
116
|
# when using the return of the condition can be used instead.
|
117
117
|
#
|
@@ -451,7 +451,9 @@ module RuboCop
|
|
451
451
|
corrector.remove_preceding(condition.loc.else, condition.loc.else.column - column)
|
452
452
|
end
|
453
453
|
|
454
|
-
return unless condition.loc.end && !same_line?(
|
454
|
+
return unless condition.loc.end && !same_line?(
|
455
|
+
condition.branches.last.parent.else_branch, condition.loc.end
|
456
|
+
)
|
455
457
|
|
456
458
|
corrector.remove_preceding(condition.loc.end, condition.loc.end.column - column)
|
457
459
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
#
|
6
|
+
# Checks for chained `dig` calls that can be collapsed into a single `dig`.
|
7
7
|
#
|
8
8
|
# @safety
|
9
9
|
# This cop is unsafe because it cannot be guaranteed that the receiver
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for empty strings being assigned inside string interpolation.
|
7
|
+
#
|
8
|
+
# Empty strings are a meaningless outcome inside of string interpolation, so we remove them.
|
9
|
+
# Alternatively, when configured to do so, we prioritise using empty strings.
|
10
|
+
#
|
11
|
+
# While this cop would also apply to variables that are only going to be used as strings,
|
12
|
+
# RuboCop can't detect that, so we only check inside of string interpolation.
|
13
|
+
#
|
14
|
+
# @example EnforcedStyle: trailing_conditional (default)
|
15
|
+
# # bad
|
16
|
+
# "#{condition ? 'foo' : ''}"
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# "#{'foo' if condition}"
|
20
|
+
#
|
21
|
+
# # bad
|
22
|
+
# "#{condition ? '' : 'foo'}"
|
23
|
+
#
|
24
|
+
# # good
|
25
|
+
# "#{'foo' unless condition}"
|
26
|
+
#
|
27
|
+
# @example EnforcedStyle: ternary
|
28
|
+
# # bad
|
29
|
+
# "#{'foo' if condition}"
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# "#{condition ? 'foo' : ''}"
|
33
|
+
#
|
34
|
+
# # bad
|
35
|
+
# "#{'foo' unless condition}"
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# "#{condition ? '' : 'foo'}"
|
39
|
+
#
|
40
|
+
class EmptyStringInsideInterpolation < Base
|
41
|
+
include ConfigurableEnforcedStyle
|
42
|
+
include Interpolation
|
43
|
+
extend AutoCorrector
|
44
|
+
|
45
|
+
MSG_TRAILING_CONDITIONAL = 'Do not use trailing conditionals in string interpolation.'
|
46
|
+
MSG_TERNARY = 'Do not return empty strings in string interpolation.'
|
47
|
+
|
48
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
49
|
+
def on_interpolation(node)
|
50
|
+
node.each_child_node(:if) do |child_node|
|
51
|
+
if style == :trailing_conditional
|
52
|
+
if empty_if_outcome?(child_node)
|
53
|
+
ternary_style_autocorrect(child_node, child_node.else_branch.source, 'unless')
|
54
|
+
end
|
55
|
+
|
56
|
+
if empty_else_outcome?(child_node)
|
57
|
+
ternary_style_autocorrect(child_node, child_node.if_branch.source, 'if')
|
58
|
+
end
|
59
|
+
elsif style == :ternary
|
60
|
+
next unless child_node.modifier_form?
|
61
|
+
|
62
|
+
ternary_component = if child_node.unless?
|
63
|
+
"'' : #{child_node.if_branch.source}"
|
64
|
+
else
|
65
|
+
"#{child_node.if_branch.source} : ''"
|
66
|
+
end
|
67
|
+
|
68
|
+
add_offense(node, message: MSG_TRAILING_CONDITIONAL) do |corrector|
|
69
|
+
corrector.replace(node, "\#{#{child_node.condition.source} ? #{ternary_component}}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def empty_if_outcome?(node)
|
79
|
+
empty_branch_outcome?(node.if_branch)
|
80
|
+
end
|
81
|
+
|
82
|
+
def empty_else_outcome?(node)
|
83
|
+
empty_branch_outcome?(node.else_branch)
|
84
|
+
end
|
85
|
+
|
86
|
+
def empty_branch_outcome?(branch)
|
87
|
+
return false unless branch
|
88
|
+
|
89
|
+
branch.nil_type? || (branch.str_type? && branch.value.empty?)
|
90
|
+
end
|
91
|
+
|
92
|
+
def ternary_style_autocorrect(node, outcome, condition)
|
93
|
+
add_offense(node, message: MSG_TERNARY) do |corrector|
|
94
|
+
corrector.replace(node, "#{outcome} #{condition} #{node.condition.source}")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -59,6 +59,7 @@ module RuboCop
|
|
59
59
|
#
|
60
60
|
class ExponentialNotation < Base
|
61
61
|
include ConfigurableEnforcedStyle
|
62
|
+
|
62
63
|
MESSAGES = {
|
63
64
|
scientific: 'Use a mantissa >= 1 and < 10.',
|
64
65
|
engineering: 'Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000.',
|
@@ -87,7 +88,7 @@ module RuboCop
|
|
87
88
|
true
|
88
89
|
end
|
89
90
|
|
90
|
-
def integral(node)
|
91
|
+
def integral?(node)
|
91
92
|
mantissa, = node.source.split('e')
|
92
93
|
/^-?[1-9](\d*[1-9])?$/.match?(mantissa)
|
93
94
|
end
|
@@ -101,7 +102,7 @@ module RuboCop
|
|
101
102
|
when :engineering
|
102
103
|
!engineering?(node)
|
103
104
|
when :integral
|
104
|
-
!integral(node)
|
105
|
+
!integral?(node)
|
105
106
|
else
|
106
107
|
false
|
107
108
|
end
|
@@ -9,7 +9,20 @@ module RuboCop
|
|
9
9
|
# On the other hand, `ENV.fetch` raises `KeyError` or returns the explicitly
|
10
10
|
# specified default value.
|
11
11
|
#
|
12
|
-
# @example
|
12
|
+
# @example DefaultToNil: true (default)
|
13
|
+
# # bad
|
14
|
+
# ENV['X']
|
15
|
+
# x = ENV['X']
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# ENV.fetch('X', nil)
|
19
|
+
# x = ENV.fetch('X', nil)
|
20
|
+
#
|
21
|
+
# # also good
|
22
|
+
# !ENV['X']
|
23
|
+
# ENV['X'].some_method # (e.g. `.nil?`)
|
24
|
+
#
|
25
|
+
# @example DefaultToNil: false
|
13
26
|
# # bad
|
14
27
|
# ENV['X']
|
15
28
|
# x = ENV['X']
|
@@ -25,7 +38,8 @@ module RuboCop
|
|
25
38
|
class FetchEnvVar < Base
|
26
39
|
extend AutoCorrector
|
27
40
|
|
28
|
-
|
41
|
+
MSG_WITH_NIL = 'Use `ENV.fetch(%<key>s, nil)` instead of `ENV[%<key>s]`.'
|
42
|
+
MSG_WITHOUT_NIL = 'Use `ENV.fetch(%<key>s)` instead of `ENV[%<key>s]`.'
|
29
43
|
RESTRICT_ON_SEND = [:[]].freeze
|
30
44
|
|
31
45
|
# @!method env_with_bracket?(node)
|
@@ -37,7 +51,7 @@ module RuboCop
|
|
37
51
|
env_with_bracket?(node) do |name_node|
|
38
52
|
break unless offensive?(node)
|
39
53
|
|
40
|
-
message = format(
|
54
|
+
message = format(offense_message, key: name_node.source)
|
41
55
|
add_offense(node, message: message) do |corrector|
|
42
56
|
corrector.replace(node, new_code(name_node))
|
43
57
|
end
|
@@ -46,6 +60,14 @@ module RuboCop
|
|
46
60
|
|
47
61
|
private
|
48
62
|
|
63
|
+
def default_to_nil?
|
64
|
+
cop_config.fetch('DefaultToNil', true)
|
65
|
+
end
|
66
|
+
|
67
|
+
def offense_message
|
68
|
+
default_to_nil? ? MSG_WITH_NIL : MSG_WITHOUT_NIL
|
69
|
+
end
|
70
|
+
|
49
71
|
def allowed_var?(node)
|
50
72
|
env_key_node = node.children.last
|
51
73
|
env_key_node.str_type? && cop_config['AllowedVars'].include?(env_key_node.value)
|
@@ -53,12 +75,12 @@ module RuboCop
|
|
53
75
|
|
54
76
|
def used_as_flag?(node)
|
55
77
|
return false if node.root?
|
56
|
-
return true if used_if_condition_in_body(node)
|
78
|
+
return true if used_if_condition_in_body?(node)
|
57
79
|
|
58
80
|
node.parent.send_type? && (node.parent.prefix_bang? || node.parent.comparison_method?)
|
59
81
|
end
|
60
82
|
|
61
|
-
def used_if_condition_in_body(node)
|
83
|
+
def used_if_condition_in_body?(node)
|
62
84
|
if_node = node.ancestors.find(&:if_type?)
|
63
85
|
|
64
86
|
return false unless (condition = if_node&.condition)
|
@@ -125,7 +147,11 @@ module RuboCop
|
|
125
147
|
end
|
126
148
|
|
127
149
|
def new_code(name_node)
|
128
|
-
|
150
|
+
if default_to_nil?
|
151
|
+
"ENV.fetch(#{name_node.source}, nil)"
|
152
|
+
else
|
153
|
+
"ENV.fetch(#{name_node.source})"
|
154
|
+
end
|
129
155
|
end
|
130
156
|
end
|
131
157
|
end
|