rubocop 0.86.0 → 0.87.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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +46 -4
  4. data/lib/rubocop.rb +7 -1
  5. data/lib/rubocop/cli.rb +0 -2
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +40 -5
  7. data/lib/rubocop/cli/command/show_cops.rb +1 -1
  8. data/lib/rubocop/config_loader.rb +22 -62
  9. data/lib/rubocop/config_obsoletion.rb +0 -1
  10. data/lib/rubocop/cop/autocorrect_logic.rb +13 -23
  11. data/lib/rubocop/cop/base.rb +399 -0
  12. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +10 -20
  13. data/lib/rubocop/cop/commissioner.rb +48 -50
  14. data/lib/rubocop/cop/cop.rb +85 -236
  15. data/lib/rubocop/cop/corrector.rb +38 -115
  16. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  17. data/lib/rubocop/cop/generator.rb +1 -1
  18. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +11 -14
  19. data/lib/rubocop/cop/layout/case_indentation.rb +18 -19
  20. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -8
  21. data/lib/rubocop/cop/layout/first_argument_indentation.rb +4 -0
  22. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -2
  23. data/lib/rubocop/cop/layout/multiline_block_layout.rb +0 -1
  24. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +19 -25
  25. data/lib/rubocop/cop/legacy/corrections_proxy.rb +49 -0
  26. data/lib/rubocop/cop/legacy/corrector.rb +29 -0
  27. data/lib/rubocop/cop/lint/interpolation_check.rb +13 -0
  28. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  29. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +2 -2
  30. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +8 -3
  31. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  32. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +27 -23
  33. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +2 -2
  34. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +8 -0
  35. data/lib/rubocop/cop/lint/syntax.rb +11 -26
  36. data/lib/rubocop/cop/lint/unused_method_argument.rb +1 -1
  37. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
  38. data/lib/rubocop/cop/metrics/block_length.rb +22 -0
  39. data/lib/rubocop/cop/metrics/class_length.rb +25 -2
  40. data/lib/rubocop/cop/metrics/method_length.rb +23 -0
  41. data/lib/rubocop/cop/metrics/module_length.rb +25 -2
  42. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +129 -0
  43. data/lib/rubocop/cop/mixin/allowed_methods.rb +19 -0
  44. data/lib/rubocop/cop/mixin/auto_corrector.rb +12 -0
  45. data/lib/rubocop/cop/mixin/code_length.rb +4 -0
  46. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -1
  47. data/lib/rubocop/cop/mixin/enforce_superclass.rb +3 -1
  48. data/lib/rubocop/cop/mixin/nil_methods.rb +3 -5
  49. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +6 -1
  50. data/lib/rubocop/cop/mixin/surrounding_space.rb +7 -2
  51. data/lib/rubocop/cop/mixin/too_many_lines.rb +3 -13
  52. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +4 -2
  53. data/lib/rubocop/cop/naming/ascii_identifiers.rb +27 -4
  54. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +2 -2
  55. data/lib/rubocop/cop/naming/method_name.rb +1 -1
  56. data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
  57. data/lib/rubocop/cop/naming/predicate_name.rb +3 -5
  58. data/lib/rubocop/cop/naming/variable_name.rb +1 -1
  59. data/lib/rubocop/cop/naming/variable_number.rb +1 -1
  60. data/lib/rubocop/cop/offense.rb +16 -2
  61. data/lib/rubocop/cop/style/accessor_grouping.rb +136 -0
  62. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +121 -0
  63. data/lib/rubocop/cop/style/class_vars.rb +21 -0
  64. data/lib/rubocop/cop/style/date_time.rb +1 -1
  65. data/lib/rubocop/cop/style/dir.rb +2 -2
  66. data/lib/rubocop/cop/style/empty_literal.rb +5 -5
  67. data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -2
  68. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -0
  69. data/lib/rubocop/cop/style/multiline_block_chain.rb +10 -1
  70. data/lib/rubocop/cop/style/mutable_constant.rb +4 -4
  71. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +2 -5
  72. data/lib/rubocop/cop/style/proc.rb +1 -1
  73. data/lib/rubocop/cop/style/random_with_offset.rb +4 -10
  74. data/lib/rubocop/cop/style/redundant_assignment.rb +117 -0
  75. data/lib/rubocop/cop/style/redundant_exception.rb +14 -10
  76. data/lib/rubocop/cop/style/redundant_fetch_block.rb +26 -7
  77. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  78. data/lib/rubocop/cop/style/redundant_parentheses.rb +7 -1
  79. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +2 -1
  80. data/lib/rubocop/cop/style/rescue_standard_error.rb +1 -1
  81. data/lib/rubocop/cop/style/signal_exception.rb +1 -1
  82. data/lib/rubocop/cop/style/stderr_puts.rb +1 -1
  83. data/lib/rubocop/cop/style/struct_inheritance.rb +2 -2
  84. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  85. data/lib/rubocop/cop/style/trivial_accessors.rb +8 -7
  86. data/lib/rubocop/cop/style/zero_length_predicate.rb +2 -2
  87. data/lib/rubocop/cop/team.rb +97 -81
  88. data/lib/rubocop/cop/utils/format_string.rb +1 -2
  89. data/lib/rubocop/name_similarity.rb +1 -3
  90. data/lib/rubocop/options.rb +15 -8
  91. data/lib/rubocop/rake_task.rb +6 -9
  92. data/lib/rubocop/rspec/cop_helper.rb +4 -4
  93. data/lib/rubocop/rspec/expect_offense.rb +10 -16
  94. data/lib/rubocop/rspec/shared_contexts.rb +7 -7
  95. data/lib/rubocop/runner.rb +31 -29
  96. data/lib/rubocop/target_ruby.rb +1 -1
  97. data/lib/rubocop/version.rb +1 -1
  98. metadata +15 -7
  99. data/lib/rubocop/cop/mixin/classish_length.rb +0 -37
@@ -25,8 +25,9 @@ module RuboCop
25
25
  # # good
26
26
  # source 'https://rubygems.org' # strongly recommended
27
27
  # source 'http://rubygems.org'
28
- class InsecureProtocolSource < Cop
28
+ class InsecureProtocolSource < Base
29
29
  include RangeHelp
30
+ extend AutoCorrector
30
31
 
31
32
  MSG = 'The source `:%<source>s` is deprecated because HTTP requests ' \
32
33
  'are insecure. ' \
@@ -35,34 +36,23 @@ module RuboCop
35
36
 
36
37
  def_node_matcher :insecure_protocol_source?, <<~PATTERN
37
38
  (send nil? :source
38
- (sym ${:gemcutter :rubygems :rubyforge}))
39
+ $(sym ${:gemcutter :rubygems :rubyforge}))
39
40
  PATTERN
40
41
 
41
42
  def on_send(node)
42
- insecure_protocol_source?(node) do |source|
43
+ insecure_protocol_source?(node) do |source_node, source|
43
44
  message = format(MSG, source: source)
44
45
 
45
46
  add_offense(
46
- node,
47
- location: range(node.first_argument.loc.expression),
47
+ source_node,
48
48
  message: message
49
- )
49
+ ) do |corrector|
50
+ corrector.replace(
51
+ source_node, "'https://rubygems.org'"
52
+ )
53
+ end
50
54
  end
51
55
  end
52
-
53
- def autocorrect(node)
54
- lambda do |corrector|
55
- corrector.replace(
56
- node.first_argument, "'https://rubygems.org'"
57
- )
58
- end
59
- end
60
-
61
- private
62
-
63
- def range(node)
64
- range_between(node.begin_pos, node.end_pos)
65
- end
66
56
  end
67
57
  end
68
58
  end
@@ -7,6 +7,35 @@ module RuboCop
7
7
  class Commissioner
8
8
  include RuboCop::AST::Traversal
9
9
 
10
+ # How a Commissioner returns the results of the investigation
11
+ # as a list of Cop::InvestigationReport and any errors caught
12
+ # during the investigation.
13
+ # Immutable
14
+ # Consider creation API private
15
+ InvestigationReport = Struct.new(:processed_source, :cop_reports, :errors) do
16
+ def cops
17
+ @cops ||= cop_reports.map(&:cop)
18
+ end
19
+
20
+ def offenses_per_cop
21
+ @offenses_per_cop ||= cop_reports.map(&:offenses)
22
+ end
23
+
24
+ def correctors
25
+ @correctors ||= cop_reports.map(&:corrector)
26
+ end
27
+
28
+ def offenses
29
+ @offenses ||= offenses_per_cop.flatten(1)
30
+ end
31
+
32
+ def merge(investigation)
33
+ InvestigationReport.new(processed_source,
34
+ cop_reports + investigation.cop_reports,
35
+ errors + investigation.errors)
36
+ end
37
+ end
38
+
10
39
  attr_reader :errors
11
40
 
12
41
  def initialize(cops, forces = [], options = {})
@@ -15,7 +44,7 @@ module RuboCop
15
44
  @options = options
16
45
  @callbacks = {}
17
46
 
18
- reset_errors
47
+ reset
19
48
  end
20
49
 
21
50
  # Create methods like :on_send, :on_super, etc. They will be called
@@ -34,15 +63,21 @@ module RuboCop
34
63
  end
35
64
  end
36
65
 
66
+ # @return [InvestigationReport]
37
67
  def investigate(processed_source)
38
- reset_errors
39
- reset_callbacks
40
- prepare(processed_source)
41
- invoke_custom_processing(@cops, processed_source)
42
- invoke_custom_processing(@forces, processed_source)
43
- walk(processed_source.ast) unless processed_source.blank?
44
- invoke_custom_post_walk_processing(@cops, processed_source)
45
- @cops.flat_map(&:offenses)
68
+ reset
69
+
70
+ @cops.each { |cop| cop.send :begin_investigation, processed_source }
71
+ if processed_source.valid_syntax?
72
+ invoke(:on_new_investigation, @cops)
73
+ invoke(:investigate, @forces, processed_source)
74
+ walk(processed_source.ast) unless @cops.empty?
75
+ invoke(:on_investigation_end, @cops)
76
+ else
77
+ invoke(:on_other_file, @cops)
78
+ end
79
+ reports = @cops.map { |cop| cop.send(:complete_investigation) }
80
+ InvestigationReport.new(processed_source, reports, @errors)
46
81
  end
47
82
 
48
83
  private
@@ -58,52 +93,15 @@ module RuboCop
58
93
  end
59
94
  end
60
95
 
61
- def reset_errors
96
+ def reset
62
97
  @errors = []
98
+ @callbacks = {}
63
99
  end
64
100
 
65
- def reset_callbacks
66
- @callbacks.clear
67
- end
68
-
69
- # TODO: Bad design.
70
- def prepare(processed_source)
71
- @cops.each { |cop| cop.processed_source = processed_source }
72
- end
73
-
74
- # There are cops/forces that require their own custom processing.
75
- # If they define the #investigate method, all input parameters passed
76
- # to the commissioner will be passed to the cop too in order to do
77
- # its own processing.
78
- #
79
- # These custom processors are invoked before the AST traversal,
80
- # so they can build initial state that is later used by callbacks
81
- # during the AST traversal.
82
- def invoke_custom_processing(cops_or_forces, processed_source)
83
- cops_or_forces.each do |cop|
84
- next unless cop.respond_to?(:investigate)
85
-
86
- with_cop_error_handling(cop) do
87
- cop.investigate(processed_source)
88
- end
89
- end
90
- end
91
-
92
- # There are cops that require their own custom processing **after**
93
- # the AST traversal. By performing the walk before invoking these
94
- # custom processors, we allow these cops to build their own
95
- # state during the primary AST traversal instead of performing their
96
- # own AST traversals. Minimizing the number of walks is more efficient.
97
- #
98
- # If they define the #investigate_post_walk method, all input parameters
99
- # passed to the commissioner will be passed to the cop too in order to do
100
- # its own processing.
101
- def invoke_custom_post_walk_processing(cops, processed_source)
101
+ def invoke(callback, cops, *args)
102
102
  cops.each do |cop|
103
- next unless cop.respond_to?(:investigate_post_walk)
104
-
105
103
  with_cop_error_handling(cop) do
106
- cop.investigate_post_walk(processed_source)
104
+ cop.send(callback, *args)
107
105
  end
108
106
  end
109
107
  end
@@ -1,36 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'uri'
4
+ require_relative 'legacy/corrections_proxy.rb'
4
5
 
5
6
  module RuboCop
6
7
  module Cop
7
- # A scaffold for concrete cops.
8
- #
9
- # The Cop class is meant to be extended.
10
- #
11
- # Cops track offenses and can autocorrect them on the fly.
12
- #
13
- # A commissioner object is responsible for traversing the AST and invoking
14
- # the specific callbacks on each cop.
15
- # If a cop needs to do its own processing of the AST or depends on
16
- # something else, it should define the `#investigate` method and do
17
- # the processing there.
18
- #
19
- # @example
20
- #
21
- # class CustomCop < Cop
22
- # def investigate(processed_source)
23
- # # Do custom processing
24
- # end
25
- # end
26
- class Cop # rubocop:disable Metrics/ClassLength
27
- extend RuboCop::AST::Sexp
28
- extend NodePattern::Macros
29
- include RuboCop::AST::Sexp
30
- include Util
31
- include IgnoredNode
32
- include AutocorrectLogic
8
+ # @deprecated Use Cop::Base instead
9
+ # Legacy scaffold for Cops.
10
+ # See https://docs.rubocop.org/rubocop/cop_api_v1_changelog.html
11
+ class Cop < Base
12
+ attr_reader :offenses
33
13
 
14
+ exclude_from_registry
15
+
16
+ # @deprecated
34
17
  Correction = Struct.new(:lambda, :node, :cop) do
35
18
  def call(corrector)
36
19
  lambda.call(corrector)
@@ -41,90 +24,16 @@ module RuboCop
41
24
  end
42
25
  end
43
26
 
44
- attr_reader :config, :offenses, :corrections
45
- attr_accessor :processed_source # TODO: Bad design.
46
-
47
- def self.inherited(subclass)
48
- Registry.global.enlist(subclass)
49
- end
50
-
51
- def self.exclude_from_registry
52
- Registry.global.dismiss(self)
53
- end
54
-
55
- def self.badge
56
- @badge ||= Badge.for(name)
57
- end
58
-
59
- def self.cop_name
60
- badge.to_s
61
- end
62
-
63
- def self.department
64
- badge.department
65
- end
66
-
67
- def self.lint?
68
- department == :Lint
69
- end
70
-
71
- # Returns true if the cop name or the cop namespace matches any of the
72
- # given names.
73
- def self.match?(given_names)
74
- return false unless given_names
75
-
76
- given_names.include?(cop_name) ||
77
- given_names.include?(department.to_s)
78
- end
79
-
80
- # List of cops that should not try to autocorrect at the same
81
- # time as this cop
82
- #
83
- # @return [Array<RuboCop::Cop::Cop>]
84
- #
85
- # @api public
86
- def self.autocorrect_incompatible_with
87
- []
88
- end
89
-
90
- def initialize(config = nil, options = nil)
91
- @config = config || Config.new
92
- @options = options || { debug: false }
93
-
94
- @offenses = []
95
- @corrections = []
96
- @corrected_nodes = {}
97
- @corrected_nodes.compare_by_identity
98
- @processed_source = nil
99
- end
100
-
101
- def join_force?(_force_class)
102
- false
103
- end
104
-
105
- def cop_config
106
- # Use department configuration as basis, but let individual cop
107
- # configuration override.
108
- @cop_config ||= @config.for_cop(self.class.department.to_s)
109
- .merge(@config.for_cop(self))
110
- end
111
-
112
- def message(_node = nil)
113
- self.class::MSG
114
- end
115
-
116
- def add_offense(node, location: :expression, message: nil, severity: nil)
117
- loc = find_location(node, location)
118
-
119
- return if duplicate_location?(loc)
120
-
121
- severity = find_severity(node, severity)
122
- message = find_message(node, message)
123
-
124
- status = enabled_line?(loc.line) ? correct(node) : :disabled
125
-
126
- @offenses << Offense.new(severity, loc, message, name, status)
127
- yield if block_given? && status != :disabled
27
+ def add_offense(node_or_range, location: :expression, message: nil, severity: nil, &block)
28
+ @v0_argument = node_or_range
29
+ range = find_location(node_or_range, location)
30
+ if block.nil? && !autocorrect?
31
+ super(range, message: message, severity: severity)
32
+ else
33
+ super(range, message: message, severity: severity) do |corrector|
34
+ emulate_v0_callsequence(corrector, &block)
35
+ end
36
+ end
128
37
  end
129
38
 
130
39
  def find_location(node, loc)
@@ -132,108 +41,43 @@ module RuboCop
132
41
  loc.is_a?(Symbol) ? node.loc.public_send(loc) : loc
133
42
  end
134
43
 
135
- def duplicate_location?(location)
136
- @offenses.any? { |o| o.location == location }
44
+ # @deprecated Use class method
45
+ def support_autocorrect?
46
+ # warn 'deprecated, use cop.class.support_autocorrect?' TODO
47
+ self.class.support_autocorrect?
137
48
  end
138
49
 
139
- def correct(node) # rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength
140
- reason = reason_to_not_correct(node)
141
- return reason if reason
142
-
143
- @corrected_nodes[node] = true
144
-
145
- if support_autocorrect?
146
- correction = autocorrect(node)
147
-
148
- if correction
149
- @corrections << Correction.new(correction, node, self)
150
- :corrected
151
- elsif disable_uncorrectable?
152
- disable_uncorrectable(node)
153
- :corrected_with_todo
154
- else
155
- :uncorrected
156
- end
157
- elsif disable_uncorrectable?
158
- disable_uncorrectable(node)
159
- :corrected_with_todo
160
- end
161
- end
162
-
163
- def reason_to_not_correct(node)
164
- return :unsupported unless correctable?
165
- return :uncorrected unless autocorrect?
166
- return :already_corrected if @corrected_nodes.key?(node)
167
-
168
- nil
50
+ def self.support_autocorrect?
51
+ method_defined?(:autocorrect)
169
52
  end
170
53
 
171
- def disable_uncorrectable(node)
172
- return unless node
173
-
174
- @disabled_lines ||= {}
175
- line = node.location.line
176
- return if @disabled_lines.key?(line)
54
+ def self.joining_forces
55
+ return unless method_defined?(:join_force?)
177
56
 
178
- @disabled_lines[line] = true
179
- @corrections << Correction.new(disable_offense(node), node, self)
180
- end
181
-
182
- def config_to_allow_offenses
183
- Formatter::DisabledConfigFormatter
184
- .config_to_allow_offenses[cop_name] ||= {}
185
- end
186
-
187
- def config_to_allow_offenses=(hash)
188
- Formatter::DisabledConfigFormatter.config_to_allow_offenses[cop_name] =
189
- hash
190
- end
191
-
192
- def target_ruby_version
193
- @config.target_ruby_version
194
- end
195
-
196
- def target_rails_version
197
- @config.target_rails_version
198
- end
199
-
200
- def parse(source, path = nil)
201
- ProcessedSource.new(source, target_ruby_version, path)
202
- end
203
-
204
- def cop_name
205
- @cop_name ||= self.class.cop_name
57
+ cop = new
58
+ Force.all.select do |force_class|
59
+ cop.join_force?(force_class)
60
+ end
206
61
  end
207
62
 
208
- alias name cop_name
63
+ # @deprecated
64
+ def corrections
65
+ # warn 'Cop#corrections is deprecated' TODO
66
+ return [] unless @last_corrector
209
67
 
210
- def relevant_file?(file)
211
- file == RuboCop::AST::ProcessedSource::STRING_SOURCE_NAME ||
212
- file_name_matches_any?(file, 'Include', true) &&
213
- !file_name_matches_any?(file, 'Exclude', false)
68
+ Legacy::CorrectionsProxy.new(@last_corrector)
214
69
  end
215
70
 
216
- def excluded_file?(file)
217
- !relevant_file?(file)
71
+ # Called before all on_... have been called
72
+ def on_new_investigation
73
+ investigate(processed_source) if respond_to?(:investigate)
74
+ super
218
75
  end
219
76
 
220
- # This method should be overridden when a cop's behavior depends
221
- # on state that lives outside of these locations:
222
- #
223
- # (1) the file under inspection
224
- # (2) the cop's source code
225
- # (3) the config (eg a .rubocop.yml file)
226
- #
227
- # For example, some cops may want to look at other parts of
228
- # the codebase being inspected to find violations. A cop may
229
- # use the presence or absence of file `foo.rb` to determine
230
- # whether a certain violation exists in `bar.rb`.
231
- #
232
- # Overriding this method allows the cop to indicate to RuboCop's
233
- # ResultCache system when those external dependencies change,
234
- # ie when the ResultCache should be invalidated.
235
- def external_dependency_checksum
236
- nil
77
+ # Called after all on_... have been called
78
+ def on_investigation_end
79
+ investigate_post_walk(processed_source) if respond_to?(:investigate_post_walk)
80
+ super
237
81
  end
238
82
 
239
83
  ### Deprecated registry access
@@ -253,58 +97,63 @@ module RuboCop
253
97
  Registry.qualified_cop_name(name, origin)
254
98
  end
255
99
 
100
+ # @deprecated
101
+ # Open issue if there's a valid use case to include this in Base
102
+ def parse(source, path = nil)
103
+ ProcessedSource.new(source, target_ruby_version, path)
104
+ end
105
+
256
106
  private
257
107
 
258
- def find_message(node, message)
259
- annotate(message || message(node))
108
+ def begin_investigation(processed_source)
109
+ super
110
+ @offenses = @current_offenses
111
+ @last_corrector = @current_corrector
260
112
  end
261
113
 
262
- def annotate(message)
263
- RuboCop::Cop::MessageAnnotator.new(
264
- config, cop_name, cop_config, @options
265
- ).annotate(message)
114
+ # Override Base
115
+ def callback_argument(_range)
116
+ @v0_argument
266
117
  end
267
118
 
268
- def file_name_matches_any?(file, parameter, default_result)
269
- patterns = cop_config[parameter]
270
- return default_result unless patterns
271
-
272
- path = nil
273
- patterns.any? do |pattern|
274
- # Try to match the absolute path, as Exclude properties are absolute.
275
- next true if match_path?(pattern, file)
119
+ def apply_correction(corrector)
120
+ suppress_clobbering { super }
121
+ end
276
122
 
277
- # Try with relative path.
278
- path ||= config.path_relative_to_config(file)
279
- match_path?(pattern, path)
123
+ # Just for legacy
124
+ def emulate_v0_callsequence(corrector)
125
+ lambda = correction_lambda
126
+ yield corrector if block_given?
127
+ unless corrector.empty?
128
+ raise 'Your cop must inherit from Cop::Base and extend AutoCorrector'
280
129
  end
281
- end
282
130
 
283
- def enabled_line?(line_number)
284
- return true if @options[:ignore_disable_comments] || !@processed_source
131
+ return unless lambda
285
132
 
286
- @processed_source.comment_config.cop_enabled_at_line?(self, line_number)
133
+ suppress_clobbering do
134
+ lambda.call(corrector)
135
+ end
287
136
  end
288
137
 
289
- def find_severity(_node, severity)
290
- custom_severity || severity || default_severity
291
- end
138
+ def correction_lambda
139
+ return unless correction_strategy == :attempt_correction && support_autocorrect?
292
140
 
293
- def default_severity
294
- self.class.lint? ? :warning : :convention
141
+ dedup_on_node(@v0_argument) do
142
+ autocorrect(@v0_argument)
143
+ end
295
144
  end
296
145
 
297
- def custom_severity
298
- severity = cop_config['Severity']
299
- return unless severity
146
+ def dedup_on_node(node)
147
+ @corrected_nodes ||= {}.compare_by_identity
148
+ yield unless @corrected_nodes.key?(node)
149
+ ensure
150
+ @corrected_nodes[node] = true
151
+ end
300
152
 
301
- if Severity::NAMES.include?(severity.to_sym)
302
- severity.to_sym
303
- else
304
- message = "Warning: Invalid severity '#{severity}'. " \
305
- "Valid severities are #{Severity::NAMES.join(', ')}."
306
- warn(Rainbow(message).red)
307
- end
153
+ def suppress_clobbering
154
+ yield
155
+ rescue ::Parser::ClobberingError
156
+ # ignore Clobbering errors
308
157
  end
309
158
  end
310
159
  end