rubocop 1.71.2 → 1.72.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 +28 -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 +40 -8
- data/lib/rubocop/config_loader_resolver.rb +21 -7
- 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_method_body.rb +22 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
- 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/format_parameter_mismatch.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +221 -0
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +74 -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/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
- 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/naming/predicate_name.rb +44 -0
- data/lib/rubocop/cop/style/redundant_format.rb +222 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +17 -3
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/utils/format_string.rb +7 -5
- 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 +141 -0
- data/lib/rubocop/plugin/load_error.rb +35 -0
- data/lib/rubocop/plugin/loader.rb +100 -0
- data/lib/rubocop/plugin/not_supported_error.rb +29 -0
- data/lib/rubocop/plugin.rb +39 -0
- data/lib/rubocop/rake_task.rb +4 -1
- 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 -0
- 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 -9
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Lint/RedundantCopDisableDirective
|
4
|
+
# rubocop:disable Style/DoubleCopDisableDirective
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module Lint
|
8
|
+
# Checks that `# rubocop:enable ...` and `# rubocop:disable ...` statements
|
9
|
+
# are strictly formatted.
|
10
|
+
#
|
11
|
+
# A comment can be added to the directive by prefixing it with `--`.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# # rubocop:disable Layout/LineLength Style/Encoding
|
16
|
+
# # ^ missing comma
|
17
|
+
#
|
18
|
+
# # bad
|
19
|
+
# # rubocop:disable
|
20
|
+
#
|
21
|
+
# # bad
|
22
|
+
# # rubocop:disable Layout/LineLength # rubocop:disable Style/Encoding
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# # rubocop:wrongmode Layout/LineLength
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# # rubocop:disable Layout/LineLength
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# # rubocop:disable Layout/LineLength, Style/Encoding
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# # rubocop:disable all
|
35
|
+
#
|
36
|
+
# # good
|
37
|
+
# # rubocop:disable Layout/LineLength -- This is a good comment.
|
38
|
+
#
|
39
|
+
class CopDirectiveSyntax < Base
|
40
|
+
COMMON_MSG = 'Malformed directive comment detected.'
|
41
|
+
|
42
|
+
MISSING_MODE_NAME_MSG = 'The mode name is missing.'
|
43
|
+
INVALID_MODE_NAME_MSG = 'The mode name must be one of `enable`, `disable`, or `todo`.'
|
44
|
+
MISSING_COP_NAME_MSG = 'The cop name is missing.'
|
45
|
+
MALFORMED_COP_NAMES_MSG = 'Cop names must be separated by commas. ' \
|
46
|
+
'Comment in the directive must start with `--`.'
|
47
|
+
|
48
|
+
def on_new_investigation
|
49
|
+
processed_source.comments.each do |comment|
|
50
|
+
directive_comment = DirectiveComment.new(comment)
|
51
|
+
next unless directive_comment.start_with_marker?
|
52
|
+
next unless directive_comment.malformed?
|
53
|
+
|
54
|
+
message = offense_message(directive_comment)
|
55
|
+
add_offense(comment, message: message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# rubocop:disable Metrics/MethodLength
|
62
|
+
def offense_message(directive_comment)
|
63
|
+
comment = directive_comment.comment
|
64
|
+
after_marker = comment.text.sub(DirectiveComment::DIRECTIVE_MARKER_REGEXP, '')
|
65
|
+
mode = after_marker.split(' ', 2).first
|
66
|
+
additional_msg = if mode.nil?
|
67
|
+
MISSING_MODE_NAME_MSG
|
68
|
+
elsif !DirectiveComment::AVAILABLE_MODES.include?(mode)
|
69
|
+
INVALID_MODE_NAME_MSG
|
70
|
+
elsif directive_comment.missing_cop_name?
|
71
|
+
MISSING_COP_NAME_MSG
|
72
|
+
else
|
73
|
+
MALFORMED_COP_NAMES_MSG
|
74
|
+
end
|
75
|
+
|
76
|
+
"#{COMMON_MSG} #{additional_msg}"
|
77
|
+
end
|
78
|
+
# rubocop:enable Metrics/MethodLength
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
# rubocop:enable Lint/RedundantCopDisableDirective
|
84
|
+
# rubocop:enable Style/DoubleCopDisableDirective
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
# expected fields for format/sprintf/#% and what is actually
|
8
8
|
# passed as arguments.
|
9
9
|
#
|
10
|
-
# In addition it checks whether different formats are used in the same
|
10
|
+
# In addition, it checks whether different formats are used in the same
|
11
11
|
# format string. Do not mix numbered, unnumbered, and named formats in
|
12
12
|
# the same format string.
|
13
13
|
#
|
@@ -0,0 +1,221 @@
|
|
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_s
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# "text"
|
46
|
+
# :sym
|
47
|
+
# 42
|
48
|
+
# 8.5
|
49
|
+
# 12r
|
50
|
+
# 1i
|
51
|
+
# []
|
52
|
+
# {}
|
53
|
+
# Set.new
|
54
|
+
#
|
55
|
+
# # bad - chaining the same conversion
|
56
|
+
# foo.to_s.to_s
|
57
|
+
#
|
58
|
+
# # good
|
59
|
+
# foo.to_s
|
60
|
+
#
|
61
|
+
# # bad - chaining a conversion to a method that is expected to return the same type
|
62
|
+
# inspect.to_s
|
63
|
+
#
|
64
|
+
# # good
|
65
|
+
# inspect
|
66
|
+
#
|
67
|
+
class RedundantTypeConversion < Base
|
68
|
+
extend AutoCorrector
|
69
|
+
|
70
|
+
MSG = 'Redundant `%<method>s` detected.'
|
71
|
+
|
72
|
+
# Maps conversion methods to the node types for the literals of that type
|
73
|
+
LITERAL_NODE_TYPES = {
|
74
|
+
to_s: %i[str dstr],
|
75
|
+
to_sym: %i[sym dsym],
|
76
|
+
to_i: %i[int],
|
77
|
+
to_f: %i[float],
|
78
|
+
to_r: %i[rational],
|
79
|
+
to_c: %i[complex],
|
80
|
+
to_a: %i[array],
|
81
|
+
to_h: %i[hash],
|
82
|
+
to_set: [] # sets don't have a literal or node type
|
83
|
+
}.freeze
|
84
|
+
|
85
|
+
# Maps each conversion method to the pattern matcher for that type's constructors
|
86
|
+
# Not every type has a constructor, for instance Symbol.
|
87
|
+
CONSTRUCTOR_MAPPING = {
|
88
|
+
to_s: 'string_constructor?',
|
89
|
+
to_i: 'integer_constructor?',
|
90
|
+
to_f: 'float_constructor?',
|
91
|
+
to_r: 'rational_constructor?',
|
92
|
+
to_c: 'complex_constructor?',
|
93
|
+
to_a: 'array_constructor?',
|
94
|
+
to_h: 'hash_constructor?',
|
95
|
+
to_set: 'set_constructor?'
|
96
|
+
}.freeze
|
97
|
+
|
98
|
+
# Methods that already are expected to return a given type, which makes a further
|
99
|
+
# conversion redundant.
|
100
|
+
TYPED_METHODS = { to_s: %i[inspect] }.freeze
|
101
|
+
|
102
|
+
CONVERSION_METHODS = Set[*LITERAL_NODE_TYPES.keys].freeze
|
103
|
+
RESTRICT_ON_SEND = CONVERSION_METHODS
|
104
|
+
|
105
|
+
private_constant :LITERAL_NODE_TYPES, :CONSTRUCTOR_MAPPING
|
106
|
+
|
107
|
+
# @!method type_constructor?(node, type_symbol)
|
108
|
+
def_node_matcher :type_constructor?, <<~PATTERN
|
109
|
+
(send {nil? (const {cbase nil?} :Kernel)} %1 ...)
|
110
|
+
PATTERN
|
111
|
+
|
112
|
+
# @!method string_constructor?(node)
|
113
|
+
def_node_matcher :string_constructor?, <<~PATTERN
|
114
|
+
{
|
115
|
+
(send (const {cbase nil?} :String) :new ...)
|
116
|
+
#type_constructor?(:String)
|
117
|
+
}
|
118
|
+
PATTERN
|
119
|
+
|
120
|
+
# @!method integer_constructor?(node)
|
121
|
+
def_node_matcher :integer_constructor?, <<~PATTERN
|
122
|
+
#type_constructor?(:Integer)
|
123
|
+
PATTERN
|
124
|
+
|
125
|
+
# @!method float_constructor?(node)
|
126
|
+
def_node_matcher :float_constructor?, <<~PATTERN
|
127
|
+
#type_constructor?(:Float)
|
128
|
+
PATTERN
|
129
|
+
|
130
|
+
# @!method rational_constructor?(node)
|
131
|
+
def_node_matcher :rational_constructor?, <<~PATTERN
|
132
|
+
#type_constructor?(:Rational)
|
133
|
+
PATTERN
|
134
|
+
|
135
|
+
# @!method complex_constructor?(node)
|
136
|
+
def_node_matcher :complex_constructor?, <<~PATTERN
|
137
|
+
#type_constructor?(:Complex)
|
138
|
+
PATTERN
|
139
|
+
|
140
|
+
# @!method array_constructor?(node)
|
141
|
+
def_node_matcher :array_constructor?, <<~PATTERN
|
142
|
+
{
|
143
|
+
(send (const {cbase nil?} :Array) {:new :[]} ...)
|
144
|
+
#type_constructor?(:Array)
|
145
|
+
}
|
146
|
+
PATTERN
|
147
|
+
|
148
|
+
# @!method hash_constructor?(node)
|
149
|
+
def_node_matcher :hash_constructor?, <<~PATTERN
|
150
|
+
{
|
151
|
+
(block (send (const {cbase nil?} :Hash) :new) ...)
|
152
|
+
(send (const {cbase nil?} :Hash) {:new :[]} ...)
|
153
|
+
(send {nil? (const {cbase nil?} :Kernel)} :Hash ...)
|
154
|
+
}
|
155
|
+
PATTERN
|
156
|
+
|
157
|
+
# @!method set_constructor?(node)
|
158
|
+
def_node_matcher :set_constructor?, <<~PATTERN
|
159
|
+
{
|
160
|
+
(send (const {cbase nil?} :Set) {:new :[]} ...)
|
161
|
+
}
|
162
|
+
PATTERN
|
163
|
+
|
164
|
+
def on_send(node)
|
165
|
+
receiver = find_receiver(node)
|
166
|
+
return unless literal_receiver?(node, receiver) ||
|
167
|
+
constructor?(node, receiver) ||
|
168
|
+
chained_conversion?(node, receiver) ||
|
169
|
+
chained_to_typed_method?(node, receiver)
|
170
|
+
|
171
|
+
message = format(MSG, method: node.method_name)
|
172
|
+
|
173
|
+
add_offense(node.loc.selector, message: message) do |corrector|
|
174
|
+
corrector.remove(node.loc.dot.join(node.loc.selector))
|
175
|
+
end
|
176
|
+
end
|
177
|
+
alias on_csend on_send
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def find_receiver(node)
|
182
|
+
receiver = node.receiver
|
183
|
+
return unless receiver
|
184
|
+
|
185
|
+
while receiver.begin_type?
|
186
|
+
break unless receiver.children.one?
|
187
|
+
|
188
|
+
receiver = receiver.children.first
|
189
|
+
end
|
190
|
+
|
191
|
+
receiver
|
192
|
+
end
|
193
|
+
|
194
|
+
def literal_receiver?(node, receiver)
|
195
|
+
return false unless receiver
|
196
|
+
|
197
|
+
receiver.type?(*LITERAL_NODE_TYPES[node.method_name])
|
198
|
+
end
|
199
|
+
|
200
|
+
def constructor?(node, receiver)
|
201
|
+
matcher = CONSTRUCTOR_MAPPING[node.method_name]
|
202
|
+
return false unless matcher
|
203
|
+
|
204
|
+
public_send(matcher, receiver)
|
205
|
+
end
|
206
|
+
|
207
|
+
def chained_conversion?(node, receiver)
|
208
|
+
return false unless receiver&.call_type?
|
209
|
+
|
210
|
+
receiver.method?(node.method_name)
|
211
|
+
end
|
212
|
+
|
213
|
+
def chained_to_typed_method?(node, receiver)
|
214
|
+
return false unless receiver&.call_type?
|
215
|
+
|
216
|
+
TYPED_METHODS.fetch(node.method_name, []).include?(receiver.method_name)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
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,74 @@
|
|
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
|
+
left_siblings.compact.select(&:send_type?).any? { |node| node.command?(:private) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def private_constantize?(right_siblings, const_value)
|
63
|
+
private_constant_arguments = right_siblings.map { |node| private_constants(node) }
|
64
|
+
|
65
|
+
private_constant_values = private_constant_arguments.flatten.filter_map do |constant|
|
66
|
+
constant.value.to_sym if constant.respond_to?(:value)
|
67
|
+
end
|
68
|
+
|
69
|
+
private_constant_values.include?(const_value)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -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
|
@@ -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
|