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,131 @@
|
|
|
1
|
+
# A statement together with the comments that read as its own: the contiguous own-line comments right above it and the
|
|
2
|
+
# comment closing its last line. A rewrite that moves the statement moves them, and a range spanning it spans them.
|
|
3
|
+
class RuboCop::Callbacksystems::Source::StatementWithComments
|
|
4
|
+
include RuboCop::Callbacksystems::Helpers
|
|
5
|
+
|
|
6
|
+
attr_reader :node
|
|
7
|
+
|
|
8
|
+
delegate :source, to: :range
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def sibling_lines_for(processed_source)
|
|
12
|
+
SIBLING_LINES_LOCK.synchronize { SIBLING_LINES[processed_source.comments] ||= SiblingLines.new }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
SIBLING_LINES = ObjectSpace::WeakMap.new
|
|
17
|
+
SIBLING_LINES_LOCK = Mutex.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(node, processed_source)
|
|
21
|
+
@node = node
|
|
22
|
+
@processed_source = processed_source
|
|
23
|
+
@source_comments = RuboCop::Callbacksystems::Source::Comments.for(processed_source)
|
|
24
|
+
@sibling_lines = self.class.sibling_lines_for(processed_source)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def contains_tooling_comment?
|
|
28
|
+
source_comments.tooling_within?(range)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def range
|
|
32
|
+
@range ||= range_between(begin_position, end_position)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def begin_position
|
|
36
|
+
@begin_position ||=
|
|
37
|
+
previous_statement_on_line? ? node.source_range.begin_pos : line_start_position_of(leading_comments.first || node)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
attr_reader :processed_source, :source_comments, :sibling_lines
|
|
42
|
+
|
|
43
|
+
def previous_statement_on_line?
|
|
44
|
+
sibling_lines.previous_on_line?(node)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def leading_comments
|
|
48
|
+
@leading_comments ||= comments_above(node.first_line - 1)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def comments_above(first_line)
|
|
52
|
+
comments = []
|
|
53
|
+
line = first_line
|
|
54
|
+
while (comment = source_comments.own_line_comment_on(line))
|
|
55
|
+
comments << comment
|
|
56
|
+
line -= 1
|
|
57
|
+
end
|
|
58
|
+
comments.reverse
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def end_position
|
|
62
|
+
[ range_through_heredocs(node).end_pos, trailing_comment&.source_range&.end_pos ].compact.max
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def trailing_comment
|
|
66
|
+
source_comments.trailing_comment_on(node.last_line) unless following_statement_on_line?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def following_statement_on_line?
|
|
70
|
+
sibling_lines.following_on_line?(node)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Same-line sibling relationships indexed without retaining AST nodes beyond the processed source's lifetime.
|
|
74
|
+
class SiblingLines
|
|
75
|
+
def initialize
|
|
76
|
+
@relationships = {}
|
|
77
|
+
@indexed_parents = Set.new
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def previous_on_line?(node)
|
|
81
|
+
relationship_for(node).first
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def following_on_line?(node)
|
|
85
|
+
relationship_for(node).last
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
attr_reader :relationships, :indexed_parents
|
|
90
|
+
|
|
91
|
+
def relationship_for(node)
|
|
92
|
+
index(node.parent)
|
|
93
|
+
relationships.fetch(key_of(node)) { [ false, false ] }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def index(parent)
|
|
97
|
+
if parent&.type?(:begin, :kwbegin) && indexed_parents.add?(key_of(parent))
|
|
98
|
+
siblings = parent.each_child_node.to_a
|
|
99
|
+
siblings.each_with_index do |sibling, position|
|
|
100
|
+
relationships[key_of(sibling)] = Relationship.new(siblings, position).to_a
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def key_of(node)
|
|
106
|
+
[ node.type, node.source_range.begin_pos, node.source_range.end_pos ]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class Relationship
|
|
110
|
+
def initialize(siblings, position)
|
|
111
|
+
@siblings = siblings
|
|
112
|
+
@position = position
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def to_a
|
|
116
|
+
[ previous_on_same_line?, following_on_same_line? ]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
attr_reader :siblings, :position
|
|
121
|
+
|
|
122
|
+
def previous_on_same_line?
|
|
123
|
+
position.positive? && siblings[position - 1].last_line == siblings[position].first_line
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def following_on_same_line?
|
|
127
|
+
position < siblings.size - 1 && siblings[position].last_line == siblings[position + 1].first_line
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::Testing::CopHelpers
|
|
2
|
+
extend RuboCop::AST::NodePattern::Macros
|
|
3
|
+
|
|
4
|
+
# @!method test_block?(node)
|
|
5
|
+
def_node_matcher :test_block?, <<~PATTERN
|
|
6
|
+
(any_block (send {nil? (self)} :test (str $_)) ...)
|
|
7
|
+
PATTERN
|
|
8
|
+
|
|
9
|
+
# @!method setup_block?(node)
|
|
10
|
+
def_node_matcher :setup_block?, <<~PATTERN
|
|
11
|
+
(any_block (send {nil? (self)} :setup) ...)
|
|
12
|
+
PATTERN
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
HTTP_METHODS = %i[ get post put patch delete ]
|
|
16
|
+
RESPONSE_ASSERTIONS = %i[ assert_response assert_redirected_to ]
|
|
17
|
+
|
|
18
|
+
def test_blocks(scope = processed_source.ast)
|
|
19
|
+
nodes_in(scope, :any_block).select { test_block?(it) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def setup_blocks(scope = processed_source.ast)
|
|
23
|
+
nodes_in(scope, :any_block).select { setup_block?(it) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def immediate_calls_in(statement)
|
|
27
|
+
immediate_calls_by_statement[statement] ||= RuboCop::Callbacksystems::Execution::Immediate.new(statement)
|
|
28
|
+
.nodes_of_type(:send, :csend)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def immediate_calls_by_statement
|
|
32
|
+
@immediate_calls_by_statement ||= {}.compare_by_identity
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def response_assertion?(send_node)
|
|
36
|
+
call_on_self?(send_node) && RESPONSE_ASSERTIONS.include?(send_node.method_name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def http_request?(send_node)
|
|
40
|
+
call_on_self?(send_node) && HTTP_METHODS.include?(send_node.method_name)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class RuboCop::Callbacksystems::Testing::FixtureCall < Data.define(:node)
|
|
2
|
+
include RuboCop::Callbacksystems::Helpers
|
|
3
|
+
|
|
4
|
+
def valid?
|
|
5
|
+
bare_send?(node) &&
|
|
6
|
+
node.arguments.size == 1 &&
|
|
7
|
+
node.first_argument.sym_type? &&
|
|
8
|
+
fixture_set?
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def signature
|
|
12
|
+
"#{node.method_name}(#{node.first_argument.source})"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def identity
|
|
16
|
+
[ node.method_name, node.first_argument.value ]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
delegate :source_buffer, to: "node.source_range", private: true
|
|
21
|
+
|
|
22
|
+
# A helper reads like a fixture call, so the project's own fixture sets are what tells the two apart.
|
|
23
|
+
def fixture_set?
|
|
24
|
+
RuboCop::Callbacksystems::Testing::FixtureNames.new(file_path, lifetime: source_buffer).include?(node.method_name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def file_path
|
|
28
|
+
source_buffer.name
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# The fixture sets a project defines, read from the yml files under the `test/fixtures` directory of the tree the test
|
|
2
|
+
# file sits in, since a project can hold more than one of those. Rails names the accessor after the path of the file, so
|
|
3
|
+
# `test/fixtures/webhook/deliveries.yml` answers `webhook_deliveries(:name)`, and what sits under `files` stays out
|
|
4
|
+
# because `file_fixture_path` points there.
|
|
5
|
+
class RuboCop::Callbacksystems::Testing::FixtureNames
|
|
6
|
+
def initialize(test_file, lifetime: nil)
|
|
7
|
+
@test_file = test_file
|
|
8
|
+
@lifetime = lifetime
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def include?(name)
|
|
12
|
+
accessors.include?(name.to_s)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
TEST_DIRECTORY = "test"
|
|
17
|
+
FIXTURE_DIRECTORY = "fixtures"
|
|
18
|
+
FILE_FIXTURE_DIRECTORY = "files"
|
|
19
|
+
|
|
20
|
+
# Calls made while one source is being investigated share a walk, while the weak lifetime makes the next
|
|
21
|
+
# investigation read the project again and lets completed ones leave no cache behind.
|
|
22
|
+
ACCESSORS_BY_LIFETIME = ObjectSpace::WeakMap.new
|
|
23
|
+
ACCESSORS_LOCK = Mutex.new
|
|
24
|
+
|
|
25
|
+
attr_reader :test_file, :lifetime
|
|
26
|
+
|
|
27
|
+
def accessors
|
|
28
|
+
@accessors ||= lifetime ? shared_accessors : discovered_accessors
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def shared_accessors
|
|
32
|
+
ACCESSORS_LOCK.synchronize { ACCESSORS_BY_LIFETIME[lifetime] ||= discovered_accessors }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def discovered_accessors
|
|
36
|
+
directory ? accessors_in(directory) : Set[]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def directory
|
|
40
|
+
@directory ||= test_directory&.join(FIXTURE_DIRECTORY)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_directory
|
|
44
|
+
Pathname.new(test_file).ascend.find { it.basename.to_s == TEST_DIRECTORY } if test_file
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def accessors_in(directory)
|
|
48
|
+
Pathname.glob(directory.join("**/*.yml")).reject { file_fixture?(it) }.to_set { accessor_for(it) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def file_fixture?(file)
|
|
52
|
+
relative_path_of(file).each_filename.first == FILE_FIXTURE_DIRECTORY
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def relative_path_of(file)
|
|
56
|
+
file.relative_path_from(directory)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def accessor_for(file)
|
|
60
|
+
relative_path_of(file).sub_ext("").to_s.tr("/", "_")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
class RuboCop::Callbacksystems::Testing::PathMapping
|
|
2
|
+
def initialize(path)
|
|
3
|
+
@path = path
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def source_path
|
|
7
|
+
return @source_path if defined?(@source_path)
|
|
8
|
+
|
|
9
|
+
app_path = source_path_in("app")
|
|
10
|
+
lib_path = source_path_in("lib")
|
|
11
|
+
|
|
12
|
+
@source_path = if app_path != path && File.exist?(app_path)
|
|
13
|
+
app_path
|
|
14
|
+
elsif lib_path != path && File.exist?(lib_path)
|
|
15
|
+
lib_path
|
|
16
|
+
else
|
|
17
|
+
namespaced_gem_source_path
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def gem_project?
|
|
22
|
+
return @gem_project if defined?(@gem_project)
|
|
23
|
+
|
|
24
|
+
@gem_project = Dir.glob(File.join(project_root_path, "*.gemspec")).any?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_path
|
|
28
|
+
return @test_path if defined?(@test_path)
|
|
29
|
+
|
|
30
|
+
@test_path = candidate_test_paths.find { File.exist?(it) }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
attr_reader :path
|
|
35
|
+
|
|
36
|
+
def source_path_in(directory)
|
|
37
|
+
path.sub(%r{(^|/)test/(.+)_test\.rb$}) do
|
|
38
|
+
"#{Regexp.last_match(1)}#{directory}/#{Regexp.last_match(2)}.rb"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def namespaced_gem_source_path
|
|
43
|
+
if gem_project? && path.match?(%r{(^|/)test/.+_test\.rb$})
|
|
44
|
+
relative_source = path.match(%r{(^|/)test/(.+)_test\.rb$}).captures.second
|
|
45
|
+
candidates = Dir.glob(File.join(project_root_path, "lib", "*", "#{relative_source}.rb"))
|
|
46
|
+
candidates.one? ? candidates.first : nil
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def project_root_path
|
|
51
|
+
project_root.empty? ? "." : project_root
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def project_root
|
|
55
|
+
project_directory_match ? matched_project_root : path
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def project_directory_match
|
|
59
|
+
@project_directory_match ||= path.match(%r{\A(?:(.*?)/)?(?:app|lib|test)/})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def matched_project_root
|
|
63
|
+
project_directory_match.captures.first.to_s.presence || absolute_root
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def absolute_root
|
|
67
|
+
path.start_with?(File::SEPARATOR) ? File::SEPARATOR : ""
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def candidate_test_paths
|
|
71
|
+
lib_test_paths + flat_gem_test_paths + app_test_paths
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def lib_test_paths
|
|
75
|
+
test_paths_for("lib")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_paths_for(root)
|
|
79
|
+
if path.match?(%r{(^|/)#{root}/})
|
|
80
|
+
[ rewrite(root, "test/#{root}/"), rewrite(root, "test/") ]
|
|
81
|
+
else
|
|
82
|
+
[]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def rewrite(root, prefix)
|
|
87
|
+
path.sub(%r{(^|/)#{root}/(.+)\.rb$}) { "#{Regexp.last_match(1)}#{prefix}#{Regexp.last_match(2)}_test.rb" }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def flat_gem_test_paths
|
|
91
|
+
if gem_project?
|
|
92
|
+
candidate = path.sub(%r{(^|/)lib/[^/]+/(.+)\.rb$}) do
|
|
93
|
+
"#{Regexp.last_match(1)}test/#{Regexp.last_match(2)}_test.rb"
|
|
94
|
+
end
|
|
95
|
+
candidate == path ? [] : [ candidate ]
|
|
96
|
+
else
|
|
97
|
+
[]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def app_test_paths
|
|
102
|
+
test_paths_for("app").last(1)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Prohibits defining methods directly in ApplicationController.
|
|
2
|
+
#
|
|
3
|
+
# Methods in ApplicationController should be extracted into concerns
|
|
4
|
+
# to keep the base controller clean and maintainable. Include concerns
|
|
5
|
+
# instead of defining methods directly.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# # bad - method defined directly
|
|
9
|
+
# class ApplicationController < ActionController::Base
|
|
10
|
+
# def current_user
|
|
11
|
+
# @current_user ||= User.find(session[:user_id])
|
|
12
|
+
# end
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# # good - use concerns
|
|
16
|
+
# class ApplicationController < ActionController::Base
|
|
17
|
+
# include Authentication
|
|
18
|
+
# include CurrentUser
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # app/controllers/concerns/current_user.rb
|
|
22
|
+
# module CurrentUser
|
|
23
|
+
# extend ActiveSupport::Concern
|
|
24
|
+
#
|
|
25
|
+
# included do
|
|
26
|
+
# helper_method :current_user
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# def current_user
|
|
30
|
+
# @current_user ||= User.find(session[:user_id])
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
class RuboCop::Cop::Callbacksystems::ApplicationControllerMethodDefinition < RuboCop::Cop::Callbacksystems::Base
|
|
35
|
+
MESSAGE = "Don't define methods in ApplicationController. Extract to a concern and include it."
|
|
36
|
+
|
|
37
|
+
def on_def(node)
|
|
38
|
+
if direct_method_definition?(node) && application_controller_class?(enclosing_class_or_module_of(node))
|
|
39
|
+
add_offense(node, message: MESSAGE)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
alias on_defs on_def
|
|
44
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# `assert expected, actual` passes whenever `expected` is truthy, so a
|
|
2
|
+
# two-argument `assert` usually means `assert_equal`. The second argument is
|
|
3
|
+
# legitimate when it reads as a failure message: a string, or a variable or
|
|
4
|
+
# method whose name says so. This replaces `Minitest/AssertWithExpectedArgument`,
|
|
5
|
+
# which only recognizes string literals and the bare names `message` and `msg`.
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# # bad
|
|
9
|
+
# assert(3, my_list.length)
|
|
10
|
+
# assert(expected, actual)
|
|
11
|
+
#
|
|
12
|
+
# # good
|
|
13
|
+
# assert_equal(3, my_list.length)
|
|
14
|
+
# assert foo, "must be present"
|
|
15
|
+
# assert foo, msg
|
|
16
|
+
# assert foo, error_message
|
|
17
|
+
# assert audit.passed?, audit.failure_message
|
|
18
|
+
# assert user.valid?, user.errors
|
|
19
|
+
#
|
|
20
|
+
class RuboCop::Cop::Callbacksystems::AssertWithExpectedArgument < RuboCop::Cop::Callbacksystems::Base
|
|
21
|
+
MESSAGE = "Did you mean to use `assert_equal(%<arguments>s)`?"
|
|
22
|
+
RESTRICT_ON_SEND = %i[ assert ]
|
|
23
|
+
MESSAGE_NAME = /message\z|\Amsg\z/
|
|
24
|
+
BOOLEAN_NODE_TYPES = %i[ and false match_pattern_p or true ]
|
|
25
|
+
BOOLEAN_METHODS = %i[ ! =~ !~ ]
|
|
26
|
+
|
|
27
|
+
# @!method assert_with_two_arguments?(node)
|
|
28
|
+
def_node_matcher :assert_with_two_arguments?, <<~PATTERN
|
|
29
|
+
(call {nil? (self)} :assert $_ $_)
|
|
30
|
+
PATTERN
|
|
31
|
+
|
|
32
|
+
def on_send(node)
|
|
33
|
+
if test_file?(processed_source.file_path)
|
|
34
|
+
assert_with_two_arguments?(node) do |asserted, second_argument|
|
|
35
|
+
unless predicate?(asserted) || message_argument?(second_argument)
|
|
36
|
+
add_offense(node, message: format(MESSAGE, arguments: node.arguments.map(&:source).join(", ")))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
alias on_csend on_send
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
# With a predicate asserted there is no expected/actual confusion, so the second argument is a message by intention.
|
|
46
|
+
def predicate?(argument)
|
|
47
|
+
BOOLEAN_NODE_TYPES.include?(argument.type) ||
|
|
48
|
+
(argument.call_type? && (argument.predicate_method? || argument.comparison_method? ||
|
|
49
|
+
BOOLEAN_METHODS.include?(argument.method_name)))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def message_argument?(argument)
|
|
53
|
+
argument.type?(:str, :dstr) || deferred_callable_block?(argument) || named_like_a_message?(argument)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def named_like_a_message?(argument)
|
|
57
|
+
name = argument.send_type? ? argument.method_name : argument.source
|
|
58
|
+
name.to_s.match?(MESSAGE_NAME)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
class RuboCop::Cop::Callbacksystems::Base < RuboCop::Cop::Base
|
|
2
|
+
include RuboCop::Callbacksystems::Helpers
|
|
3
|
+
|
|
4
|
+
exclude_from_registry
|
|
5
|
+
|
|
6
|
+
def add_offense(node_or_range, message: nil, severity: nil, rewrites_comments: false, &correction)
|
|
7
|
+
if autocorrect? && !flushing_corrections?
|
|
8
|
+
request = CorrectionRequest.new \
|
|
9
|
+
range_from_node_or_range(node_or_range), message:, severity:, correction:, rewrites_comments: rewrites_comments
|
|
10
|
+
if reserve(request.range)
|
|
11
|
+
correction_queue.add(request, enabled: correction_enabled?(request.range))
|
|
12
|
+
end
|
|
13
|
+
else
|
|
14
|
+
super(node_or_range, message:, severity:, &correction)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def on_investigation_end
|
|
19
|
+
flush_corrections
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
def flushing_corrections?
|
|
25
|
+
correction_queue.flushing?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def correction_queue
|
|
29
|
+
@correction_queue ||= CorrectionQueue.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def reserve(range)
|
|
33
|
+
reserved_locations.add?(range)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reserved_locations
|
|
37
|
+
if @reservation_source != processed_source
|
|
38
|
+
@reservation_source = processed_source
|
|
39
|
+
@reserved_locations = Set.new
|
|
40
|
+
end
|
|
41
|
+
@reserved_locations
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def correction_enabled?(range)
|
|
45
|
+
if processed_source.comment_config.respond_to?(:cop_enabled_at_lines?)
|
|
46
|
+
range.first_line.upto(range.last_line).all? { enabled_line?(it) }
|
|
47
|
+
else
|
|
48
|
+
enabled_line?(range.first_line)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def flush_corrections
|
|
53
|
+
correction_queue.flush(processed_source) do |offense, correction|
|
|
54
|
+
add_offense offense.range, message: offense.message, severity: offense.severity, &correction
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def source_comments
|
|
59
|
+
RuboCop::Callbacksystems::Source::Comments.for(processed_source)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def project_root
|
|
63
|
+
@config.base_dir_for_path_parameters
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Measuring against any width but the enforced one leaves two corrections undoing each other.
|
|
67
|
+
def max_line_length
|
|
68
|
+
config.for_cop("Layout/LineLength")["Max"]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def report(analysis, rewrites_comments: false)
|
|
72
|
+
offense = analysis.offense
|
|
73
|
+
add_offense(offense.range, message: offense.message, rewrites_comments:, &offense.correction) if offense
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def report_each(analyses, rewrites_comments: false)
|
|
77
|
+
analyses.each_offense do |offense|
|
|
78
|
+
add_offense(offense.range, message: offense.message, rewrites_comments:, &offense.correction)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class CorrectionRequest
|
|
83
|
+
attr_reader :range, :message, :severity, :correction
|
|
84
|
+
|
|
85
|
+
def initialize(range, message:, severity:, correction:, rewrites_comments:)
|
|
86
|
+
@range = range
|
|
87
|
+
@message = message
|
|
88
|
+
@severity = severity
|
|
89
|
+
@correction = correction
|
|
90
|
+
@rewrites_comments = rewrites_comments
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def batchable?(enabled:)
|
|
94
|
+
correction && enabled
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def rewrites_comments?
|
|
98
|
+
rewrites_comments
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
attr_reader :rewrites_comments
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class CorrectionQueue
|
|
106
|
+
def initialize
|
|
107
|
+
@offenses = []
|
|
108
|
+
@corrections = []
|
|
109
|
+
@flushing = false
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def add(request, enabled:)
|
|
113
|
+
offenses << request
|
|
114
|
+
corrections << request if request.batchable?(enabled:)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def flushing?
|
|
118
|
+
flushing
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def flush(processed_source)
|
|
122
|
+
batch = RuboCop::Callbacksystems::Autocorrection::Batch.new(corrections, processed_source)
|
|
123
|
+
@flushing = true
|
|
124
|
+
offenses.each { yield it, batch.correction_for(it) }
|
|
125
|
+
ensure
|
|
126
|
+
@flushing = false
|
|
127
|
+
offenses.clear
|
|
128
|
+
corrections.clear
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
attr_reader :offenses, :corrections, :flushing
|
|
133
|
+
end
|
|
134
|
+
end
|