rubocop 0.91.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -5
  3. data/config/default.yml +143 -56
  4. data/lib/rubocop.rb +17 -5
  5. data/lib/rubocop/cached_data.rb +2 -1
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  7. data/lib/rubocop/cli/command/version.rb +1 -1
  8. data/lib/rubocop/comment_config.rb +1 -1
  9. data/lib/rubocop/config.rb +4 -0
  10. data/lib/rubocop/config_loader.rb +19 -2
  11. data/lib/rubocop/config_loader_resolver.rb +7 -5
  12. data/lib/rubocop/config_regeneration.rb +33 -0
  13. data/lib/rubocop/config_validator.rb +7 -6
  14. data/lib/rubocop/cop/badge.rb +9 -24
  15. data/lib/rubocop/cop/base.rb +16 -1
  16. data/lib/rubocop/cop/bundler/duplicated_gem.rb +23 -3
  17. data/lib/rubocop/cop/commissioner.rb +36 -22
  18. data/lib/rubocop/cop/corrector.rb +3 -1
  19. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
  20. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  21. data/lib/rubocop/cop/force.rb +1 -1
  22. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -10
  23. data/lib/rubocop/cop/layout/array_alignment.rb +1 -0
  24. data/lib/rubocop/cop/layout/class_structure.rb +7 -0
  25. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  26. data/lib/rubocop/cop/layout/dot_position.rb +6 -9
  27. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -7
  28. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  29. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  30. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -11
  31. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  32. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
  33. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  34. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -0
  35. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +18 -1
  36. data/lib/rubocop/cop/lint/boolean_symbol.rb +3 -0
  37. data/lib/rubocop/cop/lint/debugger.rb +2 -3
  38. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  39. data/lib/rubocop/cop/lint/empty_block.rb +46 -0
  40. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  41. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
  42. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +17 -3
  43. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  44. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  45. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  47. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
  48. data/lib/rubocop/cop/lint/to_enum_arguments.rb +95 -0
  49. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  50. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
  51. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  52. data/lib/rubocop/cop/metrics/block_length.rb +3 -1
  53. data/lib/rubocop/cop/metrics/class_length.rb +14 -6
  54. data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
  55. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  56. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  57. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  58. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  59. data/lib/rubocop/cop/offense.rb +18 -5
  60. data/lib/rubocop/cop/security/open.rb +12 -10
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +6 -2
  62. data/lib/rubocop/cop/style/accessor_grouping.rb +3 -0
  63. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  64. data/lib/rubocop/cop/style/array_coercion.rb +4 -0
  65. data/lib/rubocop/cop/style/case_like_if.rb +20 -4
  66. data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
  67. data/lib/rubocop/cop/style/combinable_loops.rb +8 -1
  68. data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
  69. data/lib/rubocop/cop/style/date_time.rb +12 -1
  70. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +67 -0
  71. data/lib/rubocop/cop/style/explicit_block_argument.rb +6 -2
  72. data/lib/rubocop/cop/style/for.rb +0 -4
  73. data/lib/rubocop/cop/style/format_string_token.rb +48 -3
  74. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  75. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  77. data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
  78. data/lib/rubocop/cop/style/mixin_usage.rb +7 -27
  79. data/lib/rubocop/cop/style/multiple_comparison.rb +54 -7
  80. data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
  81. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +11 -3
  82. data/lib/rubocop/cop/style/raise_args.rb +0 -3
  83. data/lib/rubocop/cop/style/redundant_begin.rb +36 -8
  84. data/lib/rubocop/cop/style/redundant_condition.rb +5 -1
  85. data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
  86. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  87. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +45 -24
  88. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
  89. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  90. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  91. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  92. data/lib/rubocop/cop/style/string_concatenation.rb +14 -2
  93. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  94. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  95. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  96. data/lib/rubocop/cop/team.rb +6 -1
  97. data/lib/rubocop/cop/util.rb +1 -1
  98. data/lib/rubocop/cop/variable_force/branch.rb +0 -4
  99. data/lib/rubocop/ext/regexp_node.rb +29 -10
  100. data/lib/rubocop/ext/regexp_parser.rb +77 -0
  101. data/lib/rubocop/formatter/disabled_config_formatter.rb +12 -5
  102. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  103. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  104. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  105. data/lib/rubocop/magic_comment.rb +2 -2
  106. data/lib/rubocop/options.rb +22 -17
  107. data/lib/rubocop/result_cache.rb +8 -2
  108. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  109. data/lib/rubocop/rspec/expect_offense.rb +5 -5
  110. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  111. data/lib/rubocop/runner.rb +9 -5
  112. data/lib/rubocop/target_finder.rb +27 -26
  113. data/lib/rubocop/target_ruby.rb +1 -1
  114. data/lib/rubocop/version.rb +61 -6
  115. metadata +21 -16
  116. data/lib/rubocop/cop/mixin/regexp_literal_help.rb +0 -43
