rubocop 1.79.2 → 1.82.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 +1 -1
- data/config/default.yml +35 -7
- data/config/obsoletion.yml +4 -0
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
- data/lib/rubocop/cli.rb +8 -3
- data/lib/rubocop/comment_config.rb +62 -17
- data/lib/rubocop/config_loader.rb +5 -2
- data/lib/rubocop/config_loader_resolver.rb +7 -6
- data/lib/rubocop/config_store.rb +5 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +6 -4
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +8 -7
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -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/internal_affairs/node_pattern_groups.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- 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 +0 -4
- data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +16 -5
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/layout/trailing_whitespace.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/deprecated_open_ssl_constant.rb +4 -1
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- 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 +17 -8
- 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 +15 -6
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- 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/lint/void.rb +7 -0
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +0 -4
- data/lib/rubocop/cop/mixin/code_length.rb +1 -1
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- 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/statement_modifier.rb +0 -6
- data/lib/rubocop/cop/mixin/trailing_comma.rb +5 -6
- data/lib/rubocop/cop/naming/method_name.rb +5 -3
- data/lib/rubocop/cop/naming/predicate_method.rb +19 -6
- data/lib/rubocop/cop/security/json_load.rb +33 -11
- data/lib/rubocop/cop/style/array_intersect.rb +46 -12
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
- data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- 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/double_negation.rb +1 -1
- 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/explicit_block_argument.rb +1 -1
- 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/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- 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 +0 -4
- data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
- data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
- 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_begin.rb +34 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +26 -5
- data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +14 -11
- data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
- data/lib/rubocop/cop/style/safe_navigation.rb +18 -1
- 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/string_concatenation.rb +17 -13
- data/lib/rubocop/cop/style/super_arguments.rb +2 -2
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
- data/lib/rubocop/cop/style/unless_else.rb +10 -9
- data/lib/rubocop/cop/util.rb +2 -3
- data/lib/rubocop/cop/utils/format_string.rb +10 -0
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +9 -7
- 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 +19 -5
- data/lib/rubocop/lsp/diagnostic.rb +10 -14
- data/lib/rubocop/lsp/routes.rb +31 -2
- data/lib/rubocop/lsp/stdin_runner.rb +0 -16
- 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 +1 -1
- data/lib/rubocop/rspec/shared_contexts.rb +2 -2
- data/lib/rubocop/rspec/support.rb +1 -1
- data/lib/rubocop/runner.rb +6 -4
- data/lib/rubocop/target_finder.rb +9 -9
- data/lib/rubocop/target_ruby.rb +11 -2
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +2 -0
- data/lib/ruby_lsp/rubocop/addon.rb +23 -8
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +8 -6
|
@@ -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
|
|
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def autocorrect(corrector, node)
|
|
71
|
-
if node.
|
|
71
|
+
if node.post_condition_loop?
|
|
72
72
|
replace_begin_end_with_modifier(corrector, node)
|
|
73
73
|
elsif node.modifier_form?
|
|
74
74
|
replace_source(corrector, node.source_range, modifier_replacement(node))
|
|
@@ -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
|
|
@@ -43,24 +43,26 @@ module RuboCop
|
|
|
43
43
|
# @!method nil_check?(node)
|
|
44
44
|
def_node_matcher :nil_check?, '(send _ :nil?)'
|
|
45
45
|
|
|
46
|
+
# rubocop:disable Metrics/AbcSize
|
|
46
47
|
def on_send(node)
|
|
47
48
|
return unless node.receiver
|
|
48
49
|
|
|
49
50
|
style_check?(node) do
|
|
50
51
|
add_offense(node.loc.selector) do |corrector|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
if prefer_comparison?
|
|
53
|
+
range = node.loc.dot.join(node.loc.selector.end)
|
|
54
|
+
corrector.replace(range, ' == nil')
|
|
55
|
+
else
|
|
56
|
+
range = node.receiver.source_range.end.join(node.source_range.end)
|
|
57
|
+
corrector.replace(range, '.nil?')
|
|
58
|
+
end
|
|
58
59
|
|
|
59
60
|
parent = node.parent
|
|
60
61
|
corrector.wrap(node, '(', ')') if parent.respond_to?(:method?) && parent.method?(:!)
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
end
|
|
65
|
+
# rubocop:enable Metrics/AbcSize
|
|
64
66
|
|
|
65
67
|
private
|
|
66
68
|
|
|
@@ -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))
|
|
@@ -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
|
|
@@ -85,6 +85,29 @@ module RuboCop
|
|
|
85
85
|
end
|
|
86
86
|
alias on_defs on_def
|
|
87
87
|
|
|
88
|
+
def on_if(node)
|
|
89
|
+
return if node.modifier_form?
|
|
90
|
+
|
|
91
|
+
inspect_branches(node)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def on_case(node)
|
|
95
|
+
inspect_branches(node)
|
|
96
|
+
end
|
|
97
|
+
alias on_case_match on_case
|
|
98
|
+
|
|
99
|
+
def on_while(node)
|
|
100
|
+
return if node.modifier_form?
|
|
101
|
+
|
|
102
|
+
body = node.body
|
|
103
|
+
|
|
104
|
+
return unless body&.kwbegin_type?
|
|
105
|
+
return if body.rescue_node || body.ensure_node
|
|
106
|
+
|
|
107
|
+
register_offense(body)
|
|
108
|
+
end
|
|
109
|
+
alias on_until on_while
|
|
110
|
+
|
|
88
111
|
def on_block(node)
|
|
89
112
|
return if target_ruby_version < 2.5
|
|
90
113
|
return if node.send_node.lambda_literal?
|
|
@@ -180,6 +203,8 @@ module RuboCop
|
|
|
180
203
|
end
|
|
181
204
|
|
|
182
205
|
def begin_block_has_multiline_statements?(node)
|
|
206
|
+
return false unless node.parent
|
|
207
|
+
|
|
183
208
|
node.children.count >= 2
|
|
184
209
|
end
|
|
185
210
|
|
|
@@ -199,6 +224,15 @@ module RuboCop
|
|
|
199
224
|
def valid_begin_assignment?(node)
|
|
200
225
|
node.parent&.assignment? && !node.children.one?
|
|
201
226
|
end
|
|
227
|
+
|
|
228
|
+
def inspect_branches(node)
|
|
229
|
+
node.branches.each do |branch|
|
|
230
|
+
next unless branch&.kwbegin_type?
|
|
231
|
+
next if branch.rescue_node || branch.ensure_node
|
|
232
|
+
|
|
233
|
+
register_offense(branch)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
202
236
|
end
|
|
203
237
|
end
|
|
204
238
|
end
|
|
@@ -247,7 +247,7 @@ module RuboCop
|
|
|
247
247
|
"#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
|
|
248
248
|
elsif if_branch.true_type?
|
|
249
249
|
condition = if_branch.parent.condition
|
|
250
|
-
return condition.source if condition.arguments.empty?
|
|
250
|
+
return condition.source if condition.arguments.empty? || condition.parenthesized?
|
|
251
251
|
|
|
252
252
|
wrap_arguments_with_parens(condition)
|
|
253
253
|
else
|
|
@@ -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,6 +134,7 @@ module RuboCop
|
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
137
138
|
def all_fields_literal?(string, arguments)
|
|
138
139
|
count = 0
|
|
139
140
|
sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
|
|
@@ -141,29 +142,44 @@ module RuboCop
|
|
|
141
142
|
|
|
142
143
|
sequences.each do |sequence|
|
|
143
144
|
next if sequence.percent?
|
|
145
|
+
next if unknown_variable_width?(sequence, arguments)
|
|
144
146
|
|
|
145
147
|
hash = arguments.detect(&:hash_type?)
|
|
146
148
|
next unless (argument = find_argument(sequence, arguments, hash))
|
|
147
149
|
next unless matching_argument?(sequence, argument)
|
|
150
|
+
next if (sequence.width || sequence.precision) && argument.dstr_type?
|
|
148
151
|
|
|
149
152
|
count += 1
|
|
150
153
|
end
|
|
151
154
|
|
|
152
155
|
sequences.size == count
|
|
153
156
|
end
|
|
157
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
154
158
|
|
|
159
|
+
# If the sequence has a variable (`*`) width, it cannot be autocorrected
|
|
160
|
+
# if the width is not given as a numeric literal argument
|
|
161
|
+
def unknown_variable_width?(sequence, arguments)
|
|
162
|
+
return false unless sequence.variable_width?
|
|
163
|
+
|
|
164
|
+
argument = arguments[sequence.variable_width_argument_number - 1]
|
|
165
|
+
!numeric?(argument)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# rubocop:disable Metrics/AbcSize
|
|
155
169
|
def find_argument(sequence, arguments, hash)
|
|
156
170
|
if hash && (sequence.annotated? || sequence.template?)
|
|
157
171
|
find_hash_value_node(hash, sequence.name.to_sym).first
|
|
172
|
+
elsif sequence.variable_width?
|
|
173
|
+
# If the specifier contains `*`, the argument for the width can be ignored.
|
|
174
|
+
arguments.delete_at(sequence.variable_width_argument_number - 1)
|
|
175
|
+
arguments.shift
|
|
158
176
|
elsif sequence.arg_number
|
|
159
177
|
arguments[sequence.arg_number.to_i - 1]
|
|
160
178
|
else
|
|
161
|
-
# If the specifier contains `*`, the following arguments will be used
|
|
162
|
-
# to specify the width and can be ignored.
|
|
163
|
-
(sequence.arity - 1).times { arguments.shift }
|
|
164
179
|
arguments.shift
|
|
165
180
|
end
|
|
166
181
|
end
|
|
182
|
+
# rubocop:enable Metrics/AbcSize
|
|
167
183
|
|
|
168
184
|
def matching_argument?(sequence, argument)
|
|
169
185
|
# Template specifiers don't give a type, any acceptable literal type is ok.
|
|
@@ -214,7 +230,12 @@ module RuboCop
|
|
|
214
230
|
end
|
|
215
231
|
end
|
|
216
232
|
|
|
217
|
-
"#{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] }
|
|
218
239
|
end
|
|
219
240
|
|
|
220
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
|
|
|
@@ -24,9 +24,6 @@ module RuboCop
|
|
|
24
24
|
(send `{(send _recv _msg) str array hash const #variable?} :[] ...)
|
|
25
25
|
PATTERN
|
|
26
26
|
|
|
27
|
-
# @!method method_node_and_args(node)
|
|
28
|
-
def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
|
|
29
|
-
|
|
30
27
|
# @!method rescue?(node)
|
|
31
28
|
def_node_matcher :rescue?, '{^resbody ^^resbody}'
|
|
32
29
|
|
|
@@ -205,6 +202,7 @@ module RuboCop
|
|
|
205
202
|
return false unless node.rescue_type?
|
|
206
203
|
return false unless (parent = begin_node.parent)
|
|
207
204
|
return false if parent.if_type? && parent.ternary?
|
|
205
|
+
return false if parent.conditional? && parent.condition == begin_node
|
|
208
206
|
|
|
209
207
|
!parent.type?(:call, :array, :pair)
|
|
210
208
|
end
|
|
@@ -220,7 +218,7 @@ module RuboCop
|
|
|
220
218
|
end
|
|
221
219
|
|
|
222
220
|
def call_node?(node)
|
|
223
|
-
node.call_type? || (node.any_block_type? && !node.lambda_or_proc?)
|
|
221
|
+
node.call_type? || (node.any_block_type? && node.braces? && !node.lambda_or_proc?)
|
|
224
222
|
end
|
|
225
223
|
|
|
226
224
|
def check_send(begin_node, node)
|
|
@@ -228,7 +226,7 @@ module RuboCop
|
|
|
228
226
|
|
|
229
227
|
return check_unary(begin_node, node) if node.unary_operation?
|
|
230
228
|
|
|
231
|
-
return unless method_call_with_redundant_parentheses?(node)
|
|
229
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
|
232
230
|
return if call_chain_starts_with_int?(begin_node, node) ||
|
|
233
231
|
do_end_block_in_method_chain?(begin_node, node)
|
|
234
232
|
|
|
@@ -239,8 +237,7 @@ module RuboCop
|
|
|
239
237
|
return if begin_node.chained?
|
|
240
238
|
|
|
241
239
|
node = node.children.first while suspect_unary?(node)
|
|
242
|
-
|
|
243
|
-
return if node.send_type? && !method_call_with_redundant_parentheses?(node)
|
|
240
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
|
244
241
|
|
|
245
242
|
offense(begin_node, 'a unary operation')
|
|
246
243
|
end
|
|
@@ -302,13 +299,19 @@ module RuboCop
|
|
|
302
299
|
end
|
|
303
300
|
end
|
|
304
301
|
|
|
305
|
-
def method_call_with_redundant_parentheses?(node)
|
|
306
|
-
return false unless node.
|
|
302
|
+
def method_call_with_redundant_parentheses?(begin_node, node)
|
|
303
|
+
return false unless node.type?(:call, :super, :yield, :defined?)
|
|
307
304
|
return false if node.prefix_not?
|
|
305
|
+
return true if singular_parenthesized_parent?(begin_node)
|
|
306
|
+
|
|
307
|
+
node.arguments.empty? || parentheses?(node) || square_brackets?(node)
|
|
308
|
+
end
|
|
308
309
|
|
|
309
|
-
|
|
310
|
+
def singular_parenthesized_parent?(begin_node)
|
|
311
|
+
return true unless begin_node.parent
|
|
312
|
+
return false if begin_node.parent.type?(:splat, :kwsplat)
|
|
310
313
|
|
|
311
|
-
|
|
314
|
+
parentheses?(begin_node) && begin_node.parent.children.one?
|
|
312
315
|
end
|
|
313
316
|
|
|
314
317
|
def only_begin_arg?(args)
|
|
@@ -66,6 +66,7 @@ module RuboCop
|
|
|
66
66
|
DETERMINISTIC_REGEX.match?(regexp_node.source)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# rubocop:disable Metrics/MethodLength
|
|
69
70
|
def preferred_argument(regexp_node)
|
|
70
71
|
new_argument = replacement(regexp_node)
|
|
71
72
|
|
|
@@ -73,6 +74,13 @@ module RuboCop
|
|
|
73
74
|
new_argument.gsub!("'", "\\\\'")
|
|
74
75
|
new_argument.gsub!('\"', '"')
|
|
75
76
|
quote = "'"
|
|
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)}\\'" }
|
|
83
|
+
quote = "'"
|
|
76
84
|
elsif new_argument.include?('\'')
|
|
77
85
|
new_argument.gsub!("'", "\\\\'")
|
|
78
86
|
quote = "'"
|
|
@@ -84,6 +92,7 @@ module RuboCop
|
|
|
84
92
|
|
|
85
93
|
"#{quote}#{new_argument}#{quote}"
|
|
86
94
|
end
|
|
95
|
+
# rubocop:enable Metrics/MethodLength
|
|
87
96
|
|
|
88
97
|
def replacement(regexp_node)
|
|
89
98
|
regexp_content = regexp_node.content
|
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
|
41
41
|
ALLOWED_ALWAYS_ESCAPES = " \n[]^\\#".chars.freeze
|
|
42
42
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES = '-'.chars.freeze
|
|
43
43
|
ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES = '.*+?{}()|$'.chars.freeze
|
|
44
|
+
INTERPOLATION_SIGILS = %w[@ $].freeze
|
|
44
45
|
|
|
45
46
|
def on_regexp(node)
|
|
46
47
|
each_escape(node) do |char, index, within_character_class|
|
|
@@ -64,6 +65,7 @@ module RuboCop
|
|
|
64
65
|
# different versions of Ruby so that e.g. /\i/ != /i/
|
|
65
66
|
return true if /[[:alnum:]]/.match?(char)
|
|
66
67
|
return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
|
|
68
|
+
return true if requires_escape_to_avoid_interpolation?(node.source[index], char)
|
|
67
69
|
|
|
68
70
|
if within_character_class
|
|
69
71
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char) &&
|
|
@@ -95,6 +97,12 @@ module RuboCop
|
|
|
95
97
|
delimiters.include?(char)
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
def requires_escape_to_avoid_interpolation?(char_before_escape, escaped_char)
|
|
101
|
+
# Preserve escapes after '#' that would otherwise trigger interpolation:
|
|
102
|
+
# '#@ivar', '#@@cvar', and '#$gvar'.
|
|
103
|
+
char_before_escape == '#' && INTERPOLATION_SIGILS.include?(escaped_char)
|
|
104
|
+
end
|
|
105
|
+
|
|
98
106
|
def each_escape(node)
|
|
99
107
|
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
|
|
100
108
|
yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
|
|
@@ -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
|