rubocop 1.88.2 → 1.90.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 +139 -16
- data/exe/rubocop +1 -1
- data/lib/rubocop/cached_data.rb +7 -2
- data/lib/rubocop/cli/command/execute_runner.rb +31 -0
- data/lib/rubocop/cli.rb +2 -4
- data/lib/rubocop/comment_config/directive_range.rb +32 -0
- data/lib/rubocop/comment_config/disable_next.rb +97 -0
- data/lib/rubocop/comment_config.rb +81 -49
- data/lib/rubocop/config.rb +76 -5
- data/lib/rubocop/config_loader.rb +1 -2
- data/lib/rubocop/config_loader_resolver.rb +1 -2
- data/lib/rubocop/config_validator.rb +39 -3
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
- data/lib/rubocop/cop/badge.rb +8 -0
- data/lib/rubocop/cop/base.rb +40 -6
- data/lib/rubocop/cop/bundler.rb +19 -0
- data/lib/rubocop/cop/corrector.rb +4 -4
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
- data/lib/rubocop/cop/gemspec.rb +22 -0
- data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
- data/lib/rubocop/cop/generator.rb +4 -0
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/class_structure.rb +108 -26
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
- data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
- data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +3 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/line_length.rb +84 -12
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +12 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/space_around_operators.rb +7 -0
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +31 -34
- data/lib/rubocop/cop/layout.rb +114 -0
- data/lib/rubocop/cop/lazy_loader.rb +41 -0
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
- data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
- data/lib/rubocop/cop/lint/argument_mismatch.rb +89 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +18 -21
- data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -8
- data/lib/rubocop/cop/lint/deprecated_reference.rb +194 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +300 -22
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
- data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
- data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +8 -6
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -7
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
- data/lib/rubocop/cop/lint/missing_super.rb +31 -1
- data/lib/rubocop/cop/lint/name_typo.rb +270 -0
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -4
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +98 -19
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -2
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
- data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +38 -3
- data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
- data/lib/rubocop/cop/lint/unused_private_method.rb +234 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
- data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
- data/lib/rubocop/cop/lint/void.rb +18 -8
- data/lib/rubocop/cop/lint.rb +178 -0
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics.rb +31 -0
- data/lib/rubocop/cop/migration/department_name.rb +1 -6
- data/lib/rubocop/cop/migration.rb +13 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
- data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
- data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +51 -6
- data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
- data/lib/rubocop/cop/mixin/range_help.rb +1 -2
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +46 -4
- data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
- data/lib/rubocop/cop/mixin.rb +82 -77
- data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
- data/lib/rubocop/cop/naming/method_name.rb +5 -2
- data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
- data/lib/rubocop/cop/naming/variable_number.rb +3 -1
- data/lib/rubocop/cop/naming.rb +31 -0
- data/lib/rubocop/cop/offense.rb +37 -10
- data/lib/rubocop/cop/registry.rb +180 -46
- data/lib/rubocop/cop/security.rb +19 -0
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -6
- data/lib/rubocop/cop/style/array_first_last.rb +1 -2
- data/lib/rubocop/cop/style/array_intersect.rb +24 -5
- data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
- data/lib/rubocop/cop/style/case_like_if.rb +2 -4
- data/lib/rubocop/cop/style/class_and_module_children.rb +79 -20
- data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +23 -2
- data/lib/rubocop/cop/style/constant_visibility.rb +1 -2
- data/lib/rubocop/cop/style/copyright.rb +5 -1
- data/lib/rubocop/cop/style/directive_scope.rb +171 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +76 -12
- data/lib/rubocop/cop/style/documentation.rb +47 -0
- data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
- data/lib/rubocop/cop/style/endless_method.rb +0 -10
- data/lib/rubocop/cop/style/format_string_token.rb +1 -2
- data/lib/rubocop/cop/style/guard_clause.rb +1 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
- data/lib/rubocop/cop/style/if_unless_modifier.rb +8 -50
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
- data/lib/rubocop/cop/style/lambda.rb +4 -0
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +22 -7
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +30 -0
- data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
- data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
- data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
- data/lib/rubocop/cop/style/negative_array_index.rb +3 -3
- data/lib/rubocop/cop/style/nil_comparison.rb +1 -2
- data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_assignment.rb +12 -2
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -4
- data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +1 -2
- data/lib/rubocop/cop/style/redundant_each.rb +1 -2
- data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
- data/lib/rubocop/cop/style/redundant_format.rb +14 -7
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +78 -135
- data/lib/rubocop/cop/style/redundant_parentheses.rb +65 -67
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
- data/lib/rubocop/cop/style/redundant_return.rb +1 -2
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
- data/lib/rubocop/cop/style/safe_navigation.rb +11 -2
- data/lib/rubocop/cop/style/sample.rb +11 -6
- data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
- data/lib/rubocop/cop/style/semicolon.rb +2 -4
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +21 -5
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
- data/lib/rubocop/cop/style/static_class.rb +21 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
- data/lib/rubocop/cop/style/super_arguments.rb +1 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
- data/lib/rubocop/cop/style/time_now.rb +41 -0
- data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
- data/lib/rubocop/cop/style.rb +312 -0
- data/lib/rubocop/cop/team.rb +59 -6
- data/lib/rubocop/cop/util.rb +2 -4
- data/lib/rubocop/cop/variable_force/variable.rb +19 -8
- data/lib/rubocop/cops_documentation_generator.rb +4 -7
- data/lib/rubocop/directive_comment.rb +39 -3
- data/lib/rubocop/feature_loader.rb +0 -6
- data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
- data/lib/rubocop/formatter/html_formatter.rb +2 -4
- data/lib/rubocop/formatter/json_formatter.rb +11 -6
- data/lib/rubocop/formatter/junit_formatter.rb +26 -9
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
- data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
- data/lib/rubocop/formatter/tap_formatter.rb +3 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
- data/lib/rubocop/lockfile.rb +18 -0
- data/lib/rubocop/lsp/diagnostic.rb +4 -8
- data/lib/rubocop/lsp/routes.rb +89 -22
- data/lib/rubocop/lsp/runtime.rb +4 -0
- data/lib/rubocop/lsp/server.rb +4 -0
- data/lib/rubocop/lsp/stdin_runner.rb +20 -0
- data/lib/rubocop/mcp/server.rb +1 -2
- data/lib/rubocop/options.rb +10 -6
- data/lib/rubocop/path_util.rb +1 -2
- data/lib/rubocop/pending_cops_reporter.rb +1 -6
- data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
- data/lib/rubocop/project_index_loader.rb +16 -0
- data/lib/rubocop/rake_task.rb +1 -2
- data/lib/rubocop/result_cache.rb +6 -4
- data/lib/rubocop/rspec/expect_offense.rb +2 -4
- data/lib/rubocop/rspec/shared_contexts.rb +16 -2
- data/lib/rubocop/runner.rb +71 -43
- data/lib/rubocop/server/cache.rb +11 -7
- data/lib/rubocop/server/cli.rb +2 -4
- data/lib/rubocop/server.rb +14 -0
- data/lib/rubocop/target_finder.rb +4 -1
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/todo_audit.rb +111 -0
- data/lib/rubocop/version.rb +3 -5
- data/lib/rubocop.rb +20 -626
- data/lib/ruby_lsp/rubocop/addon.rb +1 -2
- metadata +28 -5
|
@@ -190,33 +190,58 @@ module RuboCop
|
|
|
190
190
|
|
|
191
191
|
# Validates code style on class declaration.
|
|
192
192
|
# Add offense when find a node out of expected order.
|
|
193
|
+
# A node is out of order when its category is expected earlier than
|
|
194
|
+
# the highest-priority category seen so far, so that a low-priority element
|
|
195
|
+
# (even an unmovable one) cannot mask disorder among the elements that follow it.
|
|
196
|
+
# Consecutive elements of the same category are reported only once,
|
|
197
|
+
# on the first element of the group.
|
|
193
198
|
def on_class(class_node)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
199
|
+
# Corrections are registered in reverse source order because an insertion at
|
|
200
|
+
# a given position lands before any insertion already made there;
|
|
201
|
+
# this keeps the source order of nodes moved before the same anchor.
|
|
202
|
+
out_of_order_elements(class_node).reverse_each do |node, category, previous|
|
|
203
|
+
message = format(MSG, category: category, previous: previous)
|
|
204
|
+
|
|
205
|
+
add_offense(node, message: message) do |corrector|
|
|
206
|
+
autocorrect(corrector, node)
|
|
200
207
|
end
|
|
201
|
-
previous = index
|
|
202
208
|
end
|
|
203
209
|
end
|
|
204
210
|
alias on_sclass on_class
|
|
205
211
|
|
|
206
212
|
private
|
|
207
213
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
214
|
+
def out_of_order_elements(class_node)
|
|
215
|
+
out_of_order = []
|
|
216
|
+
max_index = -1
|
|
217
|
+
previous_category = nil
|
|
218
|
+
walk_over_nested_class_definition(class_node) do |node, category|
|
|
219
|
+
index = expected_order.index(category)
|
|
220
|
+
if index < max_index && category != previous_category
|
|
221
|
+
out_of_order << [node, category, expected_order[max_index]]
|
|
222
|
+
end
|
|
223
|
+
max_index = index if index > max_index
|
|
224
|
+
previous_category = category
|
|
212
225
|
end
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
current_range = source_range_with_comment(node)
|
|
216
|
-
previous_range = source_range_with_comment(previous)
|
|
226
|
+
out_of_order
|
|
227
|
+
end
|
|
217
228
|
|
|
218
|
-
|
|
219
|
-
|
|
229
|
+
# Autocorrect by moving the node, together with the contiguous group of
|
|
230
|
+
# same-category elements that follows it, to its expected position.
|
|
231
|
+
def autocorrect(corrector, node)
|
|
232
|
+
return if dynamic_constant?(node)
|
|
233
|
+
|
|
234
|
+
anchor = insertion_anchor(node)
|
|
235
|
+
return unless anchor
|
|
236
|
+
|
|
237
|
+
anchor_range = source_range_with_comment(anchor)
|
|
238
|
+
# Reversed for the same reason offenses are registered in reverse source order:
|
|
239
|
+
# the last insertion at a position comes first.
|
|
240
|
+
movable_group(node).reverse_each do |group_node|
|
|
241
|
+
current_range = source_range_with_comment(group_node)
|
|
242
|
+
corrector.insert_before(anchor_range, current_range.source)
|
|
243
|
+
corrector.remove(current_range)
|
|
244
|
+
end
|
|
220
245
|
end
|
|
221
246
|
|
|
222
247
|
# Classifies a node to match with something in the {expected_order}
|
|
@@ -251,7 +276,7 @@ module RuboCop
|
|
|
251
276
|
key = category || name
|
|
252
277
|
visibility_key =
|
|
253
278
|
if node.def_modifier?
|
|
254
|
-
"#{name}_methods"
|
|
279
|
+
name.end_with?('_class_method') ? "#{name}s" : "#{name}_methods"
|
|
255
280
|
else
|
|
256
281
|
"#{node_visibility(node)}_#{key}"
|
|
257
282
|
end
|
|
@@ -283,7 +308,7 @@ module RuboCop
|
|
|
283
308
|
# Exploding such a node into its children would yield non-node values (e.g. a
|
|
284
309
|
# method-name `Symbol` from a `csend`) and crash later checks.
|
|
285
310
|
if class_def.type?(:begin, :kwbegin)
|
|
286
|
-
class_def
|
|
311
|
+
flatten_class_elements(class_def)
|
|
287
312
|
else
|
|
288
313
|
[class_def]
|
|
289
314
|
end
|
|
@@ -296,13 +321,10 @@ module RuboCop
|
|
|
296
321
|
private_constant?(node)
|
|
297
322
|
end
|
|
298
323
|
|
|
299
|
-
def
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
ignore?(sibling, sibling_class) ||
|
|
304
|
-
classification == sibling_class ||
|
|
305
|
-
dynamic_constant?(node)
|
|
324
|
+
def flatten_class_elements(node)
|
|
325
|
+
node.children.compact.flat_map do |child|
|
|
326
|
+
child.kwbegin_type? ? flatten_class_elements(child) : [child]
|
|
327
|
+
end
|
|
306
328
|
end
|
|
307
329
|
|
|
308
330
|
def humanize_node(node)
|
|
@@ -322,6 +344,66 @@ module RuboCop
|
|
|
322
344
|
!(expression.method?(:freeze) && expression.receiver&.recursive_basic_literal?)
|
|
323
345
|
end
|
|
324
346
|
|
|
347
|
+
# The expected position of the node: the first left sibling within its movable span
|
|
348
|
+
# whose category is expected to appear after the node's.
|
|
349
|
+
# Requiring a strictly later category keeps the order of elements sharing a category stable.
|
|
350
|
+
def insertion_anchor(node)
|
|
351
|
+
index = expected_order.index(classify(node))
|
|
352
|
+
|
|
353
|
+
movable_span(node).find do |sibling|
|
|
354
|
+
classification = classify(sibling)
|
|
355
|
+
|
|
356
|
+
!ignore?(sibling, classification) && expected_order.index(classification) > index
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# The node together with the contiguous same-category right siblings,
|
|
361
|
+
# so that the whole group moves in a single pass while the offense is
|
|
362
|
+
# reported only on its first element.
|
|
363
|
+
def movable_group(node)
|
|
364
|
+
classification = classify(node)
|
|
365
|
+
group = [node]
|
|
366
|
+
|
|
367
|
+
node.right_siblings.each do |sibling|
|
|
368
|
+
break unless classify(sibling) == classification
|
|
369
|
+
break if ignore?(sibling, classification) || dynamic_constant?(sibling)
|
|
370
|
+
|
|
371
|
+
group << sibling
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
group
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Left siblings the node may be reordered with: those after the last barrier.
|
|
378
|
+
# Ignored elements within the span are simply jumped over.
|
|
379
|
+
def movable_span(node)
|
|
380
|
+
left_siblings = node.left_siblings
|
|
381
|
+
barrier_index = left_siblings.rindex { |sibling| barrier?(node, sibling) }
|
|
382
|
+
|
|
383
|
+
barrier_index ? left_siblings[(barrier_index + 1)..] : left_siblings
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# A dynamic constant blocks every element: the cop does not move such constants,
|
|
387
|
+
# and letting other elements jump over one would change execution order just the same.
|
|
388
|
+
# A bare visibility modifier blocks only the elements whose meaning depends on
|
|
389
|
+
# the visibility section they appear in.
|
|
390
|
+
def barrier?(node, sibling)
|
|
391
|
+
dynamic_constant?(sibling) || (visibility_dependent?(node) && visibility_block?(sibling))
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Whether moving the node across a bare visibility modifier would change its meaning.
|
|
395
|
+
# This is the case for `def` nodes and for macros whose category is classified by visibility
|
|
396
|
+
# (e.g. `attr_accessor` when the expected order lists `private_attribute_macros`).
|
|
397
|
+
# Inline visibility (`private def foo`, `def self.foo`) travels with the node.
|
|
398
|
+
def visibility_dependent?(node)
|
|
399
|
+
return true if node.def_type?
|
|
400
|
+
return false if !node.send_type? || node.def_modifier?
|
|
401
|
+
|
|
402
|
+
key = find_category(node.method_name) || node.method_name.to_s
|
|
403
|
+
|
|
404
|
+
VISIBILITY_SCOPES.any? { |visibility| expected_order.include?("#{visibility}_#{key}") }
|
|
405
|
+
end
|
|
406
|
+
|
|
325
407
|
def private_constant?(node)
|
|
326
408
|
return false unless node.casgn_type? && node.namespace.nil?
|
|
327
409
|
return false unless (parent = node.parent)
|
|
@@ -161,7 +161,7 @@ module RuboCop
|
|
|
161
161
|
next_sibling.if_type? && contains_guard_clause?(next_sibling)
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
164
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
165
165
|
def last_heredoc_argument(node)
|
|
166
166
|
n = last_heredoc_argument_node(node)
|
|
167
167
|
n = n.children.first while n.respond_to?(:begin_type?) && n.begin_type?
|
|
@@ -176,7 +176,6 @@ module RuboCop
|
|
|
176
176
|
|
|
177
177
|
last_heredoc_argument(n.receiver) if n.respond_to?(:receiver)
|
|
178
178
|
end
|
|
179
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
180
179
|
|
|
181
180
|
def last_heredoc_argument_node(node)
|
|
182
181
|
return node unless node.respond_to?(:if_branch)
|
|
@@ -5,6 +5,14 @@ module RuboCop
|
|
|
5
5
|
module Layout
|
|
6
6
|
# Checks for a newline after the final magic comment.
|
|
7
7
|
#
|
|
8
|
+
# `NumberOfEmptyLines` configures the minimum number of empty lines required. Set it
|
|
9
|
+
# to `2` when using YARD, which otherwise treats the magic comments as documentation
|
|
10
|
+
# for the first module or class in the file.
|
|
11
|
+
#
|
|
12
|
+
# NOTE: `Layout/EmptyLines` has to be disabled for values greater than `1`, as it
|
|
13
|
+
# removes the extra empty lines this cop adds, and autocorrecting with both enabled
|
|
14
|
+
# loops between them.
|
|
15
|
+
#
|
|
8
16
|
# @example
|
|
9
17
|
# # good
|
|
10
18
|
# # frozen_string_literal: true
|
|
@@ -20,30 +28,80 @@ module RuboCop
|
|
|
20
28
|
# class Person
|
|
21
29
|
# # Some code
|
|
22
30
|
# end
|
|
31
|
+
#
|
|
32
|
+
# @example NumberOfEmptyLines: 1 (default)
|
|
33
|
+
# # good
|
|
34
|
+
# # frozen_string_literal: true
|
|
35
|
+
#
|
|
36
|
+
# class Person
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# @example NumberOfEmptyLines: 2
|
|
40
|
+
# # bad
|
|
41
|
+
# # frozen_string_literal: true
|
|
42
|
+
#
|
|
43
|
+
# class Person
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# # good
|
|
47
|
+
# # frozen_string_literal: true
|
|
48
|
+
#
|
|
49
|
+
#
|
|
50
|
+
# class Person
|
|
51
|
+
# end
|
|
23
52
|
class EmptyLineAfterMagicComment < Base
|
|
24
53
|
include RangeHelp
|
|
25
54
|
extend AutoCorrector
|
|
26
55
|
|
|
27
|
-
MSG = '
|
|
56
|
+
MSG = 'Expected at least %<expected>d empty %<lines>s after magic comments; found ' \
|
|
57
|
+
'%<actual>d.'
|
|
58
|
+
|
|
59
|
+
def validate_config
|
|
60
|
+
return if number_of_empty_lines.is_a?(Integer) && number_of_empty_lines.positive?
|
|
61
|
+
|
|
62
|
+
raise ValidationError,
|
|
63
|
+
'The `Layout/EmptyLineAfterMagicComment` cop only accepts a positive ' \
|
|
64
|
+
'integer for its `NumberOfEmptyLines` configuration parameter, but ' \
|
|
65
|
+
"`#{number_of_empty_lines.inspect}` was given."
|
|
66
|
+
end
|
|
28
67
|
|
|
29
68
|
def on_new_investigation
|
|
30
69
|
return unless (last_magic_comment = last_magic_comment(processed_source))
|
|
31
|
-
|
|
32
|
-
|
|
70
|
+
|
|
71
|
+
actual = empty_lines_after(last_magic_comment)
|
|
72
|
+
# Trailing empty lines are `Layout/TrailingEmptyLines`' responsibility.
|
|
73
|
+
return unless processed_source[last_magic_comment.loc.line + actual]
|
|
74
|
+
return if actual >= number_of_empty_lines
|
|
33
75
|
|
|
34
76
|
offending_range = offending_range(last_magic_comment)
|
|
35
77
|
|
|
36
|
-
add_offense(offending_range) do |corrector|
|
|
37
|
-
corrector.insert_before(offending_range, "\n")
|
|
78
|
+
add_offense(offending_range, message: message(actual)) do |corrector|
|
|
79
|
+
corrector.insert_before(offending_range, "\n" * (number_of_empty_lines - actual))
|
|
38
80
|
end
|
|
39
81
|
end
|
|
40
82
|
|
|
41
83
|
private
|
|
42
84
|
|
|
85
|
+
def message(actual)
|
|
86
|
+
format(MSG, expected: number_of_empty_lines, actual: actual,
|
|
87
|
+
lines: number_of_empty_lines == 1 ? 'line' : 'lines')
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def empty_lines_after(last_magic_comment)
|
|
91
|
+
first_line = last_magic_comment.loc.line
|
|
92
|
+
count = 0
|
|
93
|
+
count += 1 while (line = processed_source[first_line + count]) && line.strip.empty?
|
|
94
|
+
count
|
|
95
|
+
end
|
|
96
|
+
|
|
43
97
|
def offending_range(last_magic_comment)
|
|
44
98
|
source_range(processed_source.buffer, last_magic_comment.loc.line + 1, 0)
|
|
45
99
|
end
|
|
46
100
|
|
|
101
|
+
def number_of_empty_lines
|
|
102
|
+
cop_config.fetch('NumberOfEmptyLines', 1)
|
|
103
|
+
end
|
|
104
|
+
|
|
47
105
|
# Find the last magic comment in the source file.
|
|
48
106
|
#
|
|
49
107
|
# Take all comments that precede the first line of code (or just take
|
|
@@ -123,7 +123,7 @@ module RuboCop
|
|
|
123
123
|
AlignmentCorrector.align_end(corrector, processed_source, node, alignment_node(node))
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
126
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
127
127
|
def check_assignment(node, rhs)
|
|
128
128
|
# If there are method calls chained to the right hand side of the
|
|
129
129
|
# assignment, we let rhs be the receiver of those method calls before
|
|
@@ -139,7 +139,6 @@ module RuboCop
|
|
|
139
139
|
|
|
140
140
|
check_asgn_alignment(node, rhs)
|
|
141
141
|
end
|
|
142
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
143
142
|
|
|
144
143
|
def check_asgn_alignment(outer_node, inner_node)
|
|
145
144
|
align_with = {
|
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
|
41
41
|
|
|
42
42
|
@aligned_comments = aligned_locations(processed_source.comments.map(&:loc))
|
|
43
43
|
@corrected = Set.new if force_equal_sign_alignment?
|
|
44
|
+
prepare_alignment_data
|
|
44
45
|
|
|
45
46
|
processed_source.tokens.each_cons(2) do |token1, token2|
|
|
46
47
|
check_tokens(processed_source.ast, token1, token2)
|
|
@@ -95,19 +96,120 @@ module RuboCop
|
|
|
95
96
|
end_pos = token2.begin_pos - 1
|
|
96
97
|
return if end_pos <= start_pos
|
|
97
98
|
|
|
98
|
-
return if allow_for_alignment? && aligned_tok?(token2)
|
|
99
|
+
return if allow_for_alignment? && aligned_tok?(token1, token2)
|
|
99
100
|
|
|
100
101
|
yield range_between(start_pos, end_pos)
|
|
101
102
|
end
|
|
102
103
|
|
|
103
|
-
def aligned_tok?(token)
|
|
104
|
+
def aligned_tok?(previous_token, token)
|
|
104
105
|
if token.comment?
|
|
105
106
|
@aligned_comments.include?(token.line)
|
|
106
107
|
else
|
|
107
|
-
aligned_with_something?(token.pos)
|
|
108
|
+
aligned_with_something?(token.pos) &&
|
|
109
|
+
(ASSIGNMENT_OR_COMPARISON_TOKENS.include?(token.type) ||
|
|
110
|
+
spacing_varies?(previous_token, token))
|
|
108
111
|
end
|
|
109
112
|
end
|
|
110
113
|
|
|
114
|
+
def spacing_varies?(previous_token, token)
|
|
115
|
+
by_column, typed = group_profile(token.line)
|
|
116
|
+
column = token.pos.column
|
|
117
|
+
lines, spacings = typed[[column, token.type]]
|
|
118
|
+
entries = by_column[column]
|
|
119
|
+
|
|
120
|
+
if lines.size > 1
|
|
121
|
+
uniform_alignment_allowed?(token, lines, spacings)
|
|
122
|
+
elsif entries.size > 1
|
|
123
|
+
distinct_spacings(entries).size > 1
|
|
124
|
+
else
|
|
125
|
+
nearest_spacing_varies?(previous_token, token)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def uniform_alignment_allowed?(token, lines, spacings)
|
|
130
|
+
return @alignment_verdicts[lines] if @alignment_verdicts.key?(lines)
|
|
131
|
+
|
|
132
|
+
@alignment_verdicts[lines] =
|
|
133
|
+
spacings.uniq.size > 1 || aligned_on_other_column?(token, lines)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def aligned_on_other_column?(token, lines)
|
|
137
|
+
line_set = Set.new(lines)
|
|
138
|
+
by_column, = group_profile(token.line)
|
|
139
|
+
|
|
140
|
+
by_column.any? do |column, entries|
|
|
141
|
+
next false if column == token.pos.column
|
|
142
|
+
|
|
143
|
+
shared = entries.filter_map { |line, spacing| spacing if line_set.include?(line) }
|
|
144
|
+
shared.uniq.size > 1
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def distinct_spacings(entries)
|
|
149
|
+
entries.map(&:last).uniq
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def group_profile(line_number)
|
|
153
|
+
group = alignment_lines(line_number)
|
|
154
|
+
|
|
155
|
+
@group_profiles[group] ||= build_group_profile(group)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def build_group_profile(group)
|
|
159
|
+
by_column = Hash.new { |hash, key| hash[key] = [] }
|
|
160
|
+
typed = Hash.new { |hash, key| hash[key] = [[], []] }
|
|
161
|
+
|
|
162
|
+
group.each { |line| record_line_spacings(by_column, typed, line) }
|
|
163
|
+
|
|
164
|
+
[by_column, typed]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def record_line_spacings(by_column, typed, line)
|
|
168
|
+
@tokens_by_line[line]&.each_cons(2) do |previous_token, token|
|
|
169
|
+
spacing = token.begin_pos - previous_token.end_pos
|
|
170
|
+
by_column[token.pos.column] << [line, spacing]
|
|
171
|
+
lines, spacings = typed[[token.pos.column, token.type]]
|
|
172
|
+
lines << line
|
|
173
|
+
spacings << spacing
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def nearest_spacing_varies?(previous_token, token)
|
|
178
|
+
spacing = token.begin_pos - previous_token.end_pos
|
|
179
|
+
line_ranges = [(token.line - 1).downto(1),
|
|
180
|
+
(token.line + 1).upto(processed_source.lines.length)]
|
|
181
|
+
|
|
182
|
+
line_ranges.any? do |line_numbers|
|
|
183
|
+
nearest_spacing_differs?(line_numbers, token, spacing)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def nearest_spacing_differs?(line_numbers, token, spacing)
|
|
188
|
+
line_numbers.each do |line|
|
|
189
|
+
break if definition_boundary_lines.include?(line)
|
|
190
|
+
next if aligned_comment_lines.include?(line)
|
|
191
|
+
|
|
192
|
+
other_spacing = spacing_before_token_at(line, token.pos.column)
|
|
193
|
+
return other_spacing != spacing if other_spacing
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
false
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def spacing_before_token_at(line_number, column)
|
|
200
|
+
tokens = @tokens_by_line[line_number]
|
|
201
|
+
index = tokens&.index { |token| token.pos.column == column }
|
|
202
|
+
return unless index&.positive?
|
|
203
|
+
|
|
204
|
+
tokens[index].begin_pos - tokens[index - 1].end_pos
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def prepare_alignment_data
|
|
208
|
+
@group_profiles = {}.compare_by_identity
|
|
209
|
+
@alignment_verdicts = {}.compare_by_identity
|
|
210
|
+
@tokens_by_line = processed_source.tokens.group_by(&:line)
|
|
211
|
+
end
|
|
212
|
+
|
|
111
213
|
def ignored_range?(ast, start_pos)
|
|
112
214
|
ignored_ranges(ast).any? { |r| r.include?(start_pos) }
|
|
113
215
|
end
|
|
@@ -156,7 +156,8 @@ module RuboCop
|
|
|
156
156
|
return unless should_check?(node)
|
|
157
157
|
return if same_line?(node, node.first_argument)
|
|
158
158
|
return if enforce_first_argument_with_fixed_indentation? &&
|
|
159
|
-
!enable_layout_first_method_argument_line_break?
|
|
159
|
+
(!enable_layout_first_method_argument_line_break? ||
|
|
160
|
+
conflicts_with_fixed_indentation_alignment?(node))
|
|
160
161
|
|
|
161
162
|
indent = base_indentation(node) + configured_indentation_width
|
|
162
163
|
|
|
@@ -309,6 +310,25 @@ module RuboCop
|
|
|
309
310
|
def enable_layout_first_method_argument_line_break?
|
|
310
311
|
config.cop_enabled?('Layout/FirstMethodArgumentLineBreak')
|
|
311
312
|
end
|
|
313
|
+
|
|
314
|
+
def conflicts_with_fixed_indentation_alignment?(node)
|
|
315
|
+
return false unless special_inner_call_indentation?(node)
|
|
316
|
+
return false unless argument_alignment_applies?(node)
|
|
317
|
+
|
|
318
|
+
base_indentation(node) != indentation_of_method_line(node)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def argument_alignment_applies?(node)
|
|
322
|
+
return false if !node.call_type? || node.method?(:[]=)
|
|
323
|
+
|
|
324
|
+
node.arguments.size >= 2 ||
|
|
325
|
+
(node.first_argument.hash_type? && node.first_argument.pairs.count >= 2)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def indentation_of_method_line(node)
|
|
329
|
+
lineno = node.loc.selector ? node.loc.selector.line : node.loc.begin.line
|
|
330
|
+
processed_source.lines[lineno - 1] =~ /\S/
|
|
331
|
+
end
|
|
312
332
|
end
|
|
313
333
|
end
|
|
314
334
|
end
|
|
@@ -172,7 +172,7 @@ module RuboCop
|
|
|
172
172
|
'Indent the right bracket the same as the first position ' \
|
|
173
173
|
'after the preceding left parenthesis.'
|
|
174
174
|
when :parent_hash_key
|
|
175
|
-
'Indent the right bracket the same as the parent hash key.'
|
|
175
|
+
'Indent the right bracket the same as the parent hash key.'
|
|
176
176
|
else
|
|
177
177
|
'Indent the right bracket the same as the start of the line ' \
|
|
178
178
|
'where the left bracket is.'
|
|
@@ -206,7 +206,9 @@ module RuboCop
|
|
|
206
206
|
def autocorrect(corrector, node)
|
|
207
207
|
return unless node
|
|
208
208
|
|
|
209
|
-
AlignmentCorrector.correct(
|
|
209
|
+
AlignmentCorrector.correct(
|
|
210
|
+
corrector, processed_source, node, @column_delta, tab_indentation: true
|
|
211
|
+
)
|
|
210
212
|
end
|
|
211
213
|
|
|
212
214
|
# Returns a range at the first non-space column of the line the opening parenthesis is on,
|
|
@@ -98,6 +98,24 @@ module RuboCop
|
|
|
98
98
|
#
|
|
99
99
|
# name = 'John' #: String
|
|
100
100
|
#
|
|
101
|
+
# @example AllowYARDCommentBlockSeparator: false (default)
|
|
102
|
+
#
|
|
103
|
+
# # bad
|
|
104
|
+
#
|
|
105
|
+
# # Copyright (c) Example Corp
|
|
106
|
+
# #-
|
|
107
|
+
# class Client
|
|
108
|
+
# end
|
|
109
|
+
#
|
|
110
|
+
# @example AllowYARDCommentBlockSeparator: true
|
|
111
|
+
#
|
|
112
|
+
# # good
|
|
113
|
+
#
|
|
114
|
+
# # Copyright (c) Example Corp
|
|
115
|
+
# #-
|
|
116
|
+
# class Client
|
|
117
|
+
# end
|
|
118
|
+
#
|
|
101
119
|
class LeadingCommentSpace < Base
|
|
102
120
|
include RangeHelp
|
|
103
121
|
extend AutoCorrector
|
|
@@ -113,6 +131,7 @@ module RuboCop
|
|
|
113
131
|
next if gemfile_ruby_comment?(comment)
|
|
114
132
|
next if rbs_inline_annotation?(comment)
|
|
115
133
|
next if steep_annotation?(comment)
|
|
134
|
+
next if yard_comment_block_separator?(comment)
|
|
116
135
|
|
|
117
136
|
add_offense(comment) do |corrector|
|
|
118
137
|
expr = comment.source_range
|
|
@@ -197,6 +216,14 @@ module RuboCop
|
|
|
197
216
|
def steep_annotation?(comment)
|
|
198
217
|
allow_steep_annotation? && comment.text.start_with?(/#[$:]/)
|
|
199
218
|
end
|
|
219
|
+
|
|
220
|
+
def allow_yard_comment_block_separator?
|
|
221
|
+
cop_config['AllowYARDCommentBlockSeparator']
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def yard_comment_block_separator?(comment)
|
|
225
|
+
allow_yard_comment_block_separator? && comment.text.match?(/\A#-\s*\z/)
|
|
226
|
+
end
|
|
200
227
|
end
|
|
201
228
|
end
|
|
202
229
|
end
|
|
@@ -87,7 +87,7 @@ module RuboCop
|
|
|
87
87
|
corrector.replace(range, correction)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
90
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
91
91
|
def ignored_literal_ranges(ast)
|
|
92
92
|
# which lines start inside a string literal?
|
|
93
93
|
return [] if ast.nil?
|
|
@@ -106,7 +106,6 @@ module RuboCop
|
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
110
109
|
|
|
111
110
|
def comment_ranges(comments)
|
|
112
111
|
comments.map(&:source_range)
|