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.
Files changed (182) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +78 -0
  4. data/config/default.yml +590 -0
  5. data/docs/cops.md +111 -0
  6. data/lib/rubocop/callbacksystems/autocorrection/batch.rb +135 -0
  7. data/lib/rubocop/callbacksystems/autocorrection/comment_rescue.rb +112 -0
  8. data/lib/rubocop/callbacksystems/autocorrection/rehearsal.rb +65 -0
  9. data/lib/rubocop/callbacksystems/autocorrection/rewrite_permit.rb +14 -0
  10. data/lib/rubocop/callbacksystems/class_structure/builder_assignment.rb +48 -0
  11. data/lib/rubocop/callbacksystems/class_structure/class_body.rb +50 -0
  12. data/lib/rubocop/callbacksystems/class_structure/divergence.rb +31 -0
  13. data/lib/rubocop/callbacksystems/class_structure/node_visibility.rb +82 -0
  14. data/lib/rubocop/callbacksystems/class_structure/reference_order.rb +36 -0
  15. data/lib/rubocop/callbacksystems/class_structure/statement_rank.rb +84 -0
  16. data/lib/rubocop/callbacksystems/class_structure/statement_run.rb +42 -0
  17. data/lib/rubocop/callbacksystems/class_structure/visibility.rb +84 -0
  18. data/lib/rubocop/callbacksystems/cops_document.rb +78 -0
  19. data/lib/rubocop/callbacksystems/execution/deferred_local_variables.rb +138 -0
  20. data/lib/rubocop/callbacksystems/execution/discarded_expression.rb +45 -0
  21. data/lib/rubocop/callbacksystems/execution/eager_evaluation_path.rb +73 -0
  22. data/lib/rubocop/callbacksystems/execution/immediate.rb +71 -0
  23. data/lib/rubocop/callbacksystems/execution/local_variable_occurrences.rb +112 -0
  24. data/lib/rubocop/callbacksystems/hashes/key_transform_detection.rb +108 -0
  25. data/lib/rubocop/callbacksystems/hashes/keyword_options.rb +37 -0
  26. data/lib/rubocop/callbacksystems/helpers/bodies.rb +150 -0
  27. data/lib/rubocop/callbacksystems/helpers/builders.rb +103 -0
  28. data/lib/rubocop/callbacksystems/helpers/comments.rb +56 -0
  29. data/lib/rubocop/callbacksystems/helpers/corrections.rb +15 -0
  30. data/lib/rubocop/callbacksystems/helpers/methods.rb +46 -0
  31. data/lib/rubocop/callbacksystems/helpers/names.rb +65 -0
  32. data/lib/rubocop/callbacksystems/helpers/node_types.rb +154 -0
  33. data/lib/rubocop/callbacksystems/helpers/rails.rb +52 -0
  34. data/lib/rubocop/callbacksystems/helpers/ranges.rb +107 -0
  35. data/lib/rubocop/callbacksystems/helpers/recursion.rb +29 -0
  36. data/lib/rubocop/callbacksystems/helpers/variables.rb +44 -0
  37. data/lib/rubocop/callbacksystems/helpers/visibility.rb +121 -0
  38. data/lib/rubocop/callbacksystems/helpers.rb +43 -0
  39. data/lib/rubocop/callbacksystems/methods/calls.rb +38 -0
  40. data/lib/rubocop/callbacksystems/methods/definitions.rb +218 -0
  41. data/lib/rubocop/callbacksystems/methods/delegate_macro.rb +144 -0
  42. data/lib/rubocop/callbacksystems/methods/delegation_target.rb +55 -0
  43. data/lib/rubocop/callbacksystems/methods/domain.rb +77 -0
  44. data/lib/rubocop/callbacksystems/methods/implicit_invocations.rb +17 -0
  45. data/lib/rubocop/callbacksystems/methods/macro_references.rb +238 -0
  46. data/lib/rubocop/callbacksystems/methods/reference_evidence.rb +74 -0
  47. data/lib/rubocop/callbacksystems/methods/siblings.rb +24 -0
  48. data/lib/rubocop/callbacksystems/methods/statement_definitions.rb +68 -0
  49. data/lib/rubocop/callbacksystems/offense.rb +13 -0
  50. data/lib/rubocop/callbacksystems/plugin.rb +27 -0
  51. data/lib/rubocop/callbacksystems/project_index/cache.rb +16 -0
  52. data/lib/rubocop/callbacksystems/project_index/confinement_cache.rb +40 -0
  53. data/lib/rubocop/callbacksystems/project_index/constant_references.rb +74 -0
  54. data/lib/rubocop/callbacksystems/project_index/corrections.rb +35 -0
  55. data/lib/rubocop/callbacksystems/project_index/coverage.rb +56 -0
  56. data/lib/rubocop/callbacksystems/project_index/diagnostics.rb +60 -0
  57. data/lib/rubocop/callbacksystems/project_index/freshness.rb +104 -0
  58. data/lib/rubocop/callbacksystems/project_index/loader.rb +9 -0
  59. data/lib/rubocop/callbacksystems/project_index/location.rb +29 -0
  60. data/lib/rubocop/callbacksystems/project_index/nested_class_confinement.rb +297 -0
  61. data/lib/rubocop/callbacksystems/project_index/nested_class_method_dispatch.rb +50 -0
  62. data/lib/rubocop/callbacksystems/project_index/reading.rb +129 -0
  63. data/lib/rubocop/callbacksystems/project_index/source_file.rb +59 -0
  64. data/lib/rubocop/callbacksystems/project_index/source_file_match.rb +57 -0
  65. data/lib/rubocop/callbacksystems/project_index/support.rb +46 -0
  66. data/lib/rubocop/callbacksystems/source/comment_blocks.rb +43 -0
  67. data/lib/rubocop/callbacksystems/source/comments.rb +141 -0
  68. data/lib/rubocop/callbacksystems/source/file_ast.rb +15 -0
  69. data/lib/rubocop/callbacksystems/source/files_checksum.rb +43 -0
  70. data/lib/rubocop/callbacksystems/source/single_line_call.rb +181 -0
  71. data/lib/rubocop/callbacksystems/source/statement_with_comments.rb +131 -0
  72. data/lib/rubocop/callbacksystems/testing/cop_helpers.rb +42 -0
  73. data/lib/rubocop/callbacksystems/testing/fixture_call.rb +30 -0
  74. data/lib/rubocop/callbacksystems/testing/fixture_names.rb +62 -0
  75. data/lib/rubocop/callbacksystems/testing/path_mapping.rb +104 -0
  76. data/lib/rubocop/cop/callbacksystems/application_controller_method_definition.rb +44 -0
  77. data/lib/rubocop/cop/callbacksystems/assert_with_expected_argument.rb +60 -0
  78. data/lib/rubocop/cop/callbacksystems/base.rb +134 -0
  79. data/lib/rubocop/cop/callbacksystems/body_order.rb +478 -0
  80. data/lib/rubocop/cop/callbacksystems/builder_method_parentheses.rb +74 -0
  81. data/lib/rubocop/cop/callbacksystems/collapse_multiline_expression.rb +140 -0
  82. data/lib/rubocop/cop/callbacksystems/comment_block_length.rb +137 -0
  83. data/lib/rubocop/cop/callbacksystems/complex_conditional.rb +75 -0
  84. data/lib/rubocop/cop/callbacksystems/controller_test_response_assertion.rb +105 -0
  85. data/lib/rubocop/cop/callbacksystems/controller_test_response_first_assertion.rb +123 -0
  86. data/lib/rubocop/cop/callbacksystems/data_clump.rb +339 -0
  87. data/lib/rubocop/cop/callbacksystems/declarative_method_naming.rb +350 -0
  88. data/lib/rubocop/cop/callbacksystems/delegate_requires_private_option.rb +81 -0
  89. data/lib/rubocop/cop/callbacksystems/eager_loading_scope_naming.rb +386 -0
  90. data/lib/rubocop/cop/callbacksystems/early_return.rb +215 -0
  91. data/lib/rubocop/cop/callbacksystems/empty_line_before_method.rb +168 -0
  92. data/lib/rubocop/cop/callbacksystems/empty_line_between_sections.rb +169 -0
  93. data/lib/rubocop/cop/callbacksystems/empty_private_section.rb +107 -0
  94. data/lib/rubocop/cop/callbacksystems/expanded_empty_class_or_module.rb +71 -0
  95. data/lib/rubocop/cop/callbacksystems/inline_memoized_computation.rb +110 -0
  96. data/lib/rubocop/cop/callbacksystems/job_enqueue_methods.rb +93 -0
  97. data/lib/rubocop/cop/callbacksystems/max_safe_navigation_depth.rb +118 -0
  98. data/lib/rubocop/cop/callbacksystems/method_invocation_order.rb +261 -0
  99. data/lib/rubocop/cop/callbacksystems/nested_class_order.rb +255 -0
  100. data/lib/rubocop/cop/callbacksystems/no_abbreviations.rb +197 -0
  101. data/lib/rubocop/cop/callbacksystems/no_anemic_delegation.rb +101 -0
  102. data/lib/rubocop/cop/callbacksystems/no_anemic_record.rb +309 -0
  103. data/lib/rubocop/cop/callbacksystems/no_assert_select.rb +31 -0
  104. data/lib/rubocop/cop/callbacksystems/no_bang_method_without_counterpart.rb +451 -0
  105. data/lib/rubocop/cop/callbacksystems/no_call_method.rb +73 -0
  106. data/lib/rubocop/cop/callbacksystems/no_comments_in_test_class_body.rb +159 -0
  107. data/lib/rubocop/cop/callbacksystems/no_consecutive_appends.rb +261 -0
  108. data/lib/rubocop/cop/callbacksystems/no_custom_route_actions.rb +124 -0
  109. data/lib/rubocop/cop/callbacksystems/no_direct_eager_loading.rb +90 -0
  110. data/lib/rubocop/cop/callbacksystems/no_each_with_early_return.rb +58 -0
  111. data/lib/rubocop/cop/callbacksystems/no_explicit_public_modifier.rb +81 -0
  112. data/lib/rubocop/cop/callbacksystems/no_hash_with_default_block.rb +68 -0
  113. data/lib/rubocop/cop/callbacksystems/no_local_variable_return.rb +173 -0
  114. data/lib/rubocop/cop/callbacksystems/no_manual_accumulation.rb +188 -0
  115. data/lib/rubocop/cop/callbacksystems/no_mixed_memoization.rb +145 -0
  116. data/lib/rubocop/cop/callbacksystems/no_nested_modules_in_classes.rb +78 -0
  117. data/lib/rubocop/cop/callbacksystems/no_nil_at_end_of_method.rb +74 -0
  118. data/lib/rubocop/cop/callbacksystems/no_redundant_validation_name.rb +37 -0
  119. data/lib/rubocop/cop/callbacksystems/no_redundant_wrapper_method.rb +161 -0
  120. data/lib/rubocop/cop/callbacksystems/no_section_divider_comments.rb +105 -0
  121. data/lib/rubocop/cop/callbacksystems/no_send_in_tests.rb +75 -0
  122. data/lib/rubocop/cop/callbacksystems/no_service_objects.rb +64 -0
  123. data/lib/rubocop/cop/callbacksystems/no_single_letter_names.rb +47 -0
  124. data/lib/rubocop/cop/callbacksystems/no_singleton_class_in_modules.rb +43 -0
  125. data/lib/rubocop/cop/callbacksystems/no_tap_collection_building.rb +173 -0
  126. data/lib/rubocop/cop/callbacksystems/no_turbo_stream_in_controllers.rb +52 -0
  127. data/lib/rubocop/cop/callbacksystems/no_typographic_clutter.rb +159 -0
  128. data/lib/rubocop/cop/callbacksystems/no_writes_in_get_actions.rb +149 -0
  129. data/lib/rubocop/cop/callbacksystems/ordered_mixin_arguments.rb +78 -0
  130. data/lib/rubocop/cop/callbacksystems/plural_controller_names.rb +103 -0
  131. data/lib/rubocop/cop/callbacksystems/prefer_attr_reader_over_instance_variable.rb +86 -0
  132. data/lib/rubocop/cop/callbacksystems/prefer_backslash_continuation.rb +478 -0
  133. data/lib/rubocop/cop/callbacksystems/prefer_class_for_state.rb +532 -0
  134. data/lib/rubocop/cop/callbacksystems/prefer_class_new_for_empty_nested_class.rb +247 -0
  135. data/lib/rubocop/cop/callbacksystems/prefer_class_over_builder_block.rb +124 -0
  136. data/lib/rubocop/cop/callbacksystems/prefer_default_argument.rb +225 -0
  137. data/lib/rubocop/cop/callbacksystems/prefer_delegate.rb +242 -0
  138. data/lib/rubocop/cop/callbacksystems/prefer_delegate_over_instance_variable_assignment.rb +175 -0
  139. data/lib/rubocop/cop/callbacksystems/prefer_excluding.rb +204 -0
  140. data/lib/rubocop/cop/callbacksystems/prefer_extract_associated.rb +54 -0
  141. data/lib/rubocop/cop/callbacksystems/prefer_it_block_parameter.rb +156 -0
  142. data/lib/rubocop/cop/callbacksystems/prefer_many.rb +57 -0
  143. data/lib/rubocop/cop/callbacksystems/prefer_method_over_lambda.rb +211 -0
  144. data/lib/rubocop/cop/callbacksystems/prefer_method_over_variable.rb +172 -0
  145. data/lib/rubocop/cop/callbacksystems/prefer_module_for_static_class.rb +286 -0
  146. data/lib/rubocop/cop/callbacksystems/prefer_ordinal_array_access.rb +136 -0
  147. data/lib/rubocop/cop/callbacksystems/prefer_positive_wrap.rb +265 -0
  148. data/lib/rubocop/cop/callbacksystems/prefer_squish.rb +47 -0
  149. data/lib/rubocop/cop/callbacksystems/prefer_stringify_keys.rb +24 -0
  150. data/lib/rubocop/cop/callbacksystems/prefer_symbolize_keys.rb +24 -0
  151. data/lib/rubocop/cop/callbacksystems/prefer_turbo_page_refresh_broadcasts.rb +40 -0
  152. data/lib/rubocop/cop/callbacksystems/private_constants_in_private_section.rb +160 -0
  153. data/lib/rubocop/cop/callbacksystems/private_delegate_placement.rb +342 -0
  154. data/lib/rubocop/cop/callbacksystems/private_methods_in_private_section.rb +70 -0
  155. data/lib/rubocop/cop/callbacksystems/private_nested_class_method_visibility.rb +269 -0
  156. data/lib/rubocop/cop/callbacksystems/private_nested_classes.rb +94 -0
  157. data/lib/rubocop/cop/callbacksystems/public_methods_must_have_tests.rb +190 -0
  158. data/lib/rubocop/cop/callbacksystems/redundant_concern_extend.rb +157 -0
  159. data/lib/rubocop/cop/callbacksystems/redundant_constant_freeze.rb +57 -0
  160. data/lib/rubocop/cop/callbacksystems/redundant_constant_namespace.rb +134 -0
  161. data/lib/rubocop/cop/callbacksystems/reflow_comments.rb +133 -0
  162. data/lib/rubocop/cop/callbacksystems/repeated_fixture_in_tests.rb +106 -0
  163. data/lib/rubocop/cop/callbacksystems/restricted_controller_actions.rb +85 -0
  164. data/lib/rubocop/cop/callbacksystems/routes_module_scope.rb +336 -0
  165. data/lib/rubocop/cop/callbacksystems/single_line_setup_block.rb +107 -0
  166. data/lib/rubocop/cop/callbacksystems/single_singleton_class.rb +49 -0
  167. data/lib/rubocop/cop/callbacksystems/single_top_level_class_or_module.rb +160 -0
  168. data/lib/rubocop/cop/callbacksystems/single_use_fixture_variable.rb +211 -0
  169. data/lib/rubocop/cop/callbacksystems/single_use_setup_variable.rb +684 -0
  170. data/lib/rubocop/cop/callbacksystems/singular_scope_names.rb +47 -0
  171. data/lib/rubocop/cop/callbacksystems/space_inside_percent_literal.rb +73 -0
  172. data/lib/rubocop/cop/callbacksystems/system_test_browser_driver.rb +30 -0
  173. data/lib/rubocop/cop/callbacksystems/test_method_definition.rb +86 -0
  174. data/lib/rubocop/cop/callbacksystems/test_method_order.rb +319 -0
  175. data/lib/rubocop/cop/callbacksystems/too_many_instance_variables.rb +78 -0
  176. data/lib/rubocop/cop/callbacksystems/too_many_local_variables.rb +77 -0
  177. data/lib/rubocop/cop/callbacksystems/too_many_validation_guards.rb +84 -0
  178. data/lib/rubocop/cop/callbacksystems/unnecessary_local_variable.rb +176 -0
  179. data/lib/rubocop/cop/callbacksystems/unused_private_method_in_nested_class.rb +203 -0
  180. data/lib/rubocop-callbacksystems.rb +21 -0
  181. data/rubocop.yml +685 -0
  182. metadata +337 -0
