rubocop 1.66.0 → 1.67.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/README.md +1 -1
- data/config/default.yml +15 -6
- data/config/internal_affairs.yml +11 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
- data/lib/rubocop/cli/command/lsp.rb +2 -2
- data/lib/rubocop/comment_config.rb +4 -8
- data/lib/rubocop/config.rb +4 -16
- data/lib/rubocop/config_loader.rb +14 -8
- data/lib/rubocop/config_loader_resolver.rb +3 -3
- data/lib/rubocop/config_validator.rb +7 -10
- data/lib/rubocop/cop/base.rb +6 -2
- data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
- data/lib/rubocop/cop/cop.rb +8 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
- data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
- data/lib/rubocop/cop/internal_affairs.rb +16 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
- data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
- data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
- data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
- data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
- data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
- data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
- data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
- data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
- data/lib/rubocop/cop/style/collection_compact.rb +10 -10
- data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/empty_else.rb +1 -0
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +1 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -5
- data/lib/rubocop/cop/style/lambda.rb +1 -1
- data/lib/rubocop/cop/style/magic_comment_format.rb +3 -8
- data/lib/rubocop/cop/style/map_into_array.rb +54 -10
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
- data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
- data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
- data/lib/rubocop/cop/style/safe_navigation.rb +92 -50
- data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
- data/lib/rubocop/cop/style/semicolon.rb +1 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/team.rb +8 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +73 -34
- data/lib/rubocop/file_finder.rb +9 -4
- data/lib/rubocop/lsp/runtime.rb +2 -0
- data/lib/rubocop/lsp/server.rb +0 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -0
- data/lib/rubocop/runner.rb +3 -0
- data/lib/rubocop/server/cache.rb +6 -1
- data/lib/rubocop/server/core.rb +1 -0
- data/lib/rubocop/target_ruby.rb +12 -12
- data/lib/rubocop/version.rb +3 -1
- data/lib/rubocop/yaml_duplication_checker.rb +20 -26
- data/lib/rubocop.rb +2 -0
- metadata +12 -10
@@ -81,7 +81,7 @@ module RuboCop
|
|
81
81
|
# foo.baz = bar if foo
|
82
82
|
# foo.baz + bar if foo
|
83
83
|
# foo.bar > 2 if foo
|
84
|
-
class SafeNavigation < Base
|
84
|
+
class SafeNavigation < Base # rubocop:disable Metrics/ClassLength
|
85
85
|
include NilMethods
|
86
86
|
include RangeHelp
|
87
87
|
extend AutoCorrector
|
@@ -124,47 +124,112 @@ module RuboCop
|
|
124
124
|
# @!method not_nil_check?(node)
|
125
125
|
def_node_matcher :not_nil_check?, '(send (send $_ :nil?) :!)'
|
126
126
|
|
127
|
+
# @!method and_inside_begin?(node)
|
128
|
+
def_node_matcher :and_inside_begin?, '`(begin and ...)'
|
129
|
+
|
130
|
+
# @!method strip_begin(node)
|
131
|
+
def_node_matcher :strip_begin, '{ (begin $!begin) $!(begin) }'
|
132
|
+
|
127
133
|
def on_if(node)
|
128
134
|
return if allowed_if_condition?(node)
|
129
135
|
|
130
|
-
|
136
|
+
checked_variable, receiver, method_chain, _method = extract_parts_from_if(node)
|
137
|
+
return unless offending_node?(node, checked_variable, method_chain, receiver)
|
138
|
+
|
139
|
+
body = extract_if_body(node)
|
140
|
+
method_call = receiver.parent
|
141
|
+
|
142
|
+
removal_ranges = [begin_range(node, body), end_range(node, body)]
|
143
|
+
|
144
|
+
report_offense(node, method_chain, method_call, *removal_ranges) do |corrector|
|
145
|
+
corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def on_and(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
150
|
+
collect_and_clauses(node).each do |(lhs, lhs_operator_range), (rhs, _rhs_operator_range)|
|
151
|
+
lhs_not_nil_check = not_nil_check?(lhs)
|
152
|
+
lhs_receiver = lhs_not_nil_check || lhs
|
153
|
+
rhs_receiver = find_matching_receiver_invocation(strip_begin(rhs), lhs_receiver)
|
154
|
+
|
155
|
+
next if !cop_config['ConvertCodeThatCanStartToReturnNil'] && lhs_not_nil_check
|
156
|
+
next unless offending_node?(node, lhs_receiver, rhs, rhs_receiver)
|
157
|
+
|
158
|
+
# Since we are evaluating every clause in potentially a complex chain of `and` nodes,
|
159
|
+
# we need to ensure that there isn't an object check happening
|
160
|
+
lhs_method_chain = find_method_chain(lhs_receiver)
|
161
|
+
next unless lhs_method_chain == lhs_receiver || lhs_not_nil_check
|
162
|
+
|
163
|
+
report_offense(
|
164
|
+
node,
|
165
|
+
rhs, rhs_receiver,
|
166
|
+
range_with_surrounding_space(range: lhs.source_range, side: :right),
|
167
|
+
range_with_surrounding_space(range: lhs_operator_range, side: :right),
|
168
|
+
offense_range: range_between(lhs.source_range.begin_pos, rhs.source_range.end_pos)
|
169
|
+
)
|
170
|
+
end
|
131
171
|
end
|
132
172
|
|
133
|
-
def
|
134
|
-
|
173
|
+
def report_offense(node, rhs, rhs_receiver, *removal_ranges, offense_range: node)
|
174
|
+
add_offense(offense_range) do |corrector|
|
175
|
+
removal_ranges.each { |range| corrector.remove(range) }
|
176
|
+
yield corrector if block_given?
|
177
|
+
|
178
|
+
handle_comments(corrector, node, rhs)
|
179
|
+
|
180
|
+
add_safe_nav_to_all_methods_in_chain(corrector, rhs_receiver, rhs)
|
181
|
+
end
|
135
182
|
end
|
136
183
|
|
137
184
|
private
|
138
185
|
|
139
|
-
def
|
140
|
-
|
141
|
-
return if receiver != checked_variable || receiver.nil?
|
142
|
-
return if use_var_only_in_unless_modifier?(node, checked_variable)
|
143
|
-
return if chain_length(method_chain, method) > max_chain_length
|
144
|
-
return if unsafe_method_used?(method_chain, method)
|
145
|
-
return if method_chain.method?(:empty?)
|
186
|
+
def find_method_chain(node)
|
187
|
+
return node unless node&.parent&.call_type?
|
146
188
|
|
147
|
-
|
189
|
+
find_method_chain(node.parent)
|
148
190
|
end
|
149
191
|
|
150
|
-
def
|
151
|
-
|
192
|
+
def collect_and_clauses(node)
|
193
|
+
# Collect the lhs, operator and rhs of all `and` nodes
|
194
|
+
# `and` nodes can be nested and can contain `begin` nodes
|
195
|
+
# This gives us a source-ordered list of clauses that is then used to look
|
196
|
+
# for matching receivers as well as operator locations for offense and corrections
|
197
|
+
node.each_descendant(:and)
|
198
|
+
.inject(and_parts(node)) { |nodes, and_node| concat_nodes(nodes, and_node) }
|
199
|
+
.sort_by { |a| a.is_a?(RuboCop::AST::Node) ? a.source_range.begin_pos : a.begin_pos }
|
200
|
+
.each_slice(2)
|
201
|
+
.each_cons(2)
|
152
202
|
end
|
153
203
|
|
154
|
-
def
|
155
|
-
|
156
|
-
method_call = method_call(node)
|
204
|
+
def concat_nodes(nodes, and_node)
|
205
|
+
return nodes if and_node.each_ancestor(:block).any?
|
157
206
|
|
158
|
-
|
159
|
-
|
160
|
-
corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
|
161
|
-
handle_comments(corrector, node, method_call)
|
207
|
+
nodes.concat(and_parts(and_node))
|
208
|
+
end
|
162
209
|
|
163
|
-
|
210
|
+
def and_parts(node)
|
211
|
+
parts = [node.loc.operator]
|
212
|
+
parts << node.rhs unless and_inside_begin?(node.rhs)
|
213
|
+
parts << node.lhs unless node.lhs.and_type? || and_inside_begin?(node.lhs)
|
214
|
+
parts
|
164
215
|
end
|
165
216
|
|
166
|
-
def
|
167
|
-
if
|
217
|
+
def offending_node?(node, lhs_receiver, rhs, rhs_receiver) # rubocop:disable Metrics/CyclomaticComplexity
|
218
|
+
return false if lhs_receiver != rhs_receiver || rhs_receiver.nil?
|
219
|
+
return false if use_var_only_in_unless_modifier?(node, lhs_receiver)
|
220
|
+
return false if chain_length(rhs, rhs_receiver) > max_chain_length
|
221
|
+
return false if unsafe_method_used?(rhs, rhs_receiver.parent)
|
222
|
+
return false if rhs.send_type? && rhs.method?(:empty?)
|
223
|
+
|
224
|
+
true
|
225
|
+
end
|
226
|
+
|
227
|
+
def use_var_only_in_unless_modifier?(node, variable)
|
228
|
+
node.if_type? && node.unless? && !method_called?(variable)
|
229
|
+
end
|
230
|
+
|
231
|
+
def extract_if_body(node)
|
232
|
+
if node.ternary?
|
168
233
|
node.branches.find { |branch| !branch.nil_type? }
|
169
234
|
else
|
170
235
|
node.node_parts[1]
|
@@ -201,20 +266,6 @@ module RuboCop
|
|
201
266
|
node.else? || node.elsif?
|
202
267
|
end
|
203
268
|
|
204
|
-
def method_call(node)
|
205
|
-
_checked_variable, matching_receiver, = extract_parts(node)
|
206
|
-
matching_receiver.parent
|
207
|
-
end
|
208
|
-
|
209
|
-
def extract_parts(node)
|
210
|
-
case node.type
|
211
|
-
when :if
|
212
|
-
extract_parts_from_if(node)
|
213
|
-
when :and
|
214
|
-
extract_parts_from_and(node)
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
269
|
def extract_parts_from_if(node)
|
219
270
|
variable, receiver =
|
220
271
|
if node.ternary?
|
@@ -230,16 +281,6 @@ module RuboCop
|
|
230
281
|
[checked_variable, matching_receiver, receiver, method]
|
231
282
|
end
|
232
283
|
|
233
|
-
def extract_parts_from_and(node)
|
234
|
-
checked_variable, rhs = *node
|
235
|
-
if cop_config['ConvertCodeThatCanStartToReturnNil']
|
236
|
-
checked_variable = not_nil_check?(checked_variable) || checked_variable
|
237
|
-
end
|
238
|
-
|
239
|
-
checked_variable, matching_receiver, method = extract_common_parts(rhs, checked_variable)
|
240
|
-
[checked_variable, matching_receiver, rhs, method]
|
241
|
-
end
|
242
|
-
|
243
284
|
def extract_common_parts(method_chain, checked_variable)
|
244
285
|
matching_receiver = find_matching_receiver_invocation(method_chain, checked_variable)
|
245
286
|
|
@@ -249,7 +290,7 @@ module RuboCop
|
|
249
290
|
end
|
250
291
|
|
251
292
|
def find_matching_receiver_invocation(method_chain, checked_variable)
|
252
|
-
return nil unless method_chain
|
293
|
+
return nil unless method_chain.respond_to?(:receiver)
|
253
294
|
|
254
295
|
receiver = method_chain.receiver
|
255
296
|
|
@@ -259,7 +300,7 @@ module RuboCop
|
|
259
300
|
end
|
260
301
|
|
261
302
|
def chain_length(method_chain, method)
|
262
|
-
method.each_ancestor(:send).inject(
|
303
|
+
method.each_ancestor(:send).inject(0) do |total, ancestor|
|
263
304
|
break total + 1 if ancestor == method_chain
|
264
305
|
|
265
306
|
total + 1
|
@@ -310,6 +351,7 @@ module RuboCop
|
|
310
351
|
start_method.each_ancestor do |ancestor|
|
311
352
|
break unless %i[send block].include?(ancestor.type)
|
312
353
|
next unless ancestor.send_type?
|
354
|
+
next if ancestor.safe_navigation?
|
313
355
|
|
314
356
|
corrector.insert_before(ancestor.loc.dot, '&')
|
315
357
|
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
27
27
|
# so the correction may not be actually equivalent.
|
28
28
|
#
|
29
29
|
# @example
|
30
|
-
# # bad (select or find_all)
|
30
|
+
# # bad (select, filter, or find_all)
|
31
31
|
# array.select { |x| x.match? /regexp/ }
|
32
32
|
# array.select { |x| /regexp/.match?(x) }
|
33
33
|
# array.select { |x| x =~ /regexp/ }
|
@@ -47,9 +47,11 @@ module RuboCop
|
|
47
47
|
include RangeHelp
|
48
48
|
|
49
49
|
MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
|
50
|
-
RESTRICT_ON_SEND = %i[select find_all reject].freeze
|
51
|
-
REPLACEMENTS = { select: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
|
52
|
-
OPPOSITE_REPLACEMENTS = {
|
50
|
+
RESTRICT_ON_SEND = %i[select filter find_all reject].freeze
|
51
|
+
REPLACEMENTS = { select: 'grep', filter: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
|
52
|
+
OPPOSITE_REPLACEMENTS = {
|
53
|
+
select: 'grep_v', filter: 'grep_v', find_all: 'grep_v', reject: 'grep'
|
54
|
+
}.freeze
|
53
55
|
REGEXP_METHODS = %i[match? =~ !~].to_set.freeze
|
54
56
|
|
55
57
|
# @!method regexp_match?(node)
|
@@ -84,8 +86,9 @@ module RuboCop
|
|
84
86
|
}
|
85
87
|
PATTERN
|
86
88
|
|
87
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
89
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
88
90
|
def on_send(node)
|
91
|
+
return if target_ruby_version < 2.6 && node.method?(:filter)
|
89
92
|
return unless (block_node = node.block_node)
|
90
93
|
return if block_node.body&.begin_type?
|
91
94
|
return if receiver_allowed?(block_node.receiver)
|
@@ -99,7 +102,7 @@ module RuboCop
|
|
99
102
|
|
100
103
|
register_offense(node, block_node, regexp, replacement)
|
101
104
|
end
|
102
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
105
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
103
106
|
alias on_csend on_send
|
104
107
|
|
105
108
|
private
|
@@ -141,7 +141,7 @@ module RuboCop
|
|
141
141
|
|
142
142
|
def expressions_per_line(exprs)
|
143
143
|
# create a map matching lines to the number of expressions on them
|
144
|
-
exprs_lines = exprs.map(&:
|
144
|
+
exprs_lines = exprs.map(&:last_line)
|
145
145
|
exprs_lines.group_by(&:itself)
|
146
146
|
end
|
147
147
|
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
def on_class(node)
|
34
34
|
return unless struct_constructor?(node.parent_class)
|
35
35
|
|
36
|
-
add_offense(node.parent_class
|
36
|
+
add_offense(node.parent_class) do |corrector|
|
37
37
|
corrector.remove(range_with_surrounding_space(node.loc.keyword, newlines: false))
|
38
38
|
corrector.replace(node.loc.operator, '=')
|
39
39
|
|
data/lib/rubocop/cop/team.rb
CHANGED
@@ -9,11 +9,17 @@ module RuboCop
|
|
9
9
|
# For performance reasons, Team will first dispatch cops & forces in two groups,
|
10
10
|
# first the ones needed for autocorrection (if any), then the rest
|
11
11
|
# (unless autocorrections happened).
|
12
|
+
# rubocop:disable Metrics/ClassLength
|
12
13
|
class Team
|
13
14
|
# @return [Team]
|
14
15
|
def self.new(cop_or_classes, config, options = {})
|
15
16
|
# Support v0 api:
|
16
|
-
|
17
|
+
if cop_or_classes.first.is_a?(Class)
|
18
|
+
warn Rainbow(<<~WARNING).yellow, uplevel: 1
|
19
|
+
`Team.new` with cop classes is deprecated. Use `Team.mobilize` instead.
|
20
|
+
WARNING
|
21
|
+
return mobilize(cop_or_classes, config, options)
|
22
|
+
end
|
17
23
|
|
18
24
|
super
|
19
25
|
end
|
@@ -279,5 +285,6 @@ module RuboCop
|
|
279
285
|
end
|
280
286
|
end
|
281
287
|
end
|
288
|
+
# rubocop:enable Metrics/ClassLength
|
282
289
|
end
|
283
290
|
end
|
data/lib/rubocop/cop/util.rb
CHANGED
@@ -6,14 +6,38 @@ require 'fileutils'
|
|
6
6
|
# @api private
|
7
7
|
class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
8
8
|
include ::RuboCop::Cop::Documentation
|
9
|
+
CopData = Struct.new(
|
10
|
+
:cop, :description, :example_objects, :safety_objects, :see_objects, :config, keyword_init: true
|
11
|
+
)
|
12
|
+
|
13
|
+
STRUCTURE = {
|
14
|
+
name: ->(data) { cop_header(data.cop) },
|
15
|
+
required_ruby_version: ->(data) { required_ruby_version(data.cop) },
|
16
|
+
properties: ->(data) { properties(data.cop) },
|
17
|
+
description: ->(data) { "#{data.description}\n" },
|
18
|
+
safety: ->(data) { safety_object(data.safety_objects, data.cop) },
|
19
|
+
examples: ->(data) { examples(data.example_objects, data.cop) },
|
20
|
+
configuration: ->(data) { configurations(data.cop.department, data.config, data.cop) },
|
21
|
+
references: ->(data) { references(data.cop, data.see_objects) }
|
22
|
+
}.freeze
|
23
|
+
|
9
24
|
# This class will only generate documentation for cops that belong to one of
|
10
25
|
# the departments given in the `departments` array. E.g. if we only wanted
|
11
26
|
# documentation for Lint cops:
|
12
27
|
#
|
13
28
|
# CopsDocumentationGenerator.new(departments: ['Lint']).call
|
14
29
|
#
|
15
|
-
|
30
|
+
# You can append additional information:
|
31
|
+
#
|
32
|
+
# callback = ->(data) { required_rails_version(data.cop) }
|
33
|
+
# CopsDocumentationGenerator.new(extra_info: { ruby_version: callback }).call
|
34
|
+
#
|
35
|
+
# This will insert the string returned from the lambda _after_ the section from RuboCop itself.
|
36
|
+
# See `CopsDocumentationGenerator::STRUCTURE` for available sections.
|
37
|
+
#
|
38
|
+
def initialize(departments: [], extra_info: {})
|
16
39
|
@departments = departments.map(&:to_sym).sort!
|
40
|
+
@extra_info = extra_info
|
17
41
|
@cops = RuboCop::Cop::Registry.global
|
18
42
|
@config = RuboCop::ConfigLoader.default_configuration
|
19
43
|
@docs_path = "#{Dir.pwd}/docs/modules/ROOT/pages/"
|
@@ -37,24 +61,21 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
37
61
|
cops.with_department(department).sort!
|
38
62
|
end
|
39
63
|
|
40
|
-
def cops_body(
|
41
|
-
check_examples_to_have_the_default_enforced_style!(
|
42
|
-
|
43
|
-
content =
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
content << examples(examples_objects) if examples_objects.any?
|
49
|
-
content << configurations(cop.department, pars)
|
50
|
-
content << references(cop, see_objects)
|
64
|
+
def cops_body(data)
|
65
|
+
check_examples_to_have_the_default_enforced_style!(data.example_objects, data.cop)
|
66
|
+
|
67
|
+
content = +''
|
68
|
+
STRUCTURE.each do |section, block|
|
69
|
+
content << instance_exec(data, &block)
|
70
|
+
content << @extra_info[section].call(data) if @extra_info[section]
|
71
|
+
end
|
51
72
|
content
|
52
73
|
end
|
53
74
|
|
54
|
-
def check_examples_to_have_the_default_enforced_style!(
|
55
|
-
return if
|
75
|
+
def check_examples_to_have_the_default_enforced_style!(example_objects, cop)
|
76
|
+
return if example_objects.none?
|
56
77
|
|
57
|
-
examples_describing_enforced_style =
|
78
|
+
examples_describing_enforced_style = example_objects.map(&:name).grep(/EnforcedStyle:/)
|
58
79
|
return if examples_describing_enforced_style.none?
|
59
80
|
|
60
81
|
if examples_describing_enforced_style.index { |name| name.match?('default') }.nonzero?
|
@@ -66,16 +87,20 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
66
87
|
raise "Specify the default EnforcedStyle for #{cop.cop_name}"
|
67
88
|
end
|
68
89
|
|
69
|
-
def examples(
|
70
|
-
|
90
|
+
def examples(example_objects, cop)
|
91
|
+
return '' if example_objects.none?
|
92
|
+
|
93
|
+
example_objects.each_with_object(cop_subsection('Examples', cop).dup) do |example, content|
|
71
94
|
content << "\n" unless content.end_with?("\n\n")
|
72
|
-
content <<
|
95
|
+
content << example_header(example.name, cop) unless example.name == ''
|
73
96
|
content << code_example(example)
|
74
97
|
end
|
75
98
|
end
|
76
99
|
|
77
|
-
def safety_object(
|
78
|
-
|
100
|
+
def safety_object(safety_objects, cop)
|
101
|
+
return '' if safety_objects.all? { |s| s.text.blank? }
|
102
|
+
|
103
|
+
safety_objects.each_with_object(cop_subsection('Safety', cop).dup) do |safety_object, content|
|
79
104
|
next if safety_object.text.blank?
|
80
105
|
|
81
106
|
content << "\n" unless content.end_with?("\n\n")
|
@@ -115,22 +140,25 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
115
140
|
end
|
116
141
|
# rubocop:enable Metrics/MethodLength
|
117
142
|
|
118
|
-
def
|
143
|
+
def cop_header(cop)
|
119
144
|
content = +"\n"
|
120
|
-
content << "
|
145
|
+
content << "[##{to_anchor(cop.cop_name)}]\n"
|
146
|
+
content << "== #{cop.cop_name}\n"
|
121
147
|
content << "\n"
|
122
148
|
content
|
123
149
|
end
|
124
150
|
|
125
|
-
def
|
151
|
+
def cop_subsection(title, cop)
|
126
152
|
content = +"\n"
|
153
|
+
content << "[##{to_anchor(title)}-#{to_anchor(cop.cop_name)}]\n"
|
127
154
|
content << "=== #{title}\n"
|
128
155
|
content << "\n"
|
129
156
|
content
|
130
157
|
end
|
131
158
|
|
132
|
-
def
|
133
|
-
content =
|
159
|
+
def example_header(title, cop)
|
160
|
+
content = "[##{to_anchor(title)}-#{to_anchor(cop.cop_name)}]\n"
|
161
|
+
content << +"==== #{title}\n"
|
134
162
|
content << "\n"
|
135
163
|
content
|
136
164
|
end
|
@@ -142,7 +170,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
142
170
|
content
|
143
171
|
end
|
144
172
|
|
145
|
-
def configurations(department, pars)
|
173
|
+
def configurations(department, pars, cop)
|
146
174
|
return '' if pars.empty?
|
147
175
|
|
148
176
|
header = ['Name', 'Default value', 'Configurable values']
|
@@ -157,7 +185,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
157
185
|
[configuration_name(department, name), default, configurable]
|
158
186
|
end
|
159
187
|
|
160
|
-
|
188
|
+
cop_subsection('Configurable attributes', cop) + to_table(header, content)
|
161
189
|
end
|
162
190
|
|
163
191
|
def configuration_name(department, name)
|
@@ -235,7 +263,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
235
263
|
urls = RuboCop::Cop::MessageAnnotator.new(config, cop.name, cop_config, {}).urls
|
236
264
|
return '' if urls.empty? && see_objects.empty?
|
237
265
|
|
238
|
-
content =
|
266
|
+
content = cop_subsection('References', cop)
|
239
267
|
content << urls.map { |url| "* #{url}" }.join("\n")
|
240
268
|
content << "\n" unless urls.empty?
|
241
269
|
content << see_objects.map { |see| "* #{see.name}" }.join("\n")
|
@@ -283,14 +311,16 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
283
311
|
]
|
284
312
|
pars = cop_config.reject { |k| non_display_keys.include? k }
|
285
313
|
description = 'No documentation'
|
286
|
-
|
314
|
+
example_objects = safety_objects = see_objects = []
|
287
315
|
cop_code(cop) do |code_object|
|
288
316
|
description = code_object.docstring unless code_object.docstring.blank?
|
289
|
-
|
290
|
-
|
291
|
-
|
317
|
+
example_objects = code_object.tags('example')
|
318
|
+
safety_objects = code_object.tags('safety')
|
319
|
+
see_objects = code_object.tags('see')
|
292
320
|
end
|
293
|
-
|
321
|
+
data = CopData.new(cop: cop, description: description, example_objects: example_objects,
|
322
|
+
safety_objects: safety_objects, see_objects: see_objects, config: pars)
|
323
|
+
cops_body(data)
|
294
324
|
end
|
295
325
|
|
296
326
|
def cop_code(cop)
|
@@ -306,7 +336,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
306
336
|
filename = "#{department_to_basename(department)}.adoc"
|
307
337
|
content = +"=== Department xref:#{filename}[#{type_title}]\n\n"
|
308
338
|
cops_of_department(department).each do |cop|
|
309
|
-
anchor = cop.cop_name
|
339
|
+
anchor = to_anchor(cop.cop_name)
|
310
340
|
content << "* xref:#{filename}##{anchor}[#{cop.cop_name}]\n"
|
311
341
|
end
|
312
342
|
|
@@ -338,4 +368,13 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
|
|
338
368
|
|
339
369
|
status == 'pending' ? 'Pending' : 'Enabled'
|
340
370
|
end
|
371
|
+
|
372
|
+
# HTML anchor are somewhat limited in what characters they can contain, just
|
373
|
+
# accept a known-good subset. As long as it's consistent it doesn't matter.
|
374
|
+
#
|
375
|
+
# Style/AccessModifierDeclarations => styleaccessmodifierdeclarations
|
376
|
+
# OnlyFor: [] (default) => onlyfor_-__-_default_
|
377
|
+
def to_anchor(title)
|
378
|
+
title.delete('/').tr(' ', '-').gsub(/[^a-zA-Z0-9-]/, '_').downcase
|
379
|
+
end
|
341
380
|
end
|
data/lib/rubocop/file_finder.rb
CHANGED
@@ -23,15 +23,20 @@ module RuboCop
|
|
23
23
|
last_file
|
24
24
|
end
|
25
25
|
|
26
|
+
def traverse_directories_upwards(start_dir, stop_dir = nil)
|
27
|
+
Pathname.new(start_dir).expand_path.ascend do |dir|
|
28
|
+
yield(dir)
|
29
|
+
dir = dir.to_s
|
30
|
+
break if dir == stop_dir || dir == FileFinder.root_level
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
26
34
|
private
|
27
35
|
|
28
36
|
def traverse_files_upwards(filename, start_dir, stop_dir)
|
29
|
-
|
37
|
+
traverse_directories_upwards(start_dir, stop_dir) do |dir|
|
30
38
|
file = dir + filename
|
31
39
|
yield(file.to_s) if file.exist?
|
32
|
-
|
33
|
-
dir = dir.to_s
|
34
|
-
break if dir == stop_dir || dir == FileFinder.root_level
|
35
40
|
end
|
36
41
|
end
|
37
42
|
end
|
data/lib/rubocop/lsp/runtime.rb
CHANGED
data/lib/rubocop/lsp/server.rb
CHANGED
@@ -169,6 +169,7 @@ module RuboCop
|
|
169
169
|
raise 'Expected correction but no corrections were made' if new_source == source
|
170
170
|
|
171
171
|
expect(new_source).to eq(correction)
|
172
|
+
expect(@processed_source).to be_valid_syntax, 'Expected correction to be valid syntax'
|
172
173
|
end
|
173
174
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
174
175
|
|
data/lib/rubocop/runner.rb
CHANGED
@@ -362,6 +362,9 @@ module RuboCop
|
|
362
362
|
self.class.ruby_extractors.find do |ruby_extractor|
|
363
363
|
result = ruby_extractor.call(processed_source)
|
364
364
|
break result if result
|
365
|
+
rescue StandardError
|
366
|
+
raise Error, "Ruby extractor #{ruby_extractor.source_location[0]} failed to process " \
|
367
|
+
"#{processed_source.path}."
|
365
368
|
end
|
366
369
|
end
|
367
370
|
|
data/lib/rubocop/server/cache.rb
CHANGED
@@ -43,13 +43,18 @@ module RuboCop
|
|
43
43
|
@project_dir_cache_key ||= project_dir[1..].tr('/', '+')
|
44
44
|
end
|
45
45
|
|
46
|
+
# rubocop:disable Metrics/AbcSize
|
46
47
|
def restart_key
|
47
48
|
lockfile_path = LOCKFILE_NAMES.map do |lockfile_name|
|
48
49
|
Pathname(project_dir).join(lockfile_name)
|
49
50
|
end.find(&:exist?)
|
51
|
+
version_data = lockfile_path&.read || RuboCop::Version::STRING
|
52
|
+
config_data = Pathname(ConfigFinder.find_config_path(Dir.pwd)).read
|
53
|
+
todo_data = (rubocop_todo = Pathname('.rubocop_todo.yml')).exist? ? rubocop_todo.read : ''
|
50
54
|
|
51
|
-
Digest::SHA1.hexdigest(
|
55
|
+
Digest::SHA1.hexdigest(version_data + config_data + todo_data)
|
52
56
|
end
|
57
|
+
# rubocop:enable Metrics/AbcSize
|
53
58
|
|
54
59
|
def dir
|
55
60
|
Pathname.new(File.join(cache_path, project_dir_cache_key)).tap do |d|
|