rubocop-callbacksystems 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +78 -0
- data/config/default.yml +590 -0
- data/docs/cops.md +111 -0
- data/lib/rubocop/callbacksystems/autocorrection/batch.rb +135 -0
- data/lib/rubocop/callbacksystems/autocorrection/comment_rescue.rb +112 -0
- data/lib/rubocop/callbacksystems/autocorrection/rehearsal.rb +65 -0
- data/lib/rubocop/callbacksystems/autocorrection/rewrite_permit.rb +14 -0
- data/lib/rubocop/callbacksystems/class_structure/builder_assignment.rb +48 -0
- data/lib/rubocop/callbacksystems/class_structure/class_body.rb +50 -0
- data/lib/rubocop/callbacksystems/class_structure/divergence.rb +31 -0
- data/lib/rubocop/callbacksystems/class_structure/node_visibility.rb +82 -0
- data/lib/rubocop/callbacksystems/class_structure/reference_order.rb +36 -0
- data/lib/rubocop/callbacksystems/class_structure/statement_rank.rb +84 -0
- data/lib/rubocop/callbacksystems/class_structure/statement_run.rb +42 -0
- data/lib/rubocop/callbacksystems/class_structure/visibility.rb +84 -0
- data/lib/rubocop/callbacksystems/cops_document.rb +78 -0
- data/lib/rubocop/callbacksystems/execution/deferred_local_variables.rb +138 -0
- data/lib/rubocop/callbacksystems/execution/discarded_expression.rb +45 -0
- data/lib/rubocop/callbacksystems/execution/eager_evaluation_path.rb +73 -0
- data/lib/rubocop/callbacksystems/execution/immediate.rb +71 -0
- data/lib/rubocop/callbacksystems/execution/local_variable_occurrences.rb +112 -0
- data/lib/rubocop/callbacksystems/hashes/key_transform_detection.rb +108 -0
- data/lib/rubocop/callbacksystems/hashes/keyword_options.rb +37 -0
- data/lib/rubocop/callbacksystems/helpers/bodies.rb +150 -0
- data/lib/rubocop/callbacksystems/helpers/builders.rb +103 -0
- data/lib/rubocop/callbacksystems/helpers/comments.rb +56 -0
- data/lib/rubocop/callbacksystems/helpers/corrections.rb +15 -0
- data/lib/rubocop/callbacksystems/helpers/methods.rb +46 -0
- data/lib/rubocop/callbacksystems/helpers/names.rb +65 -0
- data/lib/rubocop/callbacksystems/helpers/node_types.rb +154 -0
- data/lib/rubocop/callbacksystems/helpers/rails.rb +52 -0
- data/lib/rubocop/callbacksystems/helpers/ranges.rb +107 -0
- data/lib/rubocop/callbacksystems/helpers/recursion.rb +29 -0
- data/lib/rubocop/callbacksystems/helpers/variables.rb +44 -0
- data/lib/rubocop/callbacksystems/helpers/visibility.rb +121 -0
- data/lib/rubocop/callbacksystems/helpers.rb +43 -0
- data/lib/rubocop/callbacksystems/methods/calls.rb +38 -0
- data/lib/rubocop/callbacksystems/methods/definitions.rb +218 -0
- data/lib/rubocop/callbacksystems/methods/delegate_macro.rb +144 -0
- data/lib/rubocop/callbacksystems/methods/delegation_target.rb +55 -0
- data/lib/rubocop/callbacksystems/methods/domain.rb +77 -0
- data/lib/rubocop/callbacksystems/methods/implicit_invocations.rb +17 -0
- data/lib/rubocop/callbacksystems/methods/macro_references.rb +238 -0
- data/lib/rubocop/callbacksystems/methods/reference_evidence.rb +74 -0
- data/lib/rubocop/callbacksystems/methods/siblings.rb +24 -0
- data/lib/rubocop/callbacksystems/methods/statement_definitions.rb +68 -0
- data/lib/rubocop/callbacksystems/offense.rb +13 -0
- data/lib/rubocop/callbacksystems/plugin.rb +27 -0
- data/lib/rubocop/callbacksystems/project_index/cache.rb +16 -0
- data/lib/rubocop/callbacksystems/project_index/confinement_cache.rb +40 -0
- data/lib/rubocop/callbacksystems/project_index/constant_references.rb +74 -0
- data/lib/rubocop/callbacksystems/project_index/corrections.rb +35 -0
- data/lib/rubocop/callbacksystems/project_index/coverage.rb +56 -0
- data/lib/rubocop/callbacksystems/project_index/diagnostics.rb +60 -0
- data/lib/rubocop/callbacksystems/project_index/freshness.rb +104 -0
- data/lib/rubocop/callbacksystems/project_index/loader.rb +9 -0
- data/lib/rubocop/callbacksystems/project_index/location.rb +29 -0
- data/lib/rubocop/callbacksystems/project_index/nested_class_confinement.rb +297 -0
- data/lib/rubocop/callbacksystems/project_index/nested_class_method_dispatch.rb +50 -0
- data/lib/rubocop/callbacksystems/project_index/reading.rb +129 -0
- data/lib/rubocop/callbacksystems/project_index/source_file.rb +59 -0
- data/lib/rubocop/callbacksystems/project_index/source_file_match.rb +57 -0
- data/lib/rubocop/callbacksystems/project_index/support.rb +46 -0
- data/lib/rubocop/callbacksystems/source/comment_blocks.rb +43 -0
- data/lib/rubocop/callbacksystems/source/comments.rb +141 -0
- data/lib/rubocop/callbacksystems/source/file_ast.rb +15 -0
- data/lib/rubocop/callbacksystems/source/files_checksum.rb +43 -0
- data/lib/rubocop/callbacksystems/source/single_line_call.rb +181 -0
- data/lib/rubocop/callbacksystems/source/statement_with_comments.rb +131 -0
- data/lib/rubocop/callbacksystems/testing/cop_helpers.rb +42 -0
- data/lib/rubocop/callbacksystems/testing/fixture_call.rb +30 -0
- data/lib/rubocop/callbacksystems/testing/fixture_names.rb +62 -0
- data/lib/rubocop/callbacksystems/testing/path_mapping.rb +104 -0
- data/lib/rubocop/cop/callbacksystems/application_controller_method_definition.rb +44 -0
- data/lib/rubocop/cop/callbacksystems/assert_with_expected_argument.rb +60 -0
- data/lib/rubocop/cop/callbacksystems/base.rb +134 -0
- data/lib/rubocop/cop/callbacksystems/body_order.rb +478 -0
- data/lib/rubocop/cop/callbacksystems/builder_method_parentheses.rb +74 -0
- data/lib/rubocop/cop/callbacksystems/collapse_multiline_expression.rb +140 -0
- data/lib/rubocop/cop/callbacksystems/comment_block_length.rb +137 -0
- data/lib/rubocop/cop/callbacksystems/complex_conditional.rb +75 -0
- data/lib/rubocop/cop/callbacksystems/controller_test_response_assertion.rb +105 -0
- data/lib/rubocop/cop/callbacksystems/controller_test_response_first_assertion.rb +123 -0
- data/lib/rubocop/cop/callbacksystems/data_clump.rb +339 -0
- data/lib/rubocop/cop/callbacksystems/declarative_method_naming.rb +350 -0
- data/lib/rubocop/cop/callbacksystems/delegate_requires_private_option.rb +81 -0
- data/lib/rubocop/cop/callbacksystems/eager_loading_scope_naming.rb +386 -0
- data/lib/rubocop/cop/callbacksystems/early_return.rb +215 -0
- data/lib/rubocop/cop/callbacksystems/empty_line_before_method.rb +168 -0
- data/lib/rubocop/cop/callbacksystems/empty_line_between_sections.rb +169 -0
- data/lib/rubocop/cop/callbacksystems/empty_private_section.rb +107 -0
- data/lib/rubocop/cop/callbacksystems/expanded_empty_class_or_module.rb +71 -0
- data/lib/rubocop/cop/callbacksystems/inline_memoized_computation.rb +110 -0
- data/lib/rubocop/cop/callbacksystems/job_enqueue_methods.rb +93 -0
- data/lib/rubocop/cop/callbacksystems/max_safe_navigation_depth.rb +118 -0
- data/lib/rubocop/cop/callbacksystems/method_invocation_order.rb +261 -0
- data/lib/rubocop/cop/callbacksystems/nested_class_order.rb +255 -0
- data/lib/rubocop/cop/callbacksystems/no_abbreviations.rb +197 -0
- data/lib/rubocop/cop/callbacksystems/no_anemic_delegation.rb +101 -0
- data/lib/rubocop/cop/callbacksystems/no_anemic_record.rb +309 -0
- data/lib/rubocop/cop/callbacksystems/no_assert_select.rb +31 -0
- data/lib/rubocop/cop/callbacksystems/no_bang_method_without_counterpart.rb +451 -0
- data/lib/rubocop/cop/callbacksystems/no_call_method.rb +73 -0
- data/lib/rubocop/cop/callbacksystems/no_comments_in_test_class_body.rb +159 -0
- data/lib/rubocop/cop/callbacksystems/no_consecutive_appends.rb +261 -0
- data/lib/rubocop/cop/callbacksystems/no_custom_route_actions.rb +124 -0
- data/lib/rubocop/cop/callbacksystems/no_direct_eager_loading.rb +90 -0
- data/lib/rubocop/cop/callbacksystems/no_each_with_early_return.rb +58 -0
- data/lib/rubocop/cop/callbacksystems/no_explicit_public_modifier.rb +81 -0
- data/lib/rubocop/cop/callbacksystems/no_hash_with_default_block.rb +68 -0
- data/lib/rubocop/cop/callbacksystems/no_local_variable_return.rb +173 -0
- data/lib/rubocop/cop/callbacksystems/no_manual_accumulation.rb +188 -0
- data/lib/rubocop/cop/callbacksystems/no_mixed_memoization.rb +145 -0
- data/lib/rubocop/cop/callbacksystems/no_nested_modules_in_classes.rb +78 -0
- data/lib/rubocop/cop/callbacksystems/no_nil_at_end_of_method.rb +74 -0
- data/lib/rubocop/cop/callbacksystems/no_redundant_validation_name.rb +37 -0
- data/lib/rubocop/cop/callbacksystems/no_redundant_wrapper_method.rb +161 -0
- data/lib/rubocop/cop/callbacksystems/no_section_divider_comments.rb +105 -0
- data/lib/rubocop/cop/callbacksystems/no_send_in_tests.rb +75 -0
- data/lib/rubocop/cop/callbacksystems/no_service_objects.rb +64 -0
- data/lib/rubocop/cop/callbacksystems/no_single_letter_names.rb +47 -0
- data/lib/rubocop/cop/callbacksystems/no_singleton_class_in_modules.rb +43 -0
- data/lib/rubocop/cop/callbacksystems/no_tap_collection_building.rb +173 -0
- data/lib/rubocop/cop/callbacksystems/no_turbo_stream_in_controllers.rb +52 -0
- data/lib/rubocop/cop/callbacksystems/no_typographic_clutter.rb +159 -0
- data/lib/rubocop/cop/callbacksystems/no_writes_in_get_actions.rb +149 -0
- data/lib/rubocop/cop/callbacksystems/ordered_mixin_arguments.rb +78 -0
- data/lib/rubocop/cop/callbacksystems/plural_controller_names.rb +103 -0
- data/lib/rubocop/cop/callbacksystems/prefer_attr_reader_over_instance_variable.rb +86 -0
- data/lib/rubocop/cop/callbacksystems/prefer_backslash_continuation.rb +478 -0
- data/lib/rubocop/cop/callbacksystems/prefer_class_for_state.rb +532 -0
- data/lib/rubocop/cop/callbacksystems/prefer_class_new_for_empty_nested_class.rb +247 -0
- data/lib/rubocop/cop/callbacksystems/prefer_class_over_builder_block.rb +124 -0
- data/lib/rubocop/cop/callbacksystems/prefer_default_argument.rb +225 -0
- data/lib/rubocop/cop/callbacksystems/prefer_delegate.rb +242 -0
- data/lib/rubocop/cop/callbacksystems/prefer_delegate_over_instance_variable_assignment.rb +175 -0
- data/lib/rubocop/cop/callbacksystems/prefer_excluding.rb +204 -0
- data/lib/rubocop/cop/callbacksystems/prefer_extract_associated.rb +54 -0
- data/lib/rubocop/cop/callbacksystems/prefer_it_block_parameter.rb +156 -0
- data/lib/rubocop/cop/callbacksystems/prefer_many.rb +57 -0
- data/lib/rubocop/cop/callbacksystems/prefer_method_over_lambda.rb +211 -0
- data/lib/rubocop/cop/callbacksystems/prefer_method_over_variable.rb +172 -0
- data/lib/rubocop/cop/callbacksystems/prefer_module_for_static_class.rb +286 -0
- data/lib/rubocop/cop/callbacksystems/prefer_ordinal_array_access.rb +136 -0
- data/lib/rubocop/cop/callbacksystems/prefer_positive_wrap.rb +265 -0
- data/lib/rubocop/cop/callbacksystems/prefer_squish.rb +47 -0
- data/lib/rubocop/cop/callbacksystems/prefer_stringify_keys.rb +24 -0
- data/lib/rubocop/cop/callbacksystems/prefer_symbolize_keys.rb +24 -0
- data/lib/rubocop/cop/callbacksystems/prefer_turbo_page_refresh_broadcasts.rb +40 -0
- data/lib/rubocop/cop/callbacksystems/private_constants_in_private_section.rb +160 -0
- data/lib/rubocop/cop/callbacksystems/private_delegate_placement.rb +342 -0
- data/lib/rubocop/cop/callbacksystems/private_methods_in_private_section.rb +70 -0
- data/lib/rubocop/cop/callbacksystems/private_nested_class_method_visibility.rb +269 -0
- data/lib/rubocop/cop/callbacksystems/private_nested_classes.rb +94 -0
- data/lib/rubocop/cop/callbacksystems/public_methods_must_have_tests.rb +190 -0
- data/lib/rubocop/cop/callbacksystems/redundant_concern_extend.rb +157 -0
- data/lib/rubocop/cop/callbacksystems/redundant_constant_freeze.rb +57 -0
- data/lib/rubocop/cop/callbacksystems/redundant_constant_namespace.rb +134 -0
- data/lib/rubocop/cop/callbacksystems/reflow_comments.rb +133 -0
- data/lib/rubocop/cop/callbacksystems/repeated_fixture_in_tests.rb +106 -0
- data/lib/rubocop/cop/callbacksystems/restricted_controller_actions.rb +85 -0
- data/lib/rubocop/cop/callbacksystems/routes_module_scope.rb +336 -0
- data/lib/rubocop/cop/callbacksystems/single_line_setup_block.rb +107 -0
- data/lib/rubocop/cop/callbacksystems/single_singleton_class.rb +49 -0
- data/lib/rubocop/cop/callbacksystems/single_top_level_class_or_module.rb +160 -0
- data/lib/rubocop/cop/callbacksystems/single_use_fixture_variable.rb +211 -0
- data/lib/rubocop/cop/callbacksystems/single_use_setup_variable.rb +684 -0
- data/lib/rubocop/cop/callbacksystems/singular_scope_names.rb +47 -0
- data/lib/rubocop/cop/callbacksystems/space_inside_percent_literal.rb +73 -0
- data/lib/rubocop/cop/callbacksystems/system_test_browser_driver.rb +30 -0
- data/lib/rubocop/cop/callbacksystems/test_method_definition.rb +86 -0
- data/lib/rubocop/cop/callbacksystems/test_method_order.rb +319 -0
- data/lib/rubocop/cop/callbacksystems/too_many_instance_variables.rb +78 -0
- data/lib/rubocop/cop/callbacksystems/too_many_local_variables.rb +77 -0
- data/lib/rubocop/cop/callbacksystems/too_many_validation_guards.rb +84 -0
- data/lib/rubocop/cop/callbacksystems/unnecessary_local_variable.rb +176 -0
- data/lib/rubocop/cop/callbacksystems/unused_private_method_in_nested_class.rb +203 -0
- data/lib/rubocop-callbacksystems.rb +21 -0
- data/rubocop.yml +685 -0
- metadata +337 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# The order that reads each name before the names it refers to, walked depth first from the seeds it is given. The graph
|
|
2
|
+
# maps a name to the names it refers to, and a name the graph does not hold is skipped.
|
|
3
|
+
class RuboCop::Callbacksystems::ClassStructure::ReferenceOrder
|
|
4
|
+
include Enumerable
|
|
5
|
+
|
|
6
|
+
delegate :each, to: :ordered
|
|
7
|
+
|
|
8
|
+
def initialize(seeds, graph)
|
|
9
|
+
@seeds = seeds
|
|
10
|
+
@graph = graph
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
attr_reader :seeds, :graph
|
|
15
|
+
|
|
16
|
+
def ordered
|
|
17
|
+
@ordered ||= seeds.each_with_object([]) { |name, order| gather(name, order) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def gather(name, order)
|
|
21
|
+
stack = [ name ]
|
|
22
|
+
|
|
23
|
+
until stack.empty?
|
|
24
|
+
current = stack.pop
|
|
25
|
+
next if visited.include?(current) || graph.exclude?(current)
|
|
26
|
+
|
|
27
|
+
visited.add(current)
|
|
28
|
+
order << current
|
|
29
|
+
graph.fetch(current).reverse_each { stack << it if visited.exclude?(it) }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def visited
|
|
34
|
+
@visited ||= Set.new
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# The place a statement takes in the order a class body reads: the mixins and the class attribute assignments first,
|
|
2
|
+
# then the values, the classes declared in a line, the state, the associations, the delegates, the other macros, the
|
|
3
|
+
# methods, and last the nested classes with a body of their own. The siblings are the statements of the same body, which
|
|
4
|
+
# a `private_constant` needs to find the constant it hides.
|
|
5
|
+
class RuboCop::Callbacksystems::ClassStructure::StatementRank
|
|
6
|
+
include RuboCop::Callbacksystems::Helpers
|
|
7
|
+
|
|
8
|
+
MIXIN = 0
|
|
9
|
+
VALUE = 1
|
|
10
|
+
CLASS_DECLARATION = 2
|
|
11
|
+
ATTRIBUTE = 3
|
|
12
|
+
ASSOCIATION = 4
|
|
13
|
+
DELEGATE = 5
|
|
14
|
+
MACRO = 6
|
|
15
|
+
METHOD = 7
|
|
16
|
+
NESTED_CLASS = 8
|
|
17
|
+
|
|
18
|
+
def initialize(node, siblings)
|
|
19
|
+
@node = node
|
|
20
|
+
@siblings = siblings
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_i
|
|
24
|
+
shape_rank || marker_rank || macro_rank || METHOD
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
attr_reader :node, :siblings
|
|
29
|
+
|
|
30
|
+
def shape_rank
|
|
31
|
+
if nested_body?(statement)
|
|
32
|
+
NESTED_CLASS
|
|
33
|
+
elsif statement.casgn_type?
|
|
34
|
+
value_name? ? VALUE : CLASS_DECLARATION
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# `include Detection if Rails.env.local?` is the include, whatever the condition around it.
|
|
39
|
+
def statement
|
|
40
|
+
node.if_type? && node.modifier_form? ? node.if_branch : node
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# `Naming/ConstantName` wants SCREAMING_SNAKE_CASE on a value and not on a class, so the case tells them apart.
|
|
44
|
+
def value_name?
|
|
45
|
+
statement.name.match?(/\A[A-Z][A-Z0-9_]*\z/)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# `private_constant :X` belongs with the `X` it hides, so it takes that declaration's rank.
|
|
49
|
+
def marker_rank
|
|
50
|
+
marked_ranks.max || VALUE if private_constant_marker?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def private_constant_marker?
|
|
54
|
+
bare_send?(macro) && macro.method?(:private_constant)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# `has_many :blocks do ... end` is the `has_many`, whatever the block adds to it.
|
|
58
|
+
def macro
|
|
59
|
+
call_of(statement)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def marked_ranks
|
|
63
|
+
siblings.select { marked_names.include?(declared_name_of(it)) }
|
|
64
|
+
.map { self.class.new(it, siblings).to_i }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def marked_names
|
|
68
|
+
@marked_names ||= private_constant_names_of(node)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def macro_rank
|
|
72
|
+
case
|
|
73
|
+
when mixin_macro?(macro) || class_attribute_assignment? then MIXIN
|
|
74
|
+
when attribute_macro?(macro) then ATTRIBUTE
|
|
75
|
+
when association_macro?(macro) then ASSOCIATION
|
|
76
|
+
when delegate_macro?(macro) then DELEGATE
|
|
77
|
+
when bare_send?(macro) then MACRO
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def class_attribute_assignment?
|
|
82
|
+
statement.send_type? && statement.receiver&.self_type? && statement.assignment_method?
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Statements written together with nothing between them, rewritten into the order given when every one has a place in
|
|
2
|
+
# it. The block names each statement the way the order does, and a statement answers `range` and `source`.
|
|
3
|
+
class RuboCop::Callbacksystems::ClassStructure::StatementRun
|
|
4
|
+
include RuboCop::Callbacksystems::Helpers
|
|
5
|
+
|
|
6
|
+
def initialize(statements, order:, &name_of)
|
|
7
|
+
@statements = statements
|
|
8
|
+
@order = order
|
|
9
|
+
@name_of = name_of
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def rewrite(corrector)
|
|
13
|
+
corrector.replace(range, ordered_source) if reorderable?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def reorderable?
|
|
17
|
+
ordered != statements && statements.none?(&:contains_tooling_comment?)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
attr_reader :statements, :order, :name_of
|
|
22
|
+
|
|
23
|
+
def ordered
|
|
24
|
+
@ordered ||= placed? ? statements.sort_by { positions.fetch(name_of.call(it)) } : statements
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def placed?
|
|
28
|
+
statements.all? { positions.key?(name_of.call(it)) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def positions
|
|
32
|
+
@positions ||= order.each_with_index.reverse_each.to_h
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def range
|
|
36
|
+
span_of(statements.map(&:range))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def ordered_source
|
|
40
|
+
ordered.map(&:source).join("\n\n")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# The visibility established by one class body, indexed once for every reader of that body. Section modifiers apply to
|
|
2
|
+
# the statements below them, while named modifiers are kept separately because they reach definitions above them.
|
|
3
|
+
class RuboCop::Callbacksystems::ClassStructure::Visibility
|
|
4
|
+
include RuboCop::Callbacksystems::Helpers
|
|
5
|
+
|
|
6
|
+
Assignment = Data.define(:kind, :name, :position, :level)
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def for(body)
|
|
10
|
+
body ? CACHE_LOCK.synchronize { CACHE[body] ||= new(body) } : new(nil)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
CACHE = ObjectSpace::WeakMap.new
|
|
15
|
+
CACHE_LOCK = Mutex.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(body)
|
|
19
|
+
@body = body
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def each_statement
|
|
23
|
+
if block_given?
|
|
24
|
+
statements.each_with_index { |statement, index| yield statement, transitions[index] }
|
|
25
|
+
else
|
|
26
|
+
to_enum(__method__)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def level_at(node)
|
|
31
|
+
statement_index_at(node)&.then { transitions[it] } || transitions.last
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def assignment_after(node, singleton:)
|
|
35
|
+
assignments.fetch([ singleton ? :singleton : :instance, node.method_name ], []).last&.then do |assignment|
|
|
36
|
+
assignment if assignment.position >= node.source_range.end_pos
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
NAMED_LEVELS = {
|
|
42
|
+
public: [ :instance, :public ],
|
|
43
|
+
protected: [ :instance, :protected ],
|
|
44
|
+
private: [ :instance, :private ],
|
|
45
|
+
public_class_method: [ :singleton, :public ],
|
|
46
|
+
private_class_method: [ :singleton, :private ]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
attr_reader :body
|
|
50
|
+
|
|
51
|
+
def statements
|
|
52
|
+
@statements ||= statements_in(body)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def transitions
|
|
56
|
+
@transitions ||= statements.each_with_object([ :public ]) do |statement, levels|
|
|
57
|
+
levels << (visibility_modifier_of(statement) || levels.last)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def statement_index_at(node)
|
|
62
|
+
statement_end_positions.bsearch_index { it > node.source_range.begin_pos }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def statement_end_positions
|
|
66
|
+
@statement_end_positions ||= statements.map { it.source_range.end_pos }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def assignments
|
|
70
|
+
@assignments ||= statements.flat_map { assignments_from(it) }.group_by { [ it.kind, it.name ] }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def assignments_from(statement)
|
|
74
|
+
named_level_of(statement)&.then do |kind, level|
|
|
75
|
+
name_arguments_of(statement).map do |name|
|
|
76
|
+
Assignment.new(kind:, name: name.value.to_sym, position: statement.source_range.begin_pos, level:)
|
|
77
|
+
end
|
|
78
|
+
end || []
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def named_level_of(statement)
|
|
82
|
+
NAMED_LEVELS[statement.method_name] if bare_send?(statement)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
class RuboCop::Callbacksystems::CopsDocument
|
|
4
|
+
DEFAULT_PATH = File.expand_path("../../../docs/cops.md", __dir__)
|
|
5
|
+
HEADING = <<~MARKDOWN
|
|
6
|
+
# Cops
|
|
7
|
+
|
|
8
|
+
<!-- Generated by `bin/docs`. Do not edit by hand. -->
|
|
9
|
+
|
|
10
|
+
Every cop lives under the `Callbacksystems/` namespace and ships enabled. Each one links to its source, where an `@example` block contrasts the code it rejects with the code it asks for, and `config/default.yml` holds its options and default scope.
|
|
11
|
+
MARKDOWN
|
|
12
|
+
TABLE_HEADER = [ "| Cop | Autocorrect | Description |", "|---|---|---|" ]
|
|
13
|
+
|
|
14
|
+
def initialize(path = DEFAULT_PATH)
|
|
15
|
+
@path = path
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def save
|
|
19
|
+
File.write path, text
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def text
|
|
23
|
+
[ HEADING, *TABLE_HEADER, *rows, "" ].join("\n")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def committed
|
|
27
|
+
File.read(path)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def undescribed
|
|
31
|
+
rows.select { it.description.blank? }.map(&:name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
SOURCE_DIRECTORY = "../lib/rubocop/cop/callbacksystems"
|
|
36
|
+
|
|
37
|
+
attr_reader :path
|
|
38
|
+
|
|
39
|
+
def rows
|
|
40
|
+
@rows ||= cops.map { Row.new(it, descriptions[it.cop_name]) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def cops
|
|
44
|
+
RuboCop::Cop::Registry.global.cops.select { it.department == :Callbacksystems }.sort_by(&:cop_name)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def descriptions
|
|
48
|
+
YAML.load_file(RuboCop::Callbacksystems::Plugin::CONFIGURATION_PATH).transform_values { it["Description"] }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class Row
|
|
52
|
+
attr_reader :description
|
|
53
|
+
|
|
54
|
+
def initialize(cop, description)
|
|
55
|
+
@cop = cop
|
|
56
|
+
@description = description
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def to_s
|
|
60
|
+
"| [`#{name}`](#{source_path}) | #{autocorrection} | #{description} |"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def name
|
|
64
|
+
cop.cop_name
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
attr_reader :cop
|
|
69
|
+
|
|
70
|
+
def source_path
|
|
71
|
+
"#{SOURCE_DIRECTORY}/#{cop.badge.cop_name.underscore}.rb"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def autocorrection
|
|
75
|
+
"yes" if cop.support_autocorrect?
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Local variables captured or reassigned by callable bodies that execute after their surrounding expression.
|
|
2
|
+
class RuboCop::Callbacksystems::Execution::DeferredLocalVariables
|
|
3
|
+
include RuboCop::Callbacksystems::Helpers
|
|
4
|
+
|
|
5
|
+
delegate :include?, :exclude?, to: :names
|
|
6
|
+
|
|
7
|
+
def initialize(body)
|
|
8
|
+
@body = body
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
attr_reader :body
|
|
13
|
+
|
|
14
|
+
def names
|
|
15
|
+
@names ||= Traversal.new(body).to_set.freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# An iterative lexical walk keeps bindings paired with entry and exit while deferred bodies are visited.
|
|
19
|
+
class Traversal
|
|
20
|
+
include Enumerable
|
|
21
|
+
include RuboCop::Callbacksystems::Helpers
|
|
22
|
+
|
|
23
|
+
def initialize(body)
|
|
24
|
+
@context = Context.new(Visit.new(body, false))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def each
|
|
28
|
+
if block_given?
|
|
29
|
+
advance { yield it }
|
|
30
|
+
else
|
|
31
|
+
to_enum(__method__)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
attr_reader :context
|
|
37
|
+
|
|
38
|
+
def advance
|
|
39
|
+
context.next_event.perform(context) { yield it } while context.pending?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class Context
|
|
44
|
+
def initialize(first_event)
|
|
45
|
+
@pending = [ first_event ]
|
|
46
|
+
@binding_counts = Hash.new(0)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def pending?
|
|
50
|
+
pending.any?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def next_event
|
|
54
|
+
pending.pop
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def queue(*events)
|
|
58
|
+
pending.concat(events.reverse)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def enter(names)
|
|
62
|
+
names.each { binding_counts[it] += 1 }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def leave(names)
|
|
66
|
+
names.each { binding_counts[it] -= 1 }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def bound?(name)
|
|
70
|
+
binding_counts[name].positive?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
attr_reader :pending, :binding_counts
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class Visit < Data.define(:node, :deferred)
|
|
78
|
+
include RuboCop::Callbacksystems::Helpers
|
|
79
|
+
|
|
80
|
+
def perform(context)
|
|
81
|
+
if node
|
|
82
|
+
if any_block_type?(node)
|
|
83
|
+
queue_block(context)
|
|
84
|
+
elsif lexical_definition?(node)
|
|
85
|
+
context.queue(*input_visits)
|
|
86
|
+
else
|
|
87
|
+
yield variable_name_of(node) if captured_variable?(context)
|
|
88
|
+
queue_children(context)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
def queue_block(context)
|
|
95
|
+
bindings = binding_names
|
|
96
|
+
body_deferred = deferred || deferred_callable_block?(node) || method_definition_block?(node)
|
|
97
|
+
|
|
98
|
+
context.queue \
|
|
99
|
+
*input_visits,
|
|
100
|
+
EnterBindings.new(bindings),
|
|
101
|
+
self.class.new(argument_node, body_deferred),
|
|
102
|
+
self.class.new(node.body, body_deferred),
|
|
103
|
+
LeaveBindings.new(bindings)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def binding_names
|
|
107
|
+
node.argument_list.filter_map(&:name)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def input_visits
|
|
111
|
+
immediate_inputs_of(node).map { self.class.new(it, deferred) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def argument_node
|
|
115
|
+
node.arguments if node.arguments.respond_to?(:type)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def captured_variable?(context)
|
|
119
|
+
deferred && node.type?(:lvar, :lvasgn, :match_var) && !context.bound?(variable_name_of(node))
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def queue_children(context)
|
|
123
|
+
context.queue(*node.each_child_node.map { self.class.new(it, deferred) })
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
class EnterBindings < Data.define(:names)
|
|
128
|
+
def perform(context)
|
|
129
|
+
context.enter(names)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
class LeaveBindings < Data.define(:names)
|
|
134
|
+
def perform(context)
|
|
135
|
+
context.leave(names)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Whether an expression's value is ignored, following final values through sequences and definition bodies.
|
|
2
|
+
class RuboCop::Callbacksystems::Execution::DiscardedExpression
|
|
3
|
+
def initialize(expression)
|
|
4
|
+
@current = expression
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def discarded?
|
|
8
|
+
disposition == :discarded
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
attr_reader :current
|
|
13
|
+
delegate :parent, to: :current, private: true
|
|
14
|
+
|
|
15
|
+
def disposition
|
|
16
|
+
advance until @disposition
|
|
17
|
+
@disposition
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def advance
|
|
21
|
+
if parent.nil? || discarded_sequence?
|
|
22
|
+
@disposition = :discarded
|
|
23
|
+
elsif transparent?
|
|
24
|
+
@current = parent
|
|
25
|
+
else
|
|
26
|
+
@disposition = :observed
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def discarded_sequence?
|
|
31
|
+
sequence? && current.right_sibling
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def sequence?
|
|
35
|
+
parent.type?(:begin, :kwbegin)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def transparent?
|
|
39
|
+
sequence? || definition_body?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def definition_body?
|
|
43
|
+
parent.type?(:class, :module, :sclass) && parent.body.equal?(current)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# The path to a read where an already evaluated value can move without becoming conditional, running repeatedly, or
|
|
2
|
+
# crossing earlier work whose result the moved evaluation could change.
|
|
3
|
+
class RuboCop::Callbacksystems::Execution::EagerEvaluationPath
|
|
4
|
+
SEQUENTIAL_PARENT_TYPES = %i[
|
|
5
|
+
array begin block_pass break casgn csend cvasgn dstr dsym erange gvasgn hash irange ivasgn kwbegin lvasgn next pair
|
|
6
|
+
kwsplat regexp return send splat super yield
|
|
7
|
+
]
|
|
8
|
+
EAGER_FIRST_CHILD_PARENT_TYPES = %i[ and case case_match if or ]
|
|
9
|
+
BLOCK_PARENT_TYPES = %i[ block numblock itblock ]
|
|
10
|
+
INERT_NODE_TYPES = %i[ self lvar ]
|
|
11
|
+
|
|
12
|
+
def initialize(read, within:)
|
|
13
|
+
@read = read
|
|
14
|
+
@boundary = within
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def preserves_order?
|
|
18
|
+
read_within_boundary? && steps_preserve_order?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
attr_reader :read, :boundary
|
|
23
|
+
|
|
24
|
+
def read_within_boundary?
|
|
25
|
+
read.equal?(boundary) || read.each_ancestor.any? { it.equal?(boundary) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def steps_preserve_order?
|
|
29
|
+
cursor = read
|
|
30
|
+
cursor = cursor.parent while !cursor.equal?(boundary) && Step.new(cursor).preserves_order?
|
|
31
|
+
cursor.equal?(boundary)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Step
|
|
35
|
+
def initialize(node)
|
|
36
|
+
@node = node
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def preserves_order?
|
|
40
|
+
eager_once? && preceding_nodes.all? { inert?(it) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
attr_reader :node
|
|
45
|
+
|
|
46
|
+
delegate :parent, to: :node, private: true
|
|
47
|
+
|
|
48
|
+
def eager_once?
|
|
49
|
+
sequential? || eager_first_child? || immediate_block_call?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def sequential?
|
|
53
|
+
SEQUENTIAL_PARENT_TYPES.include?(parent.type) &&
|
|
54
|
+
(!parent.csend_type? || parent.receiver.equal?(node))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def eager_first_child?
|
|
58
|
+
EAGER_FIRST_CHILD_PARENT_TYPES.include?(parent.type) && parent.children.first.equal?(node)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def immediate_block_call?
|
|
62
|
+
BLOCK_PARENT_TYPES.include?(parent.type) && parent.send_node.equal?(node)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def preceding_nodes
|
|
66
|
+
parent.each_child_node.take_while { !it.equal?(node) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def inert?(candidate)
|
|
70
|
+
candidate.recursive_basic_literal? || INERT_NODE_TYPES.include?(candidate.type)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# The nodes an expression can execute now, without entering a definition or a callable whose body runs later.
|
|
2
|
+
class RuboCop::Callbacksystems::Execution::Immediate
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
def initialize(node, deferred_blocks: [])
|
|
6
|
+
@node = node
|
|
7
|
+
@deferred_blocks = {}.compare_by_identity
|
|
8
|
+
deferred_blocks.each { @deferred_blocks[it] = true }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def nodes_of_type(*types)
|
|
12
|
+
select { it.type?(*types) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def each
|
|
16
|
+
if block_given?
|
|
17
|
+
execution.each { yield it }
|
|
18
|
+
else
|
|
19
|
+
enum_for
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
attr_reader :node, :deferred_blocks
|
|
25
|
+
|
|
26
|
+
def execution
|
|
27
|
+
Traversal.new(node, deferred_blocks)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# An iterative walk that keeps its cursor and pending nodes together with the shared boundary index.
|
|
31
|
+
class Traversal
|
|
32
|
+
include RuboCop::Callbacksystems::Helpers
|
|
33
|
+
|
|
34
|
+
def initialize(node, deferred_blocks)
|
|
35
|
+
@pending = [ node ]
|
|
36
|
+
@deferred_blocks = deferred_blocks
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def each
|
|
40
|
+
until pending.empty?
|
|
41
|
+
advance
|
|
42
|
+
visit { yield it } if current
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
attr_reader :pending, :deferred_blocks, :current
|
|
48
|
+
|
|
49
|
+
def advance
|
|
50
|
+
@current = pending.pop
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def visit
|
|
54
|
+
if execution_boundary?
|
|
55
|
+
queue immediate_inputs_of(current)
|
|
56
|
+
else
|
|
57
|
+
yield current
|
|
58
|
+
queue current.each_child_node
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def execution_boundary?
|
|
63
|
+
lexical_definition?(current) || deferred_callable_block?(current) || method_definition_block?(current) ||
|
|
64
|
+
deferred_blocks.key?(current)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def queue(nodes)
|
|
68
|
+
pending.concat(nodes.to_a.reverse)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|