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.
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