rubocop 1.40.0 → 1.41.1

Sign up to get free protection for your applications and to get access to all the features.
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: