rubocop 1.41.0 → 1.42.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +26 -1
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/config.rb +7 -7
  7. data/lib/rubocop/config_loader_resolver.rb +5 -1
  8. data/lib/rubocop/cop/base.rb +62 -61
  9. data/lib/rubocop/cop/cop.rb +28 -28
  10. data/lib/rubocop/cop/corrector.rb +23 -11
  11. data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
  12. data/lib/rubocop/cop/layout/class_structure.rb +32 -11
  13. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
  14. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  15. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +6 -6
  16. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  17. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  18. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -2
  19. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
  20. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -1
  21. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  22. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
  23. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  24. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +5 -3
  25. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  26. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -4
  27. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  28. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  29. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  30. data/lib/rubocop/cop/registry.rb +22 -22
  31. data/lib/rubocop/cop/security/compound_hash.rb +2 -1
  32. data/lib/rubocop/cop/style/alias.rb +9 -1
  33. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  34. data/lib/rubocop/cop/style/concat_array_literals.rb +22 -2
  35. data/lib/rubocop/cop/style/documentation.rb +10 -4
  36. data/lib/rubocop/cop/style/guard_clause.rb +12 -8
  37. data/lib/rubocop/cop/style/hash_syntax.rb +10 -7
  38. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
  39. data/lib/rubocop/cop/style/map_to_set.rb +61 -0
  40. data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
  41. data/lib/rubocop/cop/style/min_max_comparison.rb +73 -0
  42. data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
  43. data/lib/rubocop/cop/style/require_order.rb +4 -2
  44. data/lib/rubocop/cop/style/select_by_regexp.rb +6 -2
  45. data/lib/rubocop/cop/style/signal_exception.rb +8 -6
  46. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
  47. data/lib/rubocop/cop/style/word_array.rb +41 -0
  48. data/lib/rubocop/cop/style/yoda_expression.rb +74 -0
  49. data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
  50. data/lib/rubocop/cop/team.rb +29 -29
  51. data/lib/rubocop/cop/variable_force.rb +0 -3
  52. data/lib/rubocop/cops_documentation_generator.rb +11 -8
  53. data/lib/rubocop/formatter.rb +2 -0
  54. data/lib/rubocop/path_util.rb +6 -1
  55. data/lib/rubocop/result_cache.rb +1 -1
  56. data/lib/rubocop/runner.rb +10 -3
  57. data/lib/rubocop/target_ruby.rb +0 -1
  58. data/lib/rubocop/version.rb +1 -1
  59. data/lib/rubocop.rb +3 -0
  60. metadata +12 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77b5fe782c4a109e75c380273e08f38b9c61eb519f11f19a606a380080f1f0a2
4
- data.tar.gz: fa67b5a3f120d6f1538963f9ddbc8bd0508b7f54763bd7c76da725054c9e0459
3
+ metadata.gz: e37ffc01dbd2782dd811d4c35129f893502179457e76d8e3501d7c3683563cc2
4
+ data.tar.gz: 9dba5be8e2168e986170e8366a62c73940262bfffec0a374c3a01b3f207ca291
5
5
  SHA512:
6
- metadata.gz: 1475ad1af84e34c10ccf4905247b6e1090e0bd2969d6eb5ef2c4f4dec116841d438b42c5f4c84678870ffac0703f663e1f1ea79ce588b74caca27162f5ad6c07
7
- data.tar.gz: 267189e76baac7ebc2632d3249957987ba9a3db3df26fa1bf7abe4510a7f9baf8cf5bec399b024d1e900accd1b55f2a5c72e8ab3686d1edc656272ef9723bdaf
6
+ metadata.gz: 23769dc5239a734dc07a859a594e1cc0b732d05d24c61c1887525f9f69f748e0c23cbb84404e865e00a04bfb5c91e73d1e0bdb7908a72340ede4245fc38508c6
7
+ data.tar.gz: f016891fb54e93a8f6c418d4aa97f7400a74c187c8cf5dbbe274020ea5bda900721f2a7fefbbb368472664cf21a4b586db4fb9373510c27da6428aa490d504e6
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-22 Bozhidar Batsov
1
+ Copyright (c) 2012-23 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.41', require: false
56
+ gem 'rubocop', '~> 1.42', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
@@ -246,5 +246,5 @@ RuboCop's changelog is available [here](CHANGELOG.md).
246
246
 
