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
|
@@ -26,7 +26,8 @@ module RuboCop
|
|
|
26
26
|
# @api private
|
|
27
27
|
PUSH_POP_ARGS_PATTERN = "([+\\-]#{COP_NAME_PATTERN_NC}(?:\\s+[+\\-]#{COP_NAME_PATTERN_NC})*)"
|
|
28
28
|
# @api private
|
|
29
|
-
AVAILABLE_MODES = %w[disable enable todo push pop disable-next todo-next
|
|
29
|
+
AVAILABLE_MODES = %w[disable enable todo push pop disable-next todo-next enable-next
|
|
30
|
+
next].freeze
|
|
30
31
|
# @api private
|
|
31
32
|
# Longest first, so a `-next` mode is not matched as its prefix
|
|
32
33
|
# (`-` is a word boundary).
|
|
@@ -43,6 +44,8 @@ module RuboCop
|
|
|
43
44
|
.gsub(' ', '\s*')
|
|
44
45
|
)
|
|
45
46
|
# @api private
|
|
47
|
+
SIGNED_OPERATIONS = %w[+ -].freeze
|
|
48
|
+
# @api private
|
|
46
49
|
TRAILING_COMMENT_MARKER = '--'
|
|
47
50
|
# @api private
|
|
48
51
|
MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP = Regexp.new(
|
|
@@ -71,7 +74,7 @@ module RuboCop
|
|
|
71
74
|
# Checks if the comment is malformed as a `# rubocop:` directive
|
|
72
75
|
def malformed?
|
|
73
76
|
return true if !start_with_marker? || @match_data.nil?
|
|
74
|
-
return true if missing_cop_name?
|
|
77
|
+
return true if missing_cop_name? || invalid_signed_args?
|
|
75
78
|
|
|
76
79
|
tail = @match_data.post_match.lstrip
|
|
77
80
|
!(tail.empty? || tail.start_with?(TRAILING_COMMENT_MARKER))
|
|
@@ -84,6 +87,16 @@ module RuboCop
|
|
|
84
87
|
MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP.match?(comment.text)
|
|
85
88
|
end
|
|
86
89
|
|
|
90
|
+
# `push` and `next` arguments must be `+`/`-` prefixed cop names, and
|
|
91
|
+
# `pop` takes no arguments at all.
|
|
92
|
+
def invalid_signed_args?
|
|
93
|
+
return cops ? !cops.empty? : false if pop?
|
|
94
|
+
return false unless push? || next?
|
|
95
|
+
return false unless cops
|
|
96
|
+
|
|
97
|
+
cops.split.any? { |cop_spec| !cop_spec.start_with?('+', '-') }
|
|
98
|
+
end
|
|
99
|
+
|
|
87
100
|
# The text of the directive's optional `--` trailing comment, or `nil`
|
|
88
101
|
# when there is none.
|
|
89
102
|
def reason
|
|
@@ -150,7 +163,12 @@ module RuboCop
|
|
|
150
163
|
|
|
151
164
|
# Checks if this directive enables cops
|
|
152
165
|
def enabled?
|
|
153
|
-
mode == 'enable'
|
|
166
|
+
mode == 'enable' || enable_next?
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Checks if this directive enables cops for the next statement only
|
|
170
|
+
def enable_next?
|
|
171
|
+
mode == 'enable-next'
|
|
154
172
|
end
|
|
155
173
|
|
|
156
174
|
# Checks if this directive is a push
|
|
@@ -163,10 +181,17 @@ module RuboCop
|
|
|
163
181
|
mode == 'pop'
|
|
164
182
|
end
|
|
165
183
|
|
|
166
|
-
#
|
|
167
|
-
def
|
|
168
|
-
|
|
184
|
+
# Checks if this directive toggles cops for the next statement only
|
|
185
|
+
def next?
|
|
186
|
+
mode == 'next'
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Returns the `+`/`-` arguments of a `push` or `next` directive as a hash
|
|
190
|
+
# of operations to cop names
|
|
191
|
+
def signed_args
|
|
192
|
+
@signed_args ||= parse_signed_args
|
|
169
193
|
end
|
|
194
|
+
alias push_args signed_args
|
|
170
195
|
|
|
171
196
|
# Checks if this directive enables all cops
|
|
172
197
|
def enabled_all?
|
|
@@ -210,6 +235,8 @@ module RuboCop
|
|
|
210
235
|
end
|
|
211
236
|
|
|
212
237
|
def directive_count
|
|
238
|
+
return signed_args.values.sum(&:count) if push? || next?
|
|
239
|
+
|
|
213
240
|
raw_cop_names.count
|
|
214
241
|
end
|
|
215
242
|
|
|
@@ -244,13 +271,15 @@ module RuboCop
|
|
|
244
271
|
cops - [LINT_REDUNDANT_DIRECTIVE_COP, LINT_SYNTAX_COP]
|
|
245
272
|
end
|
|
246
273
|
|
|
247
|
-
def
|
|
248
|
-
return {} unless push? && cops
|
|
274
|
+
def parse_signed_args
|
|
275
|
+
return {} unless (push? || next?) && cops
|
|
249
276
|
|
|
250
277
|
args = {}
|
|
251
278
|
cops.split.each do |cop_spec|
|
|
252
279
|
op = cop_spec[0]
|
|
253
280
|
cop_name = cop_spec[1..]
|
|
281
|
+
next unless SIGNED_OPERATIONS.include?(op)
|
|
282
|
+
|
|
254
283
|
args[op] ||= []
|
|
255
284
|
args[op] << cop_name
|
|
256
285
|
end
|
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
|
23
23
|
Enabled
|
|
24
24
|
Exclude
|
|
25
25
|
Include
|
|
26
|
+
Preview
|
|
26
27
|
Reference
|
|
27
28
|
References
|
|
28
29
|
Safe
|
|
@@ -93,6 +94,7 @@ module RuboCop
|
|
|
93
94
|
command += ' --auto-gen-only-exclude' if @options[:auto_gen_only_exclude]
|
|
94
95
|
command += ' --disable-pending-cops' if @options[:disable_pending_cops]
|
|
95
96
|
command += ' --enable-pending-cops' if @options[:enable_pending_cops]
|
|
97
|
+
command += preview_option
|
|
96
98
|
command += exclude_limit_option
|
|
97
99
|
command += ' --no-offense-counts' unless show_offense_counts?
|
|
98
100
|
|
|
@@ -103,6 +105,14 @@ module RuboCop
|
|
|
103
105
|
command
|
|
104
106
|
end
|
|
105
107
|
|
|
108
|
+
def preview_option
|
|
109
|
+
case @options[:preview]
|
|
110
|
+
when true then ' --preview'
|
|
111
|
+
when false then ' --no-preview'
|
|
112
|
+
else ''
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
106
116
|
def exclude_limit_option
|
|
107
117
|
if no_exclude_limit?
|
|
108
118
|
' --no-exclude-limit'
|
|
@@ -259,7 +269,7 @@ module RuboCop
|
|
|
259
269
|
config = ConfigStore.new.for(parent)
|
|
260
270
|
cfg = config[cop_name] || {}
|
|
261
271
|
|
|
262
|
-
if
|
|
272
|
+
if exclude_merges?(config, cfg)
|
|
263
273
|
offending_files
|
|
264
274
|
else
|
|
265
275
|
cop_exclude = cfg['Exclude']
|
|
@@ -272,6 +282,14 @@ module RuboCop
|
|
|
272
282
|
end
|
|
273
283
|
end
|
|
274
284
|
|
|
285
|
+
# `Preview` merges `Exclude` with the default configuration, so the excludes
|
|
286
|
+
# already in the configuration do not have to be copied into the todo file.
|
|
287
|
+
def exclude_merges?(config, cop_config)
|
|
288
|
+
merge_mode_for_exclude?(config) ||
|
|
289
|
+
merge_mode_for_exclude?(cop_config) ||
|
|
290
|
+
config.preview?(@options)
|
|
291
|
+
end
|
|
292
|
+
|
|
275
293
|
def merge_mode_for_exclude?(cfg)
|
|
276
294
|
Array(cfg.to_h.dig('inherit_mode', 'merge')).include?('Exclude')
|
|
277
295
|
end
|
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
|
23
23
|
'[pa]cman' => 'PacmanFormatter',
|
|
24
24
|
'[p]rogress' => 'ProgressFormatter',
|
|
25
25
|
'[q]uiet' => 'QuietFormatter',
|
|
26
|
+
'[sa]rif' => 'SARIFFormatter',
|
|
26
27
|
'[s]imple' => 'SimpleTextFormatter',
|
|
27
28
|
'[t]ap' => 'TapFormatter',
|
|
28
29
|
'[w]orst' => 'WorstOffendersFormatter'
|
|
@@ -57,7 +58,7 @@ module RuboCop
|
|
|
57
58
|
if output_path
|
|
58
59
|
dir_path = File.dirname(output_path)
|
|
59
60
|
FileUtils.mkdir_p(dir_path)
|
|
60
|
-
output = File.open(output_path, 'w') # rubocop:disable Style/FileOpen
|
|
61
|
+
output = File.open(output_path, 'w') # rubocop:disable Style/FileOpen -- the file is closed by `close_output_files`, not by a block
|
|
61
62
|
else
|
|
62
63
|
output = $stdout
|
|
63
64
|
end
|
|
@@ -15,6 +15,10 @@ module RuboCop
|
|
|
15
15
|
@offenses_for_files[file] = offenses unless offenses.empty?
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def file_started(_file, options)
|
|
19
|
+
@config_store = options[:config_store]
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
def finished(_inspected_files)
|
|
19
23
|
@offenses_for_files.each do |file, offenses|
|
|
20
24
|
offenses.each do |offense|
|
|
@@ -31,11 +35,8 @@ module RuboCop
|
|
|
31
35
|
end
|
|
32
36
|
|
|
33
37
|
def minimum_severity_to_fail
|
|
34
|
-
@minimum_severity_to_fail ||=
|
|
35
|
-
|
|
36
|
-
name = options.fetch(:fail_level, :refactor)
|
|
37
|
-
RuboCop::Cop::Severity.new(name)
|
|
38
|
-
end
|
|
38
|
+
@minimum_severity_to_fail ||=
|
|
39
|
+
RuboCop::Cop::Severity.minimum_to_fail(options, @config_store&.for_pwd)
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def github_severity(offense)
|
|
@@ -74,7 +74,7 @@ module RuboCop
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# Make Kernel#binding public.
|
|
77
|
-
# rubocop:disable-next Lint/UselessMethodDefinition
|
|
77
|
+
# rubocop:disable-next Lint/UselessMethodDefinition -- redefined only to make `Kernel#binding` public
|
|
78
78
|
def binding
|
|
79
79
|
super
|
|
80
80
|
end
|
|
@@ -143,7 +143,7 @@ module RuboCop
|
|
|
143
143
|
}.freeze
|
|
144
144
|
|
|
145
145
|
# Make Kernel#binding public.
|
|
146
|
-
# rubocop:disable-next Lint/UselessMethodDefinition
|
|
146
|
+
# rubocop:disable-next Lint/UselessMethodDefinition -- redefined only to make `Kernel#binding` public
|
|
147
147
|
def binding
|
|
148
148
|
super
|
|
149
149
|
end
|
|
@@ -47,7 +47,7 @@ module RuboCop
|
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
# rubocop:disable-next Layout/LineLength,Metrics/AbcSize,Metrics/MethodLength
|
|
50
|
+
# rubocop:disable-next Layout/LineLength,Metrics/AbcSize,Metrics/MethodLength -- the XML is emitted line by line
|
|
51
51
|
def finished(_inspected_files)
|
|
52
52
|
output.puts %(<?xml version='1.0'?>)
|
|
53
53
|
output.puts %(<testsuites>)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest'
|
|
4
|
+
|
|
5
|
+
module RuboCop
|
|
6
|
+
module Formatter
|
|
7
|
+
# This formatter formats the report data in SARIF 2.1.0, the OASIS standard
|
|
8
|
+
# for static analysis results. It's understood by GitHub code scanning,
|
|
9
|
+
# Azure DevOps, and other tools that aggregate analyzer output.
|
|
10
|
+
#
|
|
11
|
+
# SARIF only knows three severity levels, so RuboCop's `info`, `refactor`
|
|
12
|
+
# and `convention` severities are all reported as `note`.
|
|
13
|
+
class SARIFFormatter < BaseFormatter
|
|
14
|
+
SCHEMA_URI = 'https://json.schemastore.org/sarif-2.1.0.json'
|
|
15
|
+
SARIF_VERSION = '2.1.0'
|
|
16
|
+
INFORMATION_URI = 'https://rubocop.org'
|
|
17
|
+
|
|
18
|
+
SEVERITY_LEVELS = {
|
|
19
|
+
info: 'note',
|
|
20
|
+
refactor: 'note',
|
|
21
|
+
convention: 'note',
|
|
22
|
+
warning: 'warning',
|
|
23
|
+
error: 'error',
|
|
24
|
+
fatal: 'error'
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
def initialize(output, options = {})
|
|
28
|
+
super
|
|
29
|
+
|
|
30
|
+
@rules = []
|
|
31
|
+
@rule_indexes = {}
|
|
32
|
+
@results = []
|
|
33
|
+
@config_store = nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def file_started(_file, options)
|
|
37
|
+
@config_store = options[:config_store]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def file_finished(file, offenses)
|
|
41
|
+
offenses.each do |offense|
|
|
42
|
+
@results << result_for(file, offense)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def finished(_inspected_files)
|
|
47
|
+
output.write(report.to_json)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def report
|
|
53
|
+
{
|
|
54
|
+
'$schema': SCHEMA_URI,
|
|
55
|
+
version: SARIF_VERSION,
|
|
56
|
+
runs: [{ tool: { driver: driver }, results: @results }]
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def driver
|
|
61
|
+
{
|
|
62
|
+
name: 'RuboCop',
|
|
63
|
+
version: RuboCop::Version::STRING,
|
|
64
|
+
semanticVersion: RuboCop::Version::STRING,
|
|
65
|
+
informationUri: INFORMATION_URI,
|
|
66
|
+
rules: @rules
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def result_for(file, offense)
|
|
71
|
+
result = {
|
|
72
|
+
ruleId: offense.cop_name,
|
|
73
|
+
ruleIndex: rule_index_for(file, offense),
|
|
74
|
+
level: SEVERITY_LEVELS.fetch(offense.severity.name, 'warning'),
|
|
75
|
+
message: { text: offense.message },
|
|
76
|
+
locations: [location_for(file, offense)],
|
|
77
|
+
partialFingerprints: fingerprints_for(offense)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Suppressed offenses are only reported under `--display-suppressed`.
|
|
81
|
+
# SARIF has a first-class representation for them, so consumers can
|
|
82
|
+
# tell them apart from offenses that were never triggered.
|
|
83
|
+
result[:suppressions] = [suppression_for(offense)] if offense.disabled?
|
|
84
|
+
result
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The URI is relative to the working directory, which is what consumers
|
|
88
|
+
# expect to resolve against their own checkout. No `uriBaseId`: it would
|
|
89
|
+
# have to be declared under `originalUriBaseIds` to mean anything, and
|
|
90
|
+
# every consumer that matters resolves relative paths without it.
|
|
91
|
+
def location_for(file, offense)
|
|
92
|
+
{
|
|
93
|
+
physicalLocation: {
|
|
94
|
+
artifactLocation: { uri: uri_for(file) },
|
|
95
|
+
region: region_for(offense)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def region_for(offense)
|
|
101
|
+
# SARIF columns are 1-based and `endColumn` points at the character
|
|
102
|
+
# *after* the region, while `real_last_column` is the last character.
|
|
103
|
+
{
|
|
104
|
+
startLine: offense.first_line,
|
|
105
|
+
startColumn: offense.real_column,
|
|
106
|
+
endLine: offense.last_line,
|
|
107
|
+
endColumn: offense.real_last_column + 1
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# GitHub uses these to match an alert across runs. Without them it falls
|
|
112
|
+
# back to the file path, which makes alerts churn whenever a file moves.
|
|
113
|
+
def fingerprints_for(offense)
|
|
114
|
+
source_line = offense.location.source_line
|
|
115
|
+
{ primaryLocationLineHash: Digest::SHA256.hexdigest("#{offense.cop_name}#{source_line}") }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def suppression_for(offense)
|
|
119
|
+
suppression = { kind: 'inSource' }
|
|
120
|
+
suppression[:justification] = offense.justification if offense.justification
|
|
121
|
+
suppression
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def rule_index_for(file, offense)
|
|
125
|
+
@rule_indexes[offense.cop_name] ||= begin
|
|
126
|
+
@rules << rule_for(file, offense)
|
|
127
|
+
@rules.size - 1
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def rule_for(file, offense)
|
|
132
|
+
cop_class = Cop::Registry.global.find_by_cop_name(offense.cop_name)
|
|
133
|
+
description = description_for(file, offense)
|
|
134
|
+
help_uri = cop_class && Cop::Documentation.url_for(cop_class, config_for(file))
|
|
135
|
+
|
|
136
|
+
rule = {
|
|
137
|
+
id: offense.cop_name,
|
|
138
|
+
name: offense.cop_name,
|
|
139
|
+
shortDescription: { text: description },
|
|
140
|
+
fullDescription: { text: description },
|
|
141
|
+
help: help_for(description, help_uri),
|
|
142
|
+
defaultConfiguration: { level: default_level_for(file, offense, cop_class) },
|
|
143
|
+
properties: { tags: [offense.cop_name.split('/').first] }
|
|
144
|
+
}
|
|
145
|
+
rule[:helpUri] = help_uri if help_uri
|
|
146
|
+
rule
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def help_for(description, help_uri)
|
|
150
|
+
help = { text: description }
|
|
151
|
+
help[:markdown] = "#{description}\n\n[Documentation](#{help_uri})" if help_uri
|
|
152
|
+
help
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def description_for(file, offense)
|
|
156
|
+
cop_config(file, offense)['Description'] || offense.cop_name
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def default_level_for(file, offense, cop_class)
|
|
160
|
+
severity = cop_config(file, offense)['Severity'] ||
|
|
161
|
+
(cop_class&.lint? ? :warning : :convention)
|
|
162
|
+
|
|
163
|
+
SEVERITY_LEVELS.fetch(severity.to_sym, 'warning')
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def cop_config(file, offense)
|
|
167
|
+
config_for(file)&.for_cop(offense.cop_name) || {}
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# `file_started` is not called when a formatter is used directly through
|
|
171
|
+
# the API, so there may be no config to consult.
|
|
172
|
+
def config_for(file)
|
|
173
|
+
@config_store&.for_file(file)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def uri_for(file)
|
|
177
|
+
PathUtil.smart_path(file).tr('\\', '/')
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
data/lib/rubocop/formatter.rb
CHANGED
|
@@ -24,6 +24,7 @@ module RuboCop
|
|
|
24
24
|
autoload :PacmanFormatter, 'rubocop/formatter/pacman_formatter'
|
|
25
25
|
autoload :ProgressFormatter, 'rubocop/formatter/progress_formatter'
|
|
26
26
|
autoload :QuietFormatter, 'rubocop/formatter/quiet_formatter'
|
|
27
|
+
autoload :SARIFFormatter, 'rubocop/formatter/sarif_formatter'
|
|
27
28
|
autoload :TapFormatter, 'rubocop/formatter/tap_formatter'
|
|
28
29
|
autoload :WorstOffendersFormatter, 'rubocop/formatter/worst_offenders_formatter'
|
|
29
30
|
|
|
@@ -12,6 +12,7 @@ module RuboCop
|
|
|
12
12
|
encoding: '(?:en)?coding',
|
|
13
13
|
frozen_string_literal: 'frozen[_-]string[_-]literal',
|
|
14
14
|
rbs_inline: 'rbs_inline',
|
|
15
|
+
warn_indent: 'warn[_-]indent',
|
|
15
16
|
shareable_constant_value: 'shareable[_-]constant[_-]value',
|
|
16
17
|
typed: 'typed'
|
|
17
18
|
}.freeze
|
|
@@ -38,6 +39,7 @@ module RuboCop
|
|
|
38
39
|
frozen_string_literal_specified? ||
|
|
39
40
|
encoding_specified? ||
|
|
40
41
|
rbs_inline_specified? ||
|
|
42
|
+
warn_indent_specified? ||
|
|
41
43
|
shareable_constant_value_specified? ||
|
|
42
44
|
typed_specified?
|
|
43
45
|
end
|
|
@@ -77,6 +79,13 @@ module RuboCop
|
|
|
77
79
|
specified?(frozen_string_literal)
|
|
78
80
|
end
|
|
79
81
|
|
|
82
|
+
# Was a warn_indent specified?
|
|
83
|
+
#
|
|
84
|
+
# @return [Boolean]
|
|
85
|
+
def warn_indent_specified?
|
|
86
|
+
specified?(warn_indent)
|
|
87
|
+
end
|
|
88
|
+
|
|
80
89
|
# Was a shareable_constant_value specified?
|
|
81
90
|
#
|
|
82
91
|
# @return [Boolean]
|
|
@@ -100,6 +109,13 @@ module RuboCop
|
|
|
100
109
|
end
|
|
101
110
|
end
|
|
102
111
|
|
|
112
|
+
# Expose the `warn_indent` value.
|
|
113
|
+
#
|
|
114
|
+
# @return [String] for warn_indent config
|
|
115
|
+
def warn_indent
|
|
116
|
+
extract_warn_indent
|
|
117
|
+
end
|
|
118
|
+
|
|
103
119
|
# Expose the `shareable_constant_value` value coerced to a boolean if possible.
|
|
104
120
|
#
|
|
105
121
|
# @return [String] for shareable_constant_value config
|
|
@@ -216,6 +232,10 @@ module RuboCop
|
|
|
216
232
|
# Emacs comments cannot specify RBS::inline behavior.
|
|
217
233
|
def extract_rbs_inline_value; end
|
|
218
234
|
|
|
235
|
+
def extract_warn_indent
|
|
236
|
+
match(KEYWORDS[:warn_indent])
|
|
237
|
+
end
|
|
238
|
+
|
|
219
239
|
def extract_shareable_constant_value
|
|
220
240
|
match(KEYWORDS[:shareable_constant_value])
|
|
221
241
|
end
|
|
@@ -258,6 +278,9 @@ module RuboCop
|
|
|
258
278
|
# Vim comments cannot specify RBS::inline behavior.
|
|
259
279
|
def extract_rbs_inline_value; end
|
|
260
280
|
|
|
281
|
+
# Vim comments cannot specify indentation warning behavior.
|
|
282
|
+
def warn_indent; end
|
|
283
|
+
|
|
261
284
|
# Vim comments cannot specify shareable constant values behavior.
|
|
262
285
|
def shareable_constant_value; end
|
|
263
286
|
|
|
@@ -316,6 +339,10 @@ module RuboCop
|
|
|
316
339
|
extract(/\A\s*#\s*#{KEYWORDS[:rbs_inline]}:\s*#{TOKEN}\s*\z/io)
|
|
317
340
|
end
|
|
318
341
|
|
|
342
|
+
def extract_warn_indent
|
|
343
|
+
extract(/\A\s*#\s*#{KEYWORDS[:warn_indent]}:\s*#{TOKEN}\s*\z/io)
|
|
344
|
+
end
|
|
345
|
+
|
|
319
346
|
def extract_shareable_constant_value
|
|
320
347
|
extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*#{TOKEN}\s*\z/io)
|
|
321
348
|
end
|
data/lib/rubocop/options.rb
CHANGED
|
@@ -33,6 +33,8 @@ module RuboCop
|
|
|
33
33
|
|
|
34
34
|
define_options.parse!(args)
|
|
35
35
|
|
|
36
|
+
imply_autocorrect_for_diff
|
|
37
|
+
|
|
36
38
|
@validator.validate_compatibility
|
|
37
39
|
|
|
38
40
|
if @options[:stdin]
|
|
@@ -51,6 +53,15 @@ module RuboCop
|
|
|
51
53
|
|
|
52
54
|
private
|
|
53
55
|
|
|
56
|
+
# `--diff` is a dry run of autocorrection, so it turns autocorrection on
|
|
57
|
+
# unless the user already picked a mode with `-a`, `-A` or `-x`.
|
|
58
|
+
def imply_autocorrect_for_diff
|
|
59
|
+
return unless @options[:diff] && !@options[:autocorrect]
|
|
60
|
+
|
|
61
|
+
@options[:safe_autocorrect] = true
|
|
62
|
+
@options[:autocorrect] = true
|
|
63
|
+
end
|
|
64
|
+
|
|
54
65
|
# rubocop:disable-next Metrics/AbcSize
|
|
55
66
|
def define_options
|
|
56
67
|
OptionParser.new do |opts|
|
|
@@ -92,12 +103,17 @@ module RuboCop
|
|
|
92
103
|
option(opts, '--enable-pending-cops')
|
|
93
104
|
option(opts, '--disable-all-cops')
|
|
94
105
|
option(opts, '--enable-all-cops')
|
|
106
|
+
option(opts, '--[no-]preview')
|
|
95
107
|
option(opts, '--ignore-disable-comments')
|
|
96
108
|
option(opts, '--force-exclusion')
|
|
97
109
|
option(opts, '--only-recognized-file-types')
|
|
98
110
|
option(opts, '--ignore-parent-exclusion')
|
|
99
111
|
option(opts, '--ignore-unrecognized-cops')
|
|
100
112
|
option(opts, '--force-default-config')
|
|
113
|
+
option(opts, '--changed [REVISION]') do |revision|
|
|
114
|
+
@options[:changed] = revision || ChangedFiles::DEFAULT_REVISION
|
|
115
|
+
@validator.validate_changed_revision(revision)
|
|
116
|
+
end
|
|
101
117
|
option(opts, '-s', '--stdin FILE')
|
|
102
118
|
option(opts, '--editor-mode')
|
|
103
119
|
option(opts, '-P', '--[no-]parallel')
|
|
@@ -135,7 +151,7 @@ module RuboCop
|
|
|
135
151
|
end
|
|
136
152
|
end
|
|
137
153
|
|
|
138
|
-
# rubocop:todo Naming/InclusiveLanguage
|
|
154
|
+
# rubocop:todo Naming/InclusiveLanguage -- `--auto-correct` is the deprecated flag name
|
|
139
155
|
# the autocorrect command-line arguments map to the autocorrect @options values like so:
|
|
140
156
|
# :fix_layout :autocorrect :safe_autocorrect :autocorrect_all
|
|
141
157
|
# -x, --fix-layout true true - -
|
|
@@ -161,6 +177,8 @@ module RuboCop
|
|
|
161
177
|
end
|
|
162
178
|
|
|
163
179
|
option(opts, '--disable-uncorrectable')
|
|
180
|
+
|
|
181
|
+
option(opts, '--diff')
|
|
164
182
|
end
|
|
165
183
|
end
|
|
166
184
|
# rubocop:enable Naming/InclusiveLanguage
|
|
@@ -400,6 +418,8 @@ module RuboCop
|
|
|
400
418
|
validate_display_only_failed_and_display_only_correctable
|
|
401
419
|
validate_display_only_correctable_and_autocorrect
|
|
402
420
|
validate_lsp_and_editor_mode
|
|
421
|
+
validate_diff
|
|
422
|
+
validate_changed_and_stdin
|
|
403
423
|
validate_enable_all_cops_and_disable_all_cops
|
|
404
424
|
disable_parallel_when_invalid_option_combo
|
|
405
425
|
|
|
@@ -447,6 +467,20 @@ module RuboCop
|
|
|
447
467
|
'--display-only-failed cannot be used together with other display options.'
|
|
448
468
|
end
|
|
449
469
|
|
|
470
|
+
# `--diff` promises not to write anything, and `--auto-gen-config` exists to
|
|
471
|
+
# write a file.
|
|
472
|
+
def validate_diff
|
|
473
|
+
return unless @options.key?(:diff) && @options.key?(:auto_gen_config)
|
|
474
|
+
|
|
475
|
+
raise OptionArgumentError, '--diff cannot be used with --auto-gen-config.'
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def validate_changed_and_stdin
|
|
479
|
+
return unless @options.key?(:changed) && @options.key?(:stdin)
|
|
480
|
+
|
|
481
|
+
raise OptionArgumentError, '--changed cannot be used with --stdin.'
|
|
482
|
+
end
|
|
483
|
+
|
|
450
484
|
def validate_lsp_and_editor_mode
|
|
451
485
|
return if !@options.key?(:lsp) || !@options.key?(:editor_mode)
|
|
452
486
|
|
|
@@ -511,6 +545,17 @@ module RuboCop
|
|
|
511
545
|
@incompatible_options ||= @options.keys & Options::EXITING_OPTIONS
|
|
512
546
|
end
|
|
513
547
|
|
|
548
|
+
# OptionParser hands `--changed lib/` the path as the revision, which is a
|
|
549
|
+
# natural thing to type, so point at the fix rather than at git's error.
|
|
550
|
+
def validate_changed_revision(revision)
|
|
551
|
+
return unless revision && File.exist?(revision)
|
|
552
|
+
|
|
553
|
+
raise OptionArgumentError,
|
|
554
|
+
"--changed takes a git revision, but `#{revision}` is a path. Write the revision " \
|
|
555
|
+
"as `--changed=#{revision}` if that is really what you meant, or drop it to " \
|
|
556
|
+
'compare against HEAD.'
|
|
557
|
+
end
|
|
558
|
+
|
|
514
559
|
def validate_exclude_limit_option
|
|
515
560
|
return if /^\d+$/.match?(@options[:exclude_limit])
|
|
516
561
|
|
|
@@ -572,6 +617,10 @@ module RuboCop
|
|
|
572
617
|
disable_uncorrectable: ['Used with --autocorrect to annotate any',
|
|
573
618
|
'offenses that do not support autocorrect',
|
|
574
619
|
'with `rubocop:todo` comments.'],
|
|
620
|
+
diff: ['Print a unified diff of what autocorrection',
|
|
621
|
+
'would change, without writing any files.',
|
|
622
|
+
'Turns on safe autocorrection unless a mode',
|
|
623
|
+
'was already given with -a, -A or -x.'],
|
|
575
624
|
no_exclude_limit: ['Do not set the limit for how many files to exclude.'],
|
|
576
625
|
force_exclusion: ['Any files excluded by `Exclude` in configuration',
|
|
577
626
|
'files will be excluded, even if given explicitly',
|
|
@@ -584,6 +633,10 @@ module RuboCop
|
|
|
584
633
|
ignore_parent_exclusion: ['Prevent from inheriting `AllCops/Exclude` from',
|
|
585
634
|
'parent folders.'],
|
|
586
635
|
ignore_unrecognized_cops: ['Ignore unrecognized cops or departments in the config.'],
|
|
636
|
+
changed: ['Inspect only the files that differ from a git',
|
|
637
|
+
'revision, defaulting to HEAD. Untracked files',
|
|
638
|
+
'count as changed. Pass a revision with',
|
|
639
|
+
'`--changed=REVISION`.'],
|
|
587
640
|
force_default_config: ['Use default configuration even if configuration',
|
|
588
641
|
'files are present in the directory tree.'],
|
|
589
642
|
format: ['Choose an output formatter. This option',
|
|
@@ -598,6 +651,7 @@ module RuboCop
|
|
|
598
651
|
'specified --format, or the default format',
|
|
599
652
|
'if no format is specified.'],
|
|
600
653
|
fail_level: ['Minimum severity for exit with error code.',
|
|
654
|
+
'Overrides `AllCops: FailLevel` in the configuration.',
|
|
601
655
|
' [A] autocorrect',
|
|
602
656
|
' [I] info',
|
|
603
657
|
' [R] refactor',
|
|
@@ -642,6 +696,10 @@ module RuboCop
|
|
|
642
696
|
'`AllCops/DisabledByDefault` in config files.'],
|
|
643
697
|
display_style_guide: 'Display style guide URLs in offense messages.',
|
|
644
698
|
enable_pending_cops: 'Run with pending cops.',
|
|
699
|
+
preview: ['Opt in to unstable behavior: cops that are',
|
|
700
|
+
'`Enabled: preview`, and changes to existing',
|
|
701
|
+
'cops that are not the default yet.',
|
|
702
|
+
'Overrides `AllCops: Preview`.'],
|
|
645
703
|
enable_all_cops: ['Run with all cops enabled, including those',
|
|
646
704
|
'disabled by default. Overrides',
|
|
647
705
|
'`AllCops/EnabledByDefault` and',
|
data/lib/rubocop/path_util.rb
CHANGED
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
|
44
44
|
uri.start_with?('http://', 'https://')
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
SMART_PATH_CACHE = {} # rubocop:disable Style/MutableConstant
|
|
47
|
+
SMART_PATH_CACHE = {} # rubocop:disable Style/MutableConstant -- a cache, written to at runtime
|
|
48
48
|
private_constant :SMART_PATH_CACHE
|
|
49
49
|
|
|
50
50
|
def smart_path(path)
|
|
@@ -8,7 +8,7 @@ module RuboCop
|
|
|
8
8
|
# A class for loading and resolving plugins.
|
|
9
9
|
# @api private
|
|
10
10
|
class Loader
|
|
11
|
-
# rubocop:disable Layout/LineLength
|
|
11
|
+
# rubocop:disable Layout/LineLength -- the comment on each key does not wrap
|
|
12
12
|
DEFAULT_PLUGIN_CONFIG = {
|
|
13
13
|
'enabled' => true,
|
|
14
14
|
'require_path' => nil, # If not set, will be set to the plugin name
|
data/lib/rubocop/rake_task.rb
CHANGED
|
@@ -74,7 +74,7 @@ module RuboCop
|
|
|
74
74
|
|
|
75
75
|
def setup_subtasks(name, *args, &task_block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
76
76
|
namespace(name) do
|
|
77
|
-
# rubocop:todo-next Naming/InclusiveLanguage
|
|
77
|
+
# rubocop:todo-next Naming/InclusiveLanguage -- the deprecated flag spelling is the subject here
|
|
78
78
|
task(:auto_correct, *args) do |_, task_args|
|
|
79
79
|
require 'rainbow'
|
|
80
80
|
warn Rainbow(
|