@@ -0,0 +1,154 @@
1
+ module RuboCop::Callbacksystems::Helpers::NodeTypes
2
+ BLOCK_NODE_TYPES = %i[ block numblock itblock ]
3
+
4
+ def declaration_macro?(node)
5
+ attribute_macro?(node) || delegate_macro?(node)
6
+ end
7
+
8
+ def attribute_macro?(node)
9
+ bare_send?(node) && ATTRIBUTE_MACROS.include?(node.method_name)
10
+ end
11
+
12
+ def bare_send?(node)
13
+ node&.send_type? && node.receiver.nil?
14
+ end
15
+
16
+ def delegate_macro?(node)
17
+ bare_send?(node) && DELEGATE_MACROS.include?(node.method_name)
18
+ end
19
+
20
+ def mixin_macro?(node)
21
+ bare_send?(node) && MIXIN_MACROS.include?(node.method_name)
22
+ end
23
+
24
+ def association_macro?(node)
25
+ bare_send?(node) && ASSOCIATION_MACROS.include?(node.method_name)
26
+ end
27
+
28
+ def call_on_self?(node)
29
+ node&.call_type? && (node.receiver.nil? || node.receiver.self_type?)
30
+ end
31
+
32
+ def singleton_section?(node)
33
+ node&.sclass_type? && node.identifier.self_type?
34
+ end
35
+
36
+ def structural_self?(node)
37
+ (node.parent&.sclass_type? && node.parent.identifier.equal?(node)) ||
38
+ (node.parent&.defs_type? && node.parent.receiver.equal?(node))
39
+ end
40
+
41
+ def definition_identifier?(node)
42
+ node.parent&.type?(:class, :module) && node.parent.identifier.equal?(node)
43
+ end
44
+
45
+ def reads_of(variable_name, within:)
46
+ nodes_in(within, :lvar, :ivar).select { reads_variable?(it, variable_name) }
47
+ end
48
+
49
+ def nodes_in(tree, *types)
50
+ tree ? tree.each_node(*types).to_a : []
51
+ end
52
+
53
+ def reads_variable?(node, variable_name)
54
+ node&.type?(:lvar, :ivar) && node.name == variable_name
55
+ end
56
+
57
+ def definition_nodes_in(tree)
58
+ nodes_in(tree, :class, :module, :sclass)
59
+ end
60
+
61
+ def reads_as_string?(node)
62
+ node && reads_as?(node, literals: STRING_LITERAL_TYPES, methods: STRING_RESULT_METHODS)
63
+ end
64
+
65
+ def reads_as?(node, literals:, methods:)
66
+ literals.include?(node.type) || (node.call_type? && methods.include?(node.method_name))
67
+ end
68
+
69
+ def reads_as_array?(node)
70
+ node&.array_type? || array_construction?(node)
71
+ end
72
+
73
+ def call_of(node)
74
+ any_block_type?(node) ? node.send_node : node
75
+ end
76
+
77
+ def any_block_type?(node)
78
+ node && BLOCK_NODE_TYPES.include?(node.type)
79
+ end
80
+
81
+ def core_constant?(node)
82
+ node&.const_type? && (node.namespace.nil? || node.namespace.cbase_type?)
83
+ end
84
+
85
+ def empty_collection_literal?(node)
86
+ node&.type?(:array, :hash) && node.children.empty?
87
+ end
88
+
89
+ def loop_block?(node)
90
+ node.method?(:loop)
91
+ end
92
+
93
+ def returned_expression_of(node)
94
+ node.return_type? ? node.children.first : node
95
+ end
96
+
97
+ def hash_pairs_of(send_node)
98
+ send_node.arguments.select(&:hash_type?).flat_map(&:pairs)
99
+ end
100
+
101
+ def top_level_definitions_in(node)
102
+ TopLevelDefinitions.new(node).to_a
103
+ end
104
+
105
+ private
106
+ ARRAY_CONSTRUCTION_METHODS = %i[ new [] ]
107
+ STRING_LITERAL_TYPES = %i[ dstr str xstr ]
108
+ STRING_RESULT_METHODS = %i[
109
+ to_s to_str to_json to_param to_query inspect join strip lstrip rstrip squish chomp chop
110
+ gsub sub tr delete_prefix delete_suffix upcase downcase capitalize swapcase titleize humanize
111
+ parameterize dasherize underscore camelize classify tableize demodulize truncate name
112
+ ]
113
+
114
+ ATTRIBUTE_MACROS =
115
+ %i[ attr_reader attr_writer attr_accessor attribute class_attribute has_secure_password has_secure_token ]
116
+ DELEGATE_MACROS = %i[ delegate delegate_missing_to ]
117
+ MIXIN_MACROS = %i[ include extend prepend ]
118
+ ASSOCIATION_MACROS = %i[
119
+ belongs_to has_many has_one has_and_belongs_to_many delegated_type
120
+ has_one_attached has_many_attached has_rich_text accepts_nested_attributes_for
121
+ ]
122
+
123
+ def array_construction?(node)
124
+ node&.send_type? && core_constant?(node.receiver) && node.receiver.short_name == :Array &&
125
+ ARRAY_CONSTRUCTION_METHODS.include?(node.method_name)
126
+ end
127
+
128
+ # Class and module definitions opened by a file, with only top-level begin nodes transparent.
129
+ class TopLevelDefinitions
130
+ include Enumerable
131
+
132
+ def initialize(node)
133
+ @node = node
134
+ end
135
+
136
+ def each
137
+ if block_given?
138
+ pending = [ node ].compact
139
+ until pending.empty?
140
+ current = pending.pop
141
+ case current.type
142
+ when :class, :module then yield current
143
+ when :begin then pending.concat(current.each_child_node.to_a.reverse)
144
+ end
145
+ end
146
+ else
147
+ to_enum(__method__)
148
+ end
149
+ end
150
+
151
+ private
152
+ attr_reader :node
153
+ end
154
+ end
@@ -0,0 +1,52 @@
1
+ module RuboCop::Callbacksystems::Helpers::Rails
2
+ EAGER_LOADING_METHODS = %i[ preload eager_load includes ]
3
+ CONTROLLER_SUPERCLASSES = %w[ ApplicationController ActionController::Base ActionController::API ]
4
+ STANDARD_CONTROLLER_ACTIONS = %i[ index show new create edit update destroy ]
5
+ RAILS_TEST_BASE_CLASSES = %w[
6
+ ActiveSupport::TestCase
7
+ ActionDispatch::IntegrationTest
8
+ ActionController::TestCase
9
+ ActionMailer::TestCase
10
+ ActionView::TestCase
11
+ ActiveJob::TestCase
12
+ ActionCable::TestCase
13
+ ActionCable::Channel::TestCase
14
+ ]
15
+
16
+ def controller_class?(node)
17
+ node&.class_type? && controller_superclass?(node.parent_class)
18
+ end
19
+
20
+ def controller_superclass?(superclass_node)
21
+ name = constant_name_of(superclass_node)&.delete_prefix("::")
22
+ name && (CONTROLLER_SUPERCLASSES.include?(name) || name.end_with?("Controller"))
23
+ end
24
+
25
+ def application_controller_class?(node)
26
+ node&.class_type? && node.identifier.short_name == :ApplicationController
27
+ end
28
+
29
+ def scope_definition?(node)
30
+ bare_send?(node) && node.method?(:scope) && node.first_argument&.sym_type?
31
+ end
32
+
33
+ def rails_test_base_class?(superclass_node)
34
+ RAILS_TEST_BASE_CLASSES.include?(constant_name_of(superclass_node)&.delete_prefix("::"))
35
+ end
36
+
37
+ def routes_file?(path)
38
+ path.present? && (path.include?("config/routes") || path.end_with?("routes.rb"))
39
+ end
40
+
41
+ def controller_file?(path)
42
+ path.present? && (path.split("/").include?("controllers") || path.end_with?("_controller.rb"))
43
+ end
44
+
45
+ def model_file?(path)
46
+ path.present? && path.tr("\\", "/").match?(%r{(?:\A|/)app/models/})
47
+ end
48
+
49
+ def test_file?(path)
50
+ path.present? && path.end_with?("_test.rb")
51
+ end
52
+ end
@@ -0,0 +1,107 @@
1
+ module RuboCop::Callbacksystems::Helpers::Ranges
2
+ include RuboCop::Cop::RangeHelp, RuboCop::Cop::Util
3
+
4
+ def statement_removal_range_for(node)
5
+ inline_statement_removal_range_for(node) || statement_removal_range_of(node.source_range)
6
+ end
7
+
8
+ # The buffer given explicitly is what frees this from a cop's processed source, so plain objects can use it too.
9
+ def statement_removal_range_of(source_range)
10
+ range = range_by_whole_lines(source_range, include_final_newline: true, buffer: source_range.source_buffer)
11
+ blank_line_above(range) || blank_line_below(range) || range
12
+ end
13
+
14
+ # Statements with only blank lines between them share one range, so their removal takes one blank line, not one each.
15
+ def statement_removal_ranges_of(source_ranges)
16
+ source_ranges
17
+ .slice_when { |left, right| code_between?(left, right) }
18
+ .map { statement_removal_range_of(span_of(it)) }
19
+ end
20
+
21
+ def span_of(ranges)
22
+ ranges.first.join(ranges.last)
23
+ end
24
+
25
+ def line_removal_range_for(node)
26
+ line_removal_range_of(node.source_range)
27
+ end
28
+
29
+ def line_removal_range_of(source_range)
30
+ range_by_whole_lines(source_range, include_final_newline: true, buffer: source_range.source_buffer)
31
+ end
32
+
33
+ def indentation_of(node)
34
+ " " * node.source_range.column
35
+ end
36
+
37
+ def call_prefix_of(node)
38
+ node.source_range.with(end_pos: node.loc.selector.begin_pos).source
39
+ end
40
+
41
+ def carries_heredoc?(node)
42
+ node.each_node(:str, :dstr, :xstr).any? { it.loc?(:heredoc_body) }
43
+ end
44
+
45
+ def range_through_heredocs(node)
46
+ node.source_range.with(end_pos: [ node.source_range.end_pos, *heredoc_ends_in(node) ].max)
47
+ end
48
+
49
+ def range_spanning(nodes)
50
+ span_of(nodes.map(&:source_range))
51
+ end
52
+
53
+ def fits_on_line?(node, source, max_line_length)
54
+ node.source_range.column + source.length + trailing_length_of(node) <= max_line_length
55
+ end
56
+
57
+ def trailing_length_of(node)
58
+ ending = node.source_range.end
59
+ ending.source_line.length - ending.column
60
+ end
61
+
62
+ def line_start_position_of(node)
63
+ range = node.source_range
64
+ range.begin_pos - range.column
65
+ end
66
+
67
+ private
68
+ def inline_statement_removal_range_for(node)
69
+ if same_line_siblings?([ node, node.right_sibling ])
70
+ node.source_range.with(end_pos: node.right_sibling.source_range.begin_pos)
71
+ elsif same_line_siblings?([ node.left_sibling, node ])
72
+ node.source_range.with(begin_pos: node.left_sibling.source_range.end_pos)
73
+ end
74
+ end
75
+
76
+ def same_line_siblings?(nodes)
77
+ left, right = nodes
78
+
79
+ left && right && left.parent.type?(:begin, :kwbegin) && left.parent.equal?(right.parent) &&
80
+ left.last_line == right.first_line
81
+ end
82
+
83
+ def blank_line_above(range)
84
+ buffer = range.source_buffer
85
+ line = buffer.line_for_position(range.begin_pos) - 1
86
+ range.with(begin_pos: buffer.line_range(line).begin_pos) if blank_line?(buffer, line)
87
+ end
88
+
89
+ def blank_line?(buffer, line)
90
+ line.between?(1, buffer.last_line) && buffer.source_line(line).match?(/\A[ \t]*\z/)
91
+ end
92
+
93
+ def blank_line_below(range)
94
+ buffer = range.source_buffer
95
+ line = buffer.line_for_position(range.end_pos)
96
+ end_of_blank = [ buffer.line_range(line).end_pos + 1, buffer.source.length ].min
97
+ range.with(end_pos: end_of_blank) if blank_line?(buffer, line)
98
+ end
99
+
100
+ def code_between?(left, right)
101
+ left.end.join(right.begin).source.strip.present?
102
+ end
103
+
104
+ def heredoc_ends_in(node)
105
+ node.each_node(:str, :dstr, :xstr).filter_map { it.loc.heredoc_end.end_pos if it.loc?(:heredoc_end) }
106
+ end
107
+ end
@@ -0,0 +1,29 @@
1
+ module RuboCop::Callbacksystems::Helpers::Recursion
2
+ # A recursion subject is a name passed both bare and as the receiver of a derived value: `walk(node.child, node)`.
3
+ def recursion_subject_names_in(node)
4
+ if node
5
+ calls = node.each_node(:send, :csend)
6
+ calls = [ node ].chain(calls) if node.call_type?
7
+ calls.flat_map { recursion_subjects_in(it) }.to_set
8
+ else
9
+ Set.new
10
+ end
11
+ end
12
+
13
+ def recursion_subjects_in(call)
14
+ receiver_names = derived_receiver_names_in(call)
15
+ direct_argument_names_in(call).select { receiver_names.include?(it) }
16
+ end
17
+
18
+ def derived_receiver_names_in(call)
19
+ call.arguments.filter_map { local_variable_name_of(it.receiver) if it.call_type? }.to_set
20
+ end
21
+
22
+ def local_variable_name_of(argument)
23
+ argument.children.first.to_s if argument&.lvar_type?
24
+ end
25
+
26
+ def direct_argument_names_in(call)
27
+ call.arguments.filter_map { local_variable_name_of(it) }
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ module RuboCop::Callbacksystems::Helpers::Variables
2
+ def deferred_callable_block?(node)
3
+ if any_block_type?(node)
4
+ call = call_of(node)
5
+
6
+ kernel_callable?(call) || proc_constructor?(call)
7
+ else
8
+ false
9
+ end
10
+ end
11
+
12
+ def method_definition_block?(node)
13
+ any_block_type?(node) && METHOD_DEFINITION_CALLS.include?(call_of(node).method_name)
14
+ end
15
+
16
+ def variable_name_of(node)
17
+ node.match_var_type? ? node.children.first : node.name
18
+ end
19
+
20
+ def conditional_return_of_block_argument?(return_node, block:)
21
+ value = return_node.children.first
22
+
23
+ return_node.parent.if_type? && value&.lvar_type? && block.argument_list.any? { it.name == value.name } &&
24
+ !name_rebound_between?(value.name, node: return_node, boundary: block)
25
+ end
26
+
27
+ def name_rebound_between?(name, node:, boundary:)
28
+ node.each_ancestor(:any_block).take_while { !it.equal?(boundary) }.any? do |block|
29
+ block.argument_list.any? { it.respond_to?(:name) && it.name.to_s == name.to_s }
30
+ end
31
+ end
32
+
33
+ private
34
+ METHOD_DEFINITION_CALLS = %i[ define_method define_singleton_method ]
35
+
36
+ def kernel_callable?(call)
37
+ %i[ lambda proc ].include?(call.method_name) &&
38
+ (call.receiver.nil? || (core_constant?(call.receiver) && call.receiver.short_name == :Kernel))
39
+ end
40
+
41
+ def proc_constructor?(call)
42
+ call.method?(:new) && core_constant?(call.receiver) && call.receiver.short_name == :Proc
43
+ end
44
+ end
@@ -0,0 +1,121 @@
1
+ module RuboCop::Callbacksystems::Helpers::Visibility
2
+ LEVELS = %i[ public protected private ]
3
+
4
+ def public_method?(node)
5
+ visibility_for(node).public?
6
+ end
7
+
8
+ def visibility_of(node)
9
+ visibility_for(node).level
10
+ end
11
+
12
+ def visibility_at(node, body)
13
+ visibility_for(node).level_in(body)
14
+ end
15
+
16
+ def enclosing_body_for(node)
17
+ visibility_for(node).enclosing_body
18
+ end
19
+
20
+ def enclosing_definition_of(node)
21
+ visibility_for(node).enclosing_definition
22
+ end
23
+
24
+ def private_modifier_in(body)
25
+ sections_in(body).private_modifier
26
+ end
27
+
28
+ def in_private_section?(node, body)
29
+ sections_in(body).in_private_section?(node)
30
+ end
31
+
32
+ def visibility_applied_to(node, definition)
33
+ if bare_send?(node) && node.arguments.include?(definition)
34
+ wrapped_visibility(node.method_name, definition)
35
+ end
36
+ end
37
+
38
+ def private_method?(node)
39
+ visibility_for(node).private?
40
+ end
41
+
42
+ def protected_method?(node)
43
+ visibility_for(node).protected?
44
+ end
45
+
46
+ def private_non_predicate?(node)
47
+ visibility_for(node).private_non_predicate?
48
+ end
49
+
50
+ def private_nested_class?(class_node)
51
+ enclosing = enclosing_class_or_module_of(class_node)
52
+ enclosing && private_nested_classes_in(enclosing).include?(class_node)
53
+ end
54
+
55
+ def enclosing_class_or_module_of(node)
56
+ node.each_ancestor(:class, :module).first
57
+ end
58
+
59
+ def private_nested_classes_in(class_node)
60
+ sections_in(class_node.body).private_nested_classes
61
+ end
62
+
63
+ def public_methods_in(class_node)
64
+ sections_in(class_node.body).public_method_nodes
65
+ end
66
+
67
+ def private_methods_in(class_node)
68
+ sections_in(class_node.body).private_method_nodes
69
+ end
70
+
71
+ def enclosing_method_of(node)
72
+ node.each_ancestor(:any_def).first
73
+ end
74
+
75
+ def enclosing_scope_of(node)
76
+ node.each_ancestor(:any_def, :any_block).first
77
+ end
78
+
79
+ def sibling_method_nodes_of(node)
80
+ direct_method_nodes_in(enclosing_class_or_module_of(node)&.body)
81
+ end
82
+
83
+ def leaves_public_section?(node)
84
+ %i[ private protected ].include?(visibility_modifier_of(node))
85
+ end
86
+
87
+ def visibility_modifier_of(node)
88
+ if bare_send?(node) && node.arguments.empty?
89
+ node.method_name if LEVELS.include?(node.method_name)
90
+ end
91
+ end
92
+
93
+ def private_constant_names_of(node)
94
+ names_marked_by(node, macro: :private_constant).map { it.value.to_sym }
95
+ end
96
+
97
+ def names_marked_by(node, macro:)
98
+ bare_send?(node) && node.method?(macro) ? name_arguments_of(node) : []
99
+ end
100
+
101
+ private
102
+ def visibility_for(node)
103
+ RuboCop::Callbacksystems::ClassStructure::NodeVisibility.new(node)
104
+ end
105
+
106
+ def sections_in(body)
107
+ RuboCop::Callbacksystems::ClassStructure::ClassBody.new(body)
108
+ end
109
+
110
+ def wrapped_visibility(modifier, definition)
111
+ case modifier
112
+ when *LEVELS then modifier if definition.def_type?
113
+ when :private_class_method then :private if self_singleton_definition?(definition)
114
+ when :public_class_method then :public if self_singleton_definition?(definition)
115
+ end
116
+ end
117
+
118
+ def self_singleton_definition?(definition)
119
+ definition.defs_type? && definition.receiver.self_type?
120
+ end
121
+ end
@@ -0,0 +1,43 @@
1
+ module RuboCop::Callbacksystems::Helpers
2
+ extend self
3
+
4
+ def const_added(name)
5
+ super
6
+ helper = const_get(name)
7
+ Wiring.new(self, helper).connect unless helper.is_a?(Class)
8
+ end
9
+
10
+ private
11
+ class Wiring
12
+ def initialize(helpers, submodule)
13
+ @helpers = helpers
14
+ @submodule = submodule
15
+ end
16
+
17
+ def connect
18
+ if helper_module?(submodule)
19
+ siblings.each { extend_mutually(it) }
20
+ helpers.include(submodule)
21
+ end
22
+ end
23
+
24
+ private
25
+ attr_reader :helpers, :submodule
26
+
27
+ def helper_module?(const)
28
+ const.is_a?(Module) && !const.is_a?(Class)
29
+ end
30
+
31
+ def siblings
32
+ helpers.constants.filter_map do |name|
33
+ candidate = helpers.const_get(name)
34
+ candidate if helper_module?(candidate) && candidate != submodule
35
+ end
36
+ end
37
+
38
+ def extend_mutually(other)
39
+ submodule.extend(other) unless submodule.singleton_class < other
40
+ other.extend(submodule) unless other.singleton_class < submodule
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ # Calls on self indexed by their enclosing class and method domain, so several definitions can ask who calls them
2
+ # without each walking the whole class again.
3
+ class RuboCop::Callbacksystems::Methods::Calls
4
+ include RuboCop::Callbacksystems::Helpers
5
+
6
+ def initialize(ast)
7
+ @ast = ast
8
+ end
9
+
10
+ def named(name, from:)
11
+ domain = RuboCop::Callbacksystems::Methods::Domain.new(from)
12
+ calls_in(domain.container, identity: domain.identity).fetch(name) { [] }
13
+ end
14
+
15
+ private
16
+ attr_reader :ast
17
+
18
+ def calls_in(container, identity:)
19
+ calls_by_container[container]&.fetch(identity, {}) || {}
20
+ end
21
+
22
+ def calls_by_container
23
+ @calls_by_container ||= {}.compare_by_identity.tap do |index|
24
+ nodes_in(ast, :send, :csend).each { add(it, to: index) if call_on_self?(it) }
25
+ end
26
+ end
27
+
28
+ def add(call, to:)
29
+ domain = domain_of_call(call)
30
+ scopes = to[domain.container] ||= {}
31
+ calls = scopes[domain.identity] ||= {}
32
+ (calls[call.method_name] ||= []) << call
33
+ end
34
+
35
+ def domain_of_call(call)
36
+ RuboCop::Callbacksystems::Methods::Domain.new(call)
37
+ end
38
+ end