247
247
  ## Copyright
248
248
 
249
- Copyright (c) 2012-2022 Bozhidar Batsov. See [LICENSE.txt](LICENSE.txt) for
249
+ Copyright (c) 2012-2023 Bozhidar Batsov. See [LICENSE.txt](LICENSE.txt) for
250
250
  further details.
data/config/default.yml CHANGED
@@ -3429,6 +3429,7 @@ Style/CommentedKeyword:
3429
3429
  Style/ConcatArrayLiterals:
3430
3430
  Description: 'Enforces the use of `Array#push(item)` instead of `Array#concat([item])` to avoid redundant array literals.'
3431
3431
  Enabled: pending
3432
+ Safe: false
3432
3433
  VersionAdded: '1.41'
3433
3434
 
3434
3435
  Style/ConditionalAssignment:
@@ -3900,7 +3901,7 @@ Style/HashSyntax:
3900
3901
  - never
3901
3902
  # accepts both shorthand and explicit use of hash literal value.
3902
3903
  - either
3903
- # like "either", but will avoid mixing styles in a single hash
3904
+ # forces use of the 3.1 syntax only if all values can be omitted in the hash.
3904
3905
  - consistent
3905
3906
  # Force hashes that have a symbol value to use hash rockets
3906
3907
  UseHashRocketsWithSymbolValues: false
@@ -4109,6 +4110,12 @@ Style/MapToHash:
4109
4110
  VersionAdded: '1.24'
4110
4111
  Safe: false
4111
4112
 
4113
+ Style/MapToSet:
4114
+ Description: 'Prefer `to_set` with a block over `map.to_set`.'
4115
+ Enabled: pending
4116
+ Safe: false
4117
+ VersionAdded: '1.42'
4118
+
4112
4119
  Style/MethodCallWithArgsParentheses:
4113
4120
  Description: 'Use parentheses for method calls with arguments.'
4114
4121
  StyleGuide: '#method-invocation-parens'
@@ -4167,6 +4174,12 @@ Style/MinMax:
4167
4174
  Enabled: true
4168
4175
  VersionAdded: '0.50'
4169
4176
 
4177
+ Style/MinMaxComparison:
4178
+ Description: 'Enforces the use of `max` or `min` instead of comparison for greater or less.'
4179
+ Enabled: pending
4180
+ Safe: false
4181
+ VersionAdded: '1.42'
4182
+
4170
4183
  Style/MissingElse:
4171
4184
  Description: >-
4172
4185
  Require if/case expressions to have an else branches.
@@ -5425,6 +5438,18 @@ Style/YodaCondition:
5425
5438
  VersionAdded: '0.49'
5426
5439
  VersionChanged: '0.75'
5427
5440
 
5441
+ Style/YodaExpression:
5442
+ Description: 'Forbid the use of yoda expressions.'
5443
+ Enabled: pending
5444
+ Safe: false
5445
+ VersionAdded: '1.42'
5446
+ SupportedOperators:
5447
+ - '*'
5448
+ - '+'
5449
+ - '&'
5450
+ - '|'
5451
+ - '^'
5452
+
5428
5453
  Style/ZeroLengthPredicate:
5429
5454
  Description: 'Use #empty? when testing for objects of length 0.'
5430
5455
  Enabled: true
data/lib/rubocop/cli.rb CHANGED
@@ -7,7 +7,7 @@ module RuboCop
7
7
  STATUS_SUCCESS = 0
8
8
  STATUS_OFFENSES = 1
9
9
  STATUS_ERROR = 2
