rubocop 1.60.2 → 1.63.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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/assets/output.css.erb +159 -0
  4. data/assets/output.html.erb +1 -160
  5. data/config/default.yml +64 -15
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +12 -3
  7. data/lib/rubocop/cli/command/lsp.rb +2 -2
  8. data/lib/rubocop/cli.rb +6 -1
  9. data/lib/rubocop/config.rb +37 -10
  10. data/lib/rubocop/config_finder.rb +12 -2
  11. data/lib/rubocop/config_obsoletion.rb +1 -1
  12. data/lib/rubocop/config_validator.rb +14 -5
  13. data/lib/rubocop/cop/autocorrect_logic.rb +6 -1
  14. data/lib/rubocop/cop/base.rb +52 -6
  15. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +4 -8
  16. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +5 -13
  17. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +5 -1
  18. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -0
  19. data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +8 -6
  20. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +122 -28
  21. data/lib/rubocop/cop/internal_affairs/redundant_expect_offense_arguments.rb +34 -0
  22. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  23. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +14 -7
  24. data/lib/rubocop/cop/layout/end_alignment.rb +3 -1
  25. data/lib/rubocop/cop/layout/redundant_line_break.rb +11 -3
  26. data/lib/rubocop/cop/layout/space_before_block_braces.rb +19 -10
  27. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +1 -1
  28. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -4
  29. data/lib/rubocop/cop/lint/debugger.rb +27 -2
  30. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  31. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -9
  32. data/lib/rubocop/cop/lint/redundant_with_index.rb +4 -0
  33. data/lib/rubocop/cop/lint/rescue_type.rb +1 -3
  34. data/lib/rubocop/cop/lint/script_permission.rb +3 -3
  35. data/lib/rubocop/cop/lint/syntax.rb +1 -1
  36. data/lib/rubocop/cop/lint/to_enum_arguments.rb +7 -2
  37. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  38. data/lib/rubocop/cop/lint/void.rb +6 -1
  39. data/lib/rubocop/cop/mixin/code_length.rb +12 -1
  40. data/lib/rubocop/cop/mixin/method_complexity.rb +15 -6
  41. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  42. data/lib/rubocop/cop/mixin/safe_assignment.rb +1 -1
  43. data/lib/rubocop/cop/naming/block_forwarding.rb +31 -12
  44. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  45. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -2
  46. data/lib/rubocop/cop/naming/predicate_name.rb +2 -2
  47. data/lib/rubocop/cop/registry.rb +1 -1
  48. data/lib/rubocop/cop/style/alias.rb +1 -0
  49. data/lib/rubocop/cop/style/arguments_forwarding.rb +29 -8
  50. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  51. data/lib/rubocop/cop/style/class_vars.rb +3 -3
  52. data/lib/rubocop/cop/style/collection_compact.rb +3 -3
  53. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  54. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -5
  55. data/lib/rubocop/cop/style/copyright.rb +16 -11
  56. data/lib/rubocop/cop/style/eval_with_location.rb +2 -0
  57. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  58. data/lib/rubocop/cop/style/for.rb +2 -0
  59. data/lib/rubocop/cop/style/format_string.rb +9 -9
  60. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -1
  61. data/lib/rubocop/cop/style/hash_syntax.rb +6 -2
  62. data/lib/rubocop/cop/style/inverse_methods.rb +8 -8
  63. data/lib/rubocop/cop/style/invertible_unless_condition.rb +10 -5
  64. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +5 -8
  65. data/lib/rubocop/cop/style/map_into_array.rb +175 -0
  66. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  67. data/lib/rubocop/cop/style/map_to_set.rb +1 -1
  68. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  69. data/lib/rubocop/cop/style/multiline_method_signature.rb +10 -1
  70. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +4 -0
  71. data/lib/rubocop/cop/style/nil_comparison.rb +2 -0
  72. data/lib/rubocop/cop/style/object_then.rb +5 -3
  73. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -3
  74. data/lib/rubocop/cop/style/raise_args.rb +4 -1
  75. data/lib/rubocop/cop/style/redundant_argument.rb +25 -2
  76. data/lib/rubocop/cop/style/redundant_assignment.rb +10 -2
  77. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +5 -4
  78. data/lib/rubocop/cop/style/redundant_each.rb +1 -1
  79. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  80. data/lib/rubocop/cop/style/redundant_line_continuation.rb +5 -0
  81. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  82. data/lib/rubocop/cop/style/redundant_return.rb +6 -0
  83. data/lib/rubocop/cop/style/sample.rb +1 -3
  84. data/lib/rubocop/cop/team.rb +3 -0
  85. data/lib/rubocop/cop/utils/regexp_ranges.rb +1 -1
  86. data/lib/rubocop/cops_documentation_generator.rb +4 -2
  87. data/lib/rubocop/directive_comment.rb +10 -8
  88. data/lib/rubocop/formatter/clang_style_formatter.rb +3 -7
  89. data/lib/rubocop/formatter/html_formatter.rb +30 -10
  90. data/lib/rubocop/formatter/offense_count_formatter.rb +12 -2
  91. data/lib/rubocop/formatter/tap_formatter.rb +3 -7
  92. data/lib/rubocop/lockfile.rb +34 -4
  93. data/lib/rubocop/lsp/logger.rb +1 -1
  94. data/lib/rubocop/lsp/routes.rb +1 -1
  95. data/lib/rubocop/lsp/runtime.rb +1 -1
  96. data/lib/rubocop/lsp/server.rb +5 -2
  97. data/lib/rubocop/lsp/severity.rb +1 -1
  98. data/lib/rubocop/lsp.rb +29 -0
  99. data/lib/rubocop/magic_comment.rb +1 -1
  100. data/lib/rubocop/options.rb +11 -0
  101. data/lib/rubocop/path_util.rb +6 -2
  102. data/lib/rubocop/rspec/cop_helper.rb +8 -2
  103. data/lib/rubocop/rspec/expect_offense.rb +16 -8
  104. data/lib/rubocop/rspec/shared_contexts.rb +49 -18
  105. data/lib/rubocop/rspec/support.rb +2 -1
  106. data/lib/rubocop/runner.rb +12 -2
  107. data/lib/rubocop/target_finder.rb +84 -78
  108. data/lib/rubocop/target_ruby.rb +82 -80
  109. data/lib/rubocop/version.rb +19 -4
  110. data/lib/rubocop.rb +1 -0
  111. metadata +10 -6
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # The RuboCop's built-in LSP module.
5
+ module LSP
6
+ module_function
7
+
8
+ # Returns true when LSP is enabled, false when disabled.
9
+ #
10
+ # @return [Boolean]
11
+ def enabled?
12
+ @enabled ||= false
13
+ end
14
+
15
+ # Enable LSP.
16
+ #
17
+ # @return [void]
18
+ def enable
19
+ @enabled = true
20
+ end
21
+
22
+ # Disable LSP.
23
+ #
24
+ # @return [void]
25
+ def disable
26
+ @enabled = false
27
+ end
28
+ end
29
+ end
@@ -268,7 +268,7 @@ module RuboCop
268
268
 