@@ -20,8 +20,6 @@ module RuboCop
20
20
  nil
21
21
  end
22
22
 
23
- # rubocop:disable Metrics/BlockLength
24
-
25
23
  # Abstract base class for branch classes.
26
24
  # A branch represents a conditional branch in a scope.
27
25
  #
@@ -42,8 +40,6 @@ module RuboCop
42
40
  # do_something # no branch
43
41
  # end
44
42
  Base = Struct.new(:child_node, :scope) do
45
- # rubocop:enable Metrics/BlockLength
46
-
47
43
  def self.classes
48
44
  @classes ||= []
49
45
  end
@@ -10,21 +10,22 @@ module RuboCop
10
10
  end
11
11
  private_constant :ANY
12
12
 
13
- class << self
14
- attr_reader :parsed_cache
15
- end
16
- @parsed_cache = {}
17
-
18
13
  # @return [Regexp::Expression::Root, nil]
19
- def parsed_tree
20
- return if interpolation?
14
+ # Note: we extend Regexp nodes to provide `loc` and `expression`
15
+ # see `ext/regexp_parser`.
16
+ attr_reader :parsed_tree
21
17
 
22
- str = content
23
- Ext::RegexpNode.parsed_cache[str] ||= begin
24
- Regexp::Parser.parse(str)
18
+ def assign_properties(*)
19
+ super
20
+
21
+ str = with_interpolations_blanked
22
+ @parsed_tree = begin
23
+ Regexp::Parser.parse(str, options: options)
25
24
  rescue StandardError
26
25
  nil
27
26
  end
27
+ origin = loc.begin.end
28
+ @parsed_tree&.each_expression(true) { |e| e.origin = origin }
28
29
  end
29
30
 
30
31
  def each_capture(named: ANY)
@@ -40,6 +41,24 @@ module RuboCop
40
41
  self
41
42
  end
42
43
 
44
+ private
45
+
46
+ def with_interpolations_blanked
47
+ # Ignore the trailing regopt node
48
+ children[0...-1].map do |child|
49
+ source = child.source
50
+
51
+ # We don't want to consider the contents of interpolations as part of the pattern source,
52
+ # but need to preserve their width, to allow offsets to correctly line up with the
53
+ # original source: spaces have no effect, and preserve width.
54
+ if child.begin_type?
55
+ ' ' * source.length
56
+ else
57
+ source
58
+ end
59
+ end.join
60
+ end
61
+
43
62
  AST::RegexpNode.include self
44
63
  end
45
64
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Ext
5
+ # Extensions for `regexp_parser` gem
6
+ module RegexpParser
7
+ # Source map for RegexpParser nodes
8
+ class Map < ::Parser::Source::Map
9
+ attr_reader :body, :quantifier, :begin, :end
10
+
11
+ def initialize(expression, body:, quantifier: nil, begin_l: nil, end_l: nil)
12
+ @begin = begin_l
13
+ @end = end_l
14
+ @body = body
15
+ @quantifier = quantifier
16
+ super(expression)
17
+ end
18
+ end
19
+
20
+ module Expression
21
+ # Add `expression` and `loc` to all `regexp_parser` nodes
22
+ module Base
23
+ attr_accessor :origin
24
+
25
+ # Shortcut to `loc.expression`
26
+ def expression
27
+ @expression ||= origin.adjust(begin_pos: ts, end_pos: ts + full_length)
28
+ end
29
+
30
+ # @returns a location map like `parser` does, with:
31
+ # - expression: complete expression
32
+ # - quantifier: for `+`, `{1,2}`, etc.
33
+ # - begin/end: for `[` and `]` (only CharacterSet for now)
34
+ #
35
+ # E.g.
36
+ # [a-z]{2,}
37
+ # ^^^^^^^^^ expression
38
+ # ^^^^ quantifier
39
+ # ^^^^^ body
40
+ # ^ begin
41
+ # ^ end
42
+ #
43
+ # Please open issue if you need other locations
44
+ def loc
45
+ @loc ||= begin
46
+ Map.new(expression, **build_location)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def build_location
53
+ return { body: expression } unless (q = quantifier)
54
+
55
+ body = expression.adjust(end_pos: -q.text.length)
56
+ q_loc = expression.with(begin_pos: body.end_pos)
57
+ { body: body, quantifier: q_loc }
58
+ end
59
+ end
60
+
61
+ # Provide `CharacterSet` with `begin` and `end` locations.
62
+ module CharacterSet
63
+ def build_location
64
+ h = super
65
+ body = h[:body]
66
+ h.merge!(
67
+ begin_l: body.with(end_pos: body.begin_pos + 1),
68
+ end_l: body.with(begin_pos: body.end_pos - 1)
69
+ )
70
+ end
71
+ end
72
+ end
73
+ ::Regexp::Expression::Base.include Expression::Base
74
+ ::Regexp::Expression::CharacterSet.include Expression::CharacterSet
75
+ end
76
+ end
77
+ end
@@ -32,7 +32,6 @@ module RuboCop
32
32
  @exclude_limit_option = @options[:exclude_limit]