10
- STATUS_INTERRUPTED = 128 + Signal.list['INT']
10
+ STATUS_INTERRUPTED = Signal.list['INT'] + 128
11
11
  DEFAULT_PARALLEL_OPTIONS = %i[
12
12
  color debug display_style_guide display_time display_only_fail_level_offenses
13
13
  display_only_failed except extra_details fail_level fix_layout format
@@ -21,6 +21,13 @@ module RuboCop
21
21
  DEFAULT_RAILS_VERSION = 5.0
22
22
  attr_reader :loaded_path
23
23
 
24
+ def self.create(hash, path, check: true)
25
+ config = new(hash, path)
26
+ config.check if check
27
+
28
+ config
29
+ end
30
+
24
31
  # rubocop:disable Metrics/AbcSize
25
32
  def initialize(hash = {}, loaded_path = nil)
26
33
  @loaded_path = loaded_path
@@ -39,13 +46,6 @@ module RuboCop
39
46
  end
40
47
  # rubocop:enable Metrics/AbcSize
41
48
 
42
- def self.create(hash, path, check: true)
43
- config = new(hash, path)
44
- config.check if check
45
-
46
- config
47
- end
48
-
49
49
  def loaded_features
50
50
  @loaded_features ||= ConfigLoader.loaded_features
51
51
  end
@@ -206,7 +206,11 @@ module RuboCop
206
206
  end
207
207
 
208
208
  def base_configs(path, inherit_from, file)
209
- configs = Array(inherit_from).compact.map do |f|
209
+ inherit_froms = Array(inherit_from).compact.flat_map do |f|
210
+ PathUtil.glob?(f) ? Dir.glob(f) : f
211
+ end
212
+
213
+ configs = inherit_froms.map do |f|
210
214
  ConfigLoader.load_file(inherited_file(path, f, file))
211
215
  end
212
216
 
@@ -68,6 +68,64 @@ module RuboCop
68
68
  Documentation.url_for(self) if builtin?
69
69
  end
70
70
 
71
+ def self.inherited(subclass)
72
+ super
73
+ Registry.global.enlist(subclass)
74
+ end
75
+
76
+ # Call for abstract Cop classes
77
+ def self.exclude_from_registry
78
+ Registry.global.dismiss(self)
79
+ end
80
+
81
+ # Returns if class supports autocorrect.
82
+ # It is recommended to extend AutoCorrector instead of overriding
83
+ def self.support_autocorrect?
84
+ false
85
+ end
86
+
87
+ ### Naming
88
+
89
+ def self.badge
90
+ @badge ||= Badge.for(name)
91
+ end
92
+
93
+ def self.cop_name
94
+ badge.to_s
95
+ end
96
+
97
+ def self.department
98
+ badge.department
99
+ end
100
+
101
+ def self.lint?
102
+ department == :Lint
103
+ end
104
+
105
+ # Returns true if the cop name or the cop namespace matches any of the
106
+ # given names.
107
+ def self.match?(given_names)
108
+ return false unless given_names
109
+
110
+ given_names.include?(cop_name) || given_names.include?(badge.department_name)
111
+ end
112
+
113
+ # Override and return the Force class(es) you need to join
114
+ def self.joining_forces; end
115
+
116
+ ### Persistence
117
+
118
+ # Override if your cop should be called repeatedly for multiple investigations
119
+ # Between calls to `on_new_investigation` and `on_investigation_end`,
120
+ # the result of `processed_source` will remain constant.
121
+ # You should invalidate any caches that depend on the current `processed_source`
122
+ # in the `on_new_investigation` callback.
123
+ # If your cop does autocorrections, be aware that your instance may be called
124
+ # multiple times with the same `processed_source.path` but different content.
125
+ def self.support_multiple_source?
126
+ false
127
+ end
128
+
71
129
  def initialize(config = nil, options = nil)
72
130
  @config = config || Config.new
73
131
  @options = options || { debug: false }
@@ -92,9 +150,6 @@ module RuboCop
92
150
  # Typically do nothing here
93
151
  end
94
152
 
95
- # Override and return the Force class(es) you need to join
96
- def self.joining_forces; end
97
-
98
153
  # Gets called if no message is specified when calling `add_offense` or
99
154
  # `add_global_offense`
100
155
  # Cops are discouraged to override this; instead pass your message directly
@@ -147,48 +202,6 @@ module RuboCop
147
202
  nil
148
203
  end
149
204
 
150
- def self.inherited(subclass)
151
- super
152
- Registry.global.enlist(subclass)
153
- end
154
-
155
- # Call for abstract Cop classes
156
- def self.exclude_from_registry
157
- Registry.global.dismiss(self)
158
- end
159
-
160
- # Returns if class supports autocorrect.
161
- # It is recommended to extend AutoCorrector instead of overriding
162
- def self.support_autocorrect?
163
- false
164
- end
165
-
166
- ### Naming
167
-
168
- def self.badge
169
- @badge ||= Badge.for(name)
170
- end
171
-
172
- def self.cop_name
173
- badge.to_s
174
- end
175
-
176
- def self.department
177
- badge.department
178
- end
179
-
180
- def self.lint?
181
- department == :Lint
182
- end
183
-
184
- # Returns true if the cop name or the cop namespace matches any of the
185
- # given names.
186
- def self.match?(given_names)
187
- return false unless given_names
188
-
189
- given_names.include?(cop_name) || given_names.include?(badge.department_name)
190
- end
191
-
192
205
  def cop_name
193
206
  @cop_name ||= self.class.cop_name
194
207
  end
@@ -240,19 +253,6 @@ module RuboCop
240
253
  ProcessedSource.new(source, target_ruby_version, path)
241
254
  end
242
255
 
243
- ### Persistence
244
-
245
- # Override if your cop should be called repeatedly for multiple investigations
246
- # Between calls to `on_new_investigation` and `on_investigation_end`,
247
- # the result of `processed_source` will remain constant.
248
- # You should invalidate any caches that depend on the current `processed_source`
249
- # in the `on_new_investigation` callback.
250
- # If your cop does autocorrections, be aware that your instance may be called
251
- # multiple times with the same `processed_source.path` but different content.
252
- def self.support_multiple_source?
253
- false
254
- end
255
-
256
256
  # @api private
257
257
  # Called between investigations
258
258
  def ready
@@ -271,6 +271,7 @@ module RuboCop
271
271
 
272
272
  ### Reserved for Commissioner
273
273
 
274
+ # rubocop:disable Layout/ClassStructure
274
275
  # @api private
275
276
  def callbacks_needed
276
277
  self.class.callbacks_needed
@@ -283,6 +284,7 @@ module RuboCop
283
284
  !Base.method_defined?(m) # exclude standard "callbacks" like 'on_begin_investigation'
284
285
  end
285
286
  end
287
+ # rubocop:enable Layout/ClassStructure
286
288
 
287
289
  private
288
290
 
@@ -327,11 +329,8 @@ module RuboCop
327
329
  @current_corrector = nil
328
330
  end
329
331
 
330
- # rubocop:disable Layout/ClassStructure
331
332
  EMPTY_OFFENSES = [].freeze
332
333
  private_constant :EMPTY_OFFENSES
333
- # rubocop:enable Layout/ClassStructure
334
-
335
334
  # Called to complete an investigation
336
335
  def complete_investigation
337
336
  InvestigationReport.new(
@@ -343,6 +342,7 @@ module RuboCop
343
342
 
344
343
  ### Actually private methods
345
344
 
345
+ # rubocop:disable Layout/ClassStructure
346
346
  def self.builtin?
347
347
  return false unless (m = instance_methods(false).first) # any custom method will do
348
348
 
@@ -350,6 +350,7 @@ module RuboCop
350
350
  path.start_with?(__dir__)
351
351
  end
352
352
  private_class_method :builtin?
353
+ # rubocop:enable Layout/ClassStructure
353
354
 
354
355
  def reset_investigation
355
356
  @currently_disabled_lines = @current_offenses = @processed_source = @current_corrector = nil
@@ -22,6 +22,34 @@ module RuboCop
22
22
  end
23
23
  end
24
24
 
25
+ def self.support_autocorrect?
26
+ method_defined?(:autocorrect)
27
+ end
28
+
29
+ def self.joining_forces
30
+ return unless method_defined?(:join_force?)
31
+
32
+ cop = new
33
+ Force.all.select { |force_class| cop.join_force?(force_class) }
34
+ end
35
+
36
+ ### Deprecated registry access
37
+
38
+ # @deprecated Use Registry.global
39
+ def self.registry
40
+ Registry.global
41
+ end
42
+
43
+ # @deprecated Use Registry.all
44
+ def self.all
45
+ Registry.all
46
+ end
47
+
48
+ # @deprecated Use Registry.qualified_cop_name
49
+ def self.qualified_cop_name(name, origin)
50
+ Registry.qualified_cop_name(name, origin)
51
+ end
52
+
25
53
  def add_offense(node_or_range, location: :expression, message: nil, severity: nil, &block)
26
54
  @v0_argument = node_or_range
27
55
  range = find_location(node_or_range, location)
@@ -45,17 +73,6 @@ module RuboCop
45
73
  self.class.support_autocorrect?
46
74
  end
47
75
 
48
- def self.support_autocorrect?
49
- method_defined?(:autocorrect)
50
- end
51
-
52
- def self.joining_forces
53
- return unless method_defined?(:join_force?)
54
-
55
- cop = new
56
- Force.all.select { |force_class| cop.join_force?(force_class) }
57
- end
58
-
59
76
  # @deprecated
60
77
  def corrections
61
78
  # warn 'Cop#corrections is deprecated' TODO
@@ -76,23 +93,6 @@ module RuboCop
76
93
  super
77
94
  end
78
95
 
79
- ### Deprecated registry access
80
-
81
- # @deprecated Use Registry.global
82
- def self.registry
83
- Registry.global
84
- end
85
-
86
- # @deprecated Use Registry.all
87
- def self.all
88
- Registry.all
89
- end
90
-
91
- # @deprecated Use Registry.qualified_cop_name
92
- def self.qualified_cop_name(name, origin)
93
- Registry.qualified_cop_name(name, origin)
94
- end
95
-
96
96
  private
97
97
 
98
98
  def begin_investigation(processed_source)
@@ -11,6 +11,20 @@ module RuboCop
11
11
  class Corrector < ::Parser::Source::TreeRewriter
12
12
  NOOP_CONSUMER = ->(diagnostic) {} # noop
13
13
 
14
+ # Duck typing for get to a ::Parser::Source::Buffer
15
+ def self.source_buffer(source)
16
+ source = source.processed_source if source.respond_to?(:processed_source)
17
+ source = source.buffer if source.respond_to?(:buffer)
18
+ source = source.source_buffer if source.respond_to?(:source_buffer)
19
+
20
+ unless source.is_a? ::Parser::Source::Buffer
21
+ raise TypeError, 'Expected argument to lead to a Parser::Source::Buffer ' \
22
+ "but got #{source.inspect}"
23
+ end
24
+
25
+ source
26
+ end
27
+
14
28
  # @param source [Parser::Source::Buffer, or anything
15
29
  # leading to one via `(processed_source.)buffer`]
16
30
  #
@@ -64,18 +78,16 @@ module RuboCop
64
78
  remove(to_remove)
65
79
  end
66
80
 
67
- # Duck typing for get to a ::Parser::Source::Buffer
68
- def self.source_buffer(source)
69
- source = source.processed_source if source.respond_to?(:processed_source)
70
- source = source.buffer if source.respond_to?(:buffer)
71
- source = source.source_buffer if source.respond_to?(:source_buffer)
72
-
73
- unless source.is_a? ::Parser::Source::Buffer
74
- raise TypeError, 'Expected argument to lead to a Parser::Source::Buffer ' \
75
- "but got #{source.inspect}"
76
- end
81
+ # Swaps sources at the given ranges.
82
+ #
83
+ # @param [Parser::Source::Range, RuboCop::AST::Node] node_or_range1
84
+ # @param [Parser::Source::Range, RuboCop::AST::Node] node_or_range2
85
+ def swap(node_or_range1, node_or_range2)
86
+ range1 = to_range(node_or_range1)
87
+ range2 = to_range(node_or_range2)
77
88
 
78
- source
89
+ replace(range1, range2.source)
90
+ replace(range2, range1.source)
79
91
  end
80
92
 
81
93
  private
@@ -58,8 +58,13 @@ module RuboCop
58
58
  FORBIDDEN_MSG = 'Dependency version specification is forbidden.'
59
59
  VERSION_SPECIFICATION_REGEX = /^\s*[~<>=]*\s*[0-9.]+/.freeze
60
60
 
61
- # @!method add_dependency_method_declarations(node)
62
- def_node_search :add_dependency_method_declarations, <<~PATTERN
61
+ ADD_DEPENDENCY_METHODS = %i[
62
+ add_dependency add_runtime_dependency add_development_dependency
63
+ ].freeze
64
+ RESTRICT_ON_SEND = ADD_DEPENDENCY_METHODS
65
+
66
+ # @!method add_dependency_method_declaration?(node)
67
+ def_node_matcher :add_dependency_method_declaration?, <<~PATTERN
63
68
  (send
64
69
  (lvar #match_block_variable_name?) #add_dependency_method? ...)
65
70
  PATTERN
@@ -74,18 +79,15 @@ module RuboCop
74
79
  (send _ #add_dependency_method? <(hash <(pair (sym {:branch :ref :tag}) (str _)) ...>) ...>)
75
80
  PATTERN
76
81
 
77
- def on_new_investigation
78
- return if processed_source.blank?
79
-
80
- add_dependency_method_nodes.each do |node|
81
- next if allowed_gem?(node)
82
+ def on_send(node)
83
+ return unless add_dependency_method_declaration?(node)
84
+ return if allowed_gem?(node)
82
85
 
83
- if offense?(node)
84
- add_offense(node)
85
- opposite_style_detected
86
- else
87
- correct_style_detected
88
- end
86
+ if offense?(node)
87
+ add_offense(node)
88
+ opposite_style_detected
89
+ else
90
+ correct_style_detected
89
91
  end
90
92
  end
91
93
 
@@ -116,11 +118,7 @@ module RuboCop
116
118
  end
117
119
 
118
120
  def add_dependency_method?(method_name)
119
- method_name.to_s.end_with?('_dependency')
120
- end
121
-
122
- def add_dependency_method_nodes
123
- add_dependency_method_declarations(processed_source.ast)
121
+ ADD_DEPENDENCY_METHODS.include?(method_name)
124
122
  end
125
123
 
126
124
  def offense?(node)
@@ -132,25 +132,19 @@ module RuboCop
132
132
  # end
133
133
  # end
134
134
  #
135
- # @see https://rubystyle.guide#consistent-classes
136
135
  class ClassStructure < Base
137
136
  include VisibilityHelp
138
137
  extend AutoCorrector
139
138
 
140
139
  HUMANIZED_NODE_TYPE = {
141
140
  casgn: :constants,
142
- defs: :class_methods,
141
+ defs: :public_class_methods,
143
142
  def: :public_methods,
144
143
  sclass: :class_singleton
145
144
  }.freeze
146
145
 
147
146
  MSG = '`%<category>s` is supposed to appear before `%<previous>s`.'
148
147
 
149
- # @!method dynamic_constant?(node)
150
- def_node_matcher :dynamic_constant?, <<~PATTERN
151
- (casgn nil? _ (send ...))
152
- PATTERN
153
-
154
148
  # Validates code style on class declaration.
155
149
  # Add offense when find a node out of expected order.
156
150
  def on_class(class_node)
@@ -222,7 +216,7 @@ module RuboCop
222
216
  def walk_over_nested_class_definition(class_node)
223
217
  class_elements(class_node).each do |node|
224
218
  classification = classify(node)
225
- next if ignore?(classification)
219
+ next if ignore?(node, classification)
226
220
 
227
221
  yield node, classification
228
222
  end
@@ -240,17 +234,20 @@ module RuboCop
240
234
  end
241
235
  end
242
236
 
243
- def ignore?(classification)
237
+ def ignore?(node, classification)
244
238
  classification.nil? ||
245
239
  classification.to_s.end_with?('=') ||
246
- expected_order.index(classification).nil?
240
+ expected_order.index(classification).nil? ||
241
+ private_constant?(node)
247
242
  end
248
243
 
249
244
  def ignore_for_autocorrect?(node, sibling)
250
245
  classification = classify(node)
251
246
  sibling_class = classify(sibling)
252
247
 
253
- ignore?(sibling_class) || classification == sibling_class || dynamic_constant?(node)
248
+ ignore?(sibling, sibling_class) ||
249
+ classification == sibling_class ||
250
+ dynamic_constant?(node)
254
251
  end
255
252
 
256
253
  def humanize_node(node)
@@ -262,6 +259,30 @@ module RuboCop
262
259
  HUMANIZED_NODE_TYPE[node.type] || node.type
263
260
  end
264
261
 
262
+ def dynamic_constant?(node)
263
+ return false unless node.casgn_type? && node.namespace.nil?
264
+
265
+ expression = node.expression
266
+ expression.send_type? &&
267
+ !(expression.method?(:freeze) && expression.receiver&.recursive_basic_literal?)
268
+ end
269
+
270
+ def private_constant?(node)
271
+ return false unless node.casgn_type? && node.namespace.nil?
272
+ return false unless (parent = node.parent)
273
+
274
+ parent.each_child_node(:send) do |child_node|
275
+ return true if marked_as_private_constant?(child_node, node.name)
276
+ end
277
+ false
278
+ end
279
+
280
+ def marked_as_private_constant?(node, name)
281
+ return false unless node.method?(:private_constant)
282
+
283
+ node.arguments.any? { |arg| (arg.sym_type? || arg.str_type?) && arg.value == name }
284
+ end
285
+
265
286
  def source_range_with_comment(node)
266
287
  begin_pos, end_pos =
267
288
  if (node.def_type? && !node.method?(:initialize)) ||
@@ -154,7 +154,9 @@ module RuboCop
154
154
  end
155
155
 
156
156
  def less_indented?(line)
157
- /^\s*(end\b|[)}\]])/.match?(line)
157
+ rule = config.for_cop('Layout/AccessModifierIndentation')['EnforcedStyle'] == 'outdent'
158
+ access_modifier = 'private|protected|public'
159
+ /\A\s*(end\b|[)}\]])/.match?(line) || (rule && /\A\s*(#{access_modifier})\b/.match?(line))
158
160
  end
159
161
 
160
162
  def two_alternatives?(line)
@@ -40,10 +40,13 @@ module RuboCop
40
40
  MSG = '%<type>s detected in indentation.'
41
41
 
42
42
  def on_new_investigation
43
- str_ranges = string_literal_ranges(processed_source.ast)
43
+ str_ranges = nil
44
44
 
45
45
  processed_source.lines.each.with_index(1) do |line, lineno|
46
46
  next unless (range = find_offense(line, lineno))
47
+
48
+ # Perform costly calculation only when needed.
49
+ str_ranges ||= string_literal_ranges(processed_source.ast)
47
50
  next if in_string_literal?(str_ranges, range)
48
51
 
49
52
  add_offense(range) { |corrector| autocorrect(corrector, range) }
@@ -31,15 +31,11 @@ module RuboCop
31
31
  include RangeHelp
32
32
  extend AutoCorrector
33
33
 
34
- # rubocop:disable Metrics/AbcSize
35
34
  def on_new_investigation
36
35
  return unless processed_source.raw_source.include?('\\')
37
36
 
38
37
  last_line = last_line(processed_source)
39
38
 
40
- @ignored_ranges = string_literal_ranges(processed_source.ast) +
41
- comment_ranges(processed_source.comments)
42
-
43
39
  processed_source.raw_source.lines.each_with_index do |line, index|
44
40
  break if index >= last_line
45
41
 
@@ -47,7 +43,6 @@ module RuboCop
47
43
  investigate(line, line_number)
48
44
  end
49
45
  end
50
- # rubocop:enable Metrics/AbcSize
51
46
 
52
47
  private
53
48
 
@@ -120,7 +115,12 @@ module RuboCop
120
115
  end
121
116
 
122
117
  def ignore_range?(backtick_range)
123
- @ignored_ranges.any? { |range| range.contains?(backtick_range) }
118
+ ignored_ranges.any? { |range| range.contains?(backtick_range) }
119
+ end
120
+
121
+ def ignored_ranges
122
+ @ignored_ranges ||= string_literal_ranges(processed_source.ast) +
123
+ comment_ranges(processed_source.comments)
124
124
  end
125
125
 
126
126
  def no_space_style?
@@ -91,7 +91,7 @@ module RuboCop
91
91
  if node.source.lines.first.end_with?("|\n")
92
92
  PIPE_SIZE
93
93
  else
94
- 1 + (PIPE_SIZE * 2)
94
+ (PIPE_SIZE * 2) + 1
95
95
  end
96
96
  end
97
97