rubocop 1.40.0 → 1.41.1

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +19 -0
  4. data/lib/rubocop/config.rb +28 -5
  5. data/lib/rubocop/config_loader.rb +9 -0
  6. data/lib/rubocop/config_validator.rb +1 -1
  7. data/lib/rubocop/cop/badge.rb +9 -4
  8. data/lib/rubocop/cop/base.rb +25 -9
  9. data/lib/rubocop/cop/commissioner.rb +8 -3
  10. data/lib/rubocop/cop/cop.rb +1 -1
  11. data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
  12. data/lib/rubocop/cop/layout/class_structure.rb +0 -1
  13. data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
  14. data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
  15. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
  16. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
  17. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
  18. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
  19. data/lib/rubocop/cop/layout/indentation_style.rb +3 -1
  20. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
  21. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  22. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  23. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
  24. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
  25. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
  26. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
  27. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
  28. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
  29. data/lib/rubocop/cop/layout/trailing_whitespace.rb +6 -2
  30. data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
  31. data/lib/rubocop/cop/lint/debugger.rb +3 -1
  32. data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
  33. data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
  34. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
  35. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -1
  36. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  37. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -19
  38. data/lib/rubocop/cop/metrics/class_length.rb +1 -1
  39. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  40. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  41. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  42. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
  43. data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
  44. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
  45. data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
  46. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -1
  47. data/lib/rubocop/cop/mixin/line_length_help.rb +8 -1
  48. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
  49. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
  50. data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
  51. data/lib/rubocop/cop/mixin/require_library.rb +2 -0
  52. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
  53. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  54. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  55. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
  56. data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
  57. data/lib/rubocop/cop/registry.rb +6 -3
  58. data/lib/rubocop/cop/style/alias.rb +9 -1
  59. data/lib/rubocop/cop/style/concat_array_literals.rb +66 -0
  60. data/lib/rubocop/cop/style/documentation.rb +11 -5
  61. data/lib/rubocop/cop/style/guard_clause.rb +6 -2
  62. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -3
  63. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  64. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
  65. data/lib/rubocop/cop/style/redundant_constant_base.rb +13 -0
  66. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +39 -0
  67. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -1
  68. data/lib/rubocop/cop/style/require_order.rb +61 -9
  69. data/lib/rubocop/cop/style/semicolon.rb +2 -1
  70. data/lib/rubocop/cop/util.rb +31 -4
  71. data/lib/rubocop/cops_documentation_generator.rb +33 -11
  72. data/lib/rubocop/directive_comment.rb +1 -1
  73. data/lib/rubocop/file_patterns.rb +43 -0
  74. data/lib/rubocop/formatter.rb +2 -0
  75. data/lib/rubocop/options.rb +1 -1
  76. data/lib/rubocop/path_util.rb +20 -14
  77. data/lib/rubocop/target_finder.rb +1 -1
  78. data/lib/rubocop/version.rb +1 -1
  79. data/lib/rubocop.rb +3 -1
  80. metadata +6 -4
  81. data/lib/rubocop/optimized_patterns.rb +0 -38
@@ -21,13 +21,12 @@ module RuboCop
21
21
  MSG_TERNARY = 'Do not use `if %<expr>s;` - use a ternary operator instead.'
22
22
 
23
23
  def on_normal_if_unless(node)
24
- return unless node.else_branch
25
24
  return if node.parent&.if_type?
26
25
 
27
26
  beginning = node.loc.begin
28
27
  return unless beginning&.is?(';')
29
28
 
30
- message = node.else_branch.if_type? ? MSG_IF_ELSE : MSG_TERNARY
29
+ message = node.else_branch&.if_type? ? MSG_IF_ELSE : MSG_TERNARY
31
30
 
32
31
  add_offense(node, message: format(message, expr: node.condition.source)) do |corrector|
33
32
  corrector.replace(node, autocorrect(node))
@@ -37,7 +36,7 @@ module RuboCop
37
36
  private
38
37
 
39
38
  def autocorrect(node)
40
- return correct_elsif(node) if node.else_branch.if_type?
39
+ return correct_elsif(node) if node.else_branch&.if_type?
41
40
 
42
41
  then_code = node.if_branch ? node.if_branch.source : 'nil'
43
42
  else_code = node.else_branch ? node.else_branch.source : 'nil'
@@ -51,6 +51,8 @@ module RuboCop
51
51
  NEGATED_EQUALITY_METHODS = %i[!= !~].freeze
52
52
  CAMEL_CASE = /[A-Z]+[a-z]+/.freeze
53
53
 
54
+ RESTRICT_ON_SEND = [:!].freeze
55
+
54
56
  def self.autocorrect_incompatible_with
55
57
  [Style::Not, Style::SymbolProc]
56
58
  end
@@ -55,7 +55,10 @@ module RuboCop
55
55
  private
56
56
 
57
57
  def check_token_set(index)
58
- predecessor, operator, successor = processed_source.tokens[index, 3]
58
+ tokens = processed_source.tokens
59
+ predecessor = tokens[index]
60
+ operator = tokens[index + 1]
61
+ successor = tokens[index + 2]
59
62
 
60
63
  return unless eligible_token_set?(predecessor, operator, successor)
61
64
 
@@ -10,6 +10,10 @@ module RuboCop
10
10
  # is empty, there is no need to prepend `::`, so it would be nice to consistently
11
11
  # avoid such meaningless `::` prefix to avoid confusion.
12
12
  #
13
+ # NOTE: This cop is disabled if `Lint/ConstantResolution` cop is enabled to prevent
14
+ # conflicting rules. Because it respects user configurations that want to enable
15
+ # `Lint/ConstantResolution` cop which is disabled by default.
16
+ #
13
17
  # @example
14
18
  # # bad
15
19
  # ::Const
@@ -42,6 +46,7 @@ module RuboCop
42
46
  MSG = 'Remove redundant `::`.'
43
47
 
44
48
  def on_cbase(node)
49
+ return if lint_constant_resolution_cop_enabled?
45
50
  return unless bad?(node)
46
51
 
47
52
  add_offense(node) do |corrector|
@@ -51,6 +56,14 @@ module RuboCop
51
56
 
52
57
  private
53
58
 
59
+ def lint_constant_resolution_cop_enabled?
60
+ lint_constant_resolution_config.fetch('Enabled', false)
61
+ end
62
+
63
+ def lint_constant_resolution_config
64
+ config.for_cop('Lint/ConstantResolution')
65
+ end
66
+
54
67
  def bad?(node)
55
68
  module_nesting_ancestors_of(node).none?
56
69
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant uses of double splat hash braces.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # do_something(**{foo: bar, baz: qux})
12
+ #
13
+ # # good
14
+ # do_something(foo: bar, baz: qux)
15
+ #
16
+ class RedundantDoubleSplatHashBraces < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
20
+
21
+ # @!method double_splat_hash_braces?(node)
22
+ def_node_matcher :double_splat_hash_braces?, <<~PATTERN
23
+ (hash (kwsplat (hash ...)))
24
+ PATTERN
25
+
26
+ def on_hash(node)
27
+ return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
28
+
29
+ grandparent = node.parent&.parent
30
+ return unless double_splat_hash_braces?(grandparent)
31
+
32
+ add_offense(grandparent) do |corrector|
33
+ corrector.replace(grandparent, node.pairs.map(&:source).join(', '))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -169,9 +169,10 @@ module RuboCop
169
169
  # Allow \#{foo}, \#$foo, \#@foo, and \#@@foo
170
170
  # for escaping local, global, instance and class variable interpolations
171
171
  return true if range.source.match?(/\A\\#[{$@]/)
172
-
173
172
  # Also allow #\{foo}, #\$foo, #\@foo and #\@@foo
174
173
  return true if range.adjust(begin_pos: -2).source.match?(/\A[^\\]#\\[{$@]/)
174
+ # For `\#\{foo} allow `\#` and warn `\{`
175
+ return true if range.adjust(end_pos: 1).source == '\\#\\{'
175
176
 
176
177
  false
177
178
  end
@@ -36,6 +36,33 @@ module RuboCop
36
36
  # require 'b'
37
37
  # require_relative 'c'
38
38
  # require 'a'
39
+ #
40
+ # # bad
41
+ # require 'a'
42
+ # require 'c' if foo
43
+ # require 'b'
44
+ #
45
+ # # good
46
+ # require 'a'
47
+ # require 'b'
48
+ # require 'c' if foo
49
+ #
50
+ # # bad
51
+ # require 'c'
52
+ # if foo
53
+ # require 'd'
54
+ # require 'b'
55
+ # end
56
+ # require 'a'
57
+ #
58
+ # # good
59
+ # require 'c'
60
+ # if foo
61
+ # require 'b'
62
+ # require 'd'
63
+ # end
64
+ # require 'a'
65
+ #
39
66
  class RequireOrder < Base
40
67
  extend AutoCorrector
41
68
 
@@ -43,17 +70,27 @@ module RuboCop
43
70
 
44
71
  RESTRICT_ON_SEND = %i[require require_relative].freeze
45
72
 
73
+ MSG = 'Sort `%<name>s` in alphabetical order.'
74
+
75
+ # @!method if_inside_only_require(node)
76
+ def_node_matcher :if_inside_only_require, <<~PATTERN
77
+ {
78
+ (if _ _ $(send nil? {:require :require_relative} _))
79
+ (if _ $(send nil? {:require :require_relative} _) _)
80
+ }
81
+ PATTERN
82
+
46
83
  def on_send(node)
84
+ return unless node.arguments?
85
+ return if not_modifier_form?(node.parent)
86
+
47
87
  previous_older_sibling = find_previous_older_sibling(node)
48
88
  return unless previous_older_sibling
49
89
 
50
- add_offense(
51
- node,
52
- message: "Sort `#{node.method_name}` in alphabetical order."
53
- ) do |corrector|
90
+ add_offense(node, message: format(MSG, name: node.method_name)) do |corrector|
54
91
  swap(
55
92
  range_with_comments_and_lines(previous_older_sibling),
56
- range_with_comments_and_lines(node),
93
+ range_with_comments_and_lines(node.parent.if_type? ? node.parent : node),
57
94
  corrector: corrector
58
95
  )
59
96
  end
@@ -61,16 +98,31 @@ module RuboCop
61
98
 
62
99
  private
63
100
 
64
- def find_previous_older_sibling(node)
65
- node.left_siblings.reverse.find do |sibling|
66
- break unless sibling.send_type?
67
- break unless sibling.method?(node.method_name)
101
+ def not_modifier_form?(node)
102
+ node.if_type? && !node.modifier_form?
103
+ end
104
+
105
+ def find_previous_older_sibling(node) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
106
+ search_node(node).left_siblings.reverse.find do |sibling|
107
+ sibling = sibling_node(sibling)
108
+ break unless sibling&.send_type? && sibling&.method?(node.method_name)
109
+ break unless sibling.arguments? && !sibling.receiver
68
110
  break unless in_same_section?(sibling, node)
69
111
 
70
112
  node.first_argument.source < sibling.first_argument.source
71
113
  end
72
114
  end
73
115
 
116
+ def search_node(node)
117
+ node.parent.if_type? ? node.parent : node
118
+ end
119
+
120
+ def sibling_node(node)
121
+ return if not_modifier_form?(node)
122
+
123
+ node.if_type? ? if_inside_only_require(node) : node
124
+ end
125
+
74
126
  def in_same_section?(node1, node2)
75
127
  !node1.location.expression.with(
76
128
  end_pos: node2.location.expression.end_pos
@@ -37,13 +37,14 @@ module RuboCop
37
37
  end
38
38
 
39
39
  def on_new_investigation
40
- return if processed_source.blank?
40
+ return if processed_source.blank? || !processed_source.raw_source.include?(';')
41
41
 
42
42
  check_for_line_terminator_or_opener
43
43
  end
44
44
 
45
45
  def on_begin(node)
46
46
  return if cop_config['AllowAsExpressionSeparator']
47
+ return unless node.source.include?(';')
47
48
 
48
49
  exprs = node.children
49
50
 
@@ -52,6 +52,21 @@ module RuboCop
52
52
  end
53
53
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
54
54
 
55
+ def any_descendant?(node, *types)
56
+ if block_given?
57
+ node.each_descendant(*types) do |descendant|
58
+ return true if yield(descendant)
59
+ end
60
+ else
61
+ # Use a block version to avoid allocating enumerators.
62
+ node.each_descendant do # rubocop:disable Lint/UnreachableLoop
63
+ return true
64
+ end
65
+ end
66
+
67
+ false
68
+ end
69
+
55
70
  def args_begin(node)
56
71
  loc = node.loc
57
72
  selector = if node.super_type? || node.yield_type?
@@ -71,14 +86,19 @@ module RuboCop
71
86
  def on_node(syms, sexp, excludes = [], &block)
72
87
  return to_enum(:on_node, syms, sexp, excludes) unless block
73
88
 
74
- yield sexp if Array(syms).include?(sexp.type)
75
- return if Array(excludes).include?(sexp.type)
89
+ yield sexp if include_or_equal?(syms, sexp.type)
90
+ return if include_or_equal?(excludes, sexp.type)
76
91
 
77
92
  sexp.each_child_node { |elem| on_node(syms, elem, excludes, &block) }
78
93
  end
79
94
 
95
+ LINE_BEGINS_REGEX_CACHE = Hash.new do |hash, index|
96
+ hash[index] = /^\s{#{index}}\S/
97
+ end
98
+ private_constant :LINE_BEGINS_REGEX_CACHE
99
+
80
100
  def begins_its_line?(range)
81
- range.source_line.index(/\S/) == range.column
101
+ range.source_line.match?(LINE_BEGINS_REGEX_CACHE[range.column])
82
102
  end
83
103
 
84
104
  # Returns, for example, a bare `if` node if the given node is an `if`
@@ -152,8 +172,11 @@ module RuboCop
152
172
  ' ' * (node.loc.column + offset)
153
173
  end
154
174
 
175
+ @to_supported_styles_cache = {}
176
+
155
177
  def to_supported_styles(enforced_style)
156
- enforced_style.sub(/^Enforced/, 'Supported').sub('Style', 'Styles')
178
+ @to_supported_styles_cache[enforced_style] ||=
179
+ enforced_style.sub(/^Enforced/, 'Supported').sub('Style', 'Styles')
157
180
  end
158
181
 
159
182
  private
@@ -162,6 +185,10 @@ module RuboCop
162
185
  src = src.dup if RUBY_ENGINE == 'jruby'
163
186
  src.force_encoding(Encoding.default_external).valid_encoding?
164
187
  end
188
+
189
+ def include_or_equal?(source, target)
190
+ source == target || (source.is_a?(Array) && source.include?(target))
191
+ end
165
192
  end
166
193
  end
167
194
  end
@@ -33,7 +33,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
33
33
  cops.with_department(department).sort!
34
34
  end
35
35
 
36
- def cops_body(cop, description, examples_objects, safety_objects, pars) # rubocop:disable Metrics/AbcSize
36
+ def cops_body(cop, description, examples_objects, safety_objects, see_objects, pars) # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
37
37
  check_examples_to_have_the_default_enforced_style!(examples_objects, cop)
38
38
 
39
39
  content = h2(cop.cop_name)
@@ -42,8 +42,8 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
42
42
  content << "#{description}\n"
43
43
  content << safety_object(safety_objects) if safety_objects.any? { |s| !s.text.blank? }
44
44
  content << examples(examples_objects) if examples_objects.any?
45
- content << configurations(pars)
46
- content << references(cop)
45
+ content << configurations(cop.department, pars)
46
+ content << references(cop, see_objects)
47
47
  content
48
48
  end
49
49
 
@@ -136,7 +136,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
136
136
  content
137
137
  end
138
138
 
139
- def configurations(pars)
139
+ def configurations(department, pars)
140
140
  return '' if pars.empty?
141
141
 
142
142
  header = ['Name', 'Default value', 'Configurable values']
@@ -147,12 +147,20 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
147
147
  content = configs.map do |name|
148
148
  configurable = configurable_values(pars, name)
149
149
  default = format_table_value(pars[name])
150
- [name, default, configurable]
150
+
151
+ [configuration_name(department, name), default, configurable]
151
152
  end
152
153
 
153
154
  h3('Configurable attributes') + to_table(header, content)
154
155
  end
155
156
 
157
+ def configuration_name(department, name)
158
+ return name unless name == 'AllowMultilineFinalElement'
159
+
160
+ filename = "#{department_to_basename(department)}.adoc"
161
+ "xref:#{filename}#allowmultilinefinalelement[AllowMultilineFinalElement]"
162
+ end
163
+
156
164
  # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
157
165
  def configurable_values(pars, name)
158
166
  case name
@@ -216,21 +224,34 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
216
224
  end
217
225
  end
218
226
 
219
- def references(cop)
227
+ def references(cop, see_objects) # rubocop:disable Metrics/AbcSize
220
228
  cop_config = config.for_cop(cop)
221
229
  urls = RuboCop::Cop::MessageAnnotator.new(config, cop.name, cop_config, {}).urls
222
- return '' if urls.empty?
230
+ return '' if urls.empty? && see_objects.empty?
223
231
 
224
232
  content = h3('References')
225
233
  content << urls.map { |url| "* #{url}" }.join("\n")
226
- content << "\n"
234
+ content << "\n" unless urls.empty?
235
+ content << see_objects.map { |see| "* #{see.name}" }.join("\n")
236
+ content << "\n" unless see_objects.empty?
227
237
  content
228
238
  end
229
239
 
240
+ def footer_for_department(department)
241
+ return '' unless department == :Layout
242
+
243
+ filename = "#{department_to_basename(department)}_footer.adoc"
244
+ file = "#{Dir.pwd}/docs/modules/ROOT/partials/#{filename}"
245
+ return '' unless File.exist?(file)
246
+
247
+ "\ninclude::../partials/#{filename}[]\n"
248
+ end
249
+
230
250
  def print_cops_of_department(department)
231
251
  selected_cops = cops_of_department(department)
232
252
  content = +"= #{department}\n"
233
253
  selected_cops.each { |cop| content << print_cop_with_doc(cop) }
254
+ content << footer_for_department(department)
234
255
  file_name = "#{Dir.pwd}/docs/modules/ROOT/pages/#{department_to_basename(department)}.adoc"
235
256
  File.open(file_name, 'w') do |file|
236
257
  puts "* generated #{file_name}"
@@ -238,7 +259,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
238
259
  end
239
260
  end
240
261
 
241
- def print_cop_with_doc(cop)
262
+ def print_cop_with_doc(cop) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
242
263
  cop_config = config.for_cop(cop)
243
264
  non_display_keys = %w[
244
265
  Description Enabled StyleGuide Reference Safe SafeAutoCorrect VersionAdded
@@ -246,13 +267,14 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
246
267
  ]
247
268
  pars = cop_config.reject { |k| non_display_keys.include? k }
248
269
  description = 'No documentation'
249
- examples_object = safety_object = []
270
+ examples_object = safety_object = see_object = []
250
271
  cop_code(cop) do |code_object|
251
272
  description = code_object.docstring unless code_object.docstring.blank?
252
273
  examples_object = code_object.tags('example')
253
274
  safety_object = code_object.tags('safety')
275
+ see_object = code_object.tags('see')
254
276
  end
255
- cops_body(cop, description, examples_object, safety_object, pars)
277
+ cops_body(cop, description, examples_object, safety_object, see_object, pars)
256
278
  end
257
279
 
258
280
  def cop_code(cop)
@@ -35,7 +35,7 @@ module RuboCop
35
35
 
36
36
  # Checks if this directive relates to single line
37
37
  def single_line?
38
- !self.class.before_comment(comment.text).empty?
38
+ !comment.text.start_with?(DIRECTIVE_COMMENT_REGEXP)
39
39
  end
40
40
 
41
41
  # Checks if this directive contains all the given cop names
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # A wrapper around patterns array to perform optimized search.
5
+ #
6
+ # For projects with a large set of rubocop todo files, most items in `Exclude`/`Include`
7
+ # are exact file names. It is wasteful to linearly check the list of patterns over and over
8
+ # to check if the file is relevant to the cop.
9
+ #
10
+ # This class partitions an array of patterns into a set of exact match strings and the rest
11
+ # of the patterns. This way we can firstly do a cheap check in the set and then proceed via
12
+ # the costly patterns check, if needed.
13
+ # @api private
14
+ class FilePatterns
15
+ @cache = {}.compare_by_identity
16
+
17
+ def self.from(patterns)
18
+ @cache[patterns] ||= new(patterns)
19
+ end
20
+
21
+ def initialize(patterns)
22
+ @strings = Set.new
23
+ @patterns = []
24
+ partition_patterns(patterns)
25
+ end
26
+
27
+ def match?(path)
28
+ @strings.include?(path) || @patterns.any? { |pattern| PathUtil.match_path?(pattern, path) }
29
+ end
30
+
31
+ private
32
+
33
+ def partition_patterns(patterns)
34
+ patterns.each do |pattern|
35
+ if pattern.is_a?(String) && !pattern.match?(/[*{\[?]/)
36
+ @strings << pattern
37
+ else
38
+ @patterns << pattern
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RuboCop
4
+ # The bootstrap module for formatter.
5
+ # @api private
4
6
  module Formatter
5
7
  require_relative 'formatter/text_util'
6
8
 
@@ -465,7 +465,7 @@ module RuboCop
465
465
 
466
466
  # This module contains help texts for command line options.
467
467
  # @api private
468
- module OptionsHelp # rubocop:disable Metrics/ModuleLength
468
+ module OptionsHelp
469
469
  MAX_EXCL = RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS.to_s
470
470
  FORMATTER_OPTION_LIST = RuboCop::Formatter::FormatterSet::BUILTIN_FORMATTERS_FOR_KEYS.keys
471
471
 
@@ -3,23 +3,29 @@
3
3
  module RuboCop
4
4
  # Common methods and behaviors for dealing with paths.
5
5
  module PathUtil
6
+ class << self
7
+ attr_accessor :relative_paths_cache
8
+ end
9
+ self.relative_paths_cache = Hash.new { |hash, key| hash[key] = {} }
10
+
6
11
  module_function
7
12
 
8
13
  def relative_path(path, base_dir = Dir.pwd)
9
- # Optimization for the common case where path begins with the base
10
- # dir. Just cut off the first part.
11
- if path.start_with?(base_dir)
12
- base_dir_length = base_dir.length
13
- result_length = path.length - base_dir_length - 1
14
- return path[base_dir_length + 1, result_length]
15
- end
16
-
17
- path_name = Pathname.new(File.expand_path(path))
18
- begin
19
- path_name.relative_path_from(Pathname.new(base_dir)).to_s
20
- rescue ArgumentError
21
- path
22
- end
14
+ PathUtil.relative_paths_cache[base_dir][path] ||=
15
+ # Optimization for the common case where path begins with the base
16
+ # dir. Just cut off the first part.
17
+ if path.start_with?(base_dir)
18
+ base_dir_length = base_dir.length
19
+ result_length = path.length - base_dir_length - 1
20
+ path[base_dir_length + 1, result_length]
21
+ else
22
+ path_name = Pathname.new(File.expand_path(path))
23
+ begin
24
+ path_name.relative_path_from(Pathname.new(base_dir)).to_s
25
+ rescue ArgumentError
26
+ path
27
+ end
28
+ end
23
29
  end
24
30
 
25
31
  def smart_path(path)
@@ -88,7 +88,7 @@ module RuboCop
88
88
  patterns.map! { |dir| File.join(dir, '*') }
89
89
  # We need this special case to avoid creating the pattern
90
90
  # /**/* which searches the entire file system.
91
- patterns = [File.join(dir, '**/*')] if patterns.empty?
91
+ patterns = [File.join(base_dir, '**/*')] if patterns.empty?
92
92
 
93
93
  Dir.glob(patterns, flags).select { |path| FileTest.file?(path) }
94
94
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.40.0'
6
+ STRING = '1.41.1'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -23,8 +23,8 @@ require_relative 'rubocop/ext/processed_source'
23
23
 
24
24
  require_relative 'rubocop/error'
25
25
  require_relative 'rubocop/file_finder'
26
+ require_relative 'rubocop/file_patterns'
26
27
  require_relative 'rubocop/name_similarity'
27
- require_relative 'rubocop/optimized_patterns'
28
28
  require_relative 'rubocop/path_util'
29
29
  require_relative 'rubocop/platform'
30
30
  require_relative 'rubocop/string_interpreter'
@@ -470,6 +470,7 @@ require_relative 'rubocop/cop/style/combinable_loops'
470
470
  require_relative 'rubocop/cop/style/command_literal'
471
471
  require_relative 'rubocop/cop/style/comment_annotation'
472
472
  require_relative 'rubocop/cop/style/commented_keyword'
473
+ require_relative 'rubocop/cop/style/concat_array_literals'
473
474
  require_relative 'rubocop/cop/style/conditional_assignment'
474
475
  require_relative 'rubocop/cop/style/constant_visibility'
475
476
  require_relative 'rubocop/cop/style/copyright'
@@ -545,6 +546,7 @@ require_relative 'rubocop/cop/style/open_struct_use'
545
546
  require_relative 'rubocop/cop/style/operator_method_call'
546
547
  require_relative 'rubocop/cop/style/redundant_assignment'
547
548
  require_relative 'rubocop/cop/style/redundant_constant_base'
549
+ require_relative 'rubocop/cop/style/redundant_double_splat_hash_braces'
548
550
  require_relative 'rubocop/cop/style/redundant_each'
549
551
  require_relative 'rubocop/cop/style/redundant_fetch_block'
550
552
  require_relative 'rubocop/cop/style/redundant_file_extension_in_require'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.40.0
4
+ version: 1.41.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2022-12-08 00:00:00.000000000 Z
13
+ date: 2022-12-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -691,6 +691,7 @@ files:
691
691
  - lib/rubocop/cop/style/command_literal.rb
692
692
  - lib/rubocop/cop/style/comment_annotation.rb
693
693
  - lib/rubocop/cop/style/commented_keyword.rb
694
+ - lib/rubocop/cop/style/concat_array_literals.rb
694
695
  - lib/rubocop/cop/style/conditional_assignment.rb
695
696
  - lib/rubocop/cop/style/constant_visibility.rb
696
697
  - lib/rubocop/cop/style/copyright.rb
@@ -824,6 +825,7 @@ files:
824
825
  - lib/rubocop/cop/style/redundant_condition.rb
825
826
  - lib/rubocop/cop/style/redundant_conditional.rb
826
827
  - lib/rubocop/cop/style/redundant_constant_base.rb
828
+ - lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb
827
829
  - lib/rubocop/cop/style/redundant_each.rb
828
830
  - lib/rubocop/cop/style/redundant_exception.rb
829
831
  - lib/rubocop/cop/style/redundant_fetch_block.rb
@@ -918,6 +920,7 @@ files:
918
920
  - lib/rubocop/ext/regexp_parser.rb
919
921
  - lib/rubocop/feature_loader.rb
920
922
  - lib/rubocop/file_finder.rb
923
+ - lib/rubocop/file_patterns.rb
921
924
  - lib/rubocop/formatter.rb
922
925
  - lib/rubocop/formatter/auto_gen_config_formatter.rb
923
926
  - lib/rubocop/formatter/base_formatter.rb
@@ -944,7 +947,6 @@ files:
944
947
  - lib/rubocop/lockfile.rb
945
948
  - lib/rubocop/magic_comment.rb
946
949
  - lib/rubocop/name_similarity.rb
947
- - lib/rubocop/optimized_patterns.rb
948
950
  - lib/rubocop/options.rb
949
951
  - lib/rubocop/path_util.rb
950
952
  - lib/rubocop/platform.rb
@@ -990,7 +992,7 @@ metadata:
990
992
  homepage_uri: https://rubocop.org/
991
993
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
992
994
  source_code_uri: https://github.com/rubocop/rubocop/
993
- documentation_uri: https://docs.rubocop.org/rubocop/1.40/
995
+ documentation_uri: https://docs.rubocop.org/rubocop/1.41/
994
996
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
995
997
  rubygems_mfa_required: 'true'
996
998
  post_install_message: