rubocop 1.71.2 → 1.73.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 +3 -3
- data/config/default.yml +51 -11
- data/config/internal_affairs.yml +16 -0
- data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/config.rb +4 -0
- data/lib/rubocop/config_loader.rb +44 -8
- data/lib/rubocop/config_loader_resolver.rb +23 -9
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
- data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
- data/lib/rubocop/cop/internal_affairs.rb +1 -16
- data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +26 -1
- data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +3 -3
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -14
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +104 -7
- data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +252 -0
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +80 -0
- data/lib/rubocop/cop/lint/void.rb +6 -0
- data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
- data/lib/rubocop/cop/mixin/alignment.rb +2 -2
- data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
- data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
- data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/range_help.rb +3 -3
- data/lib/rubocop/cop/mixin/string_help.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
- data/lib/rubocop/cop/naming/block_forwarding.rb +3 -3
- data/lib/rubocop/cop/naming/predicate_name.rb +44 -0
- data/lib/rubocop/cop/naming/variable_name.rb +64 -6
- data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
- data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
- data/lib/rubocop/cop/style/endless_method.rb +163 -18
- data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +34 -0
- data/lib/rubocop/cop/style/redundant_format.rb +250 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +18 -4
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/utils/format_string.rb +7 -5
- data/lib/rubocop/cops_documentation_generator.rb +12 -1
- data/lib/rubocop/directive_comment.rb +35 -2
- data/lib/rubocop/lsp/runtime.rb +2 -0
- data/lib/rubocop/lsp/server.rb +0 -2
- data/lib/rubocop/options.rb +26 -11
- data/lib/rubocop/path_util.rb +4 -0
- data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
- data/lib/rubocop/plugin/load_error.rb +26 -0
- data/lib/rubocop/plugin/loader.rb +100 -0
- data/lib/rubocop/plugin/not_supported_error.rb +29 -0
- data/lib/rubocop/plugin.rb +46 -0
- data/lib/rubocop/rake_task.rb +4 -1
- data/lib/rubocop/rspec/cop_helper.rb +9 -0
- data/lib/rubocop/rspec/shared_contexts.rb +15 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/server/cache.rb +35 -2
- data/lib/rubocop/server/cli.rb +2 -2
- data/lib/rubocop/version.rb +17 -2
- data/lib/rubocop.rb +5 -1
- data/lib/ruby_lsp/rubocop/addon.rb +7 -10
- data/lib/ruby_lsp/rubocop/{wraps_built_in_lsp_runtime.rb → runtime_adapter.rb} +5 -8
- metadata +35 -10
- data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -0,0 +1,252 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_r`, `to_c`,
|
7
|
+
# `to_a`, `to_h`, and `to_set`.
|
8
|
+
#
|
9
|
+
# When one of these methods is called on an object of the same type, that object
|
10
|
+
# is returned, making the call unnecessary. The cop detects conversion methods called
|
11
|
+
# on object literals, class constructors, class `[]` methods, and the `Kernel` methods
|
12
|
+
# `String()`, `Integer()`, `Float()`, `Rational()`, `Complex()` and `Array()`.
|
13
|
+
#
|
14
|
+
# Specifically, these cases are detected for each conversion method:
|
15
|
+
#
|
16
|
+
# * `to_s` when called on a string literal, interpolated string, heredoc,
|
17
|
+
# or with `String.new` or `String()`.
|
18
|
+
# * `to_sym` when called on a symbol literal or interpolated symbol.
|
19
|
+
# * `to_i` when called on an integer literal or with `Integer()`.
|
20
|
+
# * `to_f` when called on a float literal of with `Float()`.
|
21
|
+
# * `to_r` when called on a rational literal or with `Rational()`.
|
22
|
+
# * `to_c` when called on a complex literal of with `Complex()`.
|
23
|
+
# * `to_a` when called on an array literal, or with `Array.new`, `Array()` or `Array[]`.
|
24
|
+
# * `to_h` when called on a hash literal, or with `Hash.new`, `Hash()` or `Hash[]`.
|
25
|
+
# * `to_set` when called on `Set.new` or `Set[]`.
|
26
|
+
#
|
27
|
+
# In all cases, chaining one same `to_*` conversion methods listed above is redundant.
|
28
|
+
#
|
29
|
+
# The cop can also register an offense for chaining conversion methods on methods that are
|
30
|
+
# expected to return a specific type regardless of receiver (eg. `foo.inspect.to_s`).
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# # bad
|
34
|
+
# "text".to_s
|
35
|
+
# :sym.to_sym
|
36
|
+
# 42.to_i
|
37
|
+
# 8.5.to_f
|
38
|
+
# 12r.to_r
|
39
|
+
# 1i.to_c
|
40
|
+
# [].to_a
|
41
|
+
# {}.to_h
|
42
|
+
# Set.new.to_set
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# "text"
|
46
|
+
# :sym
|
47
|
+
# 42
|
48
|
+
# 8.5
|
49
|
+
# 12r
|
50
|
+
# 1i
|
51
|
+
# []
|
52
|
+
# {}
|
53
|
+
# Set.new
|
54
|
+
#
|
55
|
+
# # bad
|
56
|
+
# Integer(var).to_i
|
57
|
+
#
|
58
|
+
# # good
|
59
|
+
# Integer(var)
|
60
|
+
#
|
61
|
+
# # good - chaining to a type constructor with exceptions suppressed
|
62
|
+
# # in this case, `Integer()` could return `nil`
|
63
|
+
# Integer(var, exception: false).to_i
|
64
|
+
#
|
65
|
+
# # bad - chaining the same conversion
|
66
|
+
# foo.to_s.to_s
|
67
|
+
#
|
68
|
+
# # good
|
69
|
+
# foo.to_s
|
70
|
+
#
|
71
|
+
# # bad - chaining a conversion to a method that is expected to return the same type
|
72
|
+
# inspect.to_s
|
73
|
+
#
|
74
|
+
# # good
|
75
|
+
# inspect
|
76
|
+
#
|
77
|
+
class RedundantTypeConversion < Base
|
78
|
+
extend AutoCorrector
|
79
|
+
|
80
|
+
MSG = 'Redundant `%<method>s` detected.'
|
81
|
+
|
82
|
+
# Maps conversion methods to the node types for the literals of that type
|
83
|
+
LITERAL_NODE_TYPES = {
|
84
|
+
to_s: %i[str dstr],
|
85
|
+
to_sym: %i[sym dsym],
|
86
|
+
to_i: %i[int],
|
87
|
+
to_f: %i[float],
|
88
|
+
to_r: %i[rational],
|
89
|
+
to_c: %i[complex],
|
90
|
+
to_a: %i[array],
|
91
|
+
to_h: %i[hash],
|
92
|
+
to_set: [] # sets don't have a literal or node type
|
93
|
+
}.freeze
|
94
|
+
|
95
|
+
# Maps each conversion method to the pattern matcher for that type's constructors
|
96
|
+
# Not every type has a constructor, for instance Symbol.
|
97
|
+
CONSTRUCTOR_MAPPING = {
|
98
|
+
to_s: 'string_constructor?',
|
99
|
+
to_i: 'integer_constructor?',
|
100
|
+
to_f: 'float_constructor?',
|
101
|
+
to_r: 'rational_constructor?',
|
102
|
+
to_c: 'complex_constructor?',
|
103
|
+
to_a: 'array_constructor?',
|
104
|
+
to_h: 'hash_constructor?',
|
105
|
+
to_set: 'set_constructor?'
|
106
|
+
}.freeze
|
107
|
+
|
108
|
+
# Methods that already are expected to return a given type, which makes a further
|
109
|
+
# conversion redundant.
|
110
|
+
TYPED_METHODS = { to_s: %i[inspect] }.freeze
|
111
|
+
|
112
|
+
CONVERSION_METHODS = Set[*LITERAL_NODE_TYPES.keys].freeze
|
113
|
+
RESTRICT_ON_SEND = CONVERSION_METHODS
|
114
|
+
|
115
|
+
private_constant :LITERAL_NODE_TYPES, :CONSTRUCTOR_MAPPING
|
116
|
+
|
117
|
+
# @!method type_constructor?(node, type_symbol)
|
118
|
+
def_node_matcher :type_constructor?, <<~PATTERN
|
119
|
+
(send {nil? (const {cbase nil?} :Kernel)} %1 ...)
|
120
|
+
PATTERN
|
121
|
+
|
122
|
+
# @!method string_constructor?(node)
|
123
|
+
def_node_matcher :string_constructor?, <<~PATTERN
|
124
|
+
{
|
125
|
+
(send (const {cbase nil?} :String) :new ...)
|
126
|
+
#type_constructor?(:String)
|
127
|
+
}
|
128
|
+
PATTERN
|
129
|
+
|
130
|
+
# @!method integer_constructor?(node)
|
131
|
+
def_node_matcher :integer_constructor?, <<~PATTERN
|
132
|
+
#type_constructor?(:Integer)
|
133
|
+
PATTERN
|
134
|
+
|
135
|
+
# @!method float_constructor?(node)
|
136
|
+
def_node_matcher :float_constructor?, <<~PATTERN
|
137
|
+
#type_constructor?(:Float)
|
138
|
+
PATTERN
|
139
|
+
|
140
|
+
# @!method rational_constructor?(node)
|
141
|
+
def_node_matcher :rational_constructor?, <<~PATTERN
|
142
|
+
#type_constructor?(:Rational)
|
143
|
+
PATTERN
|
144
|
+
|
145
|
+
# @!method complex_constructor?(node)
|
146
|
+
def_node_matcher :complex_constructor?, <<~PATTERN
|
147
|
+
#type_constructor?(:Complex)
|
148
|
+
PATTERN
|
149
|
+
|
150
|
+
# @!method array_constructor?(node)
|
151
|
+
def_node_matcher :array_constructor?, <<~PATTERN
|
152
|
+
{
|
153
|
+
(send (const {cbase nil?} :Array) {:new :[]} ...)
|
154
|
+
#type_constructor?(:Array)
|
155
|
+
}
|
156
|
+
PATTERN
|
157
|
+
|
158
|
+
# @!method hash_constructor?(node)
|
159
|
+
def_node_matcher :hash_constructor?, <<~PATTERN
|
160
|
+
{
|
161
|
+
(block (send (const {cbase nil?} :Hash) :new) ...)
|
162
|
+
(send (const {cbase nil?} :Hash) {:new :[]} ...)
|
163
|
+
(send {nil? (const {cbase nil?} :Kernel)} :Hash ...)
|
164
|
+
}
|
165
|
+
PATTERN
|
166
|
+
|
167
|
+
# @!method set_constructor?(node)
|
168
|
+
def_node_matcher :set_constructor?, <<~PATTERN
|
169
|
+
{
|
170
|
+
(send (const {cbase nil?} :Set) {:new :[]} ...)
|
171
|
+
}
|
172
|
+
PATTERN
|
173
|
+
|
174
|
+
# @!method exception_false_keyword_argument?(node)
|
175
|
+
def_node_matcher :exception_false_keyword_argument?, <<~PATTERN
|
176
|
+
(hash (pair (sym :exception) false))
|
177
|
+
PATTERN
|
178
|
+
|
179
|
+
# rubocop:disable Metrics/AbcSize
|
180
|
+
def on_send(node)
|
181
|
+
return if hash_or_set_with_block?(node)
|
182
|
+
|
183
|
+
receiver = find_receiver(node)
|
184
|
+
return unless literal_receiver?(node, receiver) ||
|
185
|
+
constructor?(node, receiver) ||
|
186
|
+
chained_conversion?(node, receiver) ||
|
187
|
+
chained_to_typed_method?(node, receiver)
|
188
|
+
|
189
|
+
message = format(MSG, method: node.method_name)
|
190
|
+
|
191
|
+
add_offense(node.loc.selector, message: message) do |corrector|
|
192
|
+
corrector.remove(node.loc.dot.join(node.loc.selector))
|
193
|
+
end
|
194
|
+
end
|
195
|
+
# rubocop:enable Metrics/AbcSize
|
196
|
+
alias on_csend on_send
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def hash_or_set_with_block?(node)
|
201
|
+
return false if !node.method?(:to_h) && !node.method?(:to_set)
|
202
|
+
|
203
|
+
node.parent&.any_block_type? || node.last_argument&.block_pass_type?
|
204
|
+
end
|
205
|
+
|
206
|
+
def find_receiver(node)
|
207
|
+
receiver = node.receiver
|
208
|
+
return unless receiver
|
209
|
+
|
210
|
+
while receiver.begin_type?
|
211
|
+
break unless receiver.children.one?
|
212
|
+
|
213
|
+
receiver = receiver.children.first
|
214
|
+
end
|
215
|
+
|
216
|
+
receiver
|
217
|
+
end
|
218
|
+
|
219
|
+
def literal_receiver?(node, receiver)
|
220
|
+
return false unless receiver
|
221
|
+
|
222
|
+
receiver.type?(*LITERAL_NODE_TYPES[node.method_name])
|
223
|
+
end
|
224
|
+
|
225
|
+
def constructor?(node, receiver)
|
226
|
+
matcher = CONSTRUCTOR_MAPPING[node.method_name]
|
227
|
+
return false unless matcher
|
228
|
+
|
229
|
+
public_send(matcher, receiver) && !constructor_suppresses_exceptions?(receiver)
|
230
|
+
end
|
231
|
+
|
232
|
+
def constructor_suppresses_exceptions?(receiver)
|
233
|
+
# If the constructor suppresses exceptions with `exception: false`, it is possible
|
234
|
+
# it could return `nil`, and therefore a chained conversion is not redundant.
|
235
|
+
receiver.arguments.any? { |arg| exception_false_keyword_argument?(arg) }
|
236
|
+
end
|
237
|
+
|
238
|
+
def chained_conversion?(node, receiver)
|
239
|
+
return false unless receiver&.call_type?
|
240
|
+
|
241
|
+
receiver.method?(node.method_name)
|
242
|
+
end
|
243
|
+
|
244
|
+
def chained_to_typed_method?(node, receiver)
|
245
|
+
return false unless receiver&.call_type?
|
246
|
+
|
247
|
+
TYPED_METHODS.fetch(node.method_name, []).include?(receiver.method_name)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for cases where exceptions unrelated to the numeric constructors `Integer()`,
|
7
|
+
# `Float()`, `BigDecimal()`, `Complex()`, and `Rational()` may be unintentionally swallowed.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# The cop is unsafe for autocorrection because unexpected errors occurring in the argument
|
11
|
+
# passed to numeric constructor (e.g., `Integer()`) can lead to incompatible behavior.
|
12
|
+
# For example, changing it to `Integer(potential_exception_method_call, exception: false)`
|
13
|
+
# ensures that exceptions raised by `potential_exception_method_call` are not ignored.
|
14
|
+
#
|
15
|
+
# [source,ruby]
|
16
|
+
# ----
|
17
|
+
# Integer(potential_exception_method_call) rescue nil
|
18
|
+
# ----
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
#
|
22
|
+
# # bad
|
23
|
+
# Integer(arg) rescue nil
|
24
|
+
#
|
25
|
+
# # bad
|
26
|
+
# begin
|
27
|
+
# Integer(arg)
|
28
|
+
# rescue
|
29
|
+
# nil
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # bad
|
33
|
+
# begin
|
34
|
+
# Integer(arg)
|
35
|
+
# rescue
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # good
|
39
|
+
# Integer(arg, exception: false)
|
40
|
+
#
|
41
|
+
class SuppressedExceptionInNumberConversion < Base
|
42
|
+
extend AutoCorrector
|
43
|
+
extend TargetRubyVersion
|
44
|
+
|
45
|
+
MSG = 'Use `%<prefer>s` instead.'
|
46
|
+
EXPECTED_EXCEPTION_CLASSES = %w[ArgumentError TypeError ::ArgumentError ::TypeError].freeze
|
47
|
+
|
48
|
+
# @!method numeric_constructor_rescue_nil(node)
|
49
|
+
def_node_matcher :numeric_constructor_rescue_nil, <<~PATTERN
|
50
|
+
(rescue
|
51
|
+
$#numeric_method?
|
52
|
+
(resbody nil? nil? (nil)) nil?)
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
# @!method begin_numeric_constructor_rescue_nil(node)
|
56
|
+
def_node_matcher :begin_numeric_constructor_rescue_nil, <<~PATTERN
|
57
|
+
(kwbegin
|
58
|
+
(rescue
|
59
|
+
$#numeric_method?
|
60
|
+
(resbody $_? nil?
|
61
|
+
{(nil) nil?}) nil?))
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
# @!method numeric_method?(node)
|
65
|
+
def_node_matcher :numeric_method?, <<~PATTERN
|
66
|
+
{
|
67
|
+
(call #constructor_receiver? {:Integer :BigDecimal :Complex :Rational}
|
68
|
+
_ _?)
|
69
|
+
(call #constructor_receiver? :Float
|
70
|
+
_)
|
71
|
+
}
|
72
|
+
PATTERN
|
73
|
+
|
74
|
+
# @!method constructor_receiver?(node)
|
75
|
+
def_node_matcher :constructor_receiver?, <<~PATTERN
|
76
|
+
{nil? (const {nil? cbase} :Kernel)}
|
77
|
+
PATTERN
|
78
|
+
|
79
|
+
minimum_target_ruby_version 2.6
|
80
|
+
|
81
|
+
# rubocop:disable Metrics/AbcSize
|
82
|
+
def on_rescue(node)
|
83
|
+
if (method, exception_classes = begin_numeric_constructor_rescue_nil(node.parent))
|
84
|
+
return unless expected_exception_classes_only?(exception_classes)
|
85
|
+
|
86
|
+
node = node.parent
|
87
|
+
else
|
88
|
+
return unless (method = numeric_constructor_rescue_nil(node))
|
89
|
+
end
|
90
|
+
|
91
|
+
arguments = method.arguments.map(&:source) << 'exception: false'
|
92
|
+
prefer = "#{method.method_name}(#{arguments.join(', ')})"
|
93
|
+
prefer = "#{method.receiver.source}#{method.loc.dot.source}#{prefer}" if method.receiver
|
94
|
+
|
95
|
+
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
96
|
+
corrector.replace(node, prefer)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
# rubocop:enable Metrics/AbcSize
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def expected_exception_classes_only?(exception_classes)
|
104
|
+
return true unless (arguments = exception_classes.first)
|
105
|
+
|
106
|
+
(arguments.values.map(&:source) - EXPECTED_EXCEPTION_CLASSES).none?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for useless constant scoping. Private constants must be defined using
|
7
|
+
# `private_constant` or `class << self`. Even if `private` access modifier is used,
|
8
|
+
# it is public scope despite its appearance.
|
9
|
+
#
|
10
|
+
# It does not support autocorrection due to behavior change and multiple ways to fix it.
|
11
|
+
# Or a public constant may be intended.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# class Foo
|
17
|
+
# private
|
18
|
+
# PRIVATE_CONST = 42
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# class Foo
|
23
|
+
# PRIVATE_CONST = 42
|
24
|
+
# private_constant :PRIVATE_CONST
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# class Foo
|
29
|
+
# class << self
|
30
|
+
# private
|
31
|
+
# PRIVATE_CONST = 42
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# class Foo
|
37
|
+
# PUBLIC_CONST = 42 # If private scope is not intended.
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
class UselessConstantScoping < Base
|
41
|
+
MSG = 'Useless `private` access modifier for constant scope.'
|
42
|
+
|
43
|
+
# @!method private_constants(node)
|
44
|
+
def_node_matcher :private_constants, <<~PATTERN
|
45
|
+
(send nil? :private_constant $...)
|
46
|
+
PATTERN
|
47
|
+
|
48
|
+
def on_casgn(node)
|
49
|
+
return if node.each_ancestor(:sclass).any?
|
50
|
+
return unless after_private_modifier?(node.left_siblings)
|
51
|
+
return if private_constantize?(node.right_siblings, node.name)
|
52
|
+
|
53
|
+
add_offense(node)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def after_private_modifier?(left_siblings)
|
59
|
+
access_modifier_candidates = left_siblings.compact.select do |left_sibling|
|
60
|
+
left_sibling.respond_to?(:send_type?) && left_sibling.send_type?
|
61
|
+
end
|
62
|
+
|
63
|
+
access_modifier_candidates.any? do |candidate|
|
64
|
+
candidate.command?(:private) && candidate.arguments.none?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def private_constantize?(right_siblings, const_value)
|
69
|
+
private_constant_arguments = right_siblings.map { |node| private_constants(node) }
|
70
|
+
|
71
|
+
private_constant_values = private_constant_arguments.flatten.filter_map do |constant|
|
72
|
+
constant.value.to_sym if constant.respond_to?(:value)
|
73
|
+
end
|
74
|
+
|
75
|
+
private_constant_values.include?(const_value)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -125,9 +125,14 @@ module RuboCop
|
|
125
125
|
check_nonmutating(expr)
|
126
126
|
end
|
127
127
|
|
128
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
128
129
|
def check_void_op(node, &block)
|
129
130
|
node = node.children.first while node.begin_type?
|
130
131
|
return unless node.call_type? && OPERATORS.include?(node.method_name)
|
132
|
+
if !UNARY_OPERATORS.include?(node.method_name) && node.loc.dot && node.arguments.none?
|
133
|
+
return
|
134
|
+
end
|
135
|
+
|
131
136
|
return if block && yield(node)
|
132
137
|
|
133
138
|
add_offense(node.loc.selector,
|
@@ -135,6 +140,7 @@ module RuboCop
|
|
135
140
|
autocorrect_void_op(corrector, node)
|
136
141
|
end
|
137
142
|
end
|
143
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
138
144
|
|
139
145
|
def check_var(node)
|
140
146
|
return unless node.variable? || node.const_type?
|
@@ -26,6 +26,13 @@ module RuboCop
|
|
26
26
|
extend NodePattern::Macros
|
27
27
|
include RuboCop::AST::Sexp
|
28
28
|
|
29
|
+
VAR_SETTER_TO_GETTER = {
|
30
|
+
lvasgn: :lvar,
|
31
|
+
ivasgn: :ivar,
|
32
|
+
cvasgn: :cvar,
|
33
|
+
gvasgn: :gvar
|
34
|
+
}.freeze
|
35
|
+
|
29
36
|
# Plug into the calculator
|
30
37
|
def initialize(node, discount_repeated_attributes: false)
|
31
38
|
super(node)
|
@@ -114,13 +121,6 @@ module RuboCop
|
|
114
121
|
calls.fetch(value) { yield [calls, value] }
|
115
122
|
end
|
116
123
|
|
117
|
-
VAR_SETTER_TO_GETTER = {
|
118
|
-
lvasgn: :lvar,
|
119
|
-
ivasgn: :ivar,
|
120
|
-
cvasgn: :cvar,
|
121
|
-
gvasgn: :gvar
|
122
|
-
}.freeze
|
123
|
-
|
124
124
|
# @returns `[receiver, method | nil]` for the given setter `node`
|
125
125
|
# or `nil` if it is not a setter.
|
126
126
|
def setter_to_getter(node)
|
@@ -5,10 +5,10 @@ module RuboCop
|
|
5
5
|
# This module checks for nodes that should be aligned to the left or right.
|
6
6
|
# This amount is determined by the instance variable @column_delta.
|
7
7
|
module Alignment
|
8
|
-
private
|
9
|
-
|
10
8
|
SPACE = ' '
|
11
9
|
|
10
|
+
private
|
11
|
+
|
12
12
|
attr_reader :column_delta
|
13
13
|
|
14
14
|
def configured_indentation_width
|
@@ -18,12 +18,12 @@ module RuboCop
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# @deprecated Use allowed_line? instead
|
21
|
-
def ignored_line?
|
21
|
+
def ignored_line?(line)
|
22
22
|
warn Rainbow(<<~WARNING).yellow, uplevel: 1
|
23
23
|
`ignored_line?` is deprecated. Use `allowed_line?` instead.
|
24
24
|
WARNING
|
25
25
|
|
26
|
-
allowed_line?
|
26
|
+
allowed_line?(line)
|
27
27
|
end
|
28
28
|
|
29
29
|
def matches_allowed_pattern?(line)
|
@@ -31,12 +31,12 @@ module RuboCop
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# @deprecated Use matches_allowed_pattern? instead
|
34
|
-
def matches_ignored_pattern?
|
34
|
+
def matches_ignored_pattern?(line)
|
35
35
|
warn Rainbow(<<~WARNING).yellow, uplevel: 1
|
36
36
|
`matches_ignored_pattern?` is deprecated. Use `matches_allowed_pattern?` instead.
|
37
37
|
WARNING
|
38
38
|
|
39
|
-
matches_allowed_pattern?
|
39
|
+
matches_allowed_pattern?(line)
|
40
40
|
end
|
41
41
|
|
42
42
|
def allowed_patterns
|
@@ -11,6 +11,24 @@ module RuboCop
|
|
11
11
|
DO_NOT_MIX_OMIT_VALUE_MSG = "#{DO_NOT_MIX_MSG_PREFIX} #{OMIT_HASH_VALUE_MSG}"
|
12
12
|
DO_NOT_MIX_EXPLICIT_VALUE_MSG = "#{DO_NOT_MIX_MSG_PREFIX} #{EXPLICIT_HASH_VALUE_MSG}"
|
13
13
|
|
14
|
+
DefNode = Struct.new(:node) do
|
15
|
+
def selector
|
16
|
+
if node.loc.respond_to?(:selector)
|
17
|
+
node.loc.selector
|
18
|
+
else
|
19
|
+
node.loc.keyword
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def first_argument
|
24
|
+
node.first_argument
|
25
|
+
end
|
26
|
+
|
27
|
+
def last_argument
|
28
|
+
node.last_argument
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
14
32
|
def on_hash_for_mixed_shorthand(hash_node)
|
15
33
|
return if ignore_mixed_hash_shorthand_syntax?(hash_node)
|
16
34
|
|
@@ -212,24 +230,6 @@ module RuboCop
|
|
212
230
|
register_offense(pair_node, OMIT_HASH_VALUE_MSG, replacement)
|
213
231
|
end
|
214
232
|
end
|
215
|
-
|
216
|
-
DefNode = Struct.new(:node) do
|
217
|
-
def selector
|
218
|
-
if node.loc.respond_to?(:selector)
|
219
|
-
node.loc.selector
|
220
|
-
else
|
221
|
-
node.loc.keyword
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def first_argument
|
226
|
-
node.first_argument
|
227
|
-
end
|
228
|
-
|
229
|
-
def last_argument
|
230
|
-
node.last_argument
|
231
|
-
end
|
232
|
-
end
|
233
233
|
end
|
234
234
|
end
|
235
235
|
# rubocop:enable Metrics/ModuleLength
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
23
23
|
(call _ _)
|
24
24
|
(args
|
25
25
|
$(arg _key)
|
26
|
-
(arg _))
|
26
|
+
$(arg _))
|
27
27
|
{
|
28
28
|
$(send
|
29
29
|
{(lvar _key) $_ _ | _ $_ (lvar _key)})
|
@@ -67,7 +67,7 @@ module RuboCop
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def extracts_hash_subset?(block)
|
70
|
-
block_with_first_arg_check?(block) do |key_arg, send_node, method|
|
70
|
+
block_with_first_arg_check?(block) do |key_arg, value_arg, send_node, method|
|
71
71
|
# Only consider methods that have one argument
|
72
72
|
return false unless send_node.arguments.one?
|
73
73
|
|
@@ -76,15 +76,22 @@ module RuboCop
|
|
76
76
|
|
77
77
|
case method
|
78
78
|
when :include?, :exclude?
|
79
|
-
send_node
|
79
|
+
slices_key?(send_node, :first_argument, key_arg, value_arg)
|
80
80
|
when :in?
|
81
|
-
send_node
|
81
|
+
slices_key?(send_node, :receiver, key_arg, value_arg)
|
82
82
|
else
|
83
83
|
true
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
def slices_key?(send_node, method, key_arg, value_arg)
|
89
|
+
return false if using_value_variable?(send_node, value_arg)
|
90
|
+
|
91
|
+
node = method == :receiver ? send_node.receiver : send_node.first_argument
|
92
|
+
node.source == key_arg.source
|
93
|
+
end
|
94
|
+
|
88
95
|
def range_include?(send_node)
|
89
96
|
# When checking `include?`, `exclude?` and `in?` for offenses, if the receiver
|
90
97
|
# or first argument is a range, an offense should not be registered.
|
@@ -97,6 +104,14 @@ module RuboCop
|
|
97
104
|
receiver.range_type?
|
98
105
|
end
|
99
106
|
|
107
|
+
def using_value_variable?(send_node, value_arg)
|
108
|
+
# If the receiver of `include?` or `exclude?`, or the first argument of `in?` is the
|
109
|
+
# hash value block argument, an offense should not be registered.
|
110
|
+
# ie. `v.include?(k)` or `k.in?(v)`
|
111
|
+
(send_node.receiver.lvar_type? && send_node.receiver.name == value_arg.name) ||
|
112
|
+
(send_node.first_argument.lvar_type? && send_node.first_argument.name == value_arg.name)
|
113
|
+
end
|
114
|
+
|
100
115
|
def supported_subset_method?(method)
|
101
116
|
if active_support_extensions_enabled?
|
102
117
|
ACTIVE_SUPPORT_SUBSET_METHODS.include?(method)
|