33
33
  @exclude_limit = Integer(@exclude_limit_option ||
34
34
  RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS)
35
- @show_offense_counts = !@options[:no_offense_counts]
36
35
  end
37
36
 
38
37
  def file_finished(file, offenses)
@@ -56,6 +55,14 @@ module RuboCop
56
55
 
57
56
  private
58
57
 
58
+ def show_timestamp?
59
+ @options.fetch(:auto_gen_timestamp, true)
60
+ end
61
+
62
+ def show_offense_counts?
63
+ @options.fetch(:offense_counts, true)
64
+ end
65
+
59
66
  def command
60
67
  command = 'rubocop --auto-gen-config'
61
68
 
@@ -66,15 +73,15 @@ module RuboCop
66
73
  format(' --exclude-limit %<limit>d',
67
74
  limit: Integer(@exclude_limit_option))
68
75
  end
69
- command += ' --no-offense-counts' if @options[:no_offense_counts]
76
+ command += ' --no-offense-counts' unless show_offense_counts?
70
77
 
71
- command += ' --no-auto-gen-timestamp' if @options[:no_auto_gen_timestamp]
78
+ command += ' --no-auto-gen-timestamp' unless show_timestamp?
72
79
 
73
80
  command
74
81
  end
75
82
 
76
83
  def timestamp
77
- @options[:no_auto_gen_timestamp] ? '' : "on #{Time.now.utc} "
84
+ show_timestamp? ? "on #{Time.now.utc} " : ''
78
85
  end
79
86
 
80
87
  def output_offenses
@@ -112,7 +119,7 @@ module RuboCop
112
119
  end
113
120
 
114
121
  def output_cop_comments(output_buffer, cfg, cop_name, offense_count)
115
- output_buffer.puts "# Offense count: #{offense_count}" if @show_offense_counts
122
+ output_buffer.puts "# Offense count: #{offense_count}" if show_offense_counts?
116
123
 
117
124
  cop_class = Cop::Registry.global.find_by_cop_name(cop_name)
118
125
  output_buffer.puts '# Cop supports --auto-correct.' if cop_class&.support_autocorrect?
@@ -30,7 +30,7 @@ module RuboCop
30
30
 
31
31
  FORMATTER_APIS.each do |method_name|
32
32
  define_method(method_name) do |*args|
33
- each { |f| f.send(method_name, *args) }
33
+ each { |f| f.public_send(method_name, *args) }
34
34
  end
35
35
  end
36
36
 
@@ -67,7 +67,7 @@ module RuboCop
67
67
  end
68
68
 
69
69
  def total_offense_count(offense_counts)
70
- offense_counts.values.inject(0, :+)
70
+ offense_counts.values.sum
71
71
  end
72
72
  end
73
73
  end
@@ -55,7 +55,7 @@ module RuboCop
55
55
  end
56
56
 
57
57
  def total_offense_count(offense_counts)
58
- offense_counts.values.inject(0, :+)
58
+ offense_counts.values.sum
59
59
  end
60
60
  end
61
61
  end
@@ -194,7 +194,7 @@ module RuboCop
194
194
  class SimpleComment < MagicComment
195
195
  # Match `encoding` or `coding`
196
196
  def encoding
197
- extract(/\A\s*\#.*\b(?:en)?coding: (#{TOKEN})/i)
197
+ extract(/\A\s*\#.*\b(?:en)?coding: (#{TOKEN})/io)
198
198
  end
199
199
 
200
200
  private
@@ -207,7 +207,7 @@ module RuboCop
207
207
  # Case-insensitive and dashes/underscores are acceptable.
208
208
  # @see https://git.io/vM7Mg
209
209
  def extract_frozen_string_literal
210
- extract(/\A\s*#\s*frozen[_-]string[_-]literal:\s*(#{TOKEN})\s*\z/i)
210
+ extract(/\A\s*#\s*frozen[_-]string[_-]literal:\s*(#{TOKEN})\s*\z/io)
211
211
  end
212
212
  end
213
213
  end
@@ -114,20 +114,19 @@ module RuboCop
114
114
  def add_auto_gen_options(opts)
115
115
  option(opts, '--auto-gen-config')
116
116
 
117
+ option(opts, '--regenerate-todo') do
118
+ @options.replace(ConfigRegeneration.new.options.merge(@options))
119
+ end
120
+
117
121
  option(opts, '--exclude-limit COUNT') do
118
122
  @validator.validate_exclude_limit_option
119
123
  end
120
124
 
121
125
  option(opts, '--disable-uncorrectable')
122
126
 
123
- option(opts, '--no-offense-counts') do
124
- @options[:no_offense_counts] = true
125
- end
126
-
127
- option(opts, '--auto-gen-only-exclude')
128
- option(opts, '--no-auto-gen-timestamp') do
129
- @options[:no_auto_gen_timestamp] = true
130
- end
127
+ option(opts, '--[no-]offense-counts')
128
+ option(opts, '--[no-]auto-gen-only-exclude')
129
+ option(opts, '--[no-]auto-gen-timestamp')
131
130
 
132
131
  option(opts, '--init')
133
132
  end
@@ -243,6 +242,9 @@ module RuboCop
243
242
  # @api private
244
243
  class OptionsValidator
245
244
  class << self
245
+ SYNTAX_DEPARTMENTS = %w[Syntax Lint/Syntax].freeze
246
+ private_constant :SYNTAX_DEPARTMENTS
247
+
246
248
  # Cop name validation must be done later than option parsing, so it's not
247
249
  # called from within Options.
248
250
  def validate_cop_list(names)
@@ -254,7 +256,7 @@ module RuboCop
254
256
  names.each do |name|
255
257
  next if cop_names.include?(name)
256
258
  next if departments.include?(name)
257
- next if %w[Syntax Lint/Syntax].include?(name)
259
+ next if SYNTAX_DEPARTMENTS.include?(name)
258
260
 
259
261
  raise IncorrectCopNameError, format_message_from(name, cop_names)
260
262
  end
@@ -318,7 +320,7 @@ module RuboCop
318
320
 
319
321
  message = '--%<flag>s can only be used together with --auto-gen-config.'
320
322
 
321
- %i[exclude_limit no_offense_counts no_auto_gen_timestamp
323
+ %i[exclude_limit offense_counts auto_gen_timestamp
322
324
  auto_gen_only_exclude].each do |option|
323
325
  if @options.key?(option)
324
326
  raise OptionArgumentError,
@@ -423,17 +425,20 @@ module RuboCop
423
425
  config: 'Specify configuration file.',
424
426
  auto_gen_config: ['Generate a configuration file acting as a',
425
427
  'TODO list.'],
426
- no_offense_counts: ['Do not include offense counts in configuration',
427
- 'file generated by --auto-gen-config.'],
428
- no_auto_gen_timestamp:
429
- ['Do not include the date and time when',
430
- 'the --auto-gen-config was run in the file it',
431
- 'generates.'],
428
+ regenerate_todo: ['Regenerate the TODO configuration file using',
429
+ 'the last configuration. If there is no existing',
430
+ 'TODO file, acts like --auto-gen-config.'],
431
+ offense_counts: ['Include offense counts in configuration',
432
+ 'file generated by --auto-gen-config.',
433
+ 'Default is true.'],
434
+ auto_gen_timestamp:
435
+ ['Include the date and time when the --auto-gen-config',
436
+ 'was run in the file it generates. Default is true.'],
432
437
  auto_gen_only_exclude:
433
438
  ['Generate only Exclude parameters and not Max',
434
439
  'when running --auto-gen-config, except if the',
435
440
  'number of files with offenses is bigger than',
436
- 'exclude-limit.'],
441
+ 'exclude-limit. Default is false.'],
437
442
  exclude_limit: ['Used together with --auto-gen-config to',
438
443
  'set the limit for how many Exclude',
439
444
  "properties to generate. Default is #{MAX_EXCL}."],
@@ -95,6 +95,11 @@ module RuboCop
95
95
  context_checksum(team, options),
96
96
  file_checksum(file, config_store))
97
97
  @cached_data = CachedData.new(file)
98
+ @debug = options[:debug]
99
+ end
100
+
101
+ def debug?
102
+ @debug
98
103
  end
99
104
 
100
105
  def valid?
@@ -102,6 +107,7 @@ module RuboCop
102
107
  end
103
108
 
104
109
  def load
110
+ puts "Loading cache from #{@path}" if debug?
105
111
  @cached_data.from_json(IO.read(@path, encoding: Encoding::UTF_8))
106
112
  end
107
113
 
@@ -209,8 +215,8 @@ module RuboCop
209
215
  # The external dependency checksums are cached per RuboCop team so that
210
216
  # the checksums don't need to be recomputed for each file.
211
217
  def team_checksum(team)
212
- @checksum_by_team ||= {}
213
- @checksum_by_team[team.object_id] ||= team.external_dependency_checksum
218
+ @checksum_by_team ||= {}.compare_by_identity
219
+ @checksum_by_team[team] ||= team.external_dependency_checksum
214
220
  end
215
221
 
216
222
  # We combine team and options into a single "context" checksum to avoid
@@ -26,7 +26,7 @@ module CopHelper
26
26
  end
27
27
 
28
28
  def parse_source(source, file = nil)
29
- if file&.respond_to?(:write)
29
+ if file.respond_to?(:write)
30
30
  file.write(source)
31
31
  file.rewind
32
32
  file = file.path
@@ -133,14 +133,14 @@ module RuboCop
133
133
  "#{@processed_source.diagnostics.map(&:render).join("\n")}"
134
134
  end
135
135
 
136
- offenses = _investigate(cop, @processed_source)
136
+ @offenses = _investigate(cop, @processed_source)
137
137
  actual_annotations =
138
- expected_annotations.with_offense_annotations(offenses)
138
+ expected_annotations.with_offense_annotations(@offenses)
139
139
 
140
140
  expect(actual_annotations).to eq(expected_annotations), ''
141
- expect(offenses.map(&:severity).uniq).to eq([severity]) if severity
141
+ expect(@offenses.map(&:severity).uniq).to eq([severity]) if severity
142
142
 
143
- offenses
143
+ @offenses
144
144
  end
145
145
 
146
146
  def expect_correction(correction, loop: true)
@@ -157,7 +157,7 @@ module RuboCop
157
157
  break corrected_source if corrected_source == @processed_source.buffer.source
158
158
 
159
159
  if iteration > RuboCop::Runner::MAX_ITERATIONS
160
- raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [])
160
+ raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [@offenses])
161
161
  end
162
162
 
163
163
  # Prepare for next loop
@@ -138,3 +138,7 @@ end
138
138
  RSpec.shared_context 'ruby 2.7', :ruby27 do
139
139
  let(:ruby_version) { 2.7 }
140
140
  end
141
+
142
+ RSpec.shared_context 'ruby 3.0', :ruby30 do
143
+ let(:ruby_version) { 3.0 }
144
+ end
@@ -16,7 +16,11 @@ module RuboCop
16
16
  root_cause = offenses_by_iteration[loop_start..-1]
17
17
  .map { |x| x.map(&:cop_name).uniq.join(', ') }
18
18
  .join(' -> ')
19
- super "Infinite loop detected in #{path} and caused by #{root_cause}"
19
+
20
+ message = 'Infinite loop detected'
21
+ message += " in #{path}" if path
22
+ message += " and caused by #{root_cause}" if root_cause
23
+ super message
20
24
  end
21
25
  end
22
26
 
@@ -320,8 +324,8 @@ module RuboCop
320
324
  end
321
325
 
322
326
  def mobilized_cop_classes(config)
323
- @mobilized_cop_classes ||= {}
324
- @mobilized_cop_classes[config.object_id] ||= begin
327
+ @mobilized_cop_classes ||= {}.compare_by_identity
328
+ @mobilized_cop_classes[config] ||= begin
325
329
  cop_classes = Cop::Registry.all
326
330
 
327
331
  OptionsValidator.new(@options).validate_cop_options
@@ -395,8 +399,8 @@ module RuboCop
395
399
  # otherwise dormant team that can be used for config- and option-
396
400
  # level caching in ResultCache.
397
401
  def standby_team(config)
398
- @team_by_config ||= {}
399
- @team_by_config[config.object_id] ||=
402
+ @team_by_config ||= {}.compare_by_identity
403
+ @team_by_config[config] ||=
400
404
  Cop::Team.mobilize(mobilized_cop_classes(config), config, @options)
401
405
  end
402
406
  end
@@ -5,6 +5,8 @@ module RuboCop
5
5
  # and picking ruby files.
6
6
  # @api private
7
7
  class TargetFinder
8
+ HIDDEN_PATH_SUBSTRING = "#{File::SEPARATOR}."
9
+
8
10
  def initialize(config_store, options = {})
9
11
  @config_store = config_store
10
12
  @options = options
@@ -55,7 +57,8 @@ module RuboCop
55
57
  # Support Windows: Backslashes from command-line -> forward slashes
56
58
  base_dir = base_dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
57
59
  all_files = find_files(base_dir, File::FNM_DOTMATCH)
58
- hidden_files = Set.new(all_files - find_files(base_dir, 0))
60
+ # use file.include? for performance optimization
61
+ hidden_files = all_files.select { |file| file.include?(HIDDEN_PATH_SUBSTRING) }
59
62
  base_dir_config = @config_store.for(base_dir)
60
63
 
61
64
  target_files = all_files.select do |file|
@@ -78,34 +81,32 @@ module RuboCop
78
81
  # the top level directories that are excluded in configuration in the
79
82
  # normal way (dir/**/*).
80
83
  def find_files(base_dir, flags)
81
- wanted_toplevel_dirs = toplevel_dirs(base_dir, flags) -
82
- excluded_dirs(base_dir)
83
- wanted_toplevel_dirs.map! { |dir| dir << '/**/*' }
84
-
85
- pattern = if wanted_toplevel_dirs.empty?
86
- # We need this special case to avoid creating the pattern
87
- # /**/* which searches the entire file system.
88
- ["#{base_dir}/**/*"]
89
- else
90
- # Search the non-excluded top directories, but also add files
91
- # on the top level, which would otherwise not be found.
92
- wanted_toplevel_dirs.unshift("#{base_dir}/*")
84
+ # get all wanted directories first to improve speed of finding all files
85
+ exclude_pattern = combined_exclude_glob_patterns(base_dir)
86
+ dir_flags = flags | File::FNM_PATHNAME | File::FNM_EXTGLOB
87
+ patterns = wanted_dir_patterns(base_dir, exclude_pattern, dir_flags)
88
+ patterns.map! { |dir| File.join(dir, '*') }
89
+ # We need this special case to avoid creating the pattern
90
+ # /**/* which searches the entire file system.
91
+ patterns = [File.join(dir, '**/*')] if patterns.empty?
92
+
93
+ Dir.glob(patterns, flags).select { |path| FileTest.file?(path) }
94
+ end
95
+
96
+ def wanted_dir_patterns(base_dir, exclude_pattern, flags)
97
+ dirs = Dir.glob(File.join(base_dir, '*/'), flags)
98
+ .reject do |dir|
99
+ dir.end_with?('/./', '/../') || File.fnmatch?(exclude_pattern, dir, flags)
93
100
  end
94
- Dir.glob(pattern, flags).select { |path| FileTest.file?(path) }
101
+ dirs.flat_map { |dir| wanted_dir_patterns(dir, exclude_pattern, flags) }
102
+ .unshift(base_dir)
95
103
  end
96
104
 
97
- def toplevel_dirs(base_dir, flags)
98
- Dir.glob(File.join(base_dir, '*'), flags).select do |dir|
99
- File.directory?(dir) && !dir.end_with?('/.', '/..')
100
- end
101
- end
102
-
103
- def excluded_dirs(base_dir)
104
- all_cops_config = @config_store.for(base_dir).for_all_cops
105
- dir_tree_excludes = all_cops_config['Exclude'].select do |pattern|
106
- pattern.is_a?(String) && pattern.end_with?('/**/*')
107
- end
108
- dir_tree_excludes.map { |pattern| pattern.sub(%r{/\*\*/\*$}, '') }
105
+ def combined_exclude_glob_patterns(base_dir)
106
+ exclude = @config_store.for(base_dir).for_all_cops['Exclude']
107
+ patterns = exclude.select { |pattern| pattern.is_a?(String) && pattern.end_with?('/**/*') }
108
+ .map { |pattern| pattern.sub("#{base_dir}/", '') }
109
+ "#{base_dir}/{#{patterns.join(',')}}"
109
110
  end
110
111
 
111
112
  def ruby_extension?(file)