rubocop 0.67.2 → 0.68.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 +86 -233
- data/exe/rubocop +0 -12
- data/lib/rubocop.rb +13 -30
- data/lib/rubocop/ast/builder.rb +4 -0
- data/lib/rubocop/ast/node/alias_node.rb +24 -0
- data/lib/rubocop/ast/node/class_node.rb +31 -0
- data/lib/rubocop/ast/node/module_node.rb +24 -0
- data/lib/rubocop/ast/node/range_node.rb +7 -0
- data/lib/rubocop/ast/node/resbody_node.rb +12 -0
- data/lib/rubocop/ast/node/self_class_node.rb +24 -0
- data/lib/rubocop/cli.rb +40 -4
- data/lib/rubocop/config.rb +9 -7
- data/lib/rubocop/config_loader.rb +48 -7
- data/lib/rubocop/config_loader_resolver.rb +5 -4
- data/lib/rubocop/cop/commissioner.rb +24 -0
- data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +18 -6
- data/lib/rubocop/cop/internal_affairs/node_destructuring.rb +12 -14
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +9 -20
- data/lib/rubocop/cop/layout/align_arguments.rb +93 -0
- data/lib/rubocop/cop/layout/align_parameters.rb +57 -33
- data/lib/rubocop/cop/layout/class_structure.rb +5 -5
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -8
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +3 -6
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +1 -2
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +1 -0
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +292 -0
- data/lib/rubocop/cop/layout/{first_parameter_indentation.rb → indent_first_argument.rb} +11 -12
- data/lib/rubocop/cop/layout/{indent_array.rb → indent_first_array_element.rb} +2 -2
- data/lib/rubocop/cop/layout/{indent_hash.rb → indent_first_hash_element.rb} +2 -2
- data/lib/rubocop/cop/layout/indent_first_parameter.rb +96 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -16
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -4
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -16
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +6 -8
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -8
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +157 -0
- data/lib/rubocop/cop/lint/inherit_exception.rb +3 -4
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +18 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -5
- data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +25 -5
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -6
- data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -2
- data/lib/rubocop/cop/message_annotator.rb +1 -0
- data/lib/rubocop/cop/metrics/line_length.rb +139 -28
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +3 -4
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +190 -0
- data/lib/rubocop/cop/mixin/{array_hash_indentation.rb → multiline_element_indentation.rb} +3 -2
- data/lib/rubocop/cop/mixin/too_many_lines.rb +3 -7
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +33 -4
- data/lib/rubocop/cop/rails/active_record_override.rb +23 -8
- data/lib/rubocop/cop/rails/delegate.rb +5 -8
- data/lib/rubocop/cop/rails/environment_comparison.rb +5 -3
- data/lib/rubocop/cop/rails/find_each.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +3 -3
- data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
- data/lib/rubocop/cop/rails/skips_model_validations.rb +6 -7
- data/lib/rubocop/cop/rails/time_zone.rb +3 -10
- data/lib/rubocop/cop/rails/validation.rb +3 -0
- data/lib/rubocop/cop/registry.rb +3 -3
- data/lib/rubocop/cop/style/alias.rb +13 -7
- data/lib/rubocop/cop/style/block_delimiters.rb +20 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +19 -21
- data/lib/rubocop/cop/style/class_methods.rb +16 -24
- data/lib/rubocop/cop/style/conditional_assignment.rb +20 -49
- data/lib/rubocop/cop/style/documentation.rb +3 -7
- data/lib/rubocop/cop/style/format_string.rb +18 -21
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +4 -0
- data/lib/rubocop/cop/style/lambda.rb +12 -8
- data/lib/rubocop/cop/style/mixin_grouping.rb +8 -10
- data/lib/rubocop/cop/style/module_function.rb +2 -3
- data/lib/rubocop/cop/style/next.rb +10 -14
- data/lib/rubocop/cop/style/one_line_conditional.rb +5 -3
- data/lib/rubocop/cop/style/optional_arguments.rb +1 -4
- data/lib/rubocop/cop/style/random_with_offset.rb +44 -47
- data/lib/rubocop/cop/style/redundant_return.rb +6 -14
- data/lib/rubocop/cop/style/redundant_sort_by.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +3 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +2 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +20 -40
- data/lib/rubocop/cop/style/unless_else.rb +1 -2
- data/lib/rubocop/cop/style/yoda_condition.rb +8 -7
- data/lib/rubocop/cop/util.rb +2 -4
- data/lib/rubocop/file_finder.rb +5 -10
- data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -0
- data/lib/rubocop/node_pattern.rb +304 -170
- data/lib/rubocop/options.rb +4 -1
- data/lib/rubocop/rspec/shared_contexts.rb +3 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +1 -1
- metadata +26 -50
- data/lib/rubocop/cop/performance/caller.rb +0 -69
- data/lib/rubocop/cop/performance/case_when_splat.rb +0 -177
- data/lib/rubocop/cop/performance/casecmp.rb +0 -108
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +0 -78
- data/lib/rubocop/cop/performance/compare_with_block.rb +0 -122
- data/lib/rubocop/cop/performance/count.rb +0 -102
- data/lib/rubocop/cop/performance/detect.rb +0 -110
- data/lib/rubocop/cop/performance/double_start_end_with.rb +0 -94
- data/lib/rubocop/cop/performance/end_with.rb +0 -56
- data/lib/rubocop/cop/performance/fixed_size.rb +0 -97
- data/lib/rubocop/cop/performance/flat_map.rb +0 -78
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +0 -99
- data/lib/rubocop/cop/performance/open_struct.rb +0 -46
- data/lib/rubocop/cop/performance/range_include.rb +0 -50
- data/lib/rubocop/cop/performance/redundant_block_call.rb +0 -93
- data/lib/rubocop/cop/performance/redundant_match.rb +0 -56
- data/lib/rubocop/cop/performance/redundant_merge.rb +0 -183
- data/lib/rubocop/cop/performance/regexp_match.rb +0 -265
- data/lib/rubocop/cop/performance/reverse_each.rb +0 -42
- data/lib/rubocop/cop/performance/size.rb +0 -77
- data/lib/rubocop/cop/performance/start_with.rb +0 -59
- data/lib/rubocop/cop/performance/string_replacement.rb +0 -173
- data/lib/rubocop/cop/performance/times_map.rb +0 -71
- data/lib/rubocop/cop/performance/unfreeze_string.rb +0 -50
- data/lib/rubocop/cop/performance/uri_default_parser.rb +0 -47
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies uses of `Range#include?`, which iterates over each
|
7
|
-
# item in a `Range` to see if a specified item is there. In contrast,
|
8
|
-
# `Range#cover?` simply compares the target item with the beginning and
|
9
|
-
# end points of the `Range`. In a great majority of cases, this is what
|
10
|
-
# is wanted.
|
11
|
-
#
|
12
|
-
# This cop is `Safe: false` by default because `Range#include?` and
|
13
|
-
# `Range#cover?` are not equivalent behaviour.
|
14
|
-
#
|
15
|
-
# @example
|
16
|
-
# # bad
|
17
|
-
# ('a'..'z').include?('b') # => true
|
18
|
-
#
|
19
|
-
# # good
|
20
|
-
# ('a'..'z').cover?('b') # => true
|
21
|
-
#
|
22
|
-
# # Example of a case where `Range#cover?` may not provide
|
23
|
-
# # the desired result:
|
24
|
-
#
|
25
|
-
# ('a'..'z').cover?('yellow') # => true
|
26
|
-
class RangeInclude < Cop
|
27
|
-
MSG = 'Use `Range#cover?` instead of `Range#include?`.'.freeze
|
28
|
-
|
29
|
-
# TODO: If we traced out assignments of variables to their uses, we
|
30
|
-
# might pick up on a few more instances of this issue
|
31
|
-
# Right now, we only detect direct calls on a Range literal
|
32
|
-
# (We don't even catch it if the Range is in double parens)
|
33
|
-
|
34
|
-
def_node_matcher :range_include, <<-PATTERN
|
35
|
-
(send {irange erange (begin {irange erange})} :include? ...)
|
36
|
-
PATTERN
|
37
|
-
|
38
|
-
def on_send(node)
|
39
|
-
return unless range_include(node)
|
40
|
-
|
41
|
-
add_offense(node, location: :selector)
|
42
|
-
end
|
43
|
-
|
44
|
-
def autocorrect(node)
|
45
|
-
->(corrector) { corrector.replace(node.loc.selector, 'cover?') }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies the use of a `&block` parameter and `block.call`
|
7
|
-
# where `yield` would do just as well.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# def method(&block)
|
12
|
-
# block.call
|
13
|
-
# end
|
14
|
-
# def another(&func)
|
15
|
-
# func.call 1, 2, 3
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# # good
|
19
|
-
# def method
|
20
|
-
# yield
|
21
|
-
# end
|
22
|
-
# def another
|
23
|
-
# yield 1, 2, 3
|
24
|
-
# end
|
25
|
-
class RedundantBlockCall < Cop
|
26
|
-
MSG = 'Use `yield` instead of `%<argname>s.call`.'.freeze
|
27
|
-
YIELD = 'yield'.freeze
|
28
|
-
OPEN_PAREN = '('.freeze
|
29
|
-
CLOSE_PAREN = ')'.freeze
|
30
|
-
SPACE = ' '.freeze
|
31
|
-
|
32
|
-
def_node_matcher :blockarg_def, <<-PATTERN
|
33
|
-
{(def _ (args ... (blockarg $_)) $_)
|
34
|
-
(defs _ _ (args ... (blockarg $_)) $_)}
|
35
|
-
PATTERN
|
36
|
-
|
37
|
-
def_node_search :blockarg_calls, <<-PATTERN
|
38
|
-
(send (lvar %1) :call ...)
|
39
|
-
PATTERN
|
40
|
-
|
41
|
-
def_node_search :blockarg_assigned?, <<-PATTERN
|
42
|
-
(lvasgn %1 ...)
|
43
|
-
PATTERN
|
44
|
-
|
45
|
-
def on_def(node)
|
46
|
-
blockarg_def(node) do |argname, body|
|
47
|
-
next unless body
|
48
|
-
|
49
|
-
calls_to_report(argname, body).each do |blockcall|
|
50
|
-
add_offense(blockcall, message: format(MSG, argname: argname))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# offenses are registered on the `block.call` nodes
|
56
|
-
def autocorrect(node)
|
57
|
-
_receiver, _method, *args = *node
|
58
|
-
new_source = String.new(YIELD)
|
59
|
-
unless args.empty?
|
60
|
-
new_source += if parentheses?(node)
|
61
|
-
OPEN_PAREN
|
62
|
-
else
|
63
|
-
SPACE
|
64
|
-
end
|
65
|
-
|
66
|
-
new_source << args.map(&:source).join(', ')
|
67
|
-
end
|
68
|
-
|
69
|
-
new_source << CLOSE_PAREN if parentheses?(node) && !args.empty?
|
70
|
-
->(corrector) { corrector.replace(node.source_range, new_source) }
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def calls_to_report(argname, body)
|
76
|
-
return [] if blockarg_assigned?(body, argname)
|
77
|
-
|
78
|
-
calls = to_enum(:blockarg_calls, body, argname)
|
79
|
-
|
80
|
-
return [] if calls.any? { |call| args_include_block_pass?(call) }
|
81
|
-
|
82
|
-
calls
|
83
|
-
end
|
84
|
-
|
85
|
-
def args_include_block_pass?(blockcall)
|
86
|
-
_receiver, _call, *args = *blockcall
|
87
|
-
|
88
|
-
args.any?(&:block_pass_type?)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies the use of `Regexp#match` or `String#match`, which
|
7
|
-
# returns `#<MatchData>`/`nil`. The return value of `=~` is an integral
|
8
|
-
# index/`nil` and is more performant.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# # bad
|
12
|
-
# do_something if str.match(/regex/)
|
13
|
-
# while regex.match('str')
|
14
|
-
# do_something
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# # good
|
18
|
-
# method(str =~ /regex/)
|
19
|
-
# return value unless regex =~ 'str'
|
20
|
-
class RedundantMatch < Cop
|
21
|
-
MSG = 'Use `=~` in places where the `MatchData` returned by ' \
|
22
|
-
'`#match` will not be used.'.freeze
|
23
|
-
|
24
|
-
# 'match' is a fairly generic name, so we don't flag it unless we see
|
25
|
-
# a string or regexp literal on one side or the other
|
26
|
-
def_node_matcher :match_call?, <<-PATTERN
|
27
|
-
{(send {str regexp} :match _)
|
28
|
-
(send !nil? :match {str regexp})}
|
29
|
-
PATTERN
|
30
|
-
|
31
|
-
def_node_matcher :only_truthiness_matters?, <<-PATTERN
|
32
|
-
^({if while until case while_post until_post} equal?(%0) ...)
|
33
|
-
PATTERN
|
34
|
-
|
35
|
-
def on_send(node)
|
36
|
-
return unless match_call?(node) &&
|
37
|
-
(!node.value_used? || only_truthiness_matters?(node)) &&
|
38
|
-
!(node.parent && node.parent.block_type?)
|
39
|
-
|
40
|
-
add_offense(node)
|
41
|
-
end
|
42
|
-
|
43
|
-
def autocorrect(node)
|
44
|
-
# Regexp#match can take a second argument, but this cop doesn't
|
45
|
-
# register an offense in that case
|
46
|
-
return unless node.first_argument.regexp_type?
|
47
|
-
|
48
|
-
new_source =
|
49
|
-
node.receiver.source + ' =~ ' + node.first_argument.source
|
50
|
-
|
51
|
-
->(corrector) { corrector.replace(node.source_range, new_source) }
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,183 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies places where `Hash#merge!` can be replaced by
|
7
|
-
# `Hash#[]=`.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# hash.merge!(a: 1)
|
11
|
-
# hash.merge!({'key' => 'value'})
|
12
|
-
# hash.merge!(a: 1, b: 2)
|
13
|
-
class RedundantMerge < Cop
|
14
|
-
AREF_ASGN = '%<receiver>s[%<key>s] = %<value>s'.freeze
|
15
|
-
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
|
16
|
-
|
17
|
-
WITH_MODIFIER_CORRECTION = <<-RUBY.strip_indent
|
18
|
-
%<keyword>s %<condition>s
|
19
|
-
%<leading_space>s%<indent>s%<body>s
|
20
|
-
%<leading_space>send
|
21
|
-
RUBY
|
22
|
-
|
23
|
-
def_node_matcher :redundant_merge_candidate, <<-PATTERN
|
24
|
-
(send $!nil? :merge! [(hash $...) !kwsplat_type?])
|
25
|
-
PATTERN
|
26
|
-
|
27
|
-
def_node_matcher :modifier_flow_control?, <<-PATTERN
|
28
|
-
[{if while until} modifier_form?]
|
29
|
-
PATTERN
|
30
|
-
|
31
|
-
def on_send(node)
|
32
|
-
each_redundant_merge(node) do |redundant_merge_node|
|
33
|
-
add_offense(redundant_merge_node)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def autocorrect(node)
|
38
|
-
redundant_merge_candidate(node) do |receiver, pairs|
|
39
|
-
new_source = to_assignments(receiver, pairs).join("\n")
|
40
|
-
|
41
|
-
if node.parent && pairs.size > 1
|
42
|
-
correct_multiple_elements(node, node.parent, new_source)
|
43
|
-
else
|
44
|
-
correct_single_element(node, new_source)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def message(node)
|
52
|
-
redundant_merge_candidate(node) do |receiver, pairs|
|
53
|
-
assignments = to_assignments(receiver, pairs).join('; ')
|
54
|
-
|
55
|
-
format(MSG, prefer: assignments, current: node.source)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def each_redundant_merge(node)
|
60
|
-
redundant_merge_candidate(node) do |receiver, pairs|
|
61
|
-
next if non_redundant_merge?(node, receiver, pairs)
|
62
|
-
|
63
|
-
yield node
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def non_redundant_merge?(node, receiver, pairs)
|
68
|
-
non_redundant_pairs?(receiver, pairs) ||
|
69
|
-
kwsplat_used?(pairs) ||
|
70
|
-
non_redundant_value_used?(receiver, node)
|
71
|
-
end
|
72
|
-
|
73
|
-
def non_redundant_pairs?(receiver, pairs)
|
74
|
-
pairs.size > 1 && !receiver.pure? || pairs.size > max_key_value_pairs
|
75
|
-
end
|
76
|
-
|
77
|
-
def kwsplat_used?(pairs)
|
78
|
-
pairs.any?(&:kwsplat_type?)
|
79
|
-
end
|
80
|
-
|
81
|
-
def non_redundant_value_used?(receiver, node)
|
82
|
-
node.value_used? &&
|
83
|
-
!EachWithObjectInspector.new(node, receiver).value_used?
|
84
|
-
end
|
85
|
-
|
86
|
-
def correct_multiple_elements(node, parent, new_source)
|
87
|
-
if modifier_flow_control?(parent)
|
88
|
-
new_source = rewrite_with_modifier(node, parent, new_source)
|
89
|
-
node = parent
|
90
|
-
else
|
91
|
-
padding = "\n#{leading_spaces(node)}"
|
92
|
-
new_source.gsub!(/\n/, padding)
|
93
|
-
end
|
94
|
-
|
95
|
-
->(corrector) { corrector.replace(node.source_range, new_source) }
|
96
|
-
end
|
97
|
-
|
98
|
-
def correct_single_element(node, new_source)
|
99
|
-
->(corrector) { corrector.replace(node.source_range, new_source) }
|
100
|
-
end
|
101
|
-
|
102
|
-
def to_assignments(receiver, pairs)
|
103
|
-
pairs.map do |pair|
|
104
|
-
key, value = *pair
|
105
|
-
|
106
|
-
key = key.sym_type? && pair.colon? ? ":#{key.source}" : key.source
|
107
|
-
|
108
|
-
format(AREF_ASGN, receiver: receiver.source,
|
109
|
-
key: key,
|
110
|
-
value: value.source)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def rewrite_with_modifier(node, parent, new_source)
|
115
|
-
indent = ' ' * indent_width
|
116
|
-
padding = "\n#{indent + leading_spaces(node)}"
|
117
|
-
new_source.gsub!(/\n/, padding)
|
118
|
-
|
119
|
-
format(WITH_MODIFIER_CORRECTION, keyword: parent.loc.keyword.source,
|
120
|
-
condition: parent.condition.source,
|
121
|
-
leading_space: leading_spaces(node),
|
122
|
-
indent: indent,
|
123
|
-
body: new_source).chomp
|
124
|
-
end
|
125
|
-
|
126
|
-
def leading_spaces(node)
|
127
|
-
node.source_range.source_line[/\A\s*/]
|
128
|
-
end
|
129
|
-
|
130
|
-
def indent_width
|
131
|
-
@config.for_cop('IndentationWidth')['Width'] || 2
|
132
|
-
end
|
133
|
-
|
134
|
-
def max_key_value_pairs
|
135
|
-
Integer(cop_config['MaxKeyValuePairs'])
|
136
|
-
end
|
137
|
-
|
138
|
-
# A utility class for checking the use of values within an
|
139
|
-
# `each_with_object` call.
|
140
|
-
class EachWithObjectInspector
|
141
|
-
extend NodePattern::Macros
|
142
|
-
|
143
|
-
def initialize(node, receiver)
|
144
|
-
@node = node
|
145
|
-
@receiver = unwind(receiver)
|
146
|
-
end
|
147
|
-
|
148
|
-
def value_used?
|
149
|
-
return false unless eligible_receiver? && second_argument
|
150
|
-
|
151
|
-
receiver.loc.name.source == second_argument.loc.name.source
|
152
|
-
end
|
153
|
-
|
154
|
-
private
|
155
|
-
|
156
|
-
attr_reader :node, :receiver
|
157
|
-
|
158
|
-
def eligible_receiver?
|
159
|
-
receiver.respond_to?(:lvar_type?) && receiver.lvar_type?
|
160
|
-
end
|
161
|
-
|
162
|
-
def second_argument
|
163
|
-
parent = node.parent
|
164
|
-
parent = parent.parent if parent.begin_type?
|
165
|
-
|
166
|
-
@second_argument ||= each_with_object_node(parent)
|
167
|
-
end
|
168
|
-
|
169
|
-
def unwind(receiver)
|
170
|
-
while receiver.respond_to?(:send_type?) && receiver.send_type?
|
171
|
-
receiver, = *receiver
|
172
|
-
end
|
173
|
-
receiver
|
174
|
-
end
|
175
|
-
|
176
|
-
def_node_matcher :each_with_object_node, <<-PATTERN
|
177
|
-
(block (send _ :each_with_object _) (args _ $_) ...)
|
178
|
-
PATTERN
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
@@ -1,265 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# In Ruby 2.4, `String#match?`, `Regexp#match?`, and `Symbol#match?`
|
7
|
-
# have been added. The methods are faster than `match`.
|
8
|
-
# Because the methods avoid creating a `MatchData` object or saving
|
9
|
-
# backref.
|
10
|
-
# So, when `MatchData` is not used, use `match?` instead of `match`.
|
11
|
-
#
|
12
|
-
# @example
|
13
|
-
# # bad
|
14
|
-
# def foo
|
15
|
-
# if x =~ /re/
|
16
|
-
# do_something
|
17
|
-
# end
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# # bad
|
21
|
-
# def foo
|
22
|
-
# if x !~ /re/
|
23
|
-
# do_something
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# # bad
|
28
|
-
# def foo
|
29
|
-
# if x.match(/re/)
|
30
|
-
# do_something
|
31
|
-
# end
|
32
|
-
# end
|
33
|
-
#
|
34
|
-
# # bad
|
35
|
-
# def foo
|
36
|
-
# if /re/ === x
|
37
|
-
# do_something
|
38
|
-
# end
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# # good
|
42
|
-
# def foo
|
43
|
-
# if x.match?(/re/)
|
44
|
-
# do_something
|
45
|
-
# end
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# # good
|
49
|
-
# def foo
|
50
|
-
# if !x.match?(/re/)
|
51
|
-
# do_something
|
52
|
-
# end
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# # good
|
56
|
-
# def foo
|
57
|
-
# if x =~ /re/
|
58
|
-
# do_something(Regexp.last_match)
|
59
|
-
# end
|
60
|
-
# end
|
61
|
-
#
|
62
|
-
# # good
|
63
|
-
# def foo
|
64
|
-
# if x.match(/re/)
|
65
|
-
# do_something($~)
|
66
|
-
# end
|
67
|
-
# end
|
68
|
-
#
|
69
|
-
# # good
|
70
|
-
# def foo
|
71
|
-
# if /re/ === x
|
72
|
-
# do_something($~)
|
73
|
-
# end
|
74
|
-
# end
|
75
|
-
class RegexpMatch < Cop
|
76
|
-
extend TargetRubyVersion
|
77
|
-
|
78
|
-
minimum_target_ruby_version 2.4
|
79
|
-
|
80
|
-
# Constants are included in this list because it is unlikely that
|
81
|
-
# someone will store `nil` as a constant and then use it for comparison
|
82
|
-
TYPES_IMPLEMENTING_MATCH = %i[const regexp str sym].freeze
|
83
|
-
MSG =
|
84
|
-
'Use `match?` instead of `%<current>s` when `MatchData` ' \
|
85
|
-
'is not used.'.freeze
|
86
|
-
|
87
|
-
def_node_matcher :match_method?, <<-PATTERN
|
88
|
-
{
|
89
|
-
(send _recv :match _)
|
90
|
-
(send _recv :match _ (int ...))
|
91
|
-
}
|
92
|
-
PATTERN
|
93
|
-
|
94
|
-
def_node_matcher :match_operator?, <<-PATTERN
|
95
|
-
(send !nil? {:=~ :!~} !nil?)
|
96
|
-
PATTERN
|
97
|
-
|
98
|
-
def_node_matcher :match_threequals?, <<-PATTERN
|
99
|
-
(send (regexp (str _) {(regopt) (regopt _)}) :=== !nil?)
|
100
|
-
PATTERN
|
101
|
-
|
102
|
-
def match_with_lvasgn?(node)
|
103
|
-
return false unless node.match_with_lvasgn_type?
|
104
|
-
|
105
|
-
regexp, _rhs = *node
|
106
|
-
regexp.to_regexp.named_captures.empty?
|
107
|
-
end
|
108
|
-
|
109
|
-
MATCH_NODE_PATTERN = <<-PATTERN.freeze
|
110
|
-
{
|
111
|
-
#match_method?
|
112
|
-
#match_operator?
|
113
|
-
#match_threequals?
|
114
|
-
#match_with_lvasgn?
|
115
|
-
}
|
116
|
-
PATTERN
|
117
|
-
|
118
|
-
def_node_matcher :match_node?, MATCH_NODE_PATTERN
|
119
|
-
def_node_search :search_match_nodes, MATCH_NODE_PATTERN
|
120
|
-
|
121
|
-
def_node_search :last_matches, <<-PATTERN
|
122
|
-
{
|
123
|
-
(send (const nil? :Regexp) :last_match)
|
124
|
-
(send (const nil? :Regexp) :last_match _)
|
125
|
-
({back_ref nth_ref} _)
|
126
|
-
(gvar #match_gvar?)
|
127
|
-
}
|
128
|
-
PATTERN
|
129
|
-
|
130
|
-
def on_if(node)
|
131
|
-
check_condition(node.condition)
|
132
|
-
end
|
133
|
-
|
134
|
-
def on_case(node)
|
135
|
-
return if node.condition
|
136
|
-
|
137
|
-
node.each_when do |when_node|
|
138
|
-
when_node.each_condition do |condition|
|
139
|
-
check_condition(condition)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def autocorrect(node)
|
145
|
-
lambda do |corrector|
|
146
|
-
if match_method?(node)
|
147
|
-
corrector.replace(node.loc.selector, 'match?')
|
148
|
-
elsif match_operator?(node) || match_threequals?(node)
|
149
|
-
recv, oper, arg = *node
|
150
|
-
correct_operator(corrector, recv, arg, oper)
|
151
|
-
elsif match_with_lvasgn?(node)
|
152
|
-
recv, arg = *node
|
153
|
-
correct_operator(corrector, recv, arg)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
def check_condition(cond)
|
161
|
-
match_node?(cond) do
|
162
|
-
return if last_match_used?(cond)
|
163
|
-
|
164
|
-
add_offense(cond)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def message(node)
|
169
|
-
format(MSG, current: node.loc.selector.source)
|
170
|
-
end
|
171
|
-
|
172
|
-
def last_match_used?(match_node)
|
173
|
-
scope_root = scope_root(match_node)
|
174
|
-
body = scope_root ? scope_body(scope_root) : match_node.ancestors.last
|
175
|
-
|
176
|
-
return true if match_node.parent.if_type? &&
|
177
|
-
match_node.parent.modifier_form?
|
178
|
-
|
179
|
-
match_node_pos = match_node.loc.expression.begin_pos
|
180
|
-
|
181
|
-
next_match_pos = next_match_pos(body, match_node_pos, scope_root)
|
182
|
-
range = match_node_pos..next_match_pos
|
183
|
-
|
184
|
-
find_last_match(body, range, scope_root)
|
185
|
-
end
|
186
|
-
|
187
|
-
def next_match_pos(body, match_node_pos, scope_root)
|
188
|
-
node = search_match_nodes(body).find do |match|
|
189
|
-
match.loc.expression.begin_pos > match_node_pos &&
|
190
|
-
scope_root(match) == scope_root
|
191
|
-
end
|
192
|
-
node ? node.loc.expression.begin_pos : Float::INFINITY
|
193
|
-
end
|
194
|
-
|
195
|
-
def find_last_match(body, range, scope_root)
|
196
|
-
last_matches(body).find do |ref|
|
197
|
-
ref_pos = ref.loc.expression.begin_pos
|
198
|
-
range.cover?(ref_pos) &&
|
199
|
-
scope_root(ref) == scope_root
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
def scope_body(node)
|
204
|
-
children = node.children
|
205
|
-
case node.type
|
206
|
-
when :module
|
207
|
-
children[1]
|
208
|
-
when :defs
|
209
|
-
children[3]
|
210
|
-
else
|
211
|
-
children[2]
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def scope_root(node)
|
216
|
-
node.each_ancestor.find do |ancestor|
|
217
|
-
ancestor.def_type? ||
|
218
|
-
ancestor.defs_type? ||
|
219
|
-
ancestor.class_type? ||
|
220
|
-
ancestor.module_type?
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
def match_gvar?(sym)
|
225
|
-
%i[
|
226
|
-
$~
|
227
|
-
$MATCH
|
228
|
-
$PREMATCH
|
229
|
-
$POSTMATCH
|
230
|
-
$LAST_PAREN_MATCH
|
231
|
-
$LAST_MATCH_INFO
|
232
|
-
].include?(sym)
|
233
|
-
end
|
234
|
-
|
235
|
-
def correct_operator(corrector, recv, arg, oper = nil)
|
236
|
-
op_range = correction_range(recv, arg)
|
237
|
-
|
238
|
-
if TYPES_IMPLEMENTING_MATCH.include?(recv.type)
|
239
|
-
corrector.replace(op_range, '.match?(')
|
240
|
-
elsif TYPES_IMPLEMENTING_MATCH.include?(arg.type)
|
241
|
-
corrector.replace(op_range, '.match?(')
|
242
|
-
swap_receiver_and_arg(corrector, recv, arg)
|
243
|
-
else
|
244
|
-
corrector.replace(op_range, '&.match?(')
|
245
|
-
end
|
246
|
-
|
247
|
-
corrector.insert_after(arg.loc.expression, ')')
|
248
|
-
corrector.insert_before(recv.loc.expression, '!') if oper == :!~
|
249
|
-
end
|
250
|
-
|
251
|
-
def swap_receiver_and_arg(corrector, recv, arg)
|
252
|
-
corrector.replace(recv.loc.expression, arg.source)
|
253
|
-
corrector.replace(arg.loc.expression, recv.source)
|
254
|
-
end
|
255
|
-
|
256
|
-
def correction_range(recv, arg)
|
257
|
-
buffer = processed_source.buffer
|
258
|
-
op_begin_pos = recv.loc.expression.end_pos
|
259
|
-
op_end_pos = arg.loc.expression.begin_pos
|
260
|
-
Parser::Source::Range.new(buffer, op_begin_pos, op_end_pos)
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
end
|
265
|
-
end
|