rubocop 1.81.1 → 1.82.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 +1 -1
- data/config/default.yml +25 -7
- data/config/obsoletion.yml +4 -0
- data/lib/rubocop/cli.rb +2 -1
- data/lib/rubocop/comment_config.rb +62 -17
- data/lib/rubocop/config_loader.rb +2 -1
- data/lib/rubocop/config_loader_resolver.rb +7 -6
- data/lib/rubocop/cop/autocorrect_logic.rb +4 -0
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -3
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
- data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
- data/lib/rubocop/cop/layout/end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -4
- data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +12 -1
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +8 -4
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +5 -3
- data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
- data/lib/rubocop/cop/lint/debugger.rb +0 -2
- data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
- data/lib/rubocop/cop/lint/else_layout.rb +19 -0
- data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
- data/lib/rubocop/cop/lint/literal_as_condition.rb +4 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +16 -6
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -1
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +10 -2
- data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
- data/lib/rubocop/cop/lint/useless_assignment.rb +44 -16
- data/lib/rubocop/cop/lint/useless_or.rb +15 -2
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -4
- data/lib/rubocop/cop/mixin/code_length.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
- data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
- data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
- data/lib/rubocop/cop/mixin/trailing_comma.rb +7 -4
- data/lib/rubocop/cop/naming/method_name.rb +4 -2
- data/lib/rubocop/cop/naming/predicate_method.rb +4 -4
- data/lib/rubocop/cop/security/json_load.rb +33 -11
- data/lib/rubocop/cop/style/array_intersect.rb +2 -2
- data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
- data/lib/rubocop/cop/style/case_equality.rb +11 -13
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +8 -14
- data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
- data/lib/rubocop/cop/style/empty_method.rb +0 -6
- data/lib/rubocop/cop/style/endless_method.rb +15 -2
- data/lib/rubocop/cop/style/float_division.rb +15 -1
- data/lib/rubocop/cop/style/guard_clause.rb +0 -11
- data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +12 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
- data/lib/rubocop/cop/style/module_member_existence_check.rb +74 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
- data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
- data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
- data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
- data/lib/rubocop/cop/style/redundant_format.rb +10 -4
- data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
- data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +5 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
- data/lib/rubocop/cop/style/semicolon.rb +23 -7
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +8 -1
- data/lib/rubocop/cop/style/super_arguments.rb +2 -2
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
- data/lib/rubocop/cop/util.rb +2 -3
- data/lib/rubocop/cops_documentation_generator.rb +4 -4
- data/lib/rubocop/directive_comment.rb +46 -3
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
- data/lib/rubocop/lsp/diagnostic.rb +12 -17
- data/lib/rubocop/lsp/routes.rb +9 -36
- data/lib/rubocop/lsp/runtime.rb +2 -2
- data/lib/rubocop/lsp/server.rb +2 -2
- data/lib/rubocop/magic_comment.rb +20 -0
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +7 -8
- data/lib/rubocop/result_cache.rb +38 -27
- data/lib/rubocop/rspec/shared_contexts.rb +2 -2
- data/lib/rubocop/rspec/support.rb +1 -1
- data/lib/rubocop/runner.rb +4 -0
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -0
- metadata +8 -10
|
@@ -144,7 +144,7 @@ module RuboCop
|
|
|
144
144
|
MSG_REQUIRE_ALWAYS = 'Use endless method definitions.'
|
|
145
145
|
|
|
146
146
|
def on_def(node)
|
|
147
|
-
return if node.assignment_method?
|
|
147
|
+
return if node.assignment_method? || use_heredoc?(node)
|
|
148
148
|
|
|
149
149
|
case style
|
|
150
150
|
when :allow_single_line, :allow_always
|
|
@@ -198,6 +198,13 @@ module RuboCop
|
|
|
198
198
|
add_offense(node) { |corrector| correct_to_multiline(corrector, node) }
|
|
199
199
|
end
|
|
200
200
|
|
|
201
|
+
def use_heredoc?(node)
|
|
202
|
+
return false unless (body = node.body)
|
|
203
|
+
return true if body.any_str_type? && body.heredoc?
|
|
204
|
+
|
|
205
|
+
body.each_descendant(:str).any?(&:heredoc?)
|
|
206
|
+
end
|
|
207
|
+
|
|
201
208
|
def correct_to_multiline(corrector, node)
|
|
202
209
|
replacement = <<~RUBY.strip
|
|
203
210
|
def #{node.method_name}#{arguments(node)}
|
|
@@ -225,7 +232,13 @@ module RuboCop
|
|
|
225
232
|
def too_long_when_made_endless?(node)
|
|
226
233
|
return false unless config.cop_enabled?('Layout/LineLength')
|
|
227
234
|
|
|
228
|
-
|
|
235
|
+
offset = modifier_offset(node)
|
|
236
|
+
|
|
237
|
+
endless_replacement(node).length + offset > max_line_length
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def modifier_offset(node)
|
|
241
|
+
same_line?(node.parent, node) ? node.loc.column - node.parent.loc.column : 0
|
|
229
242
|
end
|
|
230
243
|
end
|
|
231
244
|
end
|
|
@@ -7,9 +7,12 @@ module RuboCop
|
|
|
7
7
|
# It is recommended to either always use `fdiv` or coerce one side only.
|
|
8
8
|
# This cop also provides other options for code consistency.
|
|
9
9
|
#
|
|
10
|
+
# For `Regexp.last_match` and nth reference (e.g., `$1`), it assumes that the value
|
|
11
|
+
# is a string matched by a regular expression, and allows conversion with `#to_f`.
|
|
12
|
+
#
|
|
10
13
|
# @safety
|
|
11
14
|
# This cop is unsafe, because if the operand variable is a string object
|
|
12
|
-
# then
|
|
15
|
+
# then `#to_f` will be removed and an error will occur.
|
|
13
16
|
#
|
|
14
17
|
# [source,ruby]
|
|
15
18
|
# ----
|
|
@@ -84,6 +87,14 @@ module RuboCop
|
|
|
84
87
|
(send !nil? :to_f)
|
|
85
88
|
PATTERN
|
|
86
89
|
|
|
90
|
+
# @!method regexp_last_match?(node)
|
|
91
|
+
def_node_matcher :regexp_last_match?, <<~PATTERN
|
|
92
|
+
{
|
|
93
|
+
(send (const {nil? cbase} :Regexp) :last_match int)
|
|
94
|
+
(:nth_ref _)
|
|
95
|
+
}
|
|
96
|
+
PATTERN
|
|
97
|
+
|
|
87
98
|
def on_send(node)
|
|
88
99
|
return unless offense_condition?(node)
|
|
89
100
|
|
|
@@ -104,6 +115,9 @@ module RuboCop
|
|
|
104
115
|
private
|
|
105
116
|
|
|
106
117
|
def offense_condition?(node)
|
|
118
|
+
return false if regexp_last_match?(node.receiver.receiver) ||
|
|
119
|
+
regexp_last_match?(node.first_argument.receiver)
|
|
120
|
+
|
|
107
121
|
case style
|
|
108
122
|
when :left_coerce
|
|
109
123
|
right_coerce?(node)
|
|
@@ -45,17 +45,6 @@ module RuboCop
|
|
|
45
45
|
# ok
|
|
46
46
|
#
|
|
47
47
|
# # bad
|
|
48
|
-
# if something
|
|
49
|
-
# foo || raise('exception')
|
|
50
|
-
# else
|
|
51
|
-
# ok
|
|
52
|
-
# end
|
|
53
|
-
#
|
|
54
|
-
# # good
|
|
55
|
-
# foo || raise('exception') if something
|
|
56
|
-
# ok
|
|
57
|
-
#
|
|
58
|
-
# # bad
|
|
59
48
|
# define_method(:test) do
|
|
60
49
|
# if something
|
|
61
50
|
# work
|
|
@@ -207,14 +207,14 @@ module RuboCop
|
|
|
207
207
|
def too_long_line_based_on_config?(range, line)
|
|
208
208
|
return false if matches_allowed_pattern?(line)
|
|
209
209
|
|
|
210
|
-
too_long =
|
|
210
|
+
too_long = too_long_line_based_on_allow_cop_directives?(range, line)
|
|
211
211
|
return too_long unless too_long == :undetermined
|
|
212
212
|
|
|
213
213
|
too_long_line_based_on_allow_uri?(line)
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
-
def
|
|
217
|
-
if
|
|
216
|
+
def too_long_line_based_on_allow_cop_directives?(range, line)
|
|
217
|
+
if allow_cop_directives? && directive_on_source_line?(range.line - 1)
|
|
218
218
|
return line_length_without_directive(line) > max_line_length
|
|
219
219
|
end
|
|
220
220
|
|
|
@@ -36,10 +36,21 @@ module RuboCop
|
|
|
36
36
|
cop_config.fetch('IncludedMacros', []).map(&:to_sym)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def included_macro_patterns
|
|
40
|
+
cop_config.fetch('IncludedMacroPatterns', [])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def matches_included_macro_pattern?(method_name)
|
|
44
|
+
included_macro_patterns.any? do |pattern|
|
|
45
|
+
Regexp.new(pattern).match?(method_name.to_s)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
39
49
|
def ignored_macro?(node)
|
|
40
50
|
cop_config['IgnoreMacros'] &&
|
|
41
51
|
node.macro? &&
|
|
42
|
-
!included_macros_list.include?(node.method_name)
|
|
52
|
+
!included_macros_list.include?(node.method_name) &&
|
|
53
|
+
!matches_included_macro_pattern?(node.method_name)
|
|
43
54
|
end
|
|
44
55
|
end
|
|
45
56
|
end
|
|
@@ -9,17 +9,20 @@ module RuboCop
|
|
|
9
9
|
# In the default style (require_parentheses), macro methods are allowed.
|
|
10
10
|
# Additional methods can be added to the `AllowedMethods` or
|
|
11
11
|
# `AllowedPatterns` list. These options are valid only in the default
|
|
12
|
-
# style. Macros can be included by either setting `IgnoreMacros` to false
|
|
13
|
-
#
|
|
12
|
+
# style. Macros can be included by either setting `IgnoreMacros` to false,
|
|
13
|
+
# adding specific macros to the `IncludedMacros` list, or using
|
|
14
|
+
# `IncludedMacroPatterns` for pattern-based matching.
|
|
14
15
|
#
|
|
15
16
|
# Precedence of options is as follows:
|
|
16
17
|
#
|
|
17
18
|
# 1. `AllowedMethods`
|
|
18
19
|
# 2. `AllowedPatterns`
|
|
19
20
|
# 3. `IncludedMacros`
|
|
21
|
+
# 4. `IncludedMacroPatterns`
|
|
20
22
|
#
|
|
21
|
-
# If a method is listed in both `IncludedMacros`
|
|
22
|
-
# then the latter takes precedence (that is, the
|
|
23
|
+
# If a method is listed in both `IncludedMacros`/`IncludedMacroPatterns`
|
|
24
|
+
# and `AllowedMethods`, then the latter takes precedence (that is, the
|
|
25
|
+
# method is allowed).
|
|
23
26
|
#
|
|
24
27
|
# In the alternative style (omit_parentheses), there are three additional
|
|
25
28
|
# options.
|
|
@@ -148,6 +151,16 @@ module RuboCop
|
|
|
148
151
|
# # still enforces parentheses on other methods
|
|
149
152
|
# array.delete(e)
|
|
150
153
|
#
|
|
154
|
+
# @example IncludedMacroPatterns: ["^assert", "^refute"]
|
|
155
|
+
#
|
|
156
|
+
# # bad
|
|
157
|
+
# assert_equal 'test', x
|
|
158
|
+
# refute_nil value
|
|
159
|
+
#
|
|
160
|
+
# # good
|
|
161
|
+
# assert_equal('test', x)
|
|
162
|
+
# refute_nil(value)
|
|
163
|
+
#
|
|
151
164
|
# @example AllowParenthesesInMultilineCall: false (default)
|
|
152
165
|
#
|
|
153
166
|
# # bad
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for usage of `Module` methods returning arrays that can be replaced
|
|
7
|
+
# with equivalent predicates.
|
|
8
|
+
#
|
|
9
|
+
# Calling a method returning an array then checking if an element is inside
|
|
10
|
+
# it is much slower than using an equivalent predicate method. For example,
|
|
11
|
+
# `instance_methods.include?` will return an array of all public and protected
|
|
12
|
+
# instance methods in the module, then check if a given method is inside that
|
|
13
|
+
# array, while `method_defined?` will do direct method lookup, which is much
|
|
14
|
+
# faster and consumes less memory.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# # bad
|
|
18
|
+
# Array.instance_methods.include?(:size)
|
|
19
|
+
# Array.instance_methods.member?(:size)
|
|
20
|
+
# Array.instance_methods(true).include?(:size)
|
|
21
|
+
#
|
|
22
|
+
# Array.instance_methods(false).include?(:find)
|
|
23
|
+
#
|
|
24
|
+
# # good
|
|
25
|
+
# Array.method_defined?(:size)
|
|
26
|
+
#
|
|
27
|
+
# Array.method_defined?(:find, false)
|
|
28
|
+
#
|
|
29
|
+
class ModuleMemberExistenceCheck < Base
|
|
30
|
+
extend AutoCorrector
|
|
31
|
+
|
|
32
|
+
MSG = 'Use `%<replacement>s` instead.'
|
|
33
|
+
|
|
34
|
+
RESTRICT_ON_SEND = %i[instance_methods].freeze
|
|
35
|
+
|
|
36
|
+
# @!method instance_methods_inclusion?(node)
|
|
37
|
+
def_node_matcher :instance_methods_inclusion?, <<~PATTERN
|
|
38
|
+
(call
|
|
39
|
+
(call _ :instance_methods _?)
|
|
40
|
+
{:include? :member?}
|
|
41
|
+
_)
|
|
42
|
+
PATTERN
|
|
43
|
+
|
|
44
|
+
def on_send(node) # rubocop:disable Metrics/AbcSize
|
|
45
|
+
return unless (parent = node.parent)
|
|
46
|
+
return unless instance_methods_inclusion?(parent)
|
|
47
|
+
return unless simple_method_argument?(node) && simple_method_argument?(parent)
|
|
48
|
+
|
|
49
|
+
offense_range = node.location.selector.join(parent.source_range.end)
|
|
50
|
+
replacement =
|
|
51
|
+
if node.first_argument.nil? || node.first_argument.true_type?
|
|
52
|
+
"method_defined?(#{parent.first_argument.source})"
|
|
53
|
+
else
|
|
54
|
+
"method_defined?(#{parent.first_argument.source}, #{node.first_argument.source})"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
add_offense(offense_range, message: format(MSG, replacement: replacement)) do |corrector|
|
|
58
|
+
corrector.replace(offense_range, replacement)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
alias on_csend on_send
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def simple_method_argument?(node)
|
|
66
|
+
return false if node.splat_argument? || node.block_argument?
|
|
67
|
+
return false if node.first_argument&.hash_type?
|
|
68
|
+
|
|
69
|
+
true
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -75,6 +75,8 @@ module RuboCop
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def correction_exceeds_max_line_length?(node)
|
|
78
|
+
return false unless max_line_length
|
|
79
|
+
|
|
78
80
|
indentation_width(node) + definition_width(node) > max_line_length
|
|
79
81
|
end
|
|
80
82
|
|
|
@@ -85,10 +87,6 @@ module RuboCop
|
|
|
85
87
|
def definition_width(node)
|
|
86
88
|
node.source_range.begin.join(node.arguments.source_range.end).length
|
|
87
89
|
end
|
|
88
|
-
|
|
89
|
-
def max_line_length
|
|
90
|
-
config.for_cop('Layout/LineLength')['Max'] || 120
|
|
91
|
-
end
|
|
92
90
|
end
|
|
93
91
|
end
|
|
94
92
|
end
|
|
@@ -55,19 +55,21 @@ module RuboCop
|
|
|
55
55
|
include OnNormalIfUnless
|
|
56
56
|
extend AutoCorrector
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
MSG_SUFFIX = 'over single-line `%<keyword>s/then/else/end` constructs.'
|
|
59
|
+
MSG_TERNARY = "Favor the ternary operator (`?:`) #{MSG_SUFFIX}"
|
|
60
|
+
MSG_MULTILINE = "Favor multi-line `%<keyword>s` #{MSG_SUFFIX}"
|
|
60
61
|
|
|
61
62
|
def on_normal_if_unless(node)
|
|
62
63
|
return unless node.single_line?
|
|
63
64
|
return unless node.else_branch
|
|
64
65
|
return if node.elsif? || node.if_branch&.begin_type?
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
multiline = multiline?(node)
|
|
68
|
+
|
|
69
|
+
add_offense(node, message: message(node, multiline)) do |corrector|
|
|
68
70
|
next if part_of_ignored_node?(node)
|
|
69
71
|
|
|
70
|
-
autocorrect(corrector, node)
|
|
72
|
+
autocorrect(corrector, node, multiline)
|
|
71
73
|
|
|
72
74
|
ignore_node(node)
|
|
73
75
|
end
|
|
@@ -75,12 +77,18 @@ module RuboCop
|
|
|
75
77
|
|
|
76
78
|
private
|
|
77
79
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
+
def multiline?(node)
|
|
81
|
+
always_multiline? || cannot_replace_to_ternary?(node)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def message(node, multiline)
|
|
85
|
+
template = multiline ? MSG_MULTILINE : MSG_TERNARY
|
|
86
|
+
|
|
87
|
+
format(template, keyword: node.keyword)
|
|
80
88
|
end
|
|
81
89
|
|
|
82
|
-
def autocorrect(corrector, node)
|
|
83
|
-
if
|
|
90
|
+
def autocorrect(corrector, node, multiline)
|
|
91
|
+
if multiline
|
|
84
92
|
IfThenCorrector.new(node, indentation: configured_indentation_width).call(corrector)
|
|
85
93
|
else
|
|
86
94
|
corrector.replace(node, ternary_correction(node))
|
|
@@ -26,9 +26,10 @@ module RuboCop
|
|
|
26
26
|
splat kwsplat forwarded_args forwarded_restarg forwarded_kwrestarg block_pass
|
|
27
27
|
].freeze
|
|
28
28
|
|
|
29
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
29
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
30
30
|
def on_send(node)
|
|
31
31
|
return unless (dot = node.loc.dot)
|
|
32
|
+
return if unary_method_no_operator?(node)
|
|
32
33
|
return if node.receiver.const_type? || !node.arguments.one?
|
|
33
34
|
|
|
34
35
|
return unless (rhs = node.first_argument)
|
|
@@ -43,10 +44,18 @@ module RuboCop
|
|
|
43
44
|
corrector.insert_after(selector, ' ') if insert_space_after?(node)
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
47
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
47
48
|
|
|
48
49
|
private
|
|
49
50
|
|
|
51
|
+
# `foo.~@` and `foo.!@` call the method `~` and `!` respectively. While those
|
|
52
|
+
# are operator methods, we don't want to actually consider them as such.
|
|
53
|
+
def unary_method_no_operator?(node)
|
|
54
|
+
return false unless node.nonmutating_unary_operator_method?
|
|
55
|
+
|
|
56
|
+
node.method_name.to_s != node.selector.source
|
|
57
|
+
end
|
|
58
|
+
|
|
50
59
|
# Checks for an acceptable case of `foo.+(bar).baz`.
|
|
51
60
|
def method_call_with_parenthesized_arg?(argument)
|
|
52
61
|
return false unless argument.parent.parent&.send_type?
|
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
|
14
14
|
#
|
|
15
15
|
# # good
|
|
16
16
|
# one, two = *foo
|
|
17
|
-
# a, b = foo
|
|
17
|
+
# a, b = foo
|
|
18
18
|
# a, b = b, a
|
|
19
19
|
#
|
|
20
20
|
# a = 1
|
|
@@ -223,7 +223,7 @@ module RuboCop
|
|
|
223
223
|
# __FILE__ is treated as a StrNode but has no begin
|
|
224
224
|
if node.str_type? && loc.respond_to?(:begin) && loc.begin.nil?
|
|
225
225
|
"'#{node.source}'"
|
|
226
|
-
elsif node.sym_type? && loc
|
|
226
|
+
elsif node.sym_type? && !node.loc?(:begin)
|
|
227
227
|
":#{node.source}"
|
|
228
228
|
else
|
|
229
229
|
node.source
|
|
@@ -37,6 +37,7 @@ module RuboCop
|
|
|
37
37
|
# array.sum(0)
|
|
38
38
|
# exit(true)
|
|
39
39
|
# exit!(false)
|
|
40
|
+
# string.to_i(10)
|
|
40
41
|
# string.split(" ")
|
|
41
42
|
# "first\nsecond".split(" ")
|
|
42
43
|
# string.chomp("\n")
|
|
@@ -49,6 +50,7 @@ module RuboCop
|
|
|
49
50
|
# array.sum
|
|
50
51
|
# exit
|
|
51
52
|
# exit!
|
|
53
|
+
# string.to_i
|
|
52
54
|
# string.split
|
|
53
55
|
# "first second".split
|
|
54
56
|
# string.chomp
|
|
@@ -89,7 +89,7 @@ module RuboCop
|
|
|
89
89
|
|
|
90
90
|
def on_send(node)
|
|
91
91
|
format_without_additional_args?(node) do |value|
|
|
92
|
-
replacement = value.source
|
|
92
|
+
replacement = escape_control_chars(value.source)
|
|
93
93
|
|
|
94
94
|
add_offense(node, message: message(node, replacement)) do |corrector|
|
|
95
95
|
corrector.replace(node, replacement)
|
|
@@ -134,7 +134,7 @@ module RuboCop
|
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
137
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
138
138
|
def all_fields_literal?(string, arguments)
|
|
139
139
|
count = 0
|
|
140
140
|
sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
|
|
@@ -147,13 +147,14 @@ module RuboCop
|
|
|
147
147
|
hash = arguments.detect(&:hash_type?)
|
|
148
148
|
next unless (argument = find_argument(sequence, arguments, hash))
|
|
149
149
|
next unless matching_argument?(sequence, argument)
|
|
150
|
+
next if (sequence.width || sequence.precision) && argument.dstr_type?
|
|
150
151
|
|
|
151
152
|
count += 1
|
|
152
153
|
end
|
|
153
154
|
|
|
154
155
|
sequences.size == count
|
|
155
156
|
end
|
|
156
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
157
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
157
158
|
|
|
158
159
|
# If the sequence has a variable (`*`) width, it cannot be autocorrected
|
|
159
160
|
# if the width is not given as a numeric literal argument
|
|
@@ -229,7 +230,12 @@ module RuboCop
|
|
|
229
230
|
end
|
|
230
231
|
end
|
|
231
232
|
|
|
232
|
-
"#{start_delimiter}#{string}#{end_delimiter}"
|
|
233
|
+
"#{start_delimiter}#{escape_control_chars(string)}#{end_delimiter}"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Escape any control characters in the string (eg. `\t` or `\n` become `\\t` or `\\n`)
|
|
237
|
+
def escape_control_chars(string)
|
|
238
|
+
string.gsub(/\p{Cc}/) { |s| s.dump[1..-2] }
|
|
233
239
|
end
|
|
234
240
|
|
|
235
241
|
def argument_values(arguments)
|
|
@@ -49,9 +49,10 @@ module RuboCop
|
|
|
49
49
|
def on_dstr(node)
|
|
50
50
|
return unless single_interpolation?(node)
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
embedded_node = node.children.first
|
|
53
|
+
return if use_match_pattern?(embedded_node)
|
|
54
54
|
|
|
55
|
+
add_offense(node) do |corrector|
|
|
55
56
|
if variable_interpolation?(embedded_node)
|
|
56
57
|
autocorrect_variable_interpolation(corrector, embedded_node, node)
|
|
57
58
|
elsif single_variable_interpolation?(embedded_node)
|
|
@@ -71,6 +72,14 @@ module RuboCop
|
|
|
71
72
|
!embedded_in_percent_array?(node)
|
|
72
73
|
end
|
|
73
74
|
|
|
75
|
+
def use_match_pattern?(node)
|
|
76
|
+
return false if target_ruby_version <= 2.7
|
|
77
|
+
|
|
78
|
+
node.children.any? do |child|
|
|
79
|
+
child.respond_to?(:match_pattern_type?) && child.match_pattern_type?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
74
83
|
def single_variable_interpolation?(node)
|
|
75
84
|
return false unless node.children.one?
|
|
76
85
|
|
|
@@ -75,6 +75,11 @@ module RuboCop
|
|
|
75
75
|
new_argument.gsub!('\"', '"')
|
|
76
76
|
quote = "'"
|
|
77
77
|
elsif new_argument.include?("\\'")
|
|
78
|
+
# Add a backslash before single quotes preceded by an even number of backslashes.
|
|
79
|
+
# An even number (including zero) of backslashes before a quote means the quote itself
|
|
80
|
+
# is not escaped.
|
|
81
|
+
# Otherwise an odd number means the quote is already escaped so this doesn't touch it.
|
|
82
|
+
new_argument.gsub!(/(?<!\\)((?:\\\\)*)'/) { "#{::Regexp.last_match(1)}\\'" }
|
|
78
83
|
quote = "'"
|
|
79
84
|
elsif new_argument.include?('\'')
|
|
80
85
|
new_argument.gsub!("'", "\\\\'")
|
|
@@ -26,17 +26,17 @@ module RuboCop
|
|
|
26
26
|
#
|
|
27
27
|
# [source,ruby]
|
|
28
28
|
# ----
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
29
|
+
# class MyString < String; end
|
|
30
|
+
# strings = [MyString.new('test'), 'test']
|
|
31
|
+
# strings.sort.last.class #=> String
|
|
32
|
+
# strings.max.class #=> MyString
|
|
33
33
|
# ----
|
|
34
34
|
#
|
|
35
35
|
# [source,ruby]
|
|
36
36
|
# ----
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
37
|
+
# words = %w(dog horse mouse)
|
|
38
|
+
# words.sort_by { |word| word.length }.last #=> 'mouse'
|
|
39
|
+
# words.max_by { |word| word.length } #=> 'horse'
|
|
40
40
|
# ----
|
|
41
41
|
#
|
|
42
42
|
# @example
|
|
@@ -69,10 +69,11 @@ module RuboCop
|
|
|
69
69
|
|
|
70
70
|
def each_semicolon
|
|
71
71
|
tokens_for_lines.each do |line, tokens|
|
|
72
|
-
semicolon_pos = semicolon_position(tokens)
|
|
72
|
+
next unless (semicolon_pos = semicolon_position(tokens))
|
|
73
|
+
|
|
73
74
|
after_expr_pos = semicolon_pos == -1 ? -2 : semicolon_pos
|
|
74
75
|
|
|
75
|
-
yield line, tokens[semicolon_pos].column, tokens[after_expr_pos]
|
|
76
|
+
yield line, tokens[semicolon_pos].column, tokens[after_expr_pos]
|
|
76
77
|
end
|
|
77
78
|
end
|
|
78
79
|
|
|
@@ -119,6 +120,7 @@ module RuboCop
|
|
|
119
120
|
tokens[1]&.type == :tSTRING_DBEG && tokens[2]&.semicolon?
|
|
120
121
|
end
|
|
121
122
|
|
|
123
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
122
124
|
def register_semicolon(line, column, after_expression, token_before_semicolon = nil)
|
|
123
125
|
range = source_range(processed_source.buffer, line, column)
|
|
124
126
|
|
|
@@ -130,14 +132,19 @@ module RuboCop
|
|
|
130
132
|
# without parentheses.
|
|
131
133
|
# See: https://github.com/rubocop/rubocop/issues/10791
|
|
132
134
|
if token_before_semicolon&.regexp_dots?
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
node = find_node(range_nodes, token_before_semicolon)
|
|
136
|
+
elsif token_before_semicolon&.type == :tLABEL
|
|
137
|
+
node = find_node(value_omission_pair_nodes, token_before_semicolon).parent
|
|
138
|
+
space = node.parent.loc.selector.end.join(node.source_range.begin)
|
|
139
|
+
corrector.remove(space)
|
|
135
140
|
end
|
|
136
141
|
|
|
142
|
+
corrector.wrap(node, '(', ')') if node
|
|
137
143
|
corrector.remove(range)
|
|
138
144
|
end
|
|
139
145
|
end
|
|
140
146
|
end
|
|
147
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
141
148
|
|
|
142
149
|
def expressions_per_line(exprs)
|
|
143
150
|
# create a map matching lines to the number of expressions on them
|
|
@@ -153,9 +160,9 @@ module RuboCop
|
|
|
153
160
|
end
|
|
154
161
|
end
|
|
155
162
|
|
|
156
|
-
def
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
def find_node(nodes, token_before_semicolon)
|
|
164
|
+
nodes.detect do |node|
|
|
165
|
+
node.source_range.overlaps?(token_before_semicolon.pos)
|
|
159
166
|
end
|
|
160
167
|
end
|
|
161
168
|
|
|
@@ -166,6 +173,15 @@ module RuboCop
|
|
|
166
173
|
@range_nodes = ast.range_type? ? [ast] : []
|
|
167
174
|
@range_nodes.concat(ast.each_descendant(:range).to_a)
|
|
168
175
|
end
|
|
176
|
+
|
|
177
|
+
def value_omission_pair_nodes
|
|
178
|
+
if instance_variable_defined?(:@value_omission_pair_nodes)
|
|
179
|
+
return @value_omission_pair_nodes
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
ast = processed_source.ast
|
|
183
|
+
@value_omission_pair_nodes = ast.each_descendant(:pair).to_a.select(&:value_omission?)
|
|
184
|
+
end
|
|
169
185
|
end
|
|
170
186
|
end
|
|
171
187
|
end
|
|
@@ -129,6 +129,7 @@ module RuboCop
|
|
|
129
129
|
corrector.remove(range_with_surrounding_space(range, newlines: false))
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
# rubocop:disable Metrics/AbcSize
|
|
132
133
|
def correct_for_basic_condition_style(corrector, node, if_branch)
|
|
133
134
|
range = range_between(
|
|
134
135
|
node.condition.source_range.end_pos, if_branch.condition.source_range.begin_pos
|
|
@@ -137,8 +138,14 @@ module RuboCop
|
|
|
137
138
|
|
|
138
139
|
corrector.replace(if_branch.condition, chainable_condition(if_branch))
|
|
139
140
|
|
|
140
|
-
|
|
141
|
+
end_range = if same_line?(node.loc.end, node.if_branch.loc.end)
|
|
142
|
+
node.loc.end
|
|
143
|
+
else
|
|
144
|
+
range_by_whole_lines(node.loc.end, include_final_newline: true)
|
|
145
|
+
end
|
|
146
|
+
corrector.remove(end_range)
|
|
141
147
|
end
|
|
148
|
+
# rubocop:enable Metrics/AbcSize
|
|
142
149
|
|
|
143
150
|
def autocorrect_outer_condition_modify_form(corrector, node, if_branch)
|
|
144
151
|
correct_node(corrector, if_branch)
|
|
@@ -109,7 +109,7 @@ module RuboCop
|
|
|
109
109
|
|
|
110
110
|
def_args.zip(super_args).each do |def_arg, super_arg|
|
|
111
111
|
next if positional_arg_same?(def_arg, super_arg)
|
|
112
|
-
next if positional_rest_arg_same(def_arg, super_arg)
|
|
112
|
+
next if positional_rest_arg_same?(def_arg, super_arg)
|
|
113
113
|
next if keyword_arg_same?(def_arg, super_arg)
|
|
114
114
|
next if keyword_rest_arg_same?(def_arg, super_arg)
|
|
115
115
|
next if block_arg_same?(def_node, super_node, def_arg, super_arg)
|
|
@@ -147,7 +147,7 @@ module RuboCop
|
|
|
147
147
|
def_arg.name == super_arg.children.first
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
-
def positional_rest_arg_same(def_arg, super_arg)
|
|
150
|
+
def positional_rest_arg_same?(def_arg, super_arg)
|
|
151
151
|
return false unless def_arg.restarg_type?
|
|
152
152
|
# anonymous forwarding
|
|
153
153
|
return true if def_arg.name.nil? && super_arg.forwarded_restarg_type?
|