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,59 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
|
|
3
|
+
class RuboCop::Callbacksystems::ProjectIndex::SourceFile
|
|
4
|
+
attr_reader :path
|
|
5
|
+
|
|
6
|
+
delegate :hash, to: :identity
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def for_document(document)
|
|
10
|
+
Rubydex::Location.new(uri: document.uri, start_line: 0, start_column: 0, end_line: 0, end_column: 0)
|
|
11
|
+
.then { new(it.to_file_path) }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(path)
|
|
16
|
+
@path = canonical_path_of(path.to_s)
|
|
17
|
+
@windows = Gem.win_platform?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def eql?(other)
|
|
21
|
+
other.is_a?(self.class) && identity == other.identity
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def source
|
|
25
|
+
Parser::Source::Buffer.new(path).read.source.encode(Encoding::UTF_8).tap do |source|
|
|
26
|
+
raise EncodingError, "Cannot index invalid UTF-8 source: #{path}" unless source.valid_encoding?
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def uri
|
|
31
|
+
"file://#{URI::DEFAULT_PARSER.escape(uri_path)}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
protected
|
|
35
|
+
def identity
|
|
36
|
+
@identity ||= normalized_path.then { windows ? it.downcase : it }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
attr_reader :windows
|
|
41
|
+
|
|
42
|
+
def canonical_path_of(path)
|
|
43
|
+
File.realpath(path)
|
|
44
|
+
rescue SystemCallError
|
|
45
|
+
File.expand_path(path)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def normalized_path
|
|
49
|
+
@normalized_path ||= path.tr("\\", "/")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def uri_path
|
|
53
|
+
if windows && !normalized_path.start_with?("/")
|
|
54
|
+
"/#{normalized_path}"
|
|
55
|
+
else
|
|
56
|
+
normalized_path
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# The source-to-file comparison shared by every cop investigating one ProcessedSource.
|
|
2
|
+
class RuboCop::Callbacksystems::ProjectIndex::SourceFileMatch
|
|
3
|
+
class << self
|
|
4
|
+
def for(processed_source)
|
|
5
|
+
return false unless valid_source?(processed_source.raw_source)
|
|
6
|
+
|
|
7
|
+
snapshot = Snapshot.new(processed_source)
|
|
8
|
+
MUTEX.synchronize do
|
|
9
|
+
entry = CACHE[processed_source]
|
|
10
|
+
|
|
11
|
+
if entry&.fingerprint == snapshot.fingerprint
|
|
12
|
+
entry.matches
|
|
13
|
+
else
|
|
14
|
+
snapshot.matches?.tap { CACHE[processed_source] = Entry.new(snapshot.fingerprint, it) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
rescue
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
CACHE = ObjectSpace::WeakMap.new
|
|
23
|
+
MUTEX = Mutex.new
|
|
24
|
+
Entry = Data.define(:fingerprint, :matches)
|
|
25
|
+
|
|
26
|
+
def valid_source?(source)
|
|
27
|
+
source.encoding == Encoding::UTF_8 && source.valid_encoding?
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
class Snapshot
|
|
33
|
+
attr_reader :fingerprint
|
|
34
|
+
|
|
35
|
+
def initialize(processed_source)
|
|
36
|
+
@source = processed_source.raw_source
|
|
37
|
+
@path = File.expand_path(processed_source.file_path)
|
|
38
|
+
@file_fingerprint = fingerprint_of(File.stat(path))
|
|
39
|
+
@fingerprint = [ path, file_fingerprint ].freeze
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def matches?
|
|
43
|
+
File.binread(path) == source.b && file_fingerprint == fingerprint_of(File.stat(path))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
attr_reader :source, :path, :file_fingerprint
|
|
48
|
+
|
|
49
|
+
def fingerprint_of(stat)
|
|
50
|
+
[ stat.dev, stat.ino, stat.size, *timestamps_of(stat) ].freeze
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def timestamps_of(stat)
|
|
54
|
+
[ stat.mtime.to_i, stat.mtime.nsec, stat.ctime.to_i, stat.ctime.nsec ]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::ProjectIndex::Support
|
|
2
|
+
include RuboCop::Cop::ProjectIndexHelp
|
|
3
|
+
|
|
4
|
+
def external_dependency_checksum
|
|
5
|
+
RuboCop::Callbacksystems::ProjectIndex::Reading.for(project_index).checksum if project_index
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
def project_index_reliable?
|
|
10
|
+
project_index && RuboCop::Callbacksystems::ProjectIndex::Freshness.reliable?(project_index) &&
|
|
11
|
+
source_matches_index? && RuboCop::Callbacksystems::ProjectIndex::Reading.for(project_index).reliable?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def source_matches_index?
|
|
15
|
+
source_matches_disk? ||
|
|
16
|
+
RuboCop::Callbacksystems::ProjectIndex::Freshness.current_source?(project_index, processed_source)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def source_matches_disk?
|
|
20
|
+
RuboCop::Callbacksystems::ProjectIndex::SourceFileMatch.for(processed_source)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def confined_declaration_of(nested_class)
|
|
24
|
+
resolve_constant_in_index(nested_class.identifier).then do |declaration|
|
|
25
|
+
declaration if confined_to_current_file?(declaration, nested_class:)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def confined_to_current_file?(declaration, nested_class:)
|
|
30
|
+
declaration.is_a?(Rubydex::Class) && project_declaration_reliable?(declaration) &&
|
|
31
|
+
definitions_in_other_files(declaration.definitions).empty? && declaration.definitions.one? &&
|
|
32
|
+
RuboCop::Callbacksystems::ProjectIndex::ConfinementCache
|
|
33
|
+
.for(nested_class, declaration:, project_index:).confined?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def project_declaration_reliable?(declaration)
|
|
37
|
+
!project_index_diagnostics.within_definitions?(
|
|
38
|
+
declaration.definitions,
|
|
39
|
+
named: [ "InvalidMethodVisibility" ]
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def project_index_diagnostics
|
|
44
|
+
RuboCop::Callbacksystems::ProjectIndex::Diagnostics.for(project_index)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# The unit the comment cops reason about: consecutive own-line `#` lines starting at the same column, two or more of
|
|
2
|
+
# them, leaving out what addresses the tooling rather than the reader.
|
|
3
|
+
class RuboCop::Callbacksystems::Source::CommentBlocks
|
|
4
|
+
include Enumerable
|
|
5
|
+
include RuboCop::Callbacksystems::Helpers
|
|
6
|
+
|
|
7
|
+
delegate :each, to: :blocks
|
|
8
|
+
|
|
9
|
+
def initialize(processed_source, reject_tooling_runs: false)
|
|
10
|
+
@processed_source = processed_source
|
|
11
|
+
@reject_tooling_runs = reject_tooling_runs
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
attr_reader :processed_source, :reject_tooling_runs
|
|
16
|
+
|
|
17
|
+
def blocks
|
|
18
|
+
comment_runs.flat_map { blocks_in(it) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def comment_runs
|
|
22
|
+
own_line_comments.chunk_while { |previous, comment| continues?(previous, comment) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def own_line_comments
|
|
26
|
+
processed_source.comments.select { it.inline? && own_line_comment?(it) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def continues?(previous, comment)
|
|
30
|
+
comment.source_range.line == previous.source_range.line + 1 &&
|
|
31
|
+
comment.source_range.column == previous.source_range.column
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def blocks_in(run)
|
|
35
|
+
if reject_tooling_runs && run.any? { tooling_comment?(it) }
|
|
36
|
+
[]
|
|
37
|
+
else
|
|
38
|
+
run.reject { tooling_comment?(it) }
|
|
39
|
+
.chunk_while { |previous, comment| continues?(previous, comment) }
|
|
40
|
+
.select(&:many?)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Comments indexed by their position in one source, so asking whether a node contains comments does not walk every
|
|
2
|
+
# comment in the file for every node the cops inspect.
|
|
3
|
+
class RuboCop::Callbacksystems::Source::Comments
|
|
4
|
+
include RuboCop::Callbacksystems::Helpers
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def for(processed_source)
|
|
8
|
+
CACHE_LOCK.synchronize { CACHE[processed_source.comments] ||= new(processed_source.comments) }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
CACHE = ObjectSpace::WeakMap.new
|
|
13
|
+
CACHE_LOCK = Mutex.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(comments)
|
|
17
|
+
@comments = comments.sort_by { it.source_range.begin_pos }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def any_within?(node_or_range)
|
|
21
|
+
first_within(range_of(node_or_range)).present?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def tooling_within?(node_or_range)
|
|
25
|
+
within(node_or_range).any? { tooling_comment?(it) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def within(node_or_range)
|
|
29
|
+
range = range_of(node_or_range)
|
|
30
|
+
CommentsWithin.new(comments, range, from: first_index_at(range.begin_pos)).to_a
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def own_line_comment_on(line)
|
|
34
|
+
own_line_comments_by_line[line]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def trailing_comment_on(line)
|
|
38
|
+
trailing_comments_by_line[line]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def any_on_lines?(lines)
|
|
42
|
+
if lines.begin <= lines.end
|
|
43
|
+
CommentsOnLines.new(comments, lines, from: first_index_on(lines.begin)).any?
|
|
44
|
+
else
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def on_lines(lines)
|
|
50
|
+
if lines.begin <= lines.end
|
|
51
|
+
CommentsOnLines.new(comments, lines, from: first_index_on(lines.begin)).to_a
|
|
52
|
+
else
|
|
53
|
+
[]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
attr_reader :comments
|
|
59
|
+
|
|
60
|
+
def first_within(range)
|
|
61
|
+
comments[first_index_at(range.begin_pos)]&.then { it if range.contains?(it.source_range) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def first_index_at(position)
|
|
65
|
+
begin_positions.bsearch_index { it >= position } || comments.size
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def begin_positions
|
|
69
|
+
@begin_positions ||= comments.map { it.source_range.begin_pos }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def range_of(node_or_range)
|
|
73
|
+
node_or_range.respond_to?(:source_range) ? node_or_range.source_range : node_or_range
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def own_line_comments_by_line
|
|
77
|
+
@own_line_comments_by_line ||= comments.select { own_line_comment?(it) }.index_by { it.loc.line }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def trailing_comments_by_line
|
|
81
|
+
@trailing_comments_by_line ||= comments.reject { own_line_comment?(it) }.index_by { it.loc.line }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def first_index_on(line)
|
|
85
|
+
line_numbers.bsearch_index { it >= line } || comments.size
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def line_numbers
|
|
89
|
+
@line_numbers ||= comments.map { it.loc.line }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class CommentsWithin
|
|
93
|
+
include Enumerable
|
|
94
|
+
|
|
95
|
+
def initialize(comments, range, from:)
|
|
96
|
+
@comments = comments
|
|
97
|
+
@range = range
|
|
98
|
+
@from = from
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def each
|
|
102
|
+
if block_given?
|
|
103
|
+
index = from
|
|
104
|
+
while comments[index] && comments[index].source_range.begin_pos <= range.end_pos
|
|
105
|
+
yield comments[index] if range.contains?(comments[index].source_range)
|
|
106
|
+
index += 1
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
to_enum(__method__)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
attr_reader :comments, :range, :from
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class CommentsOnLines
|
|
118
|
+
include Enumerable
|
|
119
|
+
|
|
120
|
+
def initialize(comments, lines, from:)
|
|
121
|
+
@comments = comments
|
|
122
|
+
@lines = lines
|
|
123
|
+
@from = from
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def each
|
|
127
|
+
if block_given?
|
|
128
|
+
index = from
|
|
129
|
+
while comments[index] && comments[index].loc.line <= lines.end
|
|
130
|
+
yield comments[index]
|
|
131
|
+
index += 1
|
|
132
|
+
end
|
|
133
|
+
else
|
|
134
|
+
to_enum(__method__)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
attr_reader :comments, :lines, :from
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module RuboCop::Callbacksystems::Source::FileAst
|
|
2
|
+
extend self
|
|
3
|
+
|
|
4
|
+
def ast(path, ruby_version:)
|
|
5
|
+
processed_source(path, ruby_version:)&.ast
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def processed_source(path, ruby_version:)
|
|
9
|
+
return unless path && File.exist?(path)
|
|
10
|
+
|
|
11
|
+
RuboCop::AST::ProcessedSource.new(File.read(path), ruby_version, path)
|
|
12
|
+
rescue SystemCallError, EncodingError
|
|
13
|
+
nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
|
|
3
|
+
module RuboCop::Callbacksystems::Source::FilesChecksum
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
def for(patterns, root: Dir.pwd, include_contents: true)
|
|
7
|
+
Contents.new(Array(patterns), File.expand_path(root), include_contents:).checksum
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
class Contents
|
|
12
|
+
def initialize(patterns, root, include_contents:)
|
|
13
|
+
@patterns = patterns
|
|
14
|
+
@root = root
|
|
15
|
+
@include_contents = include_contents
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def checksum
|
|
19
|
+
Digest::SHA256.new.tap { |digest| matching.each { append(it, to: digest) } }.hexdigest
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
attr_reader :patterns, :root, :include_contents
|
|
24
|
+
|
|
25
|
+
def matching
|
|
26
|
+
patterns.flat_map { Dir.glob(File.join(root, it)) }.select { File.file?(it) }.uniq.sort
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def append(path, to:)
|
|
30
|
+
to << relative_path_for(path) << "\0"
|
|
31
|
+
to.file(path) if include_contents?
|
|
32
|
+
to << "\0"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def relative_path_for(path)
|
|
36
|
+
path.delete_prefix("#{root}/")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def include_contents?
|
|
40
|
+
include_contents
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# The one-line form of a call and whether the call belongs on it. Cops measuring this apart end up giving the same call
|
|
2
|
+
# contradictory advice, so they read it here.
|
|
3
|
+
class RuboCop::Callbacksystems::Source::SingleLineCall
|
|
4
|
+
include RuboCop::Callbacksystems::Helpers
|
|
5
|
+
|
|
6
|
+
attr_reader :node
|
|
7
|
+
|
|
8
|
+
def initialize(node, max_line_length)
|
|
9
|
+
@node = node
|
|
10
|
+
@max_line_length = max_line_length
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def fits?
|
|
14
|
+
multiline? && !stays_multiline? && chain_collapsible? && fits_on_one_line?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def source
|
|
18
|
+
@source ||= CollapsedSource.new(node).value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
attr_reader :max_line_length
|
|
23
|
+
delegate :operator_method?, to: :node, private: true
|
|
24
|
+
|
|
25
|
+
def multiline?
|
|
26
|
+
!node.single_line?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def stays_multiline?
|
|
30
|
+
block_call? || operator_method? || link_in_chain? || chain_contains_blocks? || carries_heredoc?(node)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def block_call?
|
|
34
|
+
any_block_type?(node.parent)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def link_in_chain?
|
|
38
|
+
node.parent&.call_type? && node.equal?(node.parent.receiver)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def chain_contains_blocks?(current = node.receiver)
|
|
42
|
+
ReceiverPath.new(current).any? { any_block_type?(it) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def chain_collapsible?
|
|
46
|
+
chain_sends.all? { send_args_single_line?(it) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def chain_sends
|
|
50
|
+
[ node, *ReceiverPath.new(node.receiver).take_while(&:call_type?) ]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def send_args_single_line?(send_node)
|
|
54
|
+
send_node.arguments.all? { argument_single_line?(it) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def argument_single_line?(arg)
|
|
58
|
+
return arg.children.all?(&:single_line?) if implicit_hash?(arg)
|
|
59
|
+
|
|
60
|
+
arg.single_line?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def implicit_hash?(arg)
|
|
64
|
+
arg.hash_type? && !arg.loc.begin
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def fits_on_one_line?
|
|
68
|
+
source.exclude?("\n") && fits_on_line?(node, source, max_line_length)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The receiver nodes leading inward from a call, walked without depending on Ruby's call stack.
|
|
72
|
+
class ReceiverPath
|
|
73
|
+
include Enumerable
|
|
74
|
+
|
|
75
|
+
def initialize(node)
|
|
76
|
+
@node = node
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def each
|
|
80
|
+
if block_given?
|
|
81
|
+
current = node
|
|
82
|
+
while current
|
|
83
|
+
yield current
|
|
84
|
+
current = current.call_type? ? current.receiver : nil
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
to_enum(__method__)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
attr_reader :node
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# The collapsed source of a receiver call chain, assembled from its innermost call outward in one pass.
|
|
96
|
+
class CollapsedSource
|
|
97
|
+
include RuboCop::Callbacksystems::Helpers
|
|
98
|
+
|
|
99
|
+
def initialize(node)
|
|
100
|
+
@node = node
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def value
|
|
104
|
+
fragments.join
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
attr_reader :node
|
|
109
|
+
|
|
110
|
+
def fragments
|
|
111
|
+
calls.reverse_each.with_index.map do |call, index|
|
|
112
|
+
CallFragment.new(call, leading_receiver: index.zero?).value
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def calls
|
|
117
|
+
current = node
|
|
118
|
+
[ current ].then do |chain|
|
|
119
|
+
while current.receiver&.call_type? && !current.receiver.operator_method?
|
|
120
|
+
current = current.receiver
|
|
121
|
+
chain << current
|
|
122
|
+
end
|
|
123
|
+
chain
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class CallFragment
|
|
129
|
+
include RuboCop::Callbacksystems::Helpers
|
|
130
|
+
|
|
131
|
+
def initialize(call, leading_receiver:)
|
|
132
|
+
@call = call
|
|
133
|
+
@leading_receiver = leading_receiver
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def value
|
|
137
|
+
[ receiver_source, dot_source, call.method_name, arguments_suffix ].compact.join
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
attr_reader :call, :leading_receiver
|
|
142
|
+
|
|
143
|
+
# An index or operator call has no selector to rebuild from, so it stays as written.
|
|
144
|
+
def receiver_source
|
|
145
|
+
call.receiver.source.strip if leading_receiver && call.receiver
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def dot_source
|
|
149
|
+
call.loc.dot.source if call.receiver
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def arguments_suffix
|
|
153
|
+
if parenthesized_once_collapsed?
|
|
154
|
+
"(#{arguments_joined})"
|
|
155
|
+
elsif call.arguments?
|
|
156
|
+
" #{arguments_joined}"
|
|
157
|
+
else
|
|
158
|
+
""
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def parenthesized_once_collapsed?
|
|
163
|
+
# A backslash continuation is what lets a builder call go without parentheses, so they come back on collapse.
|
|
164
|
+
call.parenthesized? || (call.arguments? && builder_method?(call))
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def arguments_joined
|
|
168
|
+
call.arguments.flat_map { sources_of(it) }.join(", ")
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def sources_of(argument)
|
|
172
|
+
return argument.children.map { it.source.strip } if implicit_hash?(argument)
|
|
173
|
+
|
|
174
|
+
[ argument.source.strip ]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def implicit_hash?(argument)
|
|
178
|
+
argument.hash_type? && !argument.loc.begin
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|