269
269
  # Rewrite the comment without a given token type
270
270
  def without(type)
271
- if @comment.match?(/\A#\s*#{self.class::KEYWORDS[type.to_sym]}/)
271
+ if @comment.match?(/\A#\s*#{self.class::KEYWORDS[type.to_sym]}/io)
272
272
  ''
273
273
  else
274
274
  @comment
@@ -95,6 +95,7 @@ module RuboCop
95
95
  option(opts, '--ignore-unrecognized-cops')
96
96
  option(opts, '--force-default-config')
97
97
  option(opts, '-s', '--stdin FILE')
98
+ option(opts, '--editor-mode')
98
99
  option(opts, '-P', '--[no-]parallel')
99
100
  option(opts, '--raise-cop-error')
100
101
  add_severity_option(opts)
@@ -369,6 +370,7 @@ module RuboCop
369
370
  validate_display_only_failed
370
371
  validate_display_only_failed_and_display_only_correctable
371
372
  validate_display_only_correctable_and_autocorrect
373
+ validate_lsp_and_editor_mode
372
374
  disable_parallel_when_invalid_option_combo
373
375
 
374
376
  return if incompatible_options.size <= 1
@@ -416,6 +418,13 @@ module RuboCop
416
418
  format('--display-only-failed cannot be used together with other display options.')
417
419
  end
418
420
 
421
+ def validate_lsp_and_editor_mode
422
+ return if !@options.key?(:lsp) || !@options.key?(:editor_mode)
423
+
424
+ raise OptionArgumentError,
425
+ format('Do not specify `--editor-mode` as it is redundant in `--lsp`.')
426
+ end
427
+
419
428
  def validate_autocorrect
420
429
  if @options.key?(:safe_autocorrect) && @options.key?(:autocorrect_all)
421
430
  message = Rainbow(<<~MESSAGE).red
@@ -609,6 +618,8 @@ module RuboCop
609
618
  'parallel. Default is true.'],
610
619
  stdin: ['Pipe source from STDIN, using FILE in offense',
611
620
  'reports. This is useful for editor integration.'],
621
+ editor_mode: ['Optimize real-time feedback in editors,',
622
+ 'adjusting behaviors for editing experience.'],
612
623
  init: 'Generate a .rubocop.yml file in the current directory.',
613
624
  server: ['If a server process has not been started yet, start',
614
625
  'the server process and execute inspection with server.',
@@ -44,7 +44,7 @@ module RuboCop
44
44
  end
45
45
  end
46
46
 
47
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
47
+ # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
48
48
  def match_path?(pattern, path)
49
49
  case pattern
50
50
  when String
@@ -52,6 +52,10 @@ module RuboCop
52
52
  if pattern == path
53
53
  true
54
54
  elsif glob?(pattern)
55
+ # File name matching doesn't really work with relative patterns that start with "..". We
56
+ # get around that problem by converting the pattern to an absolute path.
57
+ pattern = File.expand_path(pattern) if pattern.start_with?('..')
58
+
55
59
  File.fnmatch?(pattern, path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
56
60
  end
57
61
 
@@ -66,7 +70,7 @@ module RuboCop
66
70
  end
67
71
  end
68
72
  end
69
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
73
+ # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
70
74
 
71
75
  # Returns true for an absolute Unix or Windows path.
72
76
  def absolute?(path)
@@ -6,7 +6,11 @@ require 'tempfile'
6
6
  module CopHelper
7
7
  extend RSpec::SharedContext
8
8
 
9
- let(:ruby_version) { RuboCop::TargetRuby::DEFAULT_VERSION }
9
+ let(:ruby_version) do
10
+ # The minimum version Prism can parse is 3.3.
11
+ ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : RuboCop::TargetRuby::DEFAULT_VERSION
12
+ end
13
+ let(:parser_engine) { ENV.fetch('PARSER_ENGINE', :parser_whitequark).to_sym }
10
14
  let(:rails_version) { false }
11
15
 
12
16
  def inspect_source(source, file = nil)
@@ -28,7 +32,9 @@ module CopHelper
28
32
  file = file.path
29
33
  end
30
34
 
31
- processed_source = RuboCop::ProcessedSource.new(source, ruby_version, file)
35
+ processed_source = RuboCop::ProcessedSource.new(
36
+ source, ruby_version, file, parser_engine: parser_engine
37
+ )
32
38
  processed_source.config = configuration
33
39
  processed_source.registry = registry
34
40
  processed_source
@@ -111,6 +111,7 @@ module RuboCop
111
111
  source
112
112
  end
113
113
 
114
+ # rubocop:disable Metrics/AbcSize
114
115
  def expect_offense(source, file = nil, severity: nil, chomp: false, **replacements)
115
116
  expected_annotations = parse_annotations(source, **replacements)
116
117
  source = expected_annotations.plain_source
@@ -123,10 +124,17 @@ module RuboCop
123
124
  expect(actual_annotations).to eq(expected_annotations), ''
124
125
  expect(@offenses.map(&:severity).uniq).to eq([severity]) if severity
125
126
 
127
+ # Validate that all offenses have a range that formatters can display
128
+ expect do
129
+ @offenses.each { |offense| offense.location.source_line }
130
+ end.not_to raise_error, 'One of the offenses has a misconstructed range, for ' \
131
+ 'example if the offense is on line 1 and the source is empty'
132
+
126
133
  @offenses
127
134
  end
135
+ # rubocop:enable Metrics/AbcSize
128
136
 
129
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
137
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
130
138
  def expect_correction(correction, loop: true, source: nil)
131
139
  if source
132
140
  expected_annotations = parse_annotations(source, raise_error: false)
@@ -148,7 +156,6 @@ module RuboCop
148
156
 
149
157
  break corrected_source unless loop
150
158
  break corrected_source if @last_corrector.empty?
151
- break corrected_source if corrected_source == @processed_source.buffer.source
152
159
 
153
160
  if iteration > RuboCop::Runner::MAX_ITERATIONS
154
161
  raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [@offenses])
@@ -163,19 +170,20 @@ module RuboCop
163
170
 
164
171
  expect(new_source).to eq(correction)
165
172
  end
166
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
173
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
167
174
 
168
175
  def expect_no_corrections
169
176
  raise '`expect_no_corrections` must follow `expect_offense`' unless @processed_source
170
177
 
171
178
  return if @last_corrector.empty?
172
179
 
173
- # In order to print a nice diff, e.g. what source got corrected to,
174
- # we need to run the actual corrections
175
-
180
+ # This is just here for a pretty diff if the source actually got changed
176
181
  new_source = @last_corrector.rewrite
177
-
178
182
  expect(new_source).to eq(@processed_source.buffer.source)
183
+
184
+ # There is an infinite loop if a corrector is present that did not make
185
+ # any changes. It will cause the same offense/correction on the next loop.
186
+ raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [@offenses])
179
187
  end
180
188
 
181
189
  def expect_no_offenses(source, file = nil)
@@ -212,7 +220,7 @@ module RuboCop
212
220
 
213
221
  # Parsed representation of code annotated with the `^^^ Message` style
214
222
  class AnnotatedSource
215
- ANNOTATION_PATTERN = /\A\s*(\^+|\^{}) /.freeze
223
+ ANNOTATION_PATTERN = /\A\s*(\^+|\^{}) ?/.freeze
216
224
  ABBREV = "[...]\n"
217
225
 
218
226
  # @param annotated_source [String] string passed to the matchers
@@ -100,17 +100,29 @@ RSpec.shared_context 'config' do # rubocop:disable Metrics/BlockLength
100
100
  let(:cur_cop_config) do
101
101
  RuboCop::ConfigLoader
102
102
  .default_configuration.for_cop(cop_class)
103
- .merge({
104
- 'Enabled' => true, # in case it is 'pending'
105
- 'AutoCorrect' => true # in case defaults set it to false
106
- })
103
+ .merge(
104
+ 'Enabled' => true, # in case it is 'pending'
105
+ 'AutoCorrect' => 'always' # in case defaults set it to 'disabled' or false
106
+ )
107
107
  .merge(cop_config)
108
108
  end
109
109
 
110
110
  let(:config) do
111
111
  hash = { 'AllCops' => all_cops_config, cop_class.cop_name => cur_cop_config }.merge!(other_cops)
112
112
 
113
- RuboCop::Config.new(hash, "#{Dir.pwd}/.rubocop.yml")
113
+ config = RuboCop::Config.new(hash, "#{Dir.pwd}/.rubocop.yml")
114
+
115
+ rails_version_in_gemfile = Gem::Version.new(
116
+ rails_version || RuboCop::Config::DEFAULT_RAILS_VERSION
117
+ )
118
+
119
+ allow(config).to receive(:gem_versions_in_target).and_return(
120
+ {
121
+ 'railties' => rails_version_in_gemfile
122
+ }
123
+ )
124
+
125
+ config
114
126
  end
115
127
 
116
128
  let(:cop) { cop_class.new(config, cop_options) }
@@ -128,56 +140,75 @@ RSpec.shared_context 'mock console output' do
128
140
  end
129
141
  end
130
142
 
131
- RSpec.shared_context 'lsp mode' do
143
+ RSpec.shared_context 'lsp' do
132
144
  before do
133
- allow(cop).to receive(:lsp_mode?).and_return(true)
145
+ RuboCop::LSP.enable
146
+ end
147
+
148
+ after do
149
+ RuboCop::LSP.disable
134
150
  end
135
151
  end
136
152
 
137
153
  RSpec.shared_context 'ruby 2.0' do
138
- let(:ruby_version) { 2.0 }
154
+ # Prism supports parsing Ruby 3.3+.
155
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.0 }
139
156
  end
140
157
 
141
158
  RSpec.shared_context 'ruby 2.1' do
142
- let(:ruby_version) { 2.1 }
159
+ # Prism supports parsing Ruby 3.3+.
160
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.1 }
143
161
  end
144
162
 
145
163
  RSpec.shared_context 'ruby 2.2' do
146
- let(:ruby_version) { 2.2 }
164
+ # Prism supports parsing Ruby 3.3+.
165
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.2 }
147
166
  end
148
167
 
149
168
  RSpec.shared_context 'ruby 2.3' do
150
- let(:ruby_version) { 2.3 }
169
+ # Prism supports parsing Ruby 3.3+.
170
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.3 }
151
171
  end
152
172
 
153
173
  RSpec.shared_context 'ruby 2.4' do
154
- let(:ruby_version) { 2.4 }
174
+ # Prism supports parsing Ruby 3.3+.
175
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.4 }
155
176
  end
156
177
 
157
178
  RSpec.shared_context 'ruby 2.5' do
158
- let(:ruby_version) { 2.5 }
179
+ # Prism supports parsing Ruby 3.3+.
180
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.5 }
159
181
  end
160
182
 
161
183
  RSpec.shared_context 'ruby 2.6' do
162
- let(:ruby_version) { 2.6 }
184
+ # Prism supports parsing Ruby 3.3+.
185
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.6 }
163
186
  end
164
187
 
165
188
  RSpec.shared_context 'ruby 2.7' do
166
- let(:ruby_version) { 2.7 }
189
+ # Prism supports parsing Ruby 3.3+.
190
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.7 }
167
191
  end
168
192
 
169
193
  RSpec.shared_context 'ruby 3.0' do
170
- let(:ruby_version) { 3.0 }
194
+ # Prism supports parsing Ruby 3.3+.
195
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 3.0 }
171
196
  end
172
197
 
173
198
  RSpec.shared_context 'ruby 3.1' do
174
- let(:ruby_version) { 3.1 }
199
+ # Prism supports parsing Ruby 3.3+.
200
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 3.1 }
175
201
  end
176
202
 
177
203
  RSpec.shared_context 'ruby 3.2' do
178
- let(:ruby_version) { 3.2 }
204
+ # Prism supports parsing Ruby 3.3+.
205
+ let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 3.2 }
179
206
  end
180
207
 
181
208
  RSpec.shared_context 'ruby 3.3' do
182
209
  let(:ruby_version) { 3.3 }
183
210
  end
211
+
212
+ RSpec.shared_context 'ruby 3.4' do
213
+ let(:ruby_version) { 3.4 }
214
+ end
@@ -13,7 +13,7 @@ RSpec.configure do |config|
13
13
  config.include HostEnvironmentSimulatorHelper
14
14
  config.include_context 'config', :config
15
15
  config.include_context 'isolated environment', :isolated_environment
16
- config.include_context 'lsp mode', :lsp_mode
16
+ config.include_context 'lsp', :lsp
17
17
  config.include_context 'maintain registry', :restore_registry
18
18
  config.include_context 'ruby 2.0', :ruby20
19
19
  config.include_context 'ruby 2.1', :ruby21
@@ -27,4 +27,5 @@ RSpec.configure do |config|
27
27
  config.include_context 'ruby 3.1', :ruby31
28
28
  config.include_context 'ruby 3.2', :ruby32
29
29
  config.include_context 'ruby 3.3', :ruby33
30
+ config.include_context 'ruby 3.4', :ruby34
30
31
  end
@@ -20,6 +20,9 @@ module RuboCop
20
20
  message = 'Infinite loop detected'
21
21
  message += " in #{path}" if path
22
22
  message += " and caused by #{root_cause}" if root_cause
23
+ message += ' Hint: Please update to the latest RuboCop version if not already in use,'
24
+ message += ' and report a bug if the issue still occurs on this version.'
25
+ message += ' Please check the latest version at https://rubygems.org/gems/rubocop'
23
26
  super(message)
24
27
  end
25
28
  end
@@ -467,15 +470,21 @@ module RuboCop
467
470
  end
468
471
  end
469
472
 
473
+ # rubocop:disable Metrics/MethodLength
470
474
  def get_processed_source(file)
471
475
  config = @config_store.for_file(file)
472
476
  ruby_version = config.target_ruby_version
477
+ parser_engine = config.parser_engine
473
478
 
474
479
  processed_source = if @options[:stdin]
475
- ProcessedSource.new(@options[:stdin], ruby_version, file)
480
+ ProcessedSource.new(
481
+ @options[:stdin], ruby_version, file, parser_engine: parser_engine
482
+ )
476
483
  else
477
484
  begin
478
- ProcessedSource.from_file(file, ruby_version)
485
+ ProcessedSource.from_file(
486
+ file, ruby_version, parser_engine: parser_engine
487
+ )
479
488
  rescue Errno::ENOENT
480
489
  raise RuboCop::Error, "No such file or directory: #{file}"
481
490
  end
@@ -484,6 +493,7 @@ module RuboCop
484
493
  processed_source.registry = mobilized_cop_classes(config)
485
494
  processed_source
486
495
  end
496
+ # rubocop:enable Metrics/MethodLength
487
497
 
488
498
  # A Cop::Team instance is stateful and may change when inspecting.
489
499
  # The "standby" team for a given config is an initialized but
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RuboCop
4
- # This class finds target files to inspect by scanning the directory tree
5
- # and picking ruby files.
4
+ # This class finds target files to inspect by scanning the directory tree and picking ruby files.
6
5
  # @api private
7
6
  class TargetFinder
8
7
  HIDDEN_PATH_SUBSTRING = "#{File::SEPARATOR}."
@@ -12,21 +11,8 @@ module RuboCop
12
11
  @options = options
13
12
  end
14
13
 
15
- def force_exclusion?
16
- @options[:force_exclusion]
17
- end
18
-
19
- def debug?
20
- @options[:debug]
21
- end
22
-
23
- def fail_fast?
24
- @options[:fail_fast]
25
- end
26
-
27
- # Generate a list of target files by expanding globbing patterns
28
- # (if any). If args is empty, recursively find all Ruby source
29
- # files under the current directory
14
+ # Generate a list of target files by expanding globbing patterns (if any). If args is empty,
15
+ # recursively find all Ruby source files under the current directory
30
16
  # @return [Array] array of file paths
31
17
  def find(args, mode)
32
18
  return target_files_in_dir if args.empty?
@@ -44,12 +30,11 @@ module RuboCop
44
30
  files.map { |f| File.expand_path(f) }.uniq
45
31
  end
46
32
 
