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,112 @@
|
|
|
1
|
+
# Local-variable nodes indexed by lexical scope, so several readings of one scope walk its tree only once.
|
|
2
|
+
class RuboCop::Callbacksystems::Execution::LocalVariableOccurrences
|
|
3
|
+
include RuboCop::Callbacksystems::Helpers
|
|
4
|
+
|
|
5
|
+
def initialize(tree)
|
|
6
|
+
@tree = tree
|
|
7
|
+
@by_scope = {}.compare_by_identity
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def named(name, around:)
|
|
11
|
+
occurrences_around(around).fetch(name) { [] }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def named_in_method(name, around:)
|
|
15
|
+
occurrences_in(enclosing_method_of(around)).fetch(name) { [] }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
attr_reader :tree, :by_scope
|
|
20
|
+
|
|
21
|
+
def occurrences_around(node)
|
|
22
|
+
occurrences_in(scope_of(node))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def occurrences_in(scope)
|
|
26
|
+
if scope
|
|
27
|
+
by_scope[scope] ||= OccurrencesWithin.new(scope).group_by { variable_name_of(it) }
|
|
28
|
+
else
|
|
29
|
+
{}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def scope_of(node)
|
|
34
|
+
enclosing_scope_of(node) || tree
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class OccurrencesWithin
|
|
38
|
+
include Enumerable
|
|
39
|
+
include RuboCop::Callbacksystems::Helpers
|
|
40
|
+
|
|
41
|
+
def initialize(scope)
|
|
42
|
+
@scope = scope
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def each
|
|
46
|
+
if block_given?
|
|
47
|
+
each_occurrence { yield it }
|
|
48
|
+
else
|
|
49
|
+
to_enum(__method__)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
attr_reader :scope
|
|
55
|
+
|
|
56
|
+
def each_occurrence
|
|
57
|
+
pending = [ Candidate.new(scope, scope:, nested: false) ]
|
|
58
|
+
until pending.empty?
|
|
59
|
+
candidate = pending.pop
|
|
60
|
+
yield candidate.node if candidate.occurrence?
|
|
61
|
+
candidate.children.reverse_each { pending << it }
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Candidate
|
|
66
|
+
include RuboCop::Callbacksystems::Helpers
|
|
67
|
+
|
|
68
|
+
attr_reader :node
|
|
69
|
+
|
|
70
|
+
def initialize(node, scope:, nested:)
|
|
71
|
+
@node = node
|
|
72
|
+
@scope = scope
|
|
73
|
+
@nested = nested
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def occurrence?
|
|
77
|
+
local_node? && !rebound?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def children
|
|
81
|
+
child_nodes.map { self.class.new(it, scope:, nested: true) }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
attr_reader :scope, :nested
|
|
86
|
+
|
|
87
|
+
def local_node?
|
|
88
|
+
node.type?(:lvar, :lvasgn, :match_var)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def rebound?
|
|
92
|
+
name_rebound_between?(variable_name_of(node), node:, boundary: scope)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def child_nodes
|
|
96
|
+
if nested && execution_boundary?
|
|
97
|
+
immediate_inputs_of(node)
|
|
98
|
+
else
|
|
99
|
+
node.each_child_node
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def execution_boundary?
|
|
104
|
+
lexical_definition?(node) || deferred_boundary?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def deferred_boundary?
|
|
108
|
+
deferred_callable_block?(node) || method_definition_block?(node)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# The reading PreferStringifyKeys and PreferSymbolizeKeys share: a `transform_keys` that only sends one method to each
|
|
2
|
+
# key, given as a block pass or as a block. An including cop names that method as `source_method` and the Active Support
|
|
3
|
+
# method saying the same thing as `preferred_method`.
|
|
4
|
+
module RuboCop::Callbacksystems::Hashes::KeyTransformDetection
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
extend RuboCop::Cop::AutoCorrector
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def on_send(node)
|
|
12
|
+
report key_transform_of(node)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
alias on_csend on_send
|
|
16
|
+
|
|
17
|
+
def on_block(node)
|
|
18
|
+
report key_transform_of(node)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
alias on_numblock on_block
|
|
22
|
+
alias on_itblock on_block
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def key_transform_of(node)
|
|
26
|
+
KeyTransform.new(node, source_method:, preferred_method:, source_comments:)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class KeyTransform
|
|
30
|
+
include RuboCop::Callbacksystems::Helpers
|
|
31
|
+
|
|
32
|
+
MESSAGE = "Use `%<preferred_method>s` instead of `transform_keys(&:%<source_method>s)`."
|
|
33
|
+
|
|
34
|
+
def initialize(node, source_method:, preferred_method:, source_comments:)
|
|
35
|
+
@node = node
|
|
36
|
+
@source_method = source_method
|
|
37
|
+
@preferred_method = preferred_method
|
|
38
|
+
@source_comments = source_comments
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def offense
|
|
42
|
+
if sends_source_method_to_keys?
|
|
43
|
+
RuboCop::Callbacksystems::Offense.new(node, message, correcting: uncommented?) { correct(it) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
attr_reader :node, :source_method, :preferred_method, :source_comments
|
|
49
|
+
|
|
50
|
+
def sends_source_method_to_keys?
|
|
51
|
+
transform_keys? && sends_source_method?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def transform_keys?
|
|
55
|
+
keys_call.method?(:transform_keys) && keys_call.receiver
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def keys_call
|
|
59
|
+
node.call_type? ? node : node.send_node
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def sends_source_method?
|
|
63
|
+
node.call_type? ? passes_source_method? : sends_source_method_to_parameter?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def passes_source_method?
|
|
67
|
+
keys_call.arguments.one? && block_pass_value&.sym_type? &&
|
|
68
|
+
block_pass_value.value == source_method
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def block_pass_value
|
|
72
|
+
node.first_argument.children.first if node.first_argument.block_pass_type?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def sends_source_method_to_parameter?
|
|
76
|
+
keys_call.arguments.empty? && single_parameter_block? && exact_source_method_send?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def single_parameter_block?
|
|
80
|
+
any_block_type?(node) && node.argument_list.one? && block_parameter.arg_type?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def block_parameter
|
|
84
|
+
node.argument_list.first
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def exact_source_method_send?
|
|
88
|
+
node.body&.send_type? && node.body.method?(source_method) && node.body.arguments.empty? &&
|
|
89
|
+
reads_variable?(node.body.receiver, block_parameter.name)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def message
|
|
93
|
+
format(MESSAGE, preferred_method: preferred_method, source_method: source_method)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def uncommented?
|
|
97
|
+
!source_comments.any_within?(node)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def correct(corrector)
|
|
101
|
+
corrector.replace(node, "#{keys_call.receiver.source}#{call_operator}#{preferred_method}")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def call_operator
|
|
105
|
+
keys_call.csend_type? ? "&." : "."
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Keyword options resolved in Ruby's evaluation order: the last explicit pair wins unless a later splat can replace it.
|
|
2
|
+
class RuboCop::Callbacksystems::Hashes::KeywordOptions
|
|
3
|
+
attr_reader :elements
|
|
4
|
+
|
|
5
|
+
def initialize(*hashes)
|
|
6
|
+
@elements = hashes.compact.flat_map(&:children)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def known?(key)
|
|
10
|
+
option_for(key).present? || elements.none? { unknown_option?(it) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def option_for(key)
|
|
14
|
+
explicit = explicit_options_for(key).last
|
|
15
|
+
|
|
16
|
+
explicit unless unknown_option_follows?(explicit)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def explicit_options_for(key)
|
|
20
|
+
elements.select { it.pair_type? && it.key.sym_type? && it.key.value == key }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
def unknown_option_follows?(option)
|
|
25
|
+
option && elements.drop(index_of(option) + 1).any? { unknown_option?(it) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def index_of(option)
|
|
29
|
+
elements.index { it.equal?(option) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# A non-literal key can evaluate to the option being queried just as a keyword splat can. Literal non-symbol keys
|
|
33
|
+
# cannot replace it, but abstaining for those uncommon calls keeps this reader sound without evaluating Ruby.
|
|
34
|
+
def unknown_option?(element)
|
|
35
|
+
element.kwsplat_type? || (element.pair_type? && !element.key.sym_type?)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::Helpers::Bodies
|
|
2
|
+
def assignment_count(body, *types)
|
|
3
|
+
ScopedAssignments.new(body, types).to_set { variable_name_of(it) }.size
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def immediate_inputs_of(node)
|
|
7
|
+
if any_block_type?(node)
|
|
8
|
+
call_of(node).then { [ it.receiver, *it.arguments ].compact }
|
|
9
|
+
else
|
|
10
|
+
case node.type
|
|
11
|
+
when :defs then [ node.receiver ]
|
|
12
|
+
when :class then [ node.identifier, node.parent_class ].compact
|
|
13
|
+
when :sclass then [ node.identifier ]
|
|
14
|
+
else []
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def lexical_definition?(node)
|
|
20
|
+
node.type?(:def, :defs, :class, :module, :sclass)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def statements_before(node)
|
|
24
|
+
node.parent&.type?(:begin, :kwbegin) ? node.parent.children.take_while { !it.equal?(node) } : []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def direct_definitions_in(body, type)
|
|
28
|
+
statements_in(body).select { it.type?(type) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def statements_in(body)
|
|
32
|
+
if body
|
|
33
|
+
body.type?(:begin, :kwbegin) ? body.children.to_a : [ body ]
|
|
34
|
+
else
|
|
35
|
+
[]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def runs_in(body)
|
|
40
|
+
statements_in(body).chunk_while { |left, right| yield(left) && yield(right) }.select { yield it.first }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def receiverless_method_names_in(body)
|
|
44
|
+
nodes_in(body, :send).filter_map { it.method_name if bare_send?(it) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def first_statement_in(body)
|
|
48
|
+
edge_statement(body, side: :first)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def last_statement_in(body)
|
|
52
|
+
edge_statement(body, side: :last)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def direct_method_nodes_in(body)
|
|
56
|
+
DirectMethodNodes.new(body).to_a
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
def edge_statement(body, side:)
|
|
61
|
+
current = body
|
|
62
|
+
loop do
|
|
63
|
+
current = case current&.type
|
|
64
|
+
when :begin, :kwbegin then current.children.public_send(side)
|
|
65
|
+
when :rescue then current.body
|
|
66
|
+
when :ensure then current.children.first
|
|
67
|
+
else return current
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Method definitions belonging directly to a body, with singleton sections transparent and other owners pruned.
|
|
73
|
+
class DirectMethodNodes
|
|
74
|
+
include Enumerable
|
|
75
|
+
|
|
76
|
+
def initialize(body)
|
|
77
|
+
@body = body
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def each
|
|
81
|
+
if block_given?
|
|
82
|
+
pending = [ body ].compact
|
|
83
|
+
until pending.empty?
|
|
84
|
+
node = pending.pop
|
|
85
|
+
case node.type
|
|
86
|
+
when :def, :defs then yield node
|
|
87
|
+
when :begin, :kwbegin then pending.concat(node.each_child_node.to_a.reverse)
|
|
88
|
+
when :sclass then pending << node.body if node.body
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
else
|
|
92
|
+
to_enum(__method__)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
attr_reader :body
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Assignments executed in one lexical scope, with callable and definition bodies left for their own scopes.
|
|
101
|
+
class ScopedAssignments
|
|
102
|
+
include Enumerable
|
|
103
|
+
include RuboCop::Callbacksystems::Helpers
|
|
104
|
+
|
|
105
|
+
def initialize(body, types)
|
|
106
|
+
@body = body
|
|
107
|
+
@types = types
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def each
|
|
111
|
+
if block_given?
|
|
112
|
+
@pending = [ body ].compact
|
|
113
|
+
until pending.empty?
|
|
114
|
+
advance
|
|
115
|
+
visit { yield it }
|
|
116
|
+
end
|
|
117
|
+
else
|
|
118
|
+
to_enum(__method__)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
attr_reader :body, :types, :pending, :current
|
|
124
|
+
|
|
125
|
+
def advance
|
|
126
|
+
@current = pending.pop
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def visit
|
|
130
|
+
if scope_boundary?
|
|
131
|
+
queue immediate_inputs_of(current)
|
|
132
|
+
else
|
|
133
|
+
yield current if current.type?(*types)
|
|
134
|
+
queue current.each_child_node
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def scope_boundary?
|
|
139
|
+
(!current.equal?(body) && lexical_definition?(current)) || deferred_boundary?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def deferred_boundary?
|
|
143
|
+
deferred_callable_block?(current) || method_definition_block?(current)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def queue(nodes)
|
|
147
|
+
pending.concat(nodes.to_a.reverse)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::Helpers::Builders
|
|
2
|
+
SELF_REBINDING_METHODS = %i[ instance_eval instance_exec class_eval class_exec module_eval module_exec ]
|
|
3
|
+
|
|
4
|
+
def self_preserved_between?(node, boundary:)
|
|
5
|
+
SelfPreservation.new(node, boundary:).preserved?
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self_rebinding_boundary?(node)
|
|
9
|
+
SelfBoundary.new(node).rebinds?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def nested_body?(node)
|
|
13
|
+
class_with_body?(node) || module_with_body?(node)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def class_with_body?(node)
|
|
17
|
+
node.class_type? ? node.body.present? : builder_assignment_for(node).builds_class_with_body?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def module_with_body?(node)
|
|
21
|
+
node.module_type? ? node.body.present? : builder_assignment_for(node).builds_module_with_body?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def builder_assignment_for(node)
|
|
26
|
+
RuboCop::Callbacksystems::ClassStructure::BuilderAssignment.new(node)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Whether one node keeps its lexical self before reaching a caller-selected boundary.
|
|
30
|
+
class SelfPreservation
|
|
31
|
+
def initialize(node, boundary:)
|
|
32
|
+
@node = node
|
|
33
|
+
@boundary = boundary
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def preserved?
|
|
37
|
+
ancestors.none? { SelfBoundary.new(it, descendant: node).rebinds_around_descendant? }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
attr_reader :node, :boundary
|
|
42
|
+
|
|
43
|
+
def ancestors
|
|
44
|
+
node.each_ancestor.take_while { !it.equal?(boundary) }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# A lexical construct or block whose body observes a self other than the surrounding expression.
|
|
49
|
+
class SelfBoundary
|
|
50
|
+
include RuboCop::Callbacksystems::Helpers
|
|
51
|
+
|
|
52
|
+
def initialize(node, descendant: nil)
|
|
53
|
+
@node = node
|
|
54
|
+
@descendant = descendant
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def rebinds_around_descendant?
|
|
58
|
+
rebinds? && rebound_parts.any? { contains_descendant?(it) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def rebinds?
|
|
62
|
+
lexical_definition?(node) || (any_block_type?(node) && self_rebinding_block?)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
attr_reader :node, :descendant
|
|
67
|
+
|
|
68
|
+
def self_rebinding_block?
|
|
69
|
+
method_definition_block?(node) || self_rebinding_builder? || self_rebinding_evaluator?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self_rebinding_builder?
|
|
73
|
+
core_constant?(block_call.receiver) &&
|
|
74
|
+
builder_method_for(block_call.receiver.short_name) == block_call.method_name
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def block_call
|
|
78
|
+
@block_call ||= call_of(node)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def builder_method_for(name)
|
|
82
|
+
RuboCop::Callbacksystems::ClassStructure::BuilderAssignment::CLASS_BUILDERS[name] ||
|
|
83
|
+
RuboCop::Callbacksystems::ClassStructure::BuilderAssignment::MODULE_BUILDERS[name]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self_rebinding_evaluator?
|
|
87
|
+
block_call.receiver && !block_call.receiver.self_type? &&
|
|
88
|
+
SELF_REBINDING_METHODS.include?(block_call.method_name)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def rebound_parts
|
|
92
|
+
if any_block_type?(node) || node.any_def_type?
|
|
93
|
+
[ node.arguments, node.body ].compact
|
|
94
|
+
else
|
|
95
|
+
[ node.body ].compact
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def contains_descendant?(part)
|
|
100
|
+
part.equal?(descendant) || part.source_range&.contains?(descendant.source_range)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::Helpers::Comments
|
|
2
|
+
TOOLING_MARKER = %r{
|
|
3
|
+
\A\#\s*
|
|
4
|
+
(?:
|
|
5
|
+
:?[a-z][\w-]*(?::[a-z][\w-]*)+:?
|
|
6
|
+
|
|
|
7
|
+
:[a-z][\w-]*:
|
|
8
|
+
|
|
|
9
|
+
(?:typed|rbs_inline):\s+(?:disabled|enabled|false|ignore|strict|strong|true|weak)
|
|
10
|
+
|
|
|
11
|
+
@(?:dynamic|implements|rbs|type)
|
|
12
|
+
|
|
|
13
|
+
:?(?:nocov|nodoc):?
|
|
14
|
+
|
|
|
15
|
+
[a-z][\w-]*-ignore
|
|
16
|
+
)
|
|
17
|
+
(?:\s|\z)
|
|
18
|
+
}ix
|
|
19
|
+
|
|
20
|
+
def comment_removal_range_for(comment)
|
|
21
|
+
own_line_comment?(comment) ? line_removal_range_for(comment) : trailing_comment_range_for(comment)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def own_line_comment?(comment)
|
|
25
|
+
range = comment.source_range
|
|
26
|
+
range.source_line[0...range.column].strip.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def comment_blocks_in(processed_source, reject_tooling_runs: false)
|
|
30
|
+
RuboCop::Callbacksystems::Source::CommentBlocks.new(processed_source, reject_tooling_runs:)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def prose_comments_in(processed_source)
|
|
34
|
+
processed_source.comments.reject { tooling_comment?(it) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def tooling_comment?(comment)
|
|
38
|
+
comment.text.start_with?("#!") || RuboCop::MagicComment.parse(comment.text).any? ||
|
|
39
|
+
RuboCop::DirectiveComment.new(comment).start_with_marker? || rdoc_directive?(comment) ||
|
|
40
|
+
comment.text.match?(TOOLING_MARKER)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
def trailing_comment_range_for(comment)
|
|
45
|
+
range = comment.source_range
|
|
46
|
+
range.with(begin_pos: range.begin_pos - range.column + code_before(range).rstrip.length)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def code_before(range)
|
|
50
|
+
range.source_line[0...range.column]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def rdoc_directive?(comment)
|
|
54
|
+
comment.text.match?(/\A#(?:--|\+\+)\s*\z/)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::Helpers::Corrections
|
|
2
|
+
# A shorthand keyword's synthetic value shares the key's source range, so replacing that node would rename the key.
|
|
3
|
+
def replace_expression(corrector, node, with:)
|
|
4
|
+
if shorthand_keyword_value?(node)
|
|
5
|
+
corrector.insert_after(node.parent, " #{with}")
|
|
6
|
+
else
|
|
7
|
+
corrector.replace(node, with)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
def shorthand_keyword_value?(node)
|
|
13
|
+
node.parent&.pair_type? && node.equal?(node.parent.value) && node.parent.value_omission?
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::Helpers::Methods
|
|
2
|
+
PARAMETER_TYPES = %i[ arg optarg kwarg kwoptarg ]
|
|
3
|
+
|
|
4
|
+
BUILDER_METHODS = %i[
|
|
5
|
+
new
|
|
6
|
+
create build update
|
|
7
|
+
assign_attributes update_attribute update_column update_columns
|
|
8
|
+
find find_by find_sole_by
|
|
9
|
+
find_or_create_by find_or_initialize_by create_or_find_by
|
|
10
|
+
destroy_by delete_by
|
|
11
|
+
permit expect
|
|
12
|
+
].to_set
|
|
13
|
+
|
|
14
|
+
def builder_method?(node)
|
|
15
|
+
BUILDER_METHODS.include?(node.method_name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def parameter_names_of(method_node)
|
|
19
|
+
parameter_nodes_of(method_node).map { it.children.first.to_s }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def parameter_nodes_of(method_node)
|
|
23
|
+
method_node.arguments.children.select { parameter_node?(it) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def parameter_node?(node)
|
|
27
|
+
node&.type?(*PARAMETER_TYPES)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def inside_initialize?(node)
|
|
31
|
+
node.each_ancestor(:any_def).any? { it.method?(:initialize) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# A definition below another method or a block may belong to a receiver established only at runtime. Attribute only
|
|
35
|
+
# definitions whose path to the nearest named class or module crosses neither boundary.
|
|
36
|
+
def direct_method_definition?(node)
|
|
37
|
+
enclosing_class_or_module_of(node)&.then do |owner|
|
|
38
|
+
node.each_ancestor.take_while { !it.equal?(owner) }
|
|
39
|
+
.none? { it.any_def_type? || any_block_type?(it) }
|
|
40
|
+
end || false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def single_send_private_non_predicate?(method_node)
|
|
44
|
+
method_node.body&.send_type? && private_method?(method_node) && !method_node.predicate_method?
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::Helpers::Names
|
|
2
|
+
def literal_name_of(node)
|
|
3
|
+
node.type?(:sym, :str) ? node.value.to_s : node.source
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def declared_name_of(statement)
|
|
7
|
+
case statement.type
|
|
8
|
+
when :casgn, :lvasgn then statement.name
|
|
9
|
+
when :class, :module then statement.identifier.short_name
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def name_arguments_of(node)
|
|
14
|
+
node.arguments.select { it.type?(:sym, :str) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def class_name_of(node)
|
|
18
|
+
node.class_type? ? node.identifier.source : node.name.to_s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def name_without_sigil(name)
|
|
22
|
+
name.to_s.sub(/\A(?:@@|@|\$)/, "")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def constant_name_of(node)
|
|
26
|
+
if node
|
|
27
|
+
ConstantName.new(node).to_s
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
class ConstantName
|
|
33
|
+
def initialize(node)
|
|
34
|
+
@current = node
|
|
35
|
+
@names = []
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_s
|
|
39
|
+
traverse
|
|
40
|
+
name if complete?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
attr_reader :current, :names
|
|
45
|
+
|
|
46
|
+
def traverse
|
|
47
|
+
while current&.const_type?
|
|
48
|
+
names << current.short_name
|
|
49
|
+
@current = current.namespace
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def complete?
|
|
54
|
+
current.nil? || current.cbase_type?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def name
|
|
58
|
+
"#{prefix}#{names.reverse.join("::")}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def prefix
|
|
62
|
+
"::" if current && names.any?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|