rubocop 1.90.0 → 1.91.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.
- checksums.yaml +4 -4
- data/config/default.yml +238 -10
- data/config/obsoletion.yml +9 -0
- data/lib/rubocop/changed_files.rb +112 -0
- data/lib/rubocop/cli/command/execute_runner.rb +21 -1
- data/lib/rubocop/cli.rb +9 -3
- data/lib/rubocop/comment_config/directive_range.rb +3 -1
- data/lib/rubocop/comment_config/disable_next.rb +49 -3
- data/lib/rubocop/comment_config/push_pop.rb +55 -0
- data/lib/rubocop/comment_config.rb +21 -51
- data/lib/rubocop/config.rb +10 -0
- data/lib/rubocop/config_loader.rb +12 -4
- data/lib/rubocop/config_loader_resolver.rb +49 -7
- data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
- data/lib/rubocop/config_obsoletion/removed_cop.rb +8 -0
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/config_validator.rb +18 -3
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +15 -3
- data/lib/rubocop/cop/correctors.rb +2 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +6 -5
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- data/lib/rubocop/cop/layout/line_length.rb +19 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +14 -3
- data/lib/rubocop/cop/layout/space_around_operators.rb +5 -1
- data/lib/rubocop/cop/lint/argument_mismatch.rb +14 -4
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +88 -14
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +8 -2
- data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +185 -0
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +30 -16
- data/lib/rubocop/cop/lint.rb +1 -0
- data/lib/rubocop/cop/migration/department_name.rb +1 -1
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +11 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -2
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +12 -1
- data/lib/rubocop/cop/mixin.rb +2 -2
- data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/registry.rb +9 -1
- data/lib/rubocop/cop/severity.rb +12 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +2 -2
- data/lib/rubocop/cop/style/case_like_if.rb +3 -3
- data/lib/rubocop/cop/style/constant_visibility.rb +6 -2
- data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/directive_scope.rb +154 -21
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +53 -9
- data/lib/rubocop/cop/style/documentation_method.rb +1 -9
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +12 -2
- data/lib/rubocop/cop/style/endless_method.rb +31 -12
- data/lib/rubocop/cop/style/file_null.rb +2 -2
- data/lib/rubocop/cop/style/float_division.rb +10 -16
- data/lib/rubocop/cop/style/for.rb +5 -0
- data/lib/rubocop/cop/style/format_string_token.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +4 -0
- data/lib/rubocop/cop/style/hash_lookup_method.rb +3 -0
- data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +9 -4
- data/lib/rubocop/cop/style/nil_comparison.rb +36 -8
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
- data/lib/rubocop/cop/style/not.rb +3 -1
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +4 -4
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +10 -1
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +11 -1
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +6 -6
- data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style.rb +0 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +23 -2
- data/lib/rubocop/directive_comment.rb +37 -8
- data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
- data/lib/rubocop/formatter/html_formatter.rb +2 -2
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/sarif_formatter.rb +181 -0
- data/lib/rubocop/formatter.rb +1 -0
- data/lib/rubocop/magic_comment.rb +27 -0
- data/lib/rubocop/options.rb +59 -1
- data/lib/rubocop/path_util.rb +1 -1
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +4 -4
- data/lib/rubocop/result_cache.rb +16 -5
- data/lib/rubocop/runner.rb +64 -21
- data/lib/rubocop/unified_diff.rb +257 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +4 -1
- metadata +8 -4
- data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
|
@@ -49,9 +49,9 @@ module RuboCop
|
|
|
49
49
|
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
|
50
50
|
|
|
51
51
|
generate_request(uri) do |request|
|
|
52
|
-
handle_response(http.request(request), limit, &block)
|
|
52
|
+
handle_response(http.request(request), uri, limit, &block)
|
|
53
53
|
rescue SocketError => e
|
|
54
|
-
handle_response(e, limit, &block)
|
|
54
|
+
handle_response(e, uri, limit, &block)
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
@@ -64,12 +64,12 @@ module RuboCop
|
|
|
64
64
|
yield request
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
def handle_response(response, limit, &block)
|
|
67
|
+
def handle_response(response, uri, limit, &block)
|
|
68
68
|
case response
|
|
69
69
|
when Net::HTTPSuccess, Net::HTTPNotModified, SocketError
|
|
70
70
|
yield response
|
|
71
71
|
when Net::HTTPRedirection
|
|
72
|
-
request(URI.
|
|
72
|
+
request(URI.join(uri, response['location']), limit - 1, &block)
|
|
73
73
|
else
|
|
74
74
|
begin
|
|
75
75
|
response.error!
|
data/lib/rubocop/result_cache.rb
CHANGED
|
@@ -123,7 +123,7 @@ module RuboCop
|
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def valid?
|
|
126
|
-
File.exist?(@path)
|
|
126
|
+
!@checksum_unavailable && File.exist?(@path)
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def load
|
|
@@ -132,6 +132,8 @@ module RuboCop
|
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
def save(offenses)
|
|
135
|
+
return if @checksum_unavailable
|
|
136
|
+
|
|
135
137
|
dir = File.dirname(@path)
|
|
136
138
|
|
|
137
139
|
begin
|
|
@@ -176,14 +178,23 @@ module RuboCop
|
|
|
176
178
|
end
|
|
177
179
|
|
|
178
180
|
def file_checksum(file, config_store)
|
|
181
|
+
stat = File.stat(file)
|
|
182
|
+
return unavailable_checksum unless stat.file?
|
|
183
|
+
|
|
179
184
|
digester = Digest::SHA1.new
|
|
180
|
-
mode
|
|
181
|
-
digester.update("#{file}#{mode}#{config_store.for_file(file).signature}")
|
|
185
|
+
digester.update("#{file}#{stat.mode}#{config_store.for_file(file).signature}")
|
|
182
186
|
digester.file(file)
|
|
183
187
|
digester.hexdigest
|
|
184
188
|
rescue Errno::ENOENT
|
|
185
|
-
# Spurious files that come and go should not cause a crash, at least not
|
|
186
|
-
|
|
189
|
+
# Spurious files that come and go should not cause a crash, at least not here.
|
|
190
|
+
unavailable_checksum
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Every file that fails to checksum shares this sentinel value, so the cache entry
|
|
194
|
+
# must be neither saved nor considered valid, or one file's cached results could be
|
|
195
|
+
# served as another's.
|
|
196
|
+
def unavailable_checksum
|
|
197
|
+
@checksum_unavailable = true
|
|
187
198
|
'_'
|
|
188
199
|
end
|
|
189
200
|
|
data/lib/rubocop/runner.rb
CHANGED
|
@@ -53,7 +53,7 @@ module RuboCop
|
|
|
53
53
|
Lint/RedundantCopDisableDirective RedundantCopDisableDirective Lint
|
|
54
54
|
].freeze
|
|
55
55
|
|
|
56
|
-
attr_reader :errors, :warnings
|
|
56
|
+
attr_reader :errors, :warnings, :diffs
|
|
57
57
|
attr_writer :aborting
|
|
58
58
|
|
|
59
59
|
def initialize(options, config_store)
|
|
@@ -63,6 +63,7 @@ module RuboCop
|
|
|
63
63
|
@warnings = []
|
|
64
64
|
@aborting = false
|
|
65
65
|
@inspected_files = []
|
|
66
|
+
@diffs = []
|
|
66
67
|
@report_queue = {}
|
|
67
68
|
end
|
|
68
69
|
|
|
@@ -73,7 +74,7 @@ module RuboCop
|
|
|
73
74
|
# inconsistencies between workers.
|
|
74
75
|
ResultCache.source_checksum
|
|
75
76
|
|
|
76
|
-
target_files = find_target_files(paths)
|
|
77
|
+
target_files = only_changed(find_target_files(paths))
|
|
77
78
|
build_project_index(target_files)
|
|
78
79
|
|
|
79
80
|
if @options[:list_target_files]
|
|
@@ -95,6 +96,16 @@ module RuboCop
|
|
|
95
96
|
|
|
96
97
|
private
|
|
97
98
|
|
|
99
|
+
# The project index is deliberately built from the unfiltered file list:
|
|
100
|
+
# cross-file offenses must not depend on which files happen to have changed.
|
|
101
|
+
def only_changed(target_files)
|
|
102
|
+
return target_files unless @options[:changed]
|
|
103
|
+
|
|
104
|
+
changed = ChangedFiles.new(@options[:changed]).paths
|
|
105
|
+
|
|
106
|
+
target_files.select { |file| changed.include?(file) }.freeze
|
|
107
|
+
end
|
|
108
|
+
|
|
98
109
|
def find_target_files(paths)
|
|
99
110
|
target_finder = TargetFinder.new(@config_store, @options)
|
|
100
111
|
mode = if @options[:only_recognized_file_types]
|
|
@@ -205,8 +216,7 @@ module RuboCop
|
|
|
205
216
|
end
|
|
206
217
|
|
|
207
218
|
def run_in_parallel?(files)
|
|
208
|
-
return false
|
|
209
|
-
return false unless @options[:parallel]
|
|
219
|
+
return false unless parallel_supported_by_options?
|
|
210
220
|
|
|
211
221
|
if files.size <= 1
|
|
212
222
|
puts 'Skipping parallel inspection: only a single file needs inspection' if @options[:debug]
|
|
@@ -220,6 +230,13 @@ module RuboCop
|
|
|
220
230
|
true
|
|
221
231
|
end
|
|
222
232
|
|
|
233
|
+
# `--auto-gen-config` needs the offenses of every file in one place, and
|
|
234
|
+
# `--diff` collects the diffs in this process, where a worker's copy of
|
|
235
|
+
# them would be lost.
|
|
236
|
+
def parallel_supported_by_options?
|
|
237
|
+
@options[:parallel] && !@options[:auto_gen_config] && !@options[:diff]
|
|
238
|
+
end
|
|
239
|
+
|
|
223
240
|
def project_index_disables_parallel?
|
|
224
241
|
return false if @project_index.nil? || !Gem.win_platform?
|
|
225
242
|
|
|
@@ -379,6 +396,7 @@ module RuboCop
|
|
|
379
396
|
def do_inspection_loop(file)
|
|
380
397
|
# We can reuse the prism result since the source did not change yet.
|
|
381
398
|
processed_source = get_processed_source(file, @prism_result)
|
|
399
|
+
original_source = processed_source.raw_source
|
|
382
400
|
# This variable is 2d array used to track corrected offenses after each
|
|
383
401
|
# inspection iteration. This is used to output meaningful infinite loop
|
|
384
402
|
# error message.
|
|
@@ -391,11 +409,7 @@ module RuboCop
|
|
|
391
409
|
# once. The corrections are kept in memory while iterating and written
|
|
392
410
|
# back to the file when the loop is done.
|
|
393
411
|
iterate_until_no_changes(processed_source, offenses_by_iteration) do
|
|
394
|
-
|
|
395
|
-
# only keep the corrected ones in order to avoid duplicate reporting.
|
|
396
|
-
!offenses_by_iteration.empty? && offenses_by_iteration.last.select!(&:corrected?)
|
|
397
|
-
team, new_offenses, updated_source_file = inspect_iteration(processed_source)
|
|
398
|
-
offenses_by_iteration.push(new_offenses)
|
|
412
|
+
team, updated_source_file = inspect_and_correct(processed_source, offenses_by_iteration)
|
|
399
413
|
|
|
400
414
|
# We have to reprocess the source to pickup the changes. Since the
|
|
401
415
|
# change could (theoretically) introduce parsing errors, we break the
|
|
@@ -411,15 +425,49 @@ module RuboCop
|
|
|
411
425
|
# Return summary of corrected offenses after all iterations
|
|
412
426
|
[processed_source, offenses_by_iteration.flatten.uniq]
|
|
413
427
|
ensure
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
428
|
+
finalize_corrections(file, original_source, corrected_source)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def inspect_and_correct(processed_source, offenses_by_iteration)
|
|
432
|
+
# The offenses that couldn't be corrected will be found again so we
|
|
433
|
+
# only keep the corrected ones in order to avoid duplicate reporting.
|
|
434
|
+
!offenses_by_iteration.empty? && offenses_by_iteration.last.select!(&:corrected?)
|
|
435
|
+
team, new_offenses, updated_source_file = inspect_iteration(processed_source)
|
|
436
|
+
offenses_by_iteration.push(new_offenses)
|
|
437
|
+
|
|
438
|
+
[team, updated_source_file]
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Write the file once, even when the loop was left through an exception
|
|
442
|
+
# (e.g. an infinite correction loop), like the per-iteration writes used
|
|
443
|
+
# to be. Under `--diff` nothing is written at all.
|
|
444
|
+
def finalize_corrections(file, original_source, corrected_source)
|
|
445
|
+
if @options[:diff]
|
|
446
|
+
record_diff(file, original_source, corrected_source)
|
|
447
|
+
elsif corrected_source
|
|
448
|
+
File.write(file, corrected_source)
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# `--diff` reports what autocorrection would do instead of doing it. With
|
|
453
|
+
# `--stdin` the corrected source is handed back through the options rather
|
|
454
|
+
# than deferred, so that is where the new source comes from.
|
|
455
|
+
def record_diff(file, original_source, corrected_source)
|
|
456
|
+
new_source = @options[:stdin] || corrected_source
|
|
457
|
+
return unless original_source && new_source
|
|
458
|
+
|
|
459
|
+
# The original source is the file as it is on disk, so the corrected one
|
|
460
|
+
# has to go through the same line ending conversion `File.write` would
|
|
461
|
+
# apply, or on Windows every single line reads as changed.
|
|
462
|
+
new_source = emulate_write_read_cycle(new_source)
|
|
463
|
+
|
|
464
|
+
diff = UnifiedDiff.new(PathUtil.smart_path(file), original_source, new_source).to_s
|
|
465
|
+
@diffs << diff unless diff.empty?
|
|
418
466
|
end
|
|
419
467
|
|
|
420
468
|
def inspect_iteration(processed_source)
|
|
421
469
|
team = mobilize_team(processed_source)
|
|
422
|
-
team.defer_corrections = in_memory_corrections_possible?
|
|
470
|
+
team.defer_corrections = @options[:diff] || in_memory_corrections_possible?
|
|
423
471
|
offenses, updated_source_file = inspect_file(processed_source, team)
|
|
424
472
|
[team, offenses, updated_source_file]
|
|
425
473
|
end
|
|
@@ -602,13 +650,8 @@ module RuboCop
|
|
|
602
650
|
end
|
|
603
651
|
|
|
604
652
|
def minimum_severity_to_fail
|
|
605
|
-
@minimum_severity_to_fail ||=
|
|
606
|
-
|
|
607
|
-
name = @options[:fail_level] || :refactor
|
|
608
|
-
|
|
609
|
-
# autocorrect is a fake level - use the default
|
|
610
|
-
RuboCop::Cop::Severity.new(name == :autocorrect ? :refactor : name)
|
|
611
|
-
end
|
|
653
|
+
@minimum_severity_to_fail ||=
|
|
654
|
+
RuboCop::Cop::Severity.minimum_to_fail(@options, @config_store.for_pwd)
|
|
612
655
|
end
|
|
613
656
|
|
|
614
657
|
# rubocop:disable-next Metrics/MethodLength
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
# Renders a unified diff between two versions of a file's source.
|
|
5
|
+
#
|
|
6
|
+
# RuboCop has no diffing dependency and this output is meant to be read by
|
|
7
|
+
# people (and occasionally piped into `git apply`), so the goal is a diff
|
|
8
|
+
# that is correct and readable, not one that matches `diff -u` byte for byte.
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class UnifiedDiff
|
|
12
|
+
CONTEXT_LINES = 3
|
|
13
|
+
NO_NEWLINE_MARKER = "\\n"
|
|
14
|
+
|
|
15
|
+
# Finding a minimal diff costs time and memory proportional to the square
|
|
16
|
+
# of the number of edits. Past this many edits the file was rewritten
|
|
17
|
+
# rather than corrected, so the changed region is reported as a single
|
|
18
|
+
# replacement instead.
|
|
19
|
+
MAX_EDIT_DISTANCE = 400
|
|
20
|
+
|
|
21
|
+
def initialize(path, old_source, new_source)
|
|
22
|
+
@path = path
|
|
23
|
+
@old_lines = old_source.lines
|
|
24
|
+
@new_lines = new_source.lines
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [String] the diff, or an empty string when the sources are identical
|
|
28
|
+
def to_s
|
|
29
|
+
return '' if @old_lines == @new_lines
|
|
30
|
+
|
|
31
|
+
hunks = hunks_for(edit_script)
|
|
32
|
+
return '' if hunks.empty?
|
|
33
|
+
|
|
34
|
+
["--- a/#{@path}\n", "+++ b/#{@path}\n", *hunks].join
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def edit_script
|
|
40
|
+
prefix = common_prefix_length
|
|
41
|
+
suffix = common_suffix_length(prefix)
|
|
42
|
+
old_last = @old_lines.length - suffix
|
|
43
|
+
new_last = @new_lines.length - suffix
|
|
44
|
+
|
|
45
|
+
[
|
|
46
|
+
*@old_lines[0...prefix].map { |line| [:equal, line] },
|
|
47
|
+
*middle_script(@old_lines[prefix...old_last], @new_lines[prefix...new_last]),
|
|
48
|
+
*@old_lines[old_last..].map { |line| [:equal, line] }
|
|
49
|
+
]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def common_prefix_length
|
|
53
|
+
length = 0
|
|
54
|
+
length += 1 while length < @old_lines.length && @old_lines[length] == @new_lines[length]
|
|
55
|
+
length
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def common_suffix_length(prefix)
|
|
59
|
+
length = 0
|
|
60
|
+
max = [@old_lines.length, @new_lines.length].min - prefix
|
|
61
|
+
while length < max &&
|
|
62
|
+
@old_lines[@old_lines.length - 1 - length] == @new_lines[@new_lines.length - 1 - length]
|
|
63
|
+
length += 1
|
|
64
|
+
end
|
|
65
|
+
length
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def middle_script(old_lines, new_lines)
|
|
69
|
+
# The region that actually differs is diffed on its own, so the rest of
|
|
70
|
+
# the algorithm works on these two slices rather than the whole file.
|
|
71
|
+
@from_lines = old_lines
|
|
72
|
+
@to_lines = new_lines
|
|
73
|
+
|
|
74
|
+
myers_script ||
|
|
75
|
+
[*old_lines.map { |line| [:delete, line] }, *new_lines.map { |line| [:insert, line] }]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Myers' diff algorithm. Each round `d` extends the furthest reaching path
|
|
79
|
+
# for every diagonal `k`; the first path to reach the end of both sources
|
|
80
|
+
# is a shortest edit script, which is then reconstructed from the saved
|
|
81
|
+
# rounds. Returns `nil` when the sources are too different to bother.
|
|
82
|
+
def myers_script
|
|
83
|
+
trace = []
|
|
84
|
+
furthest = Hash.new(0)
|
|
85
|
+
max_distance = [@from_lines.length + @to_lines.length, MAX_EDIT_DISTANCE].min
|
|
86
|
+
|
|
87
|
+
0.upto(max_distance) do |distance|
|
|
88
|
+
trace << furthest.dup
|
|
89
|
+
|
|
90
|
+
(-distance).step(distance, 2) do |diagonal|
|
|
91
|
+
furthest[diagonal] = advance(furthest, diagonal, distance)
|
|
92
|
+
|
|
93
|
+
return backtrack(trace) if reached_end?(furthest[diagonal], diagonal)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def reached_end?(old_index, diagonal)
|
|
101
|
+
old_index >= @from_lines.length && old_index - diagonal >= @to_lines.length
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Picks the better of the two paths reaching this diagonal, then follows
|
|
105
|
+
# the diagonal as far as the lines match.
|
|
106
|
+
def advance(furthest, diagonal, distance)
|
|
107
|
+
old_index = start_of_path(furthest, diagonal, distance)
|
|
108
|
+
|
|
109
|
+
follow_diagonal(old_index, old_index - diagonal)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# A path arrives at this diagonal either from the one above it (having
|
|
113
|
+
# inserted a line) or the one below it (having deleted one); whichever
|
|
114
|
+
# reached further wins.
|
|
115
|
+
def previous_diagonal(furthest, diagonal, distance)
|
|
116
|
+
if diagonal == -distance ||
|
|
117
|
+
(diagonal != distance && furthest[diagonal - 1] < furthest[diagonal + 1])
|
|
118
|
+
diagonal + 1
|
|
119
|
+
else
|
|
120
|
+
diagonal - 1
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def start_of_path(furthest, diagonal, distance)
|
|
125
|
+
previous = previous_diagonal(furthest, diagonal, distance)
|
|
126
|
+
|
|
127
|
+
previous > diagonal ? furthest[previous] : furthest[previous] + 1
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Matching lines cost nothing, so the path slides along them for free.
|
|
131
|
+
def follow_diagonal(old_index, new_index)
|
|
132
|
+
while old_index < @from_lines.length && new_index < @to_lines.length &&
|
|
133
|
+
@from_lines[old_index] == @to_lines[new_index]
|
|
134
|
+
old_index += 1
|
|
135
|
+
new_index += 1
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
old_index
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def backtrack(trace)
|
|
142
|
+
script = []
|
|
143
|
+
position = [@from_lines.length, @to_lines.length]
|
|
144
|
+
|
|
145
|
+
trace.each_with_index.reverse_each do |furthest, distance|
|
|
146
|
+
previous = previous_position(furthest, distance, *position)
|
|
147
|
+
position = walk_diagonal(script, position, previous)
|
|
148
|
+
next if distance.zero?
|
|
149
|
+
|
|
150
|
+
position = record_edit(script, position, previous)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
script
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Both sides step back together for as long as their lines match.
|
|
157
|
+
def walk_diagonal(script, position, previous)
|
|
158
|
+
old_index, new_index = position
|
|
159
|
+
|
|
160
|
+
while old_index > previous[0] && new_index > previous[1]
|
|
161
|
+
old_index -= 1
|
|
162
|
+
new_index -= 1
|
|
163
|
+
script.unshift([:equal, @from_lines[old_index]])
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
[old_index, new_index]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def record_edit(script, position, previous)
|
|
170
|
+
old_index, new_index = position
|
|
171
|
+
|
|
172
|
+
if old_index == previous[0]
|
|
173
|
+
new_index -= 1
|
|
174
|
+
script.unshift([:insert, @to_lines[new_index]])
|
|
175
|
+
else
|
|
176
|
+
old_index -= 1
|
|
177
|
+
script.unshift([:delete, @from_lines[old_index]])
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
[old_index, new_index]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def previous_position(furthest, distance, old_index, new_index)
|
|
184
|
+
previous = previous_diagonal(furthest, old_index - new_index, distance)
|
|
185
|
+
previous_old = furthest[previous]
|
|
186
|
+
|
|
187
|
+
[previous_old, previous_old - previous]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def hunks_for(script)
|
|
191
|
+
changed = script.each_index.reject { |index| script[index].first == :equal }
|
|
192
|
+
return [] if changed.empty?
|
|
193
|
+
|
|
194
|
+
ranges_for(changed, script.length).map { |range| hunk(script, range) }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Every change is shown with a few lines of context around it, and changes
|
|
198
|
+
# whose context would overlap are shown as a single hunk.
|
|
199
|
+
def ranges_for(changed, script_length)
|
|
200
|
+
ranges = []
|
|
201
|
+
|
|
202
|
+
changed.each do |index|
|
|
203
|
+
first = [index - CONTEXT_LINES, 0].max
|
|
204
|
+
last = [index + CONTEXT_LINES, script_length - 1].min
|
|
205
|
+
|
|
206
|
+
if ranges.last && first <= ranges.last.last + 1
|
|
207
|
+
ranges[-1] = (ranges.last.first..last)
|
|
208
|
+
else
|
|
209
|
+
ranges << (first..last)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
ranges
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def hunk(script, range)
|
|
217
|
+
old_start, new_start = start_positions(script, range.first)
|
|
218
|
+
old_count = script[range].count { |kind, _| kind != :insert }
|
|
219
|
+
new_count = script[range].count { |kind, _| kind != :delete }
|
|
220
|
+
body = script[range].map { |kind, line| formatted_line(kind, line) }
|
|
221
|
+
|
|
222
|
+
[
|
|
223
|
+
"@@ -#{position(old_start, old_count)} +#{position(new_start, new_count)} @@\n",
|
|
224
|
+
*body
|
|
225
|
+
].join
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def start_positions(script, index)
|
|
229
|
+
old_line = 1
|
|
230
|
+
new_line = 1
|
|
231
|
+
|
|
232
|
+
script[0...index].each do |entry|
|
|
233
|
+
old_line += 1 unless entry.first == :insert
|
|
234
|
+
new_line += 1 unless entry.first == :delete
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
[old_line, new_line]
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# An empty side is anchored to the line it follows, which is how `diff`
|
|
241
|
+
# reports a pure insertion or deletion, and a single line drops the count.
|
|
242
|
+
def position(start, count)
|
|
243
|
+
case count
|
|
244
|
+
when 0 then "#{start - 1},0"
|
|
245
|
+
when 1 then start.to_s
|
|
246
|
+
else "#{start},#{count}"
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def formatted_line(kind, line)
|
|
251
|
+
marker = { equal: ' ', delete: '-', insert: '+' }.fetch(kind)
|
|
252
|
+
return "#{marker}#{line}" if line.end_with?("\n")
|
|
253
|
+
|
|
254
|
+
"#{marker}#{line}\n#{NO_NEWLINE_MARKER}"
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
data/lib/rubocop/version.rb
CHANGED
data/lib/rubocop.rb
CHANGED
|
@@ -27,6 +27,7 @@ require_relative 'rubocop/ext/regexp_parser'
|
|
|
27
27
|
require_relative 'rubocop/core_ext/string'
|
|
28
28
|
require_relative 'rubocop/ext/processed_source'
|
|
29
29
|
|
|
30
|
+
require_relative 'rubocop/changed_files'
|
|
30
31
|
require_relative 'rubocop/error'
|
|
31
32
|
require_relative 'rubocop/file_finder'
|
|
32
33
|
require_relative 'rubocop/file_patterns'
|
|
@@ -34,10 +35,11 @@ require_relative 'rubocop/name_similarity'
|
|
|
34
35
|
require_relative 'rubocop/path_util'
|
|
35
36
|
require_relative 'rubocop/platform'
|
|
36
37
|
require_relative 'rubocop/string_interpreter'
|
|
38
|
+
require_relative 'rubocop/unified_diff'
|
|
37
39
|
require_relative 'rubocop/util'
|
|
38
40
|
require_relative 'rubocop/warning'
|
|
39
41
|
|
|
40
|
-
# rubocop:disable Style/RequireOrder
|
|
42
|
+
# rubocop:disable Style/RequireOrder -- ordered by load dependency, not alphabetically
|
|
41
43
|
|
|
42
44
|
require_relative 'rubocop/project_index_loader'
|
|
43
45
|
require_relative 'rubocop/cop/util'
|
|
@@ -112,6 +114,7 @@ require_relative 'rubocop/target_finder'
|
|
|
112
114
|
require_relative 'rubocop/directive_comment'
|
|
113
115
|
require_relative 'rubocop/comment_config/directive_range'
|
|
114
116
|
require_relative 'rubocop/comment_config/disable_next'
|
|
117
|
+
require_relative 'rubocop/comment_config/push_pop'
|
|
115
118
|
require_relative 'rubocop/comment_config'
|
|
116
119
|
require_relative 'rubocop/result_cache'
|
|
117
120
|
require_relative 'rubocop/runner'
|
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.
|
|
4
|
+
version: 1.91.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bozhidar Batsov
|
|
@@ -201,6 +201,7 @@ files:
|
|
|
201
201
|
- lib/rubocop/ast_aliases.rb
|
|
202
202
|
- lib/rubocop/cache_config.rb
|
|
203
203
|
- lib/rubocop/cached_data.rb
|
|
204
|
+
- lib/rubocop/changed_files.rb
|
|
204
205
|
- lib/rubocop/cli.rb
|
|
205
206
|
- lib/rubocop/cli/command.rb
|
|
206
207
|
- lib/rubocop/cli/command/auto_generate_config.rb
|
|
@@ -218,6 +219,7 @@ files:
|
|
|
218
219
|
- lib/rubocop/comment_config.rb
|
|
219
220
|
- lib/rubocop/comment_config/directive_range.rb
|
|
220
221
|
- lib/rubocop/comment_config/disable_next.rb
|
|
222
|
+
- lib/rubocop/comment_config/push_pop.rb
|
|
221
223
|
- lib/rubocop/config.rb
|
|
222
224
|
- lib/rubocop/config_finder.rb
|
|
223
225
|
- lib/rubocop/config_loader.rb
|
|
@@ -502,6 +504,7 @@ files:
|
|
|
502
504
|
- lib/rubocop/cop/lint/literal_assignment_in_condition.rb
|
|
503
505
|
- lib/rubocop/cop/lint/literal_in_interpolation.rb
|
|
504
506
|
- lib/rubocop/cop/lint/loop.rb
|
|
507
|
+
- lib/rubocop/cop/lint/misplaced_magic_comment.rb
|
|
505
508
|
- lib/rubocop/cop/lint/missing_cop_enable_directive.rb
|
|
506
509
|
- lib/rubocop/cop/lint/missing_super.rb
|
|
507
510
|
- lib/rubocop/cop/lint/mixed_case_range.rb
|
|
@@ -785,7 +788,6 @@ files:
|
|
|
785
788
|
- lib/rubocop/cop/style/document_dynamic_eval_definition.rb
|
|
786
789
|
- lib/rubocop/cop/style/documentation.rb
|
|
787
790
|
- lib/rubocop/cop/style/documentation_method.rb
|
|
788
|
-
- lib/rubocop/cop/style/double_cop_disable_directive.rb
|
|
789
791
|
- lib/rubocop/cop/style/double_negation.rb
|
|
790
792
|
- lib/rubocop/cop/style/each_for_simple_loop.rb
|
|
791
793
|
- lib/rubocop/cop/style/each_with_object.rb
|
|
@@ -1073,6 +1075,7 @@ files:
|
|
|
1073
1075
|
- lib/rubocop/formatter/pacman_formatter.rb
|
|
1074
1076
|
- lib/rubocop/formatter/progress_formatter.rb
|
|
1075
1077
|
- lib/rubocop/formatter/quiet_formatter.rb
|
|
1078
|
+
- lib/rubocop/formatter/sarif_formatter.rb
|
|
1076
1079
|
- lib/rubocop/formatter/simple_text_formatter.rb
|
|
1077
1080
|
- lib/rubocop/formatter/tap_formatter.rb
|
|
1078
1081
|
- lib/rubocop/formatter/text_util.rb
|
|
@@ -1131,6 +1134,7 @@ files:
|
|
|
1131
1134
|
- lib/rubocop/target_finder.rb
|
|
1132
1135
|
- lib/rubocop/target_ruby.rb
|
|
1133
1136
|
- lib/rubocop/todo_audit.rb
|
|
1137
|
+
- lib/rubocop/unified_diff.rb
|
|
1134
1138
|
- lib/rubocop/util.rb
|
|
1135
1139
|
- lib/rubocop/version.rb
|
|
1136
1140
|
- lib/rubocop/warning.rb
|
|
@@ -1142,9 +1146,9 @@ licenses:
|
|
|
1142
1146
|
- MIT
|
|
1143
1147
|
metadata:
|
|
1144
1148
|
homepage_uri: https://rubocop.org/
|
|
1145
|
-
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.
|
|
1149
|
+
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.91.0
|
|
1146
1150
|
source_code_uri: https://github.com/rubocop/rubocop/
|
|
1147
|
-
documentation_uri: https://docs.rubocop.org/rubocop/1.
|
|
1151
|
+
documentation_uri: https://docs.rubocop.org/rubocop/1.91/
|
|
1148
1152
|
bug_tracker_uri: https://github.com/rubocop/rubocop/issues
|
|
1149
1153
|
rubygems_mfa_required: 'true'
|
|
1150
1154
|
rdoc_options: []
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# rubocop:disable Lint/RedundantCopDisableDirective
|
|
4
|
-
# rubocop:disable Style/DoubleCopDisableDirective
|
|
5
|
-
|
|
6
|
-
module RuboCop
|
|
7
|
-
module Cop
|
|
8
|
-
module Style
|
|
9
|
-
# Detects double disable comments on one line. This is mostly to catch
|
|
10
|
-
# automatically generated comments that need to be regenerated.
|
|
11
|
-
#
|
|
12
|
-
# @example
|
|
13
|
-
# # bad
|
|
14
|
-
# def f # rubocop:disable Style/For # rubocop:disable Metrics/AbcSize
|
|
15
|
-
# end
|
|
16
|
-
#
|
|
17
|
-
# # good
|
|
18
|
-
# # rubocop:disable Metrics/AbcSize
|
|
19
|
-
# def f # rubocop:disable Style/For
|
|
20
|
-
# end
|
|
21
|
-
# # rubocop:enable Metrics/AbcSize
|
|
22
|
-
#
|
|
23
|
-
# # if both fit on one line
|
|
24
|
-
# def f # rubocop:disable Style/For, Metrics/AbcSize
|
|
25
|
-
# end
|
|
26
|
-
#
|
|
27
|
-
class DoubleCopDisableDirective < Base
|
|
28
|
-
extend AutoCorrector
|
|
29
|
-
|
|
30
|
-
# rubocop:enable Style/For, Style/DoubleCopDisableDirective
|
|
31
|
-
# rubocop:enable Lint/RedundantCopDisableDirective, Metrics/AbcSize
|
|
32
|
-
MSG = 'More than one disable comment on one line.'
|
|
33
|
-
|
|
34
|
-
def on_new_investigation
|
|
35
|
-
processed_source.comments.each do |comment|
|
|
36
|
-
next unless comment.text.scan(/# rubocop:(?:disable|todo)/).size > 1
|
|
37
|
-
|
|
38
|
-
add_offense(comment) do |corrector|
|
|
39
|
-
corrector.replace(comment, comment.text.gsub(%r{ # rubocop:(disable|todo)}, ','))
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|