47
- # Finds all Ruby source files under the current or other supplied
48
- # directory. A Ruby source file is defined as a file with the `.rb`
49
- # extension or a file with no extension that has a ruby shebang line
50
- # as its first line.
51
- # It is possible to specify includes and excludes using the config file,
52
- # so you can include other Ruby files like Rakefiles and gemspecs.
33
+ # Finds all Ruby source files under the current or other supplied directory. A Ruby source file
34
+ # is defined as a file with the `.rb` extension or a file with no extension that has a ruby
35
+ # shebang line as its first line.
36
+ # It is possible to specify includes and excludes using the config file, so you can include
37
+ # other Ruby files like Rakefiles and gemspecs.
53
38
  # @param base_dir Root directory under which to search for
54
39
  # ruby source files
55
40
  # @return [Array] Array of filenames
@@ -66,20 +51,10 @@ module RuboCop
66
51
  target_files.sort_by!(&order)
67
52
  end
68
53
 
69
- def to_inspect?(file, hidden_files, base_dir_config)
70
- return false if base_dir_config.file_to_exclude?(file)
71
- return true if !hidden_files.bsearch do |hidden_file|
72
- file <=> hidden_file
73
- end && ruby_file?(file)
74
-
75
- base_dir_config.file_to_include?(file)
76
- end
77
-
78
- # Search for files recursively starting at the given base directory using
79
- # the given flags that determine how the match is made. Excluded files will
80
- # be removed later by the caller, but as an optimization find_files removes
81
- # the top level directories that are excluded in configuration in the
82
- # normal way (dir/**/*).
54
+ # Search for files recursively starting at the given base directory using the given flags that
55
+ # determine how the match is made. Excluded files will be removed later by the caller, but as an
56
+ # optimization find_files removes the top level directories that are excluded in configuration
57
+ # in the normal way (dir/**/*).
83
58
  def find_files(base_dir, flags)
84
59
  # get all wanted directories first to improve speed of finding all files
85
60
  exclude_pattern = combined_exclude_glob_patterns(base_dir)
@@ -93,6 +68,17 @@ module RuboCop
93
68
  Dir.glob(patterns, flags).select { |path| FileTest.file?(path) }
94
69
  end
95
70
 
71
+ private
72
+
73
+ def to_inspect?(file, hidden_files, base_dir_config)
74
+ return false if base_dir_config.file_to_exclude?(file)
75
+ return true if !hidden_files.bsearch do |hidden_file|
76
+ file <=> hidden_file
77
+ end && ruby_file?(file)
78
+
79
+ base_dir_config.file_to_include?(file)
80
+ end
81
+
96
82
  def wanted_dir_patterns(base_dir, exclude_pattern, flags)
97
83
  # Escape glob characters in base_dir to avoid unwanted behavior.
98
84
  base_dir = base_dir.gsub(/[\\\{\}\[\]\*\?]/) do |reserved_glob_character|
@@ -124,21 +110,6 @@ module RuboCop
124
110
  "#{base_dir}/{#{patterns.join(',')}}"
125
111
  end
126
112
 
127
- def ruby_extension?(file)
128
- ruby_extensions.include?(File.extname(file))
129
- end
130
-
131
- def ruby_extensions
132
- @ruby_extensions ||= begin
133
- ext_patterns = all_cops_include.select { |pattern| pattern.start_with?('**/*.') }
134
- ext_patterns.map { |pattern| pattern.sub('**/*', '') }
135
- end
136
- end
137
-
138
- def ruby_filename?(file)
139
- ruby_filenames.include?(File.basename(file))
140
- end
141
-
142
113
  def ruby_filenames
143
114
  @ruby_filenames ||= begin
144
115
  file_patterns = all_cops_include.reject { |pattern| pattern.start_with?('**/*.') }
@@ -150,53 +121,72 @@ module RuboCop
150
121
  @all_cops_include ||= @config_store.for_pwd.for_all_cops['Include'].map(&:to_s)
151
122
  end
152
123
 
153
- def ruby_executable?(file)
154
- return false unless File.extname(file).empty? && File.exist?(file)
124
+ def process_explicit_path(path, mode)
125
+ files = path.include?('*') ? Dir[path] : [path]
155
126
 
156
- first_line = File.open(file, &:readline)
157
- /#!.*(#{ruby_interpreters(file).join('|')})/.match?(first_line)
158
- rescue EOFError, ArgumentError => e
159
- warn("Unprocessable file #{file}: #{e.class}, #{e.message}") if debug?
127
+ if mode == :only_recognized_file_types || force_exclusion?
128
+ files.select! { |file| included_file?(file) }
129
+ end
160
130
 
161
- false
131
+ force_exclusion? ? without_excluded(files) : files
162
132
  end
163
133
 
164
- def ruby_interpreters(file)
165
- @config_store.for(file).for_all_cops['RubyInterpreters']
134
+ def without_excluded(files)
135
+ files.reject do |file|
136
+ # When --ignore-parent-exclusion is given, we must look at the configuration associated with
137
+ # the file, but in the default case when --ignore-parent-exclusion is not given, can safely
138
+ # look only at the configuration for the current directory, since it's only the Exclude
139
+ # parameters we're going to check.
140
+ config = @config_store.for(ignore_parent_exclusion? ? file : '.')
141
+ config.file_to_exclude?(file)
142
+ end
166
143
  end
167
144
 
168
- def stdin?
169
- @options.key?(:stdin)
145
+ def included_file?(file)
146
+ ruby_file?(file) || configured_include?(file)
170
147
  end
171
148
 
172
149
  def ruby_file?(file)
173
150
  stdin? || ruby_extension?(file) || ruby_filename?(file) || ruby_executable?(file)
174
151
  end
175
152
 
176
- def configured_include?(file)
177
- @config_store.for_pwd.file_to_include?(file)
153
+ def stdin?
154
+ @options.key?(:stdin)
178
155
  end
179
156
 
180
- def included_file?(file)
181
- ruby_file?(file) || configured_include?(file)
157
+ def ruby_extension?(file)
158
+ ruby_extensions.include?(File.extname(file))
182
159
  end
183
160
 
184
- def process_explicit_path(path, mode)
185
- files = path.include?('*') ? Dir[path] : [path]
186
-
187
- if mode == :only_recognized_file_types || force_exclusion?
188
- files.select! { |file| included_file?(file) }
161
+ def ruby_extensions
162
+ @ruby_extensions ||= begin
163
+ ext_patterns = all_cops_include.select { |pattern| pattern.start_with?('**/*.') }
164
+ ext_patterns.map { |pattern| pattern.sub('**/*', '') }
189
165
  end
166
+ end
190
167
 
191
- return files unless force_exclusion?
168
+ def ruby_filename?(file)
169
+ ruby_filenames.include?(File.basename(file))
170
+ end
192
171
 
193
- files.reject do |file|
194
- config = @config_store.for(file)
195
- config.file_to_exclude?(file)
196
- end
172
+ def configured_include?(file)
173
+ @config_store.for_pwd.file_to_include?(file)
197
174
  end
198
175
 
199
- private
176
+ def ruby_executable?(file)
177
+ return false unless File.extname(file).empty? && File.exist?(file)
178
+
179
+ first_line = File.open(file, &:readline)
180
+ /#!.*(#{ruby_interpreters(file).join('|')})/.match?(first_line)
181
+ rescue EOFError, ArgumentError => e
182
+ warn("Unprocessable file #{file}: #{e.class}, #{e.message}") if debug?
183
+
184
+ false
185
+ end
186
+
187
+ def ruby_interpreters(file)
188
+ @config_store.for(file).for_all_cops['RubyInterpreters']
189
+ end
200
190
 
201
191
  def order
202
192
  if fail_fast?
@@ -206,5 +196,21 @@ module RuboCop
206
196
  :itself
207
197
  end
208
198
  end
199
+
200
+ def force_exclusion?
201
+ @options[:force_exclusion]
202
+ end
203
+
204
+ def ignore_parent_exclusion?
205
+ @options[:ignore_parent_exclusion]
206
+ end
207
+
208
+ def debug?
209
+ @options[:debug]
210
+ end
211
+
212
+ def fail_fast?
213
+ @options[:fail_fast]
214
+ end
209
215
  end
210
216
  end