rubocop 1.86.2 → 1.88.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/config/default.yml +82 -71
- data/config/obsoletion.yml +21 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -0
- data/lib/rubocop/cli.rb +2 -0
- data/lib/rubocop/config_loader.rb +17 -2
- data/lib/rubocop/config_loader_resolver.rb +11 -3
- data/lib/rubocop/config_store.rb +1 -1
- data/lib/rubocop/cop/base.rb +25 -4
- data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
- data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +14 -5
- data/lib/rubocop/cop/layout/end_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/indentation_width.rb +13 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
- data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +1 -3
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/constant_reassignment.rb +36 -4
- data/lib/rubocop/cop/lint/constant_resolution.rb +5 -5
- data/lib/rubocop/cop/lint/debugger.rb +0 -1
- data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
- data/lib/rubocop/cop/lint/empty_block.rb +3 -3
- data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -0
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +18 -3
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -11
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +5 -5
- data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +16 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +18 -9
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
- data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
- data/lib/rubocop/cop/lint/rand_one.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -3
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -7
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
- data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
- data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +6 -6
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -0
- data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
- data/lib/rubocop/cop/lint/script_permission.rb +5 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +24 -1
- data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +4 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +4 -2
- data/lib/rubocop/cop/lint/unreachable_code.rb +2 -2
- data/lib/rubocop/cop/lint/useless_assignment.rb +10 -5
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
- data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
- data/lib/rubocop/cop/lint/useless_times.rb +22 -1
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
- data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
- data/lib/rubocop/cop/mixin.rb +1 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/predicate_method.rb +2 -2
- data/lib/rubocop/cop/naming/predicate_prefix.rb +1 -1
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/registry.rb +28 -6
- data/lib/rubocop/cop/security/io_methods.rb +1 -1
- data/lib/rubocop/cop/style/alias.rb +11 -2
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/array_first_last.rb +12 -1
- data/lib/rubocop/cop/style/array_intersect.rb +4 -0
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +16 -2
- data/lib/rubocop/cop/style/case_equality.rb +14 -2
- data/lib/rubocop/cop/style/character_literal.rb +2 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +8 -0
- data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
- data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
- data/lib/rubocop/cop/style/colon_method_call.rb +13 -6
- data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
- data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +5 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +6 -1
- data/lib/rubocop/cop/style/constant_visibility.rb +4 -1
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- data/lib/rubocop/cop/style/dig_chain.rb +5 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
- data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
- data/lib/rubocop/cop/style/file_write.rb +21 -16
- data/lib/rubocop/cop/style/format_string.rb +4 -3
- data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
- data/lib/rubocop/cop/style/hash_slice.rb +16 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
- data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
- data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +105 -11
- data/lib/rubocop/cop/style/parallel_assignment.rb +8 -1
- data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
- data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
- data/lib/rubocop/cop/style/redundant_format.rb +1 -0
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
- data/lib/rubocop/cop/style/regexp_literal.rb +2 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
- data/lib/rubocop/cop/style/self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +16 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
- data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
- data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
- data/lib/rubocop/cop/style/while_until_do.rb +7 -0
- data/lib/rubocop/cop/style/word_array.rb +1 -0
- data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
- data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
- data/lib/rubocop/file_patterns.rb +9 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +14 -7
- data/lib/rubocop/options.rb +18 -0
- data/lib/rubocop/project_index_loader.rb +66 -0
- data/lib/rubocop/runner.rb +47 -3
- data/lib/rubocop/server/core.rb +6 -0
- data/lib/rubocop/version.rb +20 -2
- data/lib/rubocop.rb +1 -0
- metadata +5 -3
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Common helpers for cops that consult the project-wide static-analysis index
|
|
6
|
+
# via `Cop::Base#project_index`.
|
|
7
|
+
#
|
|
8
|
+
# Mixed-in cops gain the `external_dependency_checksum` override that invalidates
|
|
9
|
+
# the `ResultCache` whenever the indexed project files change on disk.
|
|
10
|
+
# To run index-backed analysis, cops should simply check whether `project_index` is non-nil;
|
|
11
|
+
# the runner only exposes a non-nil index when the user opted in via `AllCops/UseProjectIndex`
|
|
12
|
+
# and the underlying gem is available.
|
|
13
|
+
module ProjectIndexHelp
|
|
14
|
+
BUILTIN_DOCUMENT_URI = 'rubydex:built-in'
|
|
15
|
+
FILE_URI_PREFIX = 'file://'
|
|
16
|
+
# Matches the spurious leading slash before a Windows drive letter that
|
|
17
|
+
# remains after stripping `file://` from a `file:///C:/...` URI.
|
|
18
|
+
WINDOWS_DRIVE_PREFIX = %r{\A/(?=[A-Za-z]:[/\\])}.freeze
|
|
19
|
+
|
|
20
|
+
def external_dependency_checksum
|
|
21
|
+
return nil unless project_index
|
|
22
|
+
|
|
23
|
+
@external_dependency_checksum ||= Digest::SHA1.hexdigest(
|
|
24
|
+
project_index_signature.join("\n")
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def project_index_signature
|
|
31
|
+
project_index.documents.filter_map do |doc|
|
|
32
|
+
uri = doc.uri
|
|
33
|
+
next if uri == BUILTIN_DOCUMENT_URI
|
|
34
|
+
|
|
35
|
+
path = uri.delete_prefix(FILE_URI_PREFIX).sub(WINDOWS_DRIVE_PREFIX, '')
|
|
36
|
+
mtime, size = begin
|
|
37
|
+
stat = File.stat(path)
|
|
38
|
+
[stat.mtime.to_f, stat.size]
|
|
39
|
+
rescue StandardError
|
|
40
|
+
[0, 0]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
"#{path}:#{mtime}:#{size}"
|
|
44
|
+
end.sort
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/rubocop/cop/mixin.rb
CHANGED
|
@@ -28,6 +28,7 @@ module RuboCop
|
|
|
28
28
|
autoload :DocumentationComment, 'rubocop/cop/mixin/documentation_comment'
|
|
29
29
|
autoload :Duplication, 'rubocop/cop/mixin/duplication'
|
|
30
30
|
autoload :RangeHelp, 'rubocop/cop/mixin/range_help'
|
|
31
|
+
autoload :ProjectIndexHelp, 'rubocop/cop/mixin/project_index_help'
|
|
31
32
|
autoload :AnnotationComment, 'rubocop/cop/mixin/annotation_comment'
|
|
32
33
|
autoload :EmptyParameter, 'rubocop/cop/mixin/empty_parameter'
|
|
33
34
|
autoload :EndKeywordAlignment, 'rubocop/cop/mixin/end_keyword_alignment'
|
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
|
6
6
|
# Checks that predicate methods end with `?` and non-predicate methods do not.
|
|
7
7
|
#
|
|
8
8
|
# The names of predicate methods (methods that return a boolean value) should end
|
|
9
|
-
# in a question mark. Methods that don't return a boolean
|
|
9
|
+
# in a question mark. Methods that don't return a boolean shouldn't
|
|
10
10
|
# end in a question mark.
|
|
11
11
|
#
|
|
12
12
|
# The cop assesses a predicate method as one that returns boolean values. Likewise,
|
|
@@ -22,7 +22,7 @@ module RuboCop
|
|
|
22
22
|
# return values are detected.
|
|
23
23
|
#
|
|
24
24
|
# The cop also has `AllowedMethods` configuration in order to prevent the cop from
|
|
25
|
-
# registering an offense from a method name that does not
|
|
25
|
+
# registering an offense from a method name that does not conform to the naming
|
|
26
26
|
# guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
|
|
27
27
|
# configuration to allow method names by regular expression.
|
|
28
28
|
#
|
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
|
17
17
|
# they end with a `?`. These methods should be changed to remove the
|
|
18
18
|
# prefix.
|
|
19
19
|
#
|
|
20
|
-
# When `UseSorbetSigs` set to true (optional), the cop will only report
|
|
20
|
+
# When `UseSorbetSigs` is set to true (optional), the cop will only report
|
|
21
21
|
# offenses if the method has a Sorbet `sig` with a return type of
|
|
22
22
|
# `T::Boolean`. Dynamic methods are not supported with this configuration.
|
|
23
23
|
#
|
data/lib/rubocop/cop/registry.rb
CHANGED
|
@@ -51,6 +51,7 @@ module RuboCop
|
|
|
51
51
|
def initialize(cops = [], options = {})
|
|
52
52
|
@departments = Set.new
|
|
53
53
|
@cops_by_badge = {}
|
|
54
|
+
@lazy_loaded_cops_by_badge = {}
|
|
54
55
|
|
|
55
56
|
@enrollment_queue = cops
|
|
56
57
|
@options = options
|
|
@@ -60,6 +61,12 @@ module RuboCop
|
|
|
60
61
|
@warnings = {}
|
|
61
62
|
end
|
|
62
63
|
|
|
64
|
+
def lazy_load(cop_name, constant_name)
|
|
65
|
+
badge = Badge.parse(cop_name)
|
|
66
|
+
@departments << badge.department
|
|
67
|
+
@lazy_loaded_cops_by_badge[badge] = constant_name
|
|
68
|
+
end
|
|
69
|
+
|
|
63
70
|
def enlist(cop)
|
|
64
71
|
@enrollment_queue << cop
|
|
65
72
|
end
|
|
@@ -154,8 +161,8 @@ module RuboCop
|
|
|
154
161
|
def unqualified_cop_names
|
|
155
162
|
clear_enrollment_queue
|
|
156
163
|
@unqualified_cop_names ||=
|
|
157
|
-
|
|
158
|
-
'RedundantCopDisableDirective'
|
|
164
|
+
(@cops_by_badge.keys | @lazy_loaded_cops_by_badge.keys)
|
|
165
|
+
.to_set { |badge| File.basename(badge.to_s) } << 'RedundantCopDisableDirective'
|
|
159
166
|
end
|
|
160
167
|
|
|
161
168
|
def qualify_badge(badge)
|
|
@@ -168,17 +175,19 @@ module RuboCop
|
|
|
168
175
|
# @return [Hash{String => Array<Class>}]
|
|
169
176
|
def to_h
|
|
170
177
|
clear_enrollment_queue
|
|
178
|
+
load_all_lazy_cops
|
|
171
179
|
@cops_by_badge.to_h { |_badge, cop| [cop.cop_name, [cop]] }
|
|
172
180
|
end
|
|
173
181
|
|
|
174
182
|
def cops
|
|
175
183
|
clear_enrollment_queue
|
|
184
|
+
load_all_lazy_cops
|
|
176
185
|
@cops_by_badge.values
|
|
177
186
|
end
|
|
178
187
|
|
|
179
188
|
def length
|
|
180
189
|
clear_enrollment_queue
|
|
181
|
-
@cops_by_badge.size
|
|
190
|
+
@cops_by_badge.size + @lazy_loaded_cops_by_badge.size
|
|
182
191
|
end
|
|
183
192
|
|
|
184
193
|
def enabled(config)
|
|
@@ -214,7 +223,7 @@ module RuboCop
|
|
|
214
223
|
|
|
215
224
|
def names
|
|
216
225
|
clear_enrollment_queue
|
|
217
|
-
@cops_by_badge.keys.map(&:to_s)
|
|
226
|
+
@cops_by_badge.keys.map(&:to_s) | @lazy_loaded_cops_by_badge.keys.map(&:to_s)
|
|
218
227
|
end
|
|
219
228
|
|
|
220
229
|
def cops_for_department(department)
|
|
@@ -231,6 +240,7 @@ module RuboCop
|
|
|
231
240
|
|
|
232
241
|
def sort!
|
|
233
242
|
clear_enrollment_queue
|
|
243
|
+
load_all_lazy_cops
|
|
234
244
|
@cops_by_badge = @cops_by_badge.sort_by { |badge, _cop| badge.cop_name }.to_h
|
|
235
245
|
|
|
236
246
|
self
|
|
@@ -249,7 +259,7 @@ module RuboCop
|
|
|
249
259
|
def find_by_cop_name(cop_name)
|
|
250
260
|
clear_enrollment_queue
|
|
251
261
|
badge = Badge.parse(cop_name)
|
|
252
|
-
@cops_by_badge[badge]
|
|
262
|
+
@cops_by_badge[badge] || load_lazy_cop(badge)
|
|
253
263
|
end
|
|
254
264
|
|
|
255
265
|
# When a cop name is given returns a single-element array with the cop class.
|
|
@@ -262,6 +272,7 @@ module RuboCop
|
|
|
262
272
|
|
|
263
273
|
def freeze
|
|
264
274
|
clear_enrollment_queue
|
|
275
|
+
load_all_lazy_cops
|
|
265
276
|
unqualified_cop_names # build cache
|
|
266
277
|
super
|
|
267
278
|
end
|
|
@@ -292,6 +303,17 @@ module RuboCop
|
|
|
292
303
|
@enrollment_queue = []
|
|
293
304
|
end
|
|
294
305
|
|
|
306
|
+
def load_all_lazy_cops
|
|
307
|
+
@lazy_loaded_cops_by_badge.each_key { |badge| load_lazy_cop(badge) }
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def load_lazy_cop(badge)
|
|
311
|
+
constant_name = @lazy_loaded_cops_by_badge.delete(badge)
|
|
312
|
+
return unless constant_name
|
|
313
|
+
|
|
314
|
+
@cops_by_badge[badge] = Kernel.const_get(constant_name)
|
|
315
|
+
end
|
|
316
|
+
|
|
295
317
|
def with(cops)
|
|
296
318
|
self.class.new(cops)
|
|
297
319
|
end
|
|
@@ -313,7 +335,7 @@ module RuboCop
|
|
|
313
335
|
|
|
314
336
|
def registered?(badge)
|
|
315
337
|
clear_enrollment_queue
|
|
316
|
-
@cops_by_badge.key?(badge)
|
|
338
|
+
@cops_by_badge.key?(badge) || @lazy_loaded_cops_by_badge.key?(badge)
|
|
317
339
|
end
|
|
318
340
|
end
|
|
319
341
|
end
|
|
@@ -10,7 +10,7 @@ module RuboCop
|
|
|
10
10
|
# a subprocess is created in the same way as `Kernel#open`, and its output is returned.
|
|
11
11
|
# `Kernel#open` may allow unintentional command injection, which is the reason these
|
|
12
12
|
# `IO` methods are a security risk.
|
|
13
|
-
# Consider
|
|
13
|
+
# Consider using `File.read` to disable the behavior of subprocess invocation.
|
|
14
14
|
#
|
|
15
15
|
# @safety
|
|
16
16
|
# This cop is unsafe because false positive will occur if the variable passed as
|
|
@@ -10,7 +10,7 @@ module RuboCop
|
|
|
10
10
|
# is resolved at runtime).
|
|
11
11
|
# It also flags uses of `alias :symbol` rather than `alias bareword`.
|
|
12
12
|
#
|
|
13
|
-
# However, it will always enforce `
|
|
13
|
+
# However, it will always enforce `alias_method` when `alias` is used
|
|
14
14
|
# in an instance method definition and in a singleton method definition.
|
|
15
15
|
# If used in a block, always enforce `alias_method`
|
|
16
16
|
# unless it is an `instance_eval` block.
|
|
@@ -45,6 +45,7 @@ module RuboCop
|
|
|
45
45
|
return unless node.command?(:alias_method)
|
|
46
46
|
return unless style == :prefer_alias && alias_keyword_possible?(node)
|
|
47
47
|
return unless node.arguments.count == 2
|
|
48
|
+
return if alias_method_value_used?(node)
|
|
48
49
|
|
|
49
50
|
msg = format(MSG_ALIAS_METHOD, current: lexical_scope_type(node))
|
|
50
51
|
add_offense(node.loc.selector, message: msg) do |corrector|
|
|
@@ -80,6 +81,14 @@ module RuboCop
|
|
|
80
81
|
scope_type(node) != :dynamic && node.arguments.all?(&:sym_type?)
|
|
81
82
|
end
|
|
82
83
|
|
|
84
|
+
# `alias_method` is a method call whose return value can be used
|
|
85
|
+
# (e.g., as an argument to `public`/`module_function`, or as an assignment),
|
|
86
|
+
# but `alias` is a keyword statement that cannot appear in such positions.
|
|
87
|
+
# Detect these positions so the conversion does not produce a syntax error.
|
|
88
|
+
def alias_method_value_used?(node)
|
|
89
|
+
node.argument? || node.parent&.assignment?
|
|
90
|
+
end
|
|
91
|
+
|
|
83
92
|
def alias_method_possible?(node)
|
|
84
93
|
scope_type(node) != :instance_eval &&
|
|
85
94
|
node.children.none?(&:gvar_type?) &&
|
|
@@ -104,7 +113,7 @@ module RuboCop
|
|
|
104
113
|
return :lexical
|
|
105
114
|
when :def, :defs
|
|
106
115
|
return :dynamic
|
|
107
|
-
when :block
|
|
116
|
+
when :block, :numblock, :itblock
|
|
108
117
|
return :instance_eval if parent.method?(:instance_eval)
|
|
109
118
|
|
|
110
119
|
return :dynamic
|
|
@@ -71,7 +71,7 @@ module RuboCop
|
|
|
71
71
|
node.each_child_node do |expr|
|
|
72
72
|
if expr.send_type?
|
|
73
73
|
correct_send(expr, corrector)
|
|
74
|
-
elsif expr.
|
|
74
|
+
elsif expr.type?(:return, :next, :break, :yield) || expr.assignment?
|
|
75
75
|
correct_other(expr, corrector)
|
|
76
76
|
end
|
|
77
77
|
end
|
|
@@ -40,8 +40,9 @@ module RuboCop
|
|
|
40
40
|
|
|
41
41
|
node = innermost_braces_node(node)
|
|
42
42
|
return if node.parent && brace_method?(node.parent)
|
|
43
|
+
return if compound_assignment_target?(node)
|
|
43
44
|
|
|
44
|
-
preferred = (value
|
|
45
|
+
preferred = preferred_name(value)
|
|
45
46
|
offense_range = find_offense_range(node)
|
|
46
47
|
|
|
47
48
|
add_offense(offense_range, message: format(MSG, preferred: preferred)) do |corrector|
|
|
@@ -53,6 +54,10 @@ module RuboCop
|
|
|
53
54
|
|
|
54
55
|
private
|
|
55
56
|
|
|
57
|
+
def preferred_name(value)
|
|
58
|
+
value.zero? ? 'first' : 'last'
|
|
59
|
+
end
|
|
60
|
+
|
|
56
61
|
def preferred_value(node, value)
|
|
57
62
|
value = ".#{value}" unless node.loc.dot
|
|
58
63
|
value
|
|
@@ -74,6 +79,12 @@ module RuboCop
|
|
|
74
79
|
def brace_method?(node)
|
|
75
80
|
node.send_type? && (node.method?(:[]) || node.method?(:[]=))
|
|
76
81
|
end
|
|
82
|
+
|
|
83
|
+
# `arr[0] += 1` etc. would autocorrect to `arr.first += 1`, which calls the
|
|
84
|
+
# nonexistent `first=`/`last=` setter and raises `NoMethodError`.
|
|
85
|
+
def compound_assignment_target?(node)
|
|
86
|
+
node.parent&.type?(:op_asgn, :or_asgn, :and_asgn) && node.parent.children.first == node
|
|
87
|
+
end
|
|
77
88
|
end
|
|
78
89
|
end
|
|
79
90
|
end
|
|
@@ -145,6 +145,10 @@ module RuboCop
|
|
|
145
145
|
|
|
146
146
|
dot = dot_node.loc.dot.source
|
|
147
147
|
bang = straight?(method_name) ? '' : '!'
|
|
148
|
+
# `a&.intersection(b)&.none?` returns `nil` when `a` is `nil`, but the negated
|
|
149
|
+
# rewrite `!a&.intersect?(b)` returns `true` there, flipping the result.
|
|
150
|
+
return if bang == '!' && dot == '&.'
|
|
151
|
+
|
|
148
152
|
replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
|
|
149
153
|
|
|
150
154
|
register_offense(node, replacement)
|
|
@@ -29,6 +29,9 @@ module RuboCop
|
|
|
29
29
|
def on_send(node)
|
|
30
30
|
array, element = single_element(node)
|
|
31
31
|
return unless array
|
|
32
|
+
# `[*foo]` is not a single element: the splat can expand to any number of
|
|
33
|
+
# elements, so `intersect?([*foo])` is not equivalent to `include?(*foo)`.
|
|
34
|
+
return if element.splat_type?
|
|
32
35
|
|
|
33
36
|
add_offense(
|
|
34
37
|
node.source_range.with(begin_pos: node.loc.selector.begin_pos)
|
|
@@ -389,9 +389,23 @@ module RuboCop
|
|
|
389
389
|
|
|
390
390
|
def require_do_end?(node)
|
|
391
391
|
return false if node.braces? || node.multiline?
|
|
392
|
-
return false unless (resbody = node.each_descendant(:resbody).first)
|
|
393
392
|
|
|
394
|
-
|
|
393
|
+
body = node.body
|
|
394
|
+
return false unless body
|
|
395
|
+
# `ensure` and a block-level `rescue` are illegal inside `{ }`; only a
|
|
396
|
+
# bare modifier rescue (`expr rescue expr`) can be written with braces.
|
|
397
|
+
return true if body.ensure_type?
|
|
398
|
+
return false unless body.rescue_type?
|
|
399
|
+
|
|
400
|
+
!modifier_rescue?(body)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def modifier_rescue?(rescue_node)
|
|
404
|
+
return false if rescue_node.body.nil? || rescue_node.else_branch
|
|
405
|
+
return false unless rescue_node.resbody_branches.one?
|
|
406
|
+
|
|
407
|
+
resbody = rescue_node.resbody_branches.first
|
|
408
|
+
resbody.exceptions.empty? && resbody.exception_variable.nil?
|
|
395
409
|
end
|
|
396
410
|
|
|
397
411
|
def braces_required_method?(method_name)
|
|
@@ -96,13 +96,25 @@ module RuboCop
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def const_replacement(lhs, rhs)
|
|
99
|
-
"#{rhs
|
|
99
|
+
"#{parenthesize_if_needed(rhs)}.is_a?(#{lhs.source})"
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def send_replacement(lhs, rhs)
|
|
103
103
|
return unless self_class?(lhs)
|
|
104
104
|
|
|
105
|
-
"#{rhs
|
|
105
|
+
"#{parenthesize_if_needed(rhs)}.is_a?(#{lhs.source})"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# `Array === a + b` must become `(a + b).is_a?(Array)`, not
|
|
109
|
+
# `a + b.is_a?(Array)` (which parses as `a + (b.is_a?(Array))`).
|
|
110
|
+
def parenthesize_if_needed(node)
|
|
111
|
+
requires_parentheses?(node) ? "(#{node.source})" : node.source
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def requires_parentheses?(node)
|
|
115
|
+
return true if node.type?(:and, :or, :if, :range) || node.assignment?
|
|
116
|
+
|
|
117
|
+
node.send_type? && (node.operator_method? || node.unary_operation?)
|
|
106
118
|
end
|
|
107
119
|
end
|
|
108
120
|
end
|
|
@@ -8,8 +8,8 @@ module RuboCop
|
|
|
8
8
|
# essentially one-character strings, so this syntax
|
|
9
9
|
# is mostly redundant at this point.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
# That's a good use case of
|
|
11
|
+
# A `?` character literal can be used to express meta and control characters.
|
|
12
|
+
# That's a good use case of a `?` literal so it doesn't count as an offense.
|
|
13
13
|
#
|
|
14
14
|
# @example
|
|
15
15
|
# # bad
|
|
@@ -181,6 +181,7 @@ module RuboCop
|
|
|
181
181
|
|
|
182
182
|
def check_style(node, body, style)
|
|
183
183
|
return if node.identifier.namespace&.cbase_type?
|
|
184
|
+
return unless const_namespace?(node.identifier.namespace)
|
|
184
185
|
|
|
185
186
|
if style == :nested
|
|
186
187
|
check_nested_style(node)
|
|
@@ -189,6 +190,13 @@ module RuboCop
|
|
|
189
190
|
end
|
|
190
191
|
end
|
|
191
192
|
|
|
193
|
+
def const_namespace?(node)
|
|
194
|
+
return true if node.nil? || node.cbase_type?
|
|
195
|
+
return false unless node.const_type?
|
|
196
|
+
|
|
197
|
+
const_namespace?(node.namespace)
|
|
198
|
+
end
|
|
199
|
+
|
|
192
200
|
def check_nested_style(node)
|
|
193
201
|
return unless compact_node_name?(node)
|
|
194
202
|
return if node.parent&.type?(:class, :module)
|
|
@@ -90,25 +90,33 @@ module RuboCop
|
|
|
90
90
|
private
|
|
91
91
|
|
|
92
92
|
def class_name(class_node, node)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
unless class_name_method?(node.children.first.method_name)
|
|
94
|
+
# `var.class == 'Foo'` compares a `Class` to a `String` (always false) and
|
|
95
|
+
# has no valid `instance_of?` rewrite, so don't suggest one.
|
|
96
|
+
return if class_node.str_type?
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# When a variable or return value of a method is used, it returns nil
|
|
104
|
-
# because the type is not known and cannot be suggested.
|
|
105
|
-
return
|
|
106
|
-
end
|
|
98
|
+
return class_node.source
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if (receiver = class_node.receiver) && class_name_method?(class_node.method_name)
|
|
102
|
+
return receiver.source
|
|
107
103
|
end
|
|
108
104
|
|
|
105
|
+
return string_class_name(class_node) if class_node.str_type?
|
|
106
|
+
# When a variable or return value of a method is used, the type is not known
|
|
107
|
+
# and cannot be suggested.
|
|
108
|
+
return if unable_to_determine_type?(class_node)
|
|
109
|
+
|
|
109
110
|
class_node.source
|
|
110
111
|
end
|
|
111
112
|
|
|
113
|
+
def string_class_name(class_node)
|
|
114
|
+
value = trim_string_quotes(class_node)
|
|
115
|
+
# Avoid `::::Foo` when the name is already fully qualified.
|
|
116
|
+
value.prepend('::') if require_cbase?(class_node) && !value.start_with?('::')
|
|
117
|
+
value
|
|
118
|
+
end
|
|
119
|
+
|
|
112
120
|
def class_name_method?(method_name)
|
|
113
121
|
CLASS_NAME_METHODS.include?(method_name)
|
|
114
122
|
end
|
|
@@ -140,15 +140,21 @@ module RuboCop
|
|
|
140
140
|
|
|
141
141
|
def extract_def_from_sclass(def_node, sclass_node)
|
|
142
142
|
range = source_range_with_comment(def_node)
|
|
143
|
-
source = range
|
|
144
|
-
"def #{def_node.method_name}",
|
|
145
|
-
"def self.#{def_node.method_name}"
|
|
146
|
-
)
|
|
147
|
-
|
|
143
|
+
source = prefix_def_with_self(range, def_node)
|
|
148
144
|
source = source.gsub(/^ {#{indentation_diff(def_node, sclass_node)}}/, '')
|
|
149
145
|
[range, source.chomp]
|
|
150
146
|
end
|
|
151
147
|
|
|
148
|
+
# Splice in `self.` at the actual `def` keyword rather than substituting the
|
|
149
|
+
# first textual `def <name>`, which may appear inside a preceding comment.
|
|
150
|
+
def prefix_def_with_self(range, def_node)
|
|
151
|
+
keyword_offset = def_node.loc.keyword.begin_pos - range.begin_pos
|
|
152
|
+
name_end_offset = def_node.loc.name.end_pos - range.begin_pos
|
|
153
|
+
source = range.source.dup
|
|
154
|
+
source[keyword_offset...name_end_offset] = "def self.#{def_node.method_name}"
|
|
155
|
+
source
|
|
156
|
+
end
|
|
157
|
+
|
|
152
158
|
def indentation_diff(node1, node2)
|
|
153
159
|
node1.loc.column - node2.loc.column
|
|
154
160
|
end
|
|
@@ -24,10 +24,9 @@ module RuboCop
|
|
|
24
24
|
|
|
25
25
|
MSG = 'Do not use `::` for method calls.'
|
|
26
26
|
|
|
27
|
-
# @!method
|
|
28
|
-
def_node_matcher :
|
|
29
|
-
(
|
|
30
|
-
(const nil? :Java) _)
|
|
27
|
+
# @!method java_root?(node)
|
|
28
|
+
def_node_matcher :java_root?, <<~PATTERN
|
|
29
|
+
(const nil? :Java)
|
|
31
30
|
PATTERN
|
|
32
31
|
|
|
33
32
|
def self.autocorrect_incompatible_with
|
|
@@ -37,11 +36,19 @@ module RuboCop
|
|
|
37
36
|
def on_send(node)
|
|
38
37
|
return unless node.receiver && node.double_colon?
|
|
39
38
|
return if node.camel_case_method?
|
|
40
|
-
# ignore Java interop code like Java::int
|
|
41
|
-
return if
|
|
39
|
+
# ignore Java interop code like `Java::int` or `Java::com::method`
|
|
40
|
+
return if java_interop?(node)
|
|
42
41
|
|
|
43
42
|
add_offense(node.loc.dot) { |corrector| corrector.replace(node.loc.dot, '.') }
|
|
44
43
|
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def java_interop?(node)
|
|
48
|
+
receiver = node.receiver
|
|
49
|
+
receiver = receiver.receiver while receiver.respond_to?(:receiver) && receiver.receiver
|
|
50
|
+
java_root?(receiver)
|
|
51
|
+
end
|
|
45
52
|
end
|
|
46
53
|
end
|
|
47
54
|
end
|
|
@@ -85,8 +85,13 @@ module RuboCop
|
|
|
85
85
|
def on_for(node)
|
|
86
86
|
return unless node.parent&.begin_type?
|
|
87
87
|
return unless same_collection_looping_for?(node, node.left_sibling)
|
|
88
|
+
return unless node.body && node.left_sibling.body
|
|
88
89
|
|
|
89
90
|
add_offense(node) do |corrector|
|
|
91
|
+
# Combining loops with different iteration variables would leave the second
|
|
92
|
+
# body referencing an undefined variable, so only autocorrect when they match.
|
|
93
|
+
next unless node.variable == node.left_sibling.variable
|
|
94
|
+
|
|
90
95
|
combine_with_left_sibling(corrector, node)
|
|
91
96
|
end
|
|
92
97
|
end
|
|
@@ -90,7 +90,7 @@ module RuboCop
|
|
|
90
90
|
max = if_body.source
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
prefer = "#{
|
|
93
|
+
prefer = "#{parenthesize_if_needed(else_body)}.clamp(#{min}, #{max})"
|
|
94
94
|
|
|
95
95
|
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
|
96
96
|
autocorrect(corrector, node, prefer)
|
|
@@ -119,6 +119,17 @@ module RuboCop
|
|
|
119
119
|
|
|
120
120
|
(lhs.source == else_body && op == :<) || (rhs.source == else_body && op == :>)
|
|
121
121
|
end
|
|
122
|
+
|
|
123
|
+
# `a + b` must become `(a + b).clamp(low, high)`, not `a + b.clamp(low, high)`
|
|
124
|
+
# (which parses as `a + (b.clamp(low, high))`).
|
|
125
|
+
def parenthesize_if_needed(node)
|
|
126
|
+
if node.type?(:and, :or, :if, :range) || node.assignment? ||
|
|
127
|
+
(node.send_type? && (node.operator_method? || node.unary_operation?))
|
|
128
|
+
"(#{node.source})"
|
|
129
|
+
else
|
|
130
|
+
node.source
|
|
131
|
+
end
|
|
132
|
+
end
|
|
122
133
|
end
|
|
123
134
|
end
|
|
124
135
|
end
|
|
@@ -55,6 +55,10 @@ module RuboCop
|
|
|
55
55
|
next unless prefer
|
|
56
56
|
|
|
57
57
|
corrector.replace(offense, prefer)
|
|
58
|
+
elsif node.arguments.any? { |argument| argument.children.empty? }
|
|
59
|
+
# In-place bracket removal would leave dangling commas (e.g.
|
|
60
|
+
# `concat([], [b])` -> `push(, b)`), so rebuild the call instead.
|
|
61
|
+
corrector.replace(offense, preferred_method(node))
|
|
58
62
|
else
|
|
59
63
|
corrector.replace(node.loc.selector, 'push')
|
|
60
64
|
node.arguments.each do |argument|
|
|
@@ -75,7 +79,7 @@ module RuboCop
|
|
|
75
79
|
|
|
76
80
|
def preferred_method(node)
|
|
77
81
|
new_arguments =
|
|
78
|
-
node.arguments.
|
|
82
|
+
node.arguments.flat_map do |arg|
|
|
79
83
|
if arg.percent_literal?
|
|
80
84
|
arg.children.map { |child| child.value.inspect }
|
|
81
85
|
else
|
|
@@ -283,7 +283,10 @@ module RuboCop
|
|
|
283
283
|
|
|
284
284
|
_condition, *branches, else_branch = *assignment
|
|
285
285
|
|
|
286
|
-
|
|
286
|
+
# Use the node accessor rather than the raw destructured branch: for
|
|
287
|
+
# `x = unless cond; body; end` (no `else`) the parser puts `body` in the
|
|
288
|
+
# else slot, but `else_branch` correctly reports there is no `else` clause.
|
|
289
|
+
return unless assignment.else_branch
|
|
287
290
|
return if allowed_single_line?([*branches, else_branch])
|
|
288
291
|
|
|
289
292
|
add_offense(node, message: ASSIGN_TO_CONDITION_MSG) do |corrector|
|
|
@@ -657,6 +660,8 @@ module RuboCop
|
|
|
657
660
|
remove_whitespace_in_branches(corrector, branch, condition, column)
|
|
658
661
|
|
|
659
662
|
parent_keyword = branch.parent.loc.keyword
|
|
663
|
+
return if same_line?(parent_keyword, condition)
|
|
664
|
+
|
|
660
665
|
corrector.remove_preceding(parent_keyword, parent_keyword.column - column)
|
|
661
666
|
end
|
|
662
667
|
end
|
|
@@ -89,7 +89,10 @@ module RuboCop
|
|
|
89
89
|
|
|
90
90
|
arguments = arguments.first.children.first.to_a if arguments.first&.splat_type?
|
|
91
91
|
constant_values = arguments.map do |argument|
|
|
92
|
-
|
|
92
|
+
# `respond_to?(:value)` is too broad: `int`/`float` nodes respond to it
|
|
93
|
+
# but their value is a `Numeric`, which has no `to_sym` (e.g.
|
|
94
|
+
# `private_constant 42`). Only symbol/string arguments are real names.
|
|
95
|
+
argument.value.to_sym if argument.type?(:sym, :str)
|
|
93
96
|
end
|
|
94
97
|
|
|
95
98
|
constant_values.include?(node.name)
|
|
@@ -59,12 +59,12 @@ module RuboCop
|
|
|
59
59
|
|
|
60
60
|
# @!method historic_date?(node)
|
|
61
61
|
def_node_matcher :historic_date?, <<~PATTERN
|
|
62
|
-
(
|
|
62
|
+
(call _ _ _ (const (const {nil? (cbase)} :Date) _))
|
|
63
63
|
PATTERN
|
|
64
64
|
|
|
65
65
|
# @!method to_datetime?(node)
|
|
66
66
|
def_node_matcher :to_datetime?, <<~PATTERN
|
|
67
|
-
(call
|
|
67
|
+
(call !nil? :to_datetime)
|
|
68
68
|
PATTERN
|
|
69
69
|
|
|
70
70
|
def on_send(node)
|