rubocop 0.67.2 → 0.68.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +86 -233
  4. data/exe/rubocop +0 -12
  5. data/lib/rubocop.rb +13 -30
  6. data/lib/rubocop/ast/builder.rb +4 -0
  7. data/lib/rubocop/ast/node/alias_node.rb +24 -0
  8. data/lib/rubocop/ast/node/class_node.rb +31 -0
  9. data/lib/rubocop/ast/node/module_node.rb +24 -0
  10. data/lib/rubocop/ast/node/range_node.rb +7 -0
  11. data/lib/rubocop/ast/node/resbody_node.rb +12 -0
  12. data/lib/rubocop/ast/node/self_class_node.rb +24 -0
  13. data/lib/rubocop/cli.rb +40 -4
  14. data/lib/rubocop/config.rb +9 -7
  15. data/lib/rubocop/config_loader.rb +48 -7
  16. data/lib/rubocop/config_loader_resolver.rb +5 -4
  17. data/lib/rubocop/cop/commissioner.rb +24 -0
  18. data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +18 -6
  19. data/lib/rubocop/cop/internal_affairs/node_destructuring.rb +12 -14
  20. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +9 -20
  21. data/lib/rubocop/cop/layout/align_arguments.rb +93 -0
  22. data/lib/rubocop/cop/layout/align_parameters.rb +57 -33
  23. data/lib/rubocop/cop/layout/class_structure.rb +5 -5
  24. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -8
  25. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +3 -6
  26. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +1 -2
  27. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +1 -0
  28. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +292 -0
  29. data/lib/rubocop/cop/layout/{first_parameter_indentation.rb → indent_first_argument.rb} +11 -12
  30. data/lib/rubocop/cop/layout/{indent_array.rb → indent_first_array_element.rb} +2 -2
  31. data/lib/rubocop/cop/layout/{indent_hash.rb → indent_first_hash_element.rb} +2 -2
  32. data/lib/rubocop/cop/layout/indent_first_parameter.rb +96 -0
  33. data/lib/rubocop/cop/layout/indentation_width.rb +4 -16
  34. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -4
  35. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -16
  36. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -2
  37. data/lib/rubocop/cop/lint/duplicate_methods.rb +6 -8
  38. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -8
  39. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +157 -0
  40. data/lib/rubocop/cop/lint/inherit_exception.rb +3 -4
  41. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +18 -1
  42. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -5
  43. data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +25 -5
  44. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -6
  45. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -2
  46. data/lib/rubocop/cop/message_annotator.rb +1 -0
  47. data/lib/rubocop/cop/metrics/line_length.rb +139 -28
  48. data/lib/rubocop/cop/metrics/perceived_complexity.rb +3 -4
  49. data/lib/rubocop/cop/mixin/check_line_breakable.rb +190 -0
  50. data/lib/rubocop/cop/mixin/{array_hash_indentation.rb → multiline_element_indentation.rb} +3 -2
  51. data/lib/rubocop/cop/mixin/too_many_lines.rb +3 -7
  52. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +33 -4
  53. data/lib/rubocop/cop/rails/active_record_override.rb +23 -8
  54. data/lib/rubocop/cop/rails/delegate.rb +5 -8
  55. data/lib/rubocop/cop/rails/environment_comparison.rb +5 -3
  56. data/lib/rubocop/cop/rails/find_each.rb +1 -1
  57. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +3 -3
  58. data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
  59. data/lib/rubocop/cop/rails/skips_model_validations.rb +6 -7
  60. data/lib/rubocop/cop/rails/time_zone.rb +3 -10
  61. data/lib/rubocop/cop/rails/validation.rb +3 -0
  62. data/lib/rubocop/cop/registry.rb +3 -3
  63. data/lib/rubocop/cop/style/alias.rb +13 -7
  64. data/lib/rubocop/cop/style/block_delimiters.rb +20 -0
  65. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -21
  66. data/lib/rubocop/cop/style/class_methods.rb +16 -24
  67. data/lib/rubocop/cop/style/conditional_assignment.rb +20 -49
  68. data/lib/rubocop/cop/style/documentation.rb +3 -7
  69. data/lib/rubocop/cop/style/format_string.rb +18 -21
  70. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  71. data/lib/rubocop/cop/style/inverse_methods.rb +4 -0
  72. data/lib/rubocop/cop/style/lambda.rb +12 -8
  73. data/lib/rubocop/cop/style/mixin_grouping.rb +8 -10
  74. data/lib/rubocop/cop/style/module_function.rb +2 -3
  75. data/lib/rubocop/cop/style/next.rb +10 -14
  76. data/lib/rubocop/cop/style/one_line_conditional.rb +5 -3
  77. data/lib/rubocop/cop/style/optional_arguments.rb +1 -4
  78. data/lib/rubocop/cop/style/random_with_offset.rb +44 -47
  79. data/lib/rubocop/cop/style/redundant_return.rb +6 -14
  80. data/lib/rubocop/cop/style/redundant_sort_by.rb +1 -1
  81. data/lib/rubocop/cop/style/safe_navigation.rb +3 -0
  82. data/lib/rubocop/cop/style/struct_inheritance.rb +2 -3
  83. data/lib/rubocop/cop/style/symbol_proc.rb +20 -40
  84. data/lib/rubocop/cop/style/unless_else.rb +1 -2
  85. data/lib/rubocop/cop/style/yoda_condition.rb +8 -7
  86. data/lib/rubocop/cop/util.rb +2 -4
  87. data/lib/rubocop/file_finder.rb +5 -10
  88. data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -0
  89. data/lib/rubocop/node_pattern.rb +304 -170
  90. data/lib/rubocop/options.rb +4 -1
  91. data/lib/rubocop/rspec/shared_contexts.rb +3 -0
  92. data/lib/rubocop/version.rb +1 -1
  93. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  94. metadata +26 -50
  95. data/lib/rubocop/cop/performance/caller.rb +0 -69
  96. data/lib/rubocop/cop/performance/case_when_splat.rb +0 -177
  97. data/lib/rubocop/cop/performance/casecmp.rb +0 -108
  98. data/lib/rubocop/cop/performance/chain_array_allocation.rb +0 -78
  99. data/lib/rubocop/cop/performance/compare_with_block.rb +0 -122
  100. data/lib/rubocop/cop/performance/count.rb +0 -102
  101. data/lib/rubocop/cop/performance/detect.rb +0 -110
  102. data/lib/rubocop/cop/performance/double_start_end_with.rb +0 -94
  103. data/lib/rubocop/cop/performance/end_with.rb +0 -56
  104. data/lib/rubocop/cop/performance/fixed_size.rb +0 -97
  105. data/lib/rubocop/cop/performance/flat_map.rb +0 -78
  106. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +0 -99
  107. data/lib/rubocop/cop/performance/open_struct.rb +0 -46
  108. data/lib/rubocop/cop/performance/range_include.rb +0 -50
  109. data/lib/rubocop/cop/performance/redundant_block_call.rb +0 -93
  110. data/lib/rubocop/cop/performance/redundant_match.rb +0 -56
  111. data/lib/rubocop/cop/performance/redundant_merge.rb +0 -183
  112. data/lib/rubocop/cop/performance/regexp_match.rb +0 -265
  113. data/lib/rubocop/cop/performance/reverse_each.rb +0 -42
  114. data/lib/rubocop/cop/performance/size.rb +0 -77
  115. data/lib/rubocop/cop/performance/start_with.rb +0 -59
  116. data/lib/rubocop/cop/performance/string_replacement.rb +0 -173
  117. data/lib/rubocop/cop/performance/times_map.rb +0 -71
  118. data/lib/rubocop/cop/performance/unfreeze_string.rb +0 -50
  119. 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