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
|
@@ -435,7 +435,7 @@ module RuboCop
|
|
|
435
435
|
|
|
436
436
|
def anon_block_scope_id(anon_block)
|
|
437
437
|
parent = anon_block.parent
|
|
438
|
-
return unless parent&.type?(:any_block, :begin, :call, :casgn, :any_def)
|
|
438
|
+
return unless parent&.type?(:any_block, :begin, :call, :casgn, :ivasgn, :any_def)
|
|
439
439
|
|
|
440
440
|
if (receiver = scope_receiver(parent, anon_block))
|
|
441
441
|
"#{receiver.source}.#{parent.method_name}"
|
|
@@ -63,7 +63,7 @@ module RuboCop
|
|
|
63
63
|
class EmptyBlock < Base
|
|
64
64
|
MSG = 'Empty block detected.'
|
|
65
65
|
|
|
66
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
66
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- an empty block has no parameters to number
|
|
67
67
|
return if node.body
|
|
68
68
|
return if allow_empty_lambdas? && node.lambda_or_proc?
|
|
69
69
|
return if cop_config['AllowComments'] && allow_comment?(node)
|
|
@@ -48,7 +48,7 @@ module RuboCop
|
|
|
48
48
|
add_offense(node.lhs) do |corrector|
|
|
49
49
|
# Don't autocorrect `'foo' && return` because having `return` as
|
|
50
50
|
# the leftmost node can lead to a void value expression syntax error.
|
|
51
|
-
next if node.rhs
|
|
51
|
+
next if void_value_expression?(node.rhs)
|
|
52
52
|
|
|
53
53
|
corrector.replace(node, node.rhs.source)
|
|
54
54
|
end
|
|
@@ -60,7 +60,7 @@ module RuboCop
|
|
|
60
60
|
add_offense(node.lhs) do |corrector|
|
|
61
61
|
# Don't autocorrect `'foo' && return` because having `return` as
|
|
62
62
|
# the leftmost node can lead to a void value expression syntax error.
|
|
63
|
-
next if node.rhs
|
|
63
|
+
next if void_value_expression?(node.rhs)
|
|
64
64
|
|
|
65
65
|
corrector.replace(node, node.rhs.source)
|
|
66
66
|
end
|
|
@@ -175,6 +175,12 @@ module RuboCop
|
|
|
175
175
|
|
|
176
176
|
private
|
|
177
177
|
|
|
178
|
+
def void_value_expression?(node)
|
|
179
|
+
node = node.children.last while node&.begin_type?
|
|
180
|
+
|
|
181
|
+
node&.type?(:return, :break, :next)
|
|
182
|
+
end
|
|
183
|
+
|
|
178
184
|
def check_for_literal(node)
|
|
179
185
|
cond = condition(node)
|
|
180
186
|
if cond.literal?
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# Checks for magic comments placed where Ruby silently ignores them.
|
|
7
|
+
#
|
|
8
|
+
# The `encoding` magic comment is only honored on the first line of a file,
|
|
9
|
+
# or on the second line when the first line is a shebang. Anywhere else it
|
|
10
|
+
# is silently ignored - even directly below another comment or a blank line.
|
|
11
|
+
#
|
|
12
|
+
# Other magic comments (such as `frozen_string_literal`) are honored
|
|
13
|
+
# anywhere before the first token of code, but are ignored after any code,
|
|
14
|
+
# with a warning emitted only when running Ruby with `-w`.
|
|
15
|
+
#
|
|
16
|
+
# A magic comment misplaced ahead of a shebang also renders the shebang
|
|
17
|
+
# ineffective, since a shebang is only recognized on the first line.
|
|
18
|
+
#
|
|
19
|
+
# NOTE: An `encoding` comment that is only preceded by other magic
|
|
20
|
+
# comments is not flagged by this cop; that case is handled by
|
|
21
|
+
# `Lint/OrderedMagicComments`. `shareable_constant_value` is never
|
|
22
|
+
# flagged, as Ruby intentionally allows it mid-file with block scoping.
|
|
23
|
+
#
|
|
24
|
+
# @safety
|
|
25
|
+
# This cop's autocorrection is unsafe because moving the magic comment
|
|
26
|
+
# to its effective position activates it, which changes runtime
|
|
27
|
+
# behavior (e.g. the source encoding or string mutability).
|
|
28
|
+
#
|
|
29
|
+
# @example
|
|
30
|
+
# # bad
|
|
31
|
+
# # Documentation comment
|
|
32
|
+
# # encoding: ascii-8bit
|
|
33
|
+
# puts 'hello'
|
|
34
|
+
#
|
|
35
|
+
# # good
|
|
36
|
+
# # encoding: ascii-8bit
|
|
37
|
+
# # Documentation comment
|
|
38
|
+
# puts 'hello'
|
|
39
|
+
#
|
|
40
|
+
# # bad
|
|
41
|
+
# require 'foo'
|
|
42
|
+
# # frozen_string_literal: true
|
|
43
|
+
#
|
|
44
|
+
# # good
|
|
45
|
+
# # frozen_string_literal: true
|
|
46
|
+
# require 'foo'
|
|
47
|
+
#
|
|
48
|
+
# # bad
|
|
49
|
+
# # frozen_string_literal: true
|
|
50
|
+
# #!/usr/bin/env ruby
|
|
51
|
+
# puts 'hello'
|
|
52
|
+
#
|
|
53
|
+
# # good
|
|
54
|
+
# #!/usr/bin/env ruby
|
|
55
|
+
# # frozen_string_literal: true
|
|
56
|
+
# puts 'hello'
|
|
57
|
+
class MisplacedMagicComment < Base
|
|
58
|
+
include RangeHelp
|
|
59
|
+
extend AutoCorrector
|
|
60
|
+
|
|
61
|
+
MSG_ENCODING = 'The `encoding` magic comment is ignored unless placed on the first ' \
|
|
62
|
+
'line (or below a shebang on the first line).'
|
|
63
|
+
MSG_AFTER_CODE = 'The `%<directive>s` magic comment is ignored after any code.'
|
|
64
|
+
MSG_ABOVE_SHEBANG = 'A magic comment above a shebang renders the shebang ineffective.'
|
|
65
|
+
|
|
66
|
+
def on_new_investigation
|
|
67
|
+
return if processed_source.buffer.source.empty?
|
|
68
|
+
|
|
69
|
+
check_magic_comment_above_shebang
|
|
70
|
+
processed_source.comments.each { |comment| check_comment(comment) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def check_comment(comment)
|
|
76
|
+
return unless magic_comment_shaped?(comment.text)
|
|
77
|
+
|
|
78
|
+
magic_comment = MagicComment.parse(comment.text)
|
|
79
|
+
return unless magic_comment.valid?
|
|
80
|
+
|
|
81
|
+
if magic_comment.encoding_specified?
|
|
82
|
+
return unless known_encoding?(magic_comment.encoding)
|
|
83
|
+
|
|
84
|
+
check_encoding_comment(comment)
|
|
85
|
+
elsif magic_comment.frozen_string_literal_specified?
|
|
86
|
+
check_top_block_comment(comment, 'frozen_string_literal')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The Emacs and Vim regexps in `MagicComment` match anywhere inside a
|
|
91
|
+
# comment, which would flag documentation that merely quotes a magic
|
|
92
|
+
# comment (e.g. `# # -*- coding: UTF-8 -*-`). Require the magic
|
|
93
|
+
# comment to start right after the `#`.
|
|
94
|
+
def magic_comment_shaped?(text)
|
|
95
|
+
/\A#(?![^#]*#)/.match?(text)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Prose that happens to open with `Encoding:` parses as an encoding
|
|
99
|
+
# comment (`# Encoding: force given encoding` yields `force`). Ruby
|
|
100
|
+
# ignores a misplaced encoding comment entirely, so a name no encoding
|
|
101
|
+
# answers to means the line is a comment, not a directive.
|
|
102
|
+
def known_encoding?(name)
|
|
103
|
+
Encoding.find(name)
|
|
104
|
+
true
|
|
105
|
+
rescue ArgumentError
|
|
106
|
+
false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def shebang?
|
|
110
|
+
processed_source.lines.first.to_s.start_with?('#!')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# First line that may carry an effective encoding comment.
|
|
114
|
+
def effective_encoding_line
|
|
115
|
+
shebang? ? 2 : 1
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def first_code_token
|
|
119
|
+
@first_code_token ||= processed_source.sorted_tokens.find { |token| !token.comment? }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def check_encoding_comment(comment)
|
|
123
|
+
line = comment.source_range.line
|
|
124
|
+
return if line == effective_encoding_line && comment_starts_line?(comment)
|
|
125
|
+
# Leave `# frozen_string_literal: true` + `# encoding: x` runs at the
|
|
126
|
+
# very top to Lint/OrderedMagicComments, which already reorders them.
|
|
127
|
+
return if preceded_only_by_magic_comments?(comment)
|
|
128
|
+
|
|
129
|
+
add_offense(comment, message: MSG_ENCODING) do |corrector|
|
|
130
|
+
move_comment(corrector, comment, effective_encoding_line)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def check_top_block_comment(comment, directive)
|
|
135
|
+
return unless first_code_token
|
|
136
|
+
return unless comment.source_range.begin_pos > first_code_token.begin_pos
|
|
137
|
+
|
|
138
|
+
message = format(MSG_AFTER_CODE, directive: directive)
|
|
139
|
+
add_offense(comment, message: message) do |corrector|
|
|
140
|
+
move_comment(corrector, comment, effective_encoding_line)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def check_magic_comment_above_shebang
|
|
145
|
+
shebang_comment = processed_source.comments.find do |comment|
|
|
146
|
+
comment.source_range.line > 1 && comment.text.start_with?('#!') &&
|
|
147
|
+
comment.source_range.column.zero?
|
|
148
|
+
end
|
|
149
|
+
return unless shebang_comment
|
|
150
|
+
# Only flag when what precedes the shebang is magic comments -
|
|
151
|
+
# a `#!` comment deep inside a file is not an intended shebang.
|
|
152
|
+
return unless preceded_only_by_magic_comments?(shebang_comment)
|
|
153
|
+
|
|
154
|
+
add_offense(shebang_comment, message: MSG_ABOVE_SHEBANG)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def preceded_only_by_magic_comments?(comment)
|
|
158
|
+
(1...comment.source_range.line).all? do |line_number|
|
|
159
|
+
text = processed_source.lines[line_number - 1]
|
|
160
|
+
(line_number == 1 && text.start_with?('#!')) || MagicComment.parse(text).valid?
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def comment_starts_line?(comment)
|
|
165
|
+
processed_source.lines[comment.source_range.line - 1].lstrip.start_with?('#')
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def move_comment(corrector, comment, target_line)
|
|
169
|
+
removal_range = if comment_starts_line?(comment)
|
|
170
|
+
range_by_whole_lines(comment.source_range, include_final_newline: true)
|
|
171
|
+
else
|
|
172
|
+
range_with_surrounding_space(comment.source_range, side: :left)
|
|
173
|
+
end
|
|
174
|
+
corrector.remove(removal_range)
|
|
175
|
+
target_range = processed_source.buffer.line_range(target_line)
|
|
176
|
+
if comment.source_range.line < target_line
|
|
177
|
+
corrector.insert_after(target_range, "\n#{comment.text}")
|
|
178
|
+
else
|
|
179
|
+
corrector.insert_before(target_range, "#{comment.text}\n")
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -95,7 +95,7 @@ module RuboCop
|
|
|
95
95
|
MSG = 'Method definitions must not be nested. Use `lambda` instead.'
|
|
96
96
|
|
|
97
97
|
def on_def(node)
|
|
98
|
-
subject, = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
|
98
|
+
subject, = *node # rubocop:disable InternalAffairs/NodeDestructuring -- the first child differs between `def` and `defs`
|
|
99
99
|
return if node.defs_type? && allowed_subject_type?(subject)
|
|
100
100
|
|
|
101
101
|
def_ancestor = node.each_ancestor(:any_def).first
|
|
@@ -67,7 +67,7 @@ module RuboCop
|
|
|
67
67
|
|
|
68
68
|
# NOTE: itblock is not handled because this cop is limited to Ruby <= 2.7
|
|
69
69
|
# via `maximum_target_ruby_version`, so itblock nodes (Ruby 3.4+) are never encountered.
|
|
70
|
-
def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler
|
|
70
|
+
def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler -- limited to Ruby <= 2.7, where `it` blocks do not exist
|
|
71
71
|
return unless node.body
|
|
72
72
|
return unless unsorted_dir_loop?(node.send_node)
|
|
73
73
|
|
|
@@ -46,13 +46,29 @@ module RuboCop
|
|
|
46
46
|
|
|
47
47
|
def valid_first_argument?(first_arg)
|
|
48
48
|
first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg) ||
|
|
49
|
-
compound_range?(first_arg)
|
|
49
|
+
compound_range?(first_arg) || invalid_bare_argument?(first_arg)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def compound_range?(first_arg)
|
|
53
53
|
first_arg.range_type? && first_arg.parenthesized_call?
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def invalid_bare_argument?(node)
|
|
57
|
+
node = node.children.first while node&.begin_type? && node.children.one?
|
|
58
|
+
return false unless node
|
|
59
|
+
|
|
60
|
+
keyword_operator?(node) || modifier_expression?(node)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def keyword_operator?(node)
|
|
64
|
+
(node.operator_keyword? && node.semantic_operator?) ||
|
|
65
|
+
node.rescue_type? || (node.send_type? && node.prefix_not?)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def modifier_expression?(node)
|
|
69
|
+
node.type?(:if, :while, :until) && node.modifier_form?
|
|
70
|
+
end
|
|
71
|
+
|
|
56
72
|
def chained_calls?(node)
|
|
57
73
|
first_argument = node.first_argument
|
|
58
74
|
first_argument.call_type? && (node.children.last&.children&.count || 0) > 1
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# The Lint/RedundantCopDisableDirective cop needs to be disabled so as
|
|
4
4
|
# to be able to provide a (bad) example of a redundant disable.
|
|
5
|
-
# rubocop:disable-next Lint/RedundantCopDisableDirective
|
|
5
|
+
# rubocop:disable-next Lint/RedundantCopDisableDirective -- the examples below read as real directives
|
|
6
6
|
module RuboCop
|
|
7
7
|
module Cop
|
|
8
8
|
module Lint
|
|
@@ -155,17 +155,29 @@ module RuboCop
|
|
|
155
155
|
end
|
|
156
156
|
end
|
|
157
157
|
|
|
158
|
-
# A
|
|
159
|
-
# of a code line)
|
|
158
|
+
# A next-statement directive with no statement attached (or misplaced
|
|
159
|
+
# at the end of a code line) affects nothing, so it is redundant by
|
|
160
|
+
# definition.
|
|
160
161
|
def each_detached_next_directive
|
|
161
162
|
processed_source.comment_config.detached_next_directives.each do |directive|
|
|
162
|
-
|
|
163
|
+
names = if directive.next?
|
|
164
|
+
directive.signed_args.values.flatten
|
|
165
|
+
else
|
|
166
|
+
directive.raw_cop_names
|
|
167
|
+
end
|
|
168
|
+
names.each { |cop| yield directive.comment, cop }
|
|
163
169
|
end
|
|
164
170
|
end
|
|
165
171
|
|
|
166
172
|
def skip_directive?(comment)
|
|
173
|
+
# A cop disabled in the configuration is represented by a synthetic directive that
|
|
174
|
+
# has no comment in the source, so there is nothing to report or to remove.
|
|
175
|
+
# Its range starts at `CONFIG_DISABLED_LINE_RANGE_MIN` and runs to the end of the file,
|
|
176
|
+
# but a `pop` splits it, and the pieces in between are bounded on both sides.
|
|
177
|
+
return true if comment.is_a?(CommentConfig::ConfigDisabledCopDirectiveComment)
|
|
178
|
+
|
|
167
179
|
directive = DirectiveComment.new(comment)
|
|
168
|
-
directive.push? || directive.pop?
|
|
180
|
+
directive.push? || directive.pop? || directive.next?
|
|
169
181
|
end
|
|
170
182
|
|
|
171
183
|
def find_redundant_directive(cop, comment, line_range, next_range)
|
|
@@ -190,11 +202,11 @@ module RuboCop
|
|
|
190
202
|
|
|
191
203
|
comment = directive_comment(range)
|
|
192
204
|
|
|
193
|
-
next
|
|
194
|
-
#
|
|
195
|
-
# to disable a few select cops first and then all cops
|
|
196
|
-
# down in the code.
|
|
197
|
-
next if all_disabled?(comment)
|
|
205
|
+
# `push`/`next` signed arguments are not analyzed for redundancy
|
|
206
|
+
# (yet), and comments disabling all cops don't count since it's
|
|
207
|
+
# reasonable to disable a few select cops first and then all cops
|
|
208
|
+
# further down in the code.
|
|
209
|
+
next if comment.nil? || skip_directive?(comment) || all_disabled?(comment)
|
|
198
210
|
|
|
199
211
|
redundant =
|
|
200
212
|
if department_disabled?(cop, comment)
|
|
@@ -280,18 +292,20 @@ module RuboCop
|
|
|
280
292
|
end
|
|
281
293
|
|
|
282
294
|
def add_offense_for_entire_comment(comment, cops)
|
|
283
|
-
|
|
295
|
+
directive = DirectiveComment.new(comment)
|
|
296
|
+
location = directive.range
|
|
284
297
|
cop_names = cops.sort.map { |c| describe(c) }.join(', ')
|
|
285
298
|
|
|
286
299
|
# An unknown cop may just not be loaded in this run (e.g. a custom
|
|
287
300
|
# cop whose configuration failed to load) - removing its directive
|
|
288
301
|
# would destroy something that cannot be restored, so only report.
|
|
289
302
|
# A misplaced EOL `disable-next` should be moved, not deleted.
|
|
303
|
+
message = message(cop_names, enabling: directive.enable_next?)
|
|
290
304
|
if any_unknown_cop?(cops) || misplaced_next_directive?(comment)
|
|
291
|
-
return add_offense(location, message: message
|
|
305
|
+
return add_offense(location, message: message)
|
|
292
306
|
end
|
|
293
307
|
|
|
294
|
-
add_offense(location, message: message
|
|
308
|
+
add_offense(location, message: message) do |corrector|
|
|
295
309
|
remove_entire_comment(corrector, comment)
|
|
296
310
|
end
|
|
297
311
|
end
|
|
@@ -333,7 +347,7 @@ module RuboCop
|
|
|
333
347
|
|
|
334
348
|
def misplaced_next_directive?(comment)
|
|
335
349
|
directive = DirectiveComment.new(comment)
|
|
336
|
-
directive.disable_next? &&
|
|
350
|
+
(directive.disable_next? || directive.next? || directive.enable_next?) &&
|
|
337
351
|
!processed_source.comment_config.comment_only_line?(directive.line_number)
|
|
338
352
|
end
|
|
339
353
|
|
|
@@ -396,8 +410,8 @@ module RuboCop
|
|
|
396
410
|
similar ? "`#{cop}` (did you mean `#{similar}`?)" : "`#{cop}` (unknown cop)"
|
|
397
411
|
end
|
|
398
412
|
|
|
399
|
-
def message(cop_names)
|
|
400
|
-
"Unnecessary disabling of #{cop_names}."
|
|
413
|
+
def message(cop_names, enabling: false)
|
|
414
|
+
"Unnecessary #{enabling ? 'enabling' : 'disabling'} of #{cop_names}."
|
|
401
415
|
end
|
|
402
416
|
|
|
403
417
|
def all_cop_names
|
data/lib/rubocop/cop/lint.rb
CHANGED
|
@@ -76,6 +76,7 @@ module RuboCop
|
|
|
76
76
|
register_cop :LiteralAssignmentInCondition, "#{__dir__}/lint/literal_assignment_in_condition"
|
|
77
77
|
register_cop :LiteralInInterpolation, "#{__dir__}/lint/literal_in_interpolation"
|
|
78
78
|
register_cop :Loop, "#{__dir__}/lint/loop"
|
|
79
|
+
register_cop :MisplacedMagicComment, "#{__dir__}/lint/misplaced_magic_comment"
|
|
79
80
|
register_cop :MissingCopEnableDirective, "#{__dir__}/lint/missing_cop_enable_directive"
|
|
80
81
|
register_cop :MissingSuper, "#{__dir__}/lint/missing_super"
|
|
81
82
|
register_cop :MixedCaseRange, "#{__dir__}/lint/mixed_case_range"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Lint/RedundantCopDisableDirective needs to be disabled so as
|
|
4
4
|
# to be able to provide examples of rubocop:disable comments.
|
|
5
|
-
# rubocop:disable-next Lint/RedundantCopDisableDirective
|
|
5
|
+
# rubocop:disable-next Lint/RedundantCopDisableDirective -- the examples below read as real directives
|
|
6
6
|
module RuboCop
|
|
7
7
|
module Cop
|
|
8
8
|
module Migration
|
|
@@ -66,7 +66,7 @@ module RuboCop
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
# @deprecated Use processed_source.line_with_comment?(line)
|
|
69
|
-
def end_of_line_comment(line) # rubocop:disable Naming/PredicateMethod
|
|
69
|
+
def end_of_line_comment(line) # rubocop:disable Naming/PredicateMethod -- a deprecated shim kept under its original name
|
|
70
70
|
warn Rainbow(<<~WARNING).yellow, uplevel: 1
|
|
71
71
|
`end_of_line_comment` is deprecated. Use `processed_source.line_with_comment?` instead.
|
|
72
72
|
WARNING
|
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
|
45
45
|
match.captures
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
KEYWORDS_REGEX_CACHE = {} # rubocop:disable Style/MutableConstant
|
|
48
|
+
KEYWORDS_REGEX_CACHE = {} # rubocop:disable Style/MutableConstant -- a cache, written to at runtime
|
|
49
49
|
private_constant :KEYWORDS_REGEX_CACHE
|
|
50
50
|
|
|
51
51
|
def regex
|
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
|
55
55
|
# Common functionality for checking alignment of hash values.
|
|
56
56
|
module ValueAlignment
|
|
57
57
|
def checkable_layout?(node)
|
|
58
|
-
!node.pairs_on_same_line? && !node.mixed_delimiters?
|
|
58
|
+
!node.pairs_on_same_line? && !node.mixed_delimiters? && alignable_values?(node)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def deltas(first_pair, current_pair)
|
|
@@ -68,6 +68,16 @@ module RuboCop
|
|
|
68
68
|
|
|
69
69
|
private
|
|
70
70
|
|
|
71
|
+
def alignable_values?(node)
|
|
72
|
+
return false if node.pairs.first.value_omission?
|
|
73
|
+
|
|
74
|
+
node.pairs.none? { |pair| value_on_later_line?(pair) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def value_on_later_line?(pair)
|
|
78
|
+
pair.value && pair.key.last_line != pair.value.first_line
|
|
79
|
+
end
|
|
80
|
+
|
|
71
81
|
def separator_delta(first_pair, current_pair, key_delta)
|
|
72
82
|
if current_pair.hash_rocket?
|
|
73
83
|
hash_rocket_delta(first_pair, current_pair) - key_delta
|
|
@@ -87,13 +87,17 @@ module RuboCop
|
|
|
87
87
|
def ignore_mixed_hash_shorthand_syntax?(hash_node)
|
|
88
88
|
target_ruby_version <= 3.0 ||
|
|
89
89
|
!%w[consistent either_consistent].include?(enforced_shorthand_syntax) ||
|
|
90
|
-
!hash_node.hash_type?
|
|
90
|
+
!hash_node.hash_type? || hash_rockets_enforced?(hash_node)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def ignore_hash_shorthand_syntax?(pair_node)
|
|
94
94
|
target_ruby_version <= 3.0 || enforced_shorthand_syntax == 'either' ||
|
|
95
95
|
%w[consistent either_consistent].include?(enforced_shorthand_syntax) ||
|
|
96
|
-
!pair_node.parent.hash_type?
|
|
96
|
+
!pair_node.parent.hash_type? || hash_rockets_enforced?(pair_node.parent)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def hash_rockets_enforced?(hash_node)
|
|
100
|
+
style == :hash_rockets || force_hash_rockets?(hash_node.pairs)
|
|
97
101
|
end
|
|
98
102
|
|
|
99
103
|
def enforced_shorthand_syntax
|
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
|
38
38
|
(block (send _ :each_with_object (hash)) ...)}
|
|
39
39
|
PATTERN
|
|
40
40
|
|
|
41
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
41
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- the patterns matched here all name their block parameters
|
|
42
42
|
on_bad_each_with_object(node) do |*match|
|
|
43
43
|
handle_possible_offense(node, match, 'each_with_object')
|
|
44
44
|
end
|
|
@@ -182,7 +182,18 @@ module RuboCop
|
|
|
182
182
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
183
183
|
# rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
184
184
|
def relevant_assignment_lines(line_range)
|
|
185
|
-
relevant_lines(line_range)
|
|
185
|
+
relevant_lines(line_range, interrupting_operator_lines) do |line_number|
|
|
186
|
+
assignment_lines.include?(line_number)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def interrupting_operator_lines
|
|
191
|
+
@interrupting_operator_lines ||=
|
|
192
|
+
processed_source.tokens.each_with_object(Set.new) do |token, lines|
|
|
193
|
+
next unless ASSIGNMENT_OR_COMPARISON_TOKENS.include?(token.type)
|
|
194
|
+
|
|
195
|
+
lines << token.line unless assignment_lines.include?(token.line)
|
|
196
|
+
end
|
|
186
197
|
end
|
|
187
198
|
|
|
188
199
|
def alignment_lines(line_number)
|
data/lib/rubocop/cop/mixin.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module RuboCop
|
|
4
|
-
module Cop # rubocop:disable Style/Documentation
|
|
4
|
+
module Cop # rubocop:disable Style/Documentation -- a namespace that only autoloads the mixins below
|
|
5
5
|
# Autoloads mixin modules included by cops. Mixins are autoloaded to reduce the number of
|
|
6
6
|
# requires because they're used only when the relevant cop class is loaded.
|
|
7
7
|
|
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
|
14
14
|
autoload :AllowedReceivers, "#{__dir__}/mixin/allowed_receivers"
|
|
15
15
|
autoload :ForbiddenIdentifiers, "#{__dir__}/mixin/forbidden_identifiers"
|
|
16
16
|
autoload :ForbiddenPattern, "#{__dir__}/mixin/forbidden_pattern"
|
|
17
|
-
autoload :AutoCorrector, "#{__dir__}/mixin/auto_corrector" # rubocop:todo Naming/InclusiveLanguage
|
|
17
|
+
autoload :AutoCorrector, "#{__dir__}/mixin/auto_corrector" # rubocop:todo Naming/InclusiveLanguage -- the file is named `auto_corrector.rb`
|
|
18
18
|
autoload :CheckAssignment, "#{__dir__}/mixin/check_assignment"
|
|
19
19
|
autoload :CheckLineBreakable, "#{__dir__}/mixin/check_line_breakable"
|
|
20
20
|
autoload :CheckSingleLineSuitability, "#{__dir__}/mixin/check_single_line_suitability"
|
|
@@ -62,7 +62,10 @@ module RuboCop
|
|
|
62
62
|
MSG = 'Use %<style>s block forwarding.'
|
|
63
63
|
|
|
64
64
|
def self.autocorrect_incompatible_with
|
|
65
|
-
[
|
|
65
|
+
[
|
|
66
|
+
Lint::AmbiguousOperator, Style::ArgumentsForwarding,
|
|
67
|
+
Style::ExplicitBlockArgument, Style::MethodDefParentheses
|
|
68
|
+
]
|
|
66
69
|
end
|
|
67
70
|
|
|
68
71
|
def on_def(node)
|
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
|
38
38
|
class BlockParameterName < Base
|
|
39
39
|
include UncommunicativeName
|
|
40
40
|
|
|
41
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
41
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- there are no parameter names to check
|
|
42
42
|
return unless node.arguments?
|
|
43
43
|
|
|
44
44
|
check(node, node.arguments)
|
data/lib/rubocop/cop/registry.rb
CHANGED
|
@@ -280,6 +280,12 @@ module RuboCop
|
|
|
280
280
|
config.enabled_new_cop?(cop_name)
|
|
281
281
|
end
|
|
282
282
|
|
|
283
|
+
# Preview cops are opt-in and stay silent otherwise, so unlike pending cops
|
|
284
|
+
# they are never reported as needing a decision.
|
|
285
|
+
def enabled_preview_cop?(cop_cfg, config)
|
|
286
|
+
cop_cfg.fetch('Enabled') == 'preview' && config.preview?(@options)
|
|
287
|
+
end
|
|
288
|
+
|
|
283
289
|
def names
|
|
284
290
|
clear_enrollment_queue
|
|
285
291
|
@cops_by_badge.keys.map(&:to_s)
|
|
@@ -420,7 +426,9 @@ module RuboCop
|
|
|
420
426
|
# caching which expects cop names or cop classes as keys.
|
|
421
427
|
cfg = config.for_cop(cop_name)
|
|
422
428
|
|
|
423
|
-
cop_enabled = cfg.fetch('Enabled') == true ||
|
|
429
|
+
cop_enabled = cfg.fetch('Enabled') == true ||
|
|
430
|
+
enabled_pending_cop?(cfg, config, cop_name) ||
|
|
431
|
+
enabled_preview_cop?(cfg, config)
|
|
424
432
|
|
|
425
433
|
if options.fetch(:safe, false)
|
|
426
434
|
cop_enabled && cfg.fetch('Safe', true)
|
data/lib/rubocop/cop/severity.rb
CHANGED
|
@@ -21,6 +21,18 @@ module RuboCop
|
|
|
21
21
|
# any of `:info`, `:refactor`, `:convention`, `:warning`, `:error` or `:fatal`.
|
|
22
22
|
attr_reader :name
|
|
23
23
|
|
|
24
|
+
# The lowest severity that makes a run fail: `--fail-level` when given,
|
|
25
|
+
# otherwise `AllCops: FailLevel`. `autocorrect` is a pseudo level on the
|
|
26
|
+
# command line that gates on corrections instead, so it leaves the
|
|
27
|
+
# threshold at the configured one.
|
|
28
|
+
def self.minimum_to_fail(options, config)
|
|
29
|
+
name = options[:fail_level]
|
|
30
|
+
if name.nil? || name == :autocorrect
|
|
31
|
+
name = (config&.for_all_cops&.[]('FailLevel') || :refactor).to_sym
|
|
32
|
+
end
|
|
33
|
+
new(name)
|
|
34
|
+
end
|
|
35
|
+
|
|
24
36
|
def self.name_from_code(code)
|
|
25
37
|
name = code.to_sym
|
|
26
38
|
CODE_TABLE[name] || name
|
|
@@ -316,7 +316,7 @@ module RuboCop
|
|
|
316
316
|
end
|
|
317
317
|
|
|
318
318
|
def select_grouped_def_nodes(node)
|
|
319
|
-
node.right_siblings.take_while do |sibling|
|
|
319
|
+
node.right_siblings.compact.take_while do |sibling|
|
|
320
320
|
!(sibling.send_type? && sibling.bare_access_modifier_declaration?)
|
|
321
321
|
end.select(&:def_type?)
|
|
322
322
|
end
|
|
@@ -347,7 +347,8 @@ module RuboCop
|
|
|
347
347
|
end
|
|
348
348
|
|
|
349
349
|
def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
|
|
350
|
-
|
|
350
|
+
modifier_index = begin_node.children.find_index { |child| child.equal?(modifier_node) }
|
|
351
|
+
def_node = begin_node.children[modifier_index + 1]
|
|
351
352
|
# Stop the removal range at the first comment that precedes the def, if
|
|
352
353
|
# any exist. Without this, comments between the modifier and the def are
|
|
353
354
|
# dropped because they fall inside the removed range.
|
|
@@ -202,7 +202,7 @@ module RuboCop
|
|
|
202
202
|
rest_arg, kwrest_arg, block_arg = *forwardable_args
|
|
203
203
|
registered_block_arg_offense = false
|
|
204
204
|
|
|
205
|
-
send_classifications.each do |send_node, c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
|
|
205
|
+
send_classifications.each do |send_node, c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength -- the block parameter list does not wrap
|
|
206
206
|
if !forward_rest && !forward_kwrest && c != :all_anonymous
|
|
207
207
|
if allow_anonymous_forwarding_in_block?(forward_block_arg)
|
|
208
208
|
register_forward_block_arg_offense(!forward_rest, node.arguments, block_arg)
|
|
@@ -232,7 +232,7 @@ module RuboCop
|
|
|
232
232
|
|
|
233
233
|
rest_arg, kwrest_arg, block_arg = *forwardable_args
|
|
234
234
|
|
|
235
|
-
send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
|
|
235
|
+
send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength -- the block parameter list does not wrap
|
|
236
236
|
if allow_anonymous_forwarding_in_block?(forward_rest)
|
|
237
237
|
register_forward_args_offense(def_node.arguments, rest_arg)
|
|
238
238
|
register_forward_args_offense(send_node, forward_rest)
|