rubocop-sorbet 0.7.0 → 0.7.1
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 +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/.rubocop.yml +12 -1
- data/.yardopts +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +23 -17
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/bin/rspec +4 -2
- data/bin/rubocop +4 -2
- data/config/default.yml +23 -2
- data/lib/rubocop/cop/sorbet/binding_constant_without_type_alias.rb +105 -0
- data/lib/rubocop/cop/sorbet/callback_conditionals_binding.rb +39 -11
- data/lib/rubocop/cop/sorbet/constants_from_strings.rb +5 -3
- data/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb +27 -23
- data/lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb +26 -27
- data/lib/rubocop/cop/sorbet/forbid_t_unsafe.rb +7 -3
- data/lib/rubocop/cop/sorbet/forbid_t_untyped.rb +7 -3
- data/lib/rubocop/cop/sorbet/forbid_untyped_struct_props.rb +13 -11
- data/lib/rubocop/cop/sorbet/implicit_conversion_method.rb +56 -0
- data/lib/rubocop/cop/sorbet/mixin/target_sorbet_version.rb +49 -0
- data/lib/rubocop/cop/sorbet/mutable_constant_sorbet_aware_behaviour.rb +8 -5
- data/lib/rubocop/cop/sorbet/obsolete_strict_memoization.rb +92 -0
- data/lib/rubocop/cop/sorbet/one_ancestor_per_line.rb +8 -3
- data/lib/rubocop/cop/sorbet/rbi/forbid_extend_t_sig_helpers_in_shims.rb +12 -18
- data/lib/rubocop/cop/sorbet/rbi/forbid_rbi_outside_of_allowed_paths.rb +8 -7
- data/lib/rubocop/cop/sorbet/rbi/single_line_rbi_class_module_definitions.rb +12 -18
- data/lib/rubocop/cop/sorbet/redundant_extend_t_sig.rb +5 -6
- data/lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb +3 -2
- data/lib/rubocop/cop/sorbet/sigils/enforce_single_sigil.rb +6 -5
- data/lib/rubocop/cop/sorbet/sigils/false_sigil.rb +1 -1
- data/lib/rubocop/cop/sorbet/sigils/has_sigil.rb +1 -1
- data/lib/rubocop/cop/sorbet/sigils/ignore_sigil.rb +1 -1
- data/lib/rubocop/cop/sorbet/sigils/strict_sigil.rb +1 -1
- data/lib/rubocop/cop/sorbet/sigils/strong_sigil.rb +1 -1
- data/lib/rubocop/cop/sorbet/sigils/true_sigil.rb +1 -1
- data/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb +50 -25
- data/lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb +19 -30
- data/lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb +3 -2
- data/lib/rubocop/cop/sorbet/signatures/empty_line_after_sig.rb +3 -3
- data/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb +8 -4
- data/lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb +5 -4
- data/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb +8 -3
- data/lib/rubocop/cop/sorbet/signatures/signature_cop.rb +6 -1
- data/lib/rubocop/cop/sorbet/type_alias_name.rb +10 -17
- data/lib/rubocop/cop/sorbet_cops.rb +6 -1
- data/lib/rubocop/sorbet/inject.rb +9 -7
- data/lib/rubocop/sorbet/version.rb +2 -1
- data/lib/rubocop/sorbet.rb +1 -0
- data/manual/cops.md +2 -0
- data/manual/cops_sorbet.md +137 -31
- data/rubocop-sorbet.gemspec +1 -0
- data/tasks/cops_documentation.rake +16 -5
- metadata +8 -4
- data/lib/rubocop/cop/sorbet/binding_constants_without_type_alias.rb +0 -127
@@ -6,7 +6,7 @@ require "rubocop"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Disallows use of `T.untyped` or `T.nilable(T.untyped)`
|
10
10
|
# as a prop type for `T::Struct` or `T::ImmutableStruct`.
|
11
11
|
#
|
12
12
|
# @example
|
@@ -22,38 +22,40 @@ module RuboCop
|
|
22
22
|
# const :foo, Integer
|
23
23
|
# prop :bar, T.nilable(String)
|
24
24
|
# end
|
25
|
-
class ForbidUntypedStructProps < RuboCop::Cop::
|
25
|
+
class ForbidUntypedStructProps < RuboCop::Cop::Base
|
26
26
|
MSG = "Struct props cannot be T.untyped"
|
27
27
|
|
28
|
+
# @!method t_struct(node)
|
28
29
|
def_node_matcher :t_struct, <<~PATTERN
|
29
|
-
(const (const nil? :T) :Struct)
|
30
|
-
PATTERN
|
31
|
-
|
32
|
-
def_node_matcher :t_immutable_struct, <<~PATTERN
|
33
|
-
(const (const nil? :T) :ImmutableStruct)
|
30
|
+
(const (const nil? :T) {:Struct :ImmutableStruct})
|
34
31
|
PATTERN
|
35
32
|
|
33
|
+
# @!method t_untyped(node)
|
36
34
|
def_node_matcher :t_untyped, <<~PATTERN
|
37
35
|
(send (const nil? :T) :untyped)
|
38
36
|
PATTERN
|
39
37
|
|
38
|
+
# @!method t_nilable_untyped(node)
|
40
39
|
def_node_matcher :t_nilable_untyped, <<~PATTERN
|
41
40
|
(send (const nil? :T) :nilable {#t_untyped #t_nilable_untyped})
|
42
41
|
PATTERN
|
43
42
|
|
43
|
+
# @!method subclass_of_t_struct?(node)
|
44
44
|
def_node_matcher :subclass_of_t_struct?, <<~PATTERN
|
45
|
-
(class (const ...)
|
45
|
+
(class (const ...) #t_struct ...)
|
46
46
|
PATTERN
|
47
47
|
|
48
|
+
# @!method untyped_props(node)
|
49
|
+
# Search for untyped prop/const declarations and capture their types
|
48
50
|
def_node_search :untyped_props, <<~PATTERN
|
49
|
-
(send nil? {:prop :const} _ {#t_untyped #t_nilable_untyped} ...)
|
51
|
+
(send nil? {:prop :const} _ ${#t_untyped #t_nilable_untyped} ...)
|
50
52
|
PATTERN
|
51
53
|
|
52
54
|
def on_class(node)
|
53
55
|
return unless subclass_of_t_struct?(node)
|
54
56
|
|
55
|
-
untyped_props(node).each do |
|
56
|
-
add_offense(
|
57
|
+
untyped_props(node).each do |prop_type|
|
58
|
+
add_offense(prop_type)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubocop"
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module Sorbet
|
8
|
+
# Disallows declaring implicit conversion methods.
|
9
|
+
# Since Sorbet is a nominal (not structural) type system,
|
10
|
+
# implicit conversion is currently unsupported.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# def to_str; end
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# def to_str(x); end
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# def self.to_str; end
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# def self.to_str(x); end
|
25
|
+
#
|
26
|
+
# # bad
|
27
|
+
# alias to_str to_s
|
28
|
+
#
|
29
|
+
# @see https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html
|
30
|
+
# @note Since the arity of aliased methods is not checked, false positives may result.
|
31
|
+
class ImplicitConversionMethod < RuboCop::Cop::Base
|
32
|
+
IMPLICIT_CONVERSION_METHODS = [:to_ary, :to_int, :to_hash, :to_str].freeze
|
33
|
+
MSG = "Avoid implicit conversion methods, as Sorbet does not support them. " \
|
34
|
+
"Explicity convert to the desired type instead."
|
35
|
+
RESTRICT_ON_SEND = [:alias_method].freeze
|
36
|
+
|
37
|
+
def on_alias(node)
|
38
|
+
new_id = node.new_identifier
|
39
|
+
add_offense(new_id) if IMPLICIT_CONVERSION_METHODS.include?(new_id.value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_def(node)
|
43
|
+
return unless IMPLICIT_CONVERSION_METHODS.include?(node.method_name)
|
44
|
+
return unless node.arguments.empty?
|
45
|
+
|
46
|
+
add_offense(node)
|
47
|
+
end
|
48
|
+
alias_method :on_defs, :on_def
|
49
|
+
|
50
|
+
def on_send(node)
|
51
|
+
add_offense(node.first_argument) if IMPLICIT_CONVERSION_METHODS.include?(node.first_argument.value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Sorbet
|
6
|
+
module TargetSorbetVersion
|
7
|
+
class << self
|
8
|
+
def included(target)
|
9
|
+
target.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# The version of the Sorbet static type checker required by this cop
|
15
|
+
def minimum_target_sorbet_static_version(version)
|
16
|
+
@minimum_target_sorbet_static_version = Gem::Version.new(version)
|
17
|
+
end
|
18
|
+
|
19
|
+
def support_target_sorbet_static_version?(version)
|
20
|
+
@minimum_target_sorbet_static_version <= Gem::Version.new(version)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def enabled_for_sorbet_static_version?
|
25
|
+
sorbet_static_version = target_sorbet_static_version_from_bundler_lock_file
|
26
|
+
return false unless sorbet_static_version
|
27
|
+
|
28
|
+
self.class.support_target_sorbet_static_version?(sorbet_static_version)
|
29
|
+
end
|
30
|
+
|
31
|
+
def target_sorbet_static_version_from_bundler_lock_file
|
32
|
+
# Do memoization with the `defined?` pattern since sorbet-static version might be `nil`
|
33
|
+
if defined?(@target_sorbet_static_version_from_bundler_lock_file)
|
34
|
+
@target_sorbet_static_version_from_bundler_lock_file
|
35
|
+
else
|
36
|
+
@target_sorbet_static_version_from_bundler_lock_file = read_sorbet_static_version_from_bundler_lock_file
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def read_sorbet_static_version_from_bundler_lock_file
|
41
|
+
require "bundler"
|
42
|
+
::Bundler.locked_gems.specs.find { |spec| spec.name == "sorbet-static" }&.version
|
43
|
+
rescue LoadError, Bundler::GemfileNotFound
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -6,10 +6,13 @@ module RuboCop
|
|
6
6
|
module Cop
|
7
7
|
module Sorbet
|
8
8
|
module MutableConstantSorbetAwareBehaviour
|
9
|
-
|
10
|
-
base
|
11
|
-
|
12
|
-
|
9
|
+
class << self
|
10
|
+
def prepended(base)
|
11
|
+
# @!method t_let(node)
|
12
|
+
base.def_node_matcher(:t_let, <<~PATTERN)
|
13
|
+
(send (const nil? :T) :let $_constant _type)
|
14
|
+
PATTERN
|
15
|
+
end
|
13
16
|
end
|
14
17
|
|
15
18
|
def on_assignment(value)
|
@@ -25,5 +28,5 @@ module RuboCop
|
|
25
28
|
end
|
26
29
|
|
27
30
|
RuboCop::Cop::Style::MutableConstant.prepend(
|
28
|
-
RuboCop::Cop::Sorbet::MutableConstantSorbetAwareBehaviour
|
31
|
+
RuboCop::Cop::Sorbet::MutableConstantSorbetAwareBehaviour,
|
29
32
|
)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubocop"
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module Sorbet
|
8
|
+
# Checks for the obsolete pattern for initializing instance variables that was required for older Sorbet
|
9
|
+
# versions in `#typed: strict` files.
|
10
|
+
#
|
11
|
+
# It's no longer required, as of Sorbet 0.5.10210
|
12
|
+
# See https://sorbet.org/docs/type-assertions#put-type-assertions-behind-memoization
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# sig { returns(Foo) }
|
18
|
+
# def foo
|
19
|
+
# @foo = T.let(@foo, T.nilable(Foo))
|
20
|
+
# @foo ||= Foo.new
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # bad
|
24
|
+
# sig { returns(Foo) }
|
25
|
+
# def foo
|
26
|
+
# # This would have been a mistake, causing the memoized value to be discarded and recomputed on every call.
|
27
|
+
# @foo = T.let(nil, T.nilable(Foo))
|
28
|
+
# @foo ||= Foo.new
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# sig { returns(Foo) }
|
33
|
+
# def foo
|
34
|
+
# @foo ||= T.let(Foo.new, T.nilable(Foo))
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
class ObsoleteStrictMemoization < RuboCop::Cop::Base
|
38
|
+
include RuboCop::Cop::MatchRange
|
39
|
+
include RuboCop::Cop::Alignment
|
40
|
+
include RuboCop::Cop::LineLengthHelp
|
41
|
+
include RuboCop::Cop::RangeHelp
|
42
|
+
extend AutoCorrector
|
43
|
+
|
44
|
+
include TargetSorbetVersion
|
45
|
+
minimum_target_sorbet_static_version "0.5.10210"
|
46
|
+
|
47
|
+
MSG = "This two-stage workaround for memoization in `#typed: strict` files is no longer necessary. " \
|
48
|
+
"See https://sorbet.org/docs/type-assertions#put-type-assertions-behind-memoization."
|
49
|
+
|
50
|
+
# @!method legacy_memoization_pattern?(node)
|
51
|
+
def_node_matcher :legacy_memoization_pattern?, <<~PATTERN
|
52
|
+
(begin
|
53
|
+
... # Ignore any other lines that come first.
|
54
|
+
$(ivasgn $_ivar # First line: @_ivar = ...
|
55
|
+
(send # T.let(_ivar, T.nilable(_ivar_type))
|
56
|
+
$(const {nil? cbase} :T) :let
|
57
|
+
{(ivar _ivar) nil}
|
58
|
+
(send (const {nil? cbase} :T) :nilable $_ivar_type))) # T.nilable(_ivar_type)
|
59
|
+
$(or-asgn (ivasgn _ivar) $_initialization_expr)) # Second line: @_ivar ||= _initialization_expr
|
60
|
+
PATTERN
|
61
|
+
|
62
|
+
def on_begin(node)
|
63
|
+
legacy_memoization_pattern?(node) do |first_asgn_node, ivar, t, ivar_type, second_or_asgn_node, init_expr| # rubocop:disable Metrics/ParameterLists
|
64
|
+
add_offense(first_asgn_node) do |corrector|
|
65
|
+
indent = offset(node)
|
66
|
+
correction = "#{ivar} ||= #{t.source}.let(#{init_expr.source}, #{t.source}.nilable(#{ivar_type.source}))"
|
67
|
+
|
68
|
+
# We know good places to put line breaks, if required.
|
69
|
+
if line_length(indent + correction) > max_line_length || correction.include?("\n")
|
70
|
+
correction = <<~RUBY.chomp
|
71
|
+
#{ivar} ||= #{t.source}.let(
|
72
|
+
#{indent} #{init_expr.source.gsub("\n", "\n#{indent}")},
|
73
|
+
#{indent} #{t.source}.nilable(#{ivar_type.source.gsub("\n", "\n#{indent}")}),
|
74
|
+
#{indent})
|
75
|
+
RUBY
|
76
|
+
end
|
77
|
+
|
78
|
+
corrector.replace(
|
79
|
+
range_between(first_asgn_node.source_range.begin_pos, second_or_asgn_node.source_range.end_pos),
|
80
|
+
correction,
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def relevant_file?(file)
|
87
|
+
super && enabled_for_sorbet_static_version?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -6,7 +6,7 @@ require "rubocop"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Ensures one ancestor per requires_ancestor line
|
10
10
|
# rather than chaining them as a comma-separated list.
|
11
11
|
#
|
12
12
|
# @example
|
@@ -21,17 +21,20 @@ module RuboCop
|
|
21
21
|
# requires_ancestor Kernel
|
22
22
|
# requires_ancestor Minitest::Assertions
|
23
23
|
# end
|
24
|
-
class OneAncestorPerLine < RuboCop::Cop::Cop
|
24
|
+
class OneAncestorPerLine < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
|
25
25
|
MSG = "Cannot require more than one ancestor per line"
|
26
26
|
|
27
|
+
# @!method requires_ancestors(node)
|
27
28
|
def_node_search :requires_ancestors, <<~PATTERN
|
28
29
|
(send nil? :requires_ancestor ...)
|
29
30
|
PATTERN
|
30
31
|
|
32
|
+
# @!method more_than_one_ancestor(node)
|
31
33
|
def_node_matcher :more_than_one_ancestor, <<~PATTERN
|
32
34
|
(send nil? :requires_ancestor const const+)
|
33
35
|
PATTERN
|
34
36
|
|
37
|
+
# @!method abstract?(node)
|
35
38
|
def_node_search :abstract?, <<~PATTERN
|
36
39
|
(send nil? :abstract!)
|
37
40
|
PATTERN
|
@@ -39,17 +42,19 @@ module RuboCop
|
|
39
42
|
def on_module(node)
|
40
43
|
return unless node.body
|
41
44
|
return unless requires_ancestors(node)
|
45
|
+
|
42
46
|
process_node(node)
|
43
47
|
end
|
44
48
|
|
45
49
|
def on_class(node)
|
46
50
|
return unless abstract?(node)
|
47
51
|
return unless requires_ancestors(node)
|
52
|
+
|
48
53
|
process_node(node)
|
49
54
|
end
|
50
55
|
|
51
56
|
def autocorrect(node)
|
52
|
-
->
|
57
|
+
->(corrector) do
|
53
58
|
ra_call = node.parent
|
54
59
|
split_ra_calls = ra_call.source.gsub(/,\s+/, new_ra_line(ra_call.loc.column))
|
55
60
|
corrector.replace(ra_call, split_ra_calls)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Sorbet
|
6
|
-
#
|
6
|
+
# Ensures RBI shims do not include a call to extend T::Sig
|
7
7
|
# or to extend T::Helpers
|
8
8
|
#
|
9
9
|
# @example
|
@@ -22,30 +22,24 @@ module RuboCop
|
|
22
22
|
# sig { returns(String) }
|
23
23
|
# def foo; end
|
24
24
|
# end
|
25
|
-
class ForbidExtendTSigHelpersInShims < RuboCop::Cop::
|
25
|
+
class ForbidExtendTSigHelpersInShims < RuboCop::Cop::Base
|
26
|
+
extend AutoCorrector
|
26
27
|
include RangeHelp
|
27
28
|
|
28
29
|
MSG = "Extending T::Sig or T::Helpers in a shim is unnecessary"
|
29
|
-
RESTRICT_ON_SEND = [:extend]
|
30
|
+
RESTRICT_ON_SEND = [:extend].freeze
|
30
31
|
|
31
|
-
|
32
|
-
|
32
|
+
# @!method extend_t_sig_or_helpers?(node)
|
33
|
+
def_node_matcher :extend_t_sig_or_helpers?, <<~PATTERN
|
34
|
+
(send nil? :extend (const (const nil? :T) {:Sig :Helpers}))
|
33
35
|
PATTERN
|
34
36
|
|
35
|
-
def_node_matcher :extend_t_helpers?, <<~PATTERN
|
36
|
-
(send nil? :extend (const (const nil? :T) :Helpers))
|
37
|
-
PATTERN
|
38
|
-
|
39
|
-
def autocorrect(node)
|
40
|
-
-> (corrector) do
|
41
|
-
corrector.remove(
|
42
|
-
range_by_whole_lines(node.source_range, include_final_newline: true)
|
43
|
-
)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
37
|
def on_send(node)
|
48
|
-
|
38
|
+
extend_t_sig_or_helpers?(node) do
|
39
|
+
add_offense(node) do |corrector|
|
40
|
+
corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
|
41
|
+
end
|
42
|
+
end
|
49
43
|
end
|
50
44
|
end
|
51
45
|
end
|
@@ -5,7 +5,7 @@ require "pathname"
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Sorbet
|
8
|
-
#
|
8
|
+
# Makes sure that RBI files are always located under the defined allowed paths.
|
9
9
|
#
|
10
10
|
# Options:
|
11
11
|
#
|
@@ -20,7 +20,7 @@ module RuboCop
|
|
20
20
|
# # rbi/external_interface.rbi
|
21
21
|
# # sorbet/rbi/some_file.rbi
|
22
22
|
# # sorbet/rbi/any/path/for/file.rbi
|
23
|
-
class ForbidRBIOutsideOfAllowedPaths < RuboCop::Cop::Cop
|
23
|
+
class ForbidRBIOutsideOfAllowedPaths < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
|
24
24
|
include RangeHelp
|
25
25
|
|
26
26
|
def investigate(processed_source)
|
@@ -30,14 +30,14 @@ module RuboCop
|
|
30
30
|
add_offense(
|
31
31
|
nil,
|
32
32
|
location: source_range(processed_source.buffer, 1, 0),
|
33
|
-
message: "AllowedPaths expects an array"
|
33
|
+
message: "AllowedPaths expects an array",
|
34
34
|
)
|
35
35
|
return
|
36
36
|
elsif paths.empty?
|
37
37
|
add_offense(
|
38
38
|
nil,
|
39
39
|
location: source_range(processed_source.buffer, 1, 0),
|
40
|
-
message: "AllowedPaths cannot be empty"
|
40
|
+
message: "AllowedPaths cannot be empty",
|
41
41
|
)
|
42
42
|
return
|
43
43
|
end
|
@@ -49,15 +49,16 @@ module RuboCop
|
|
49
49
|
add_offense(
|
50
50
|
nil,
|
51
51
|
location: source_range(processed_source.buffer, 1, 0),
|
52
|
-
message: "RBI file path should match one of: #{paths.join(", ")}"
|
52
|
+
message: "RBI file path should match one of: #{paths.join(", ")}",
|
53
53
|
) if paths.none? { |pattern| File.fnmatch(pattern, rel_path) }
|
54
54
|
end
|
55
55
|
|
56
56
|
private
|
57
57
|
|
58
58
|
def allowed_paths
|
59
|
-
paths = cop_config["AllowedPaths"]
|
60
|
-
return
|
59
|
+
paths = cop_config["AllowedPaths"] # rubocop:todo InternalAffairs/UndefinedConfig
|
60
|
+
return unless paths.is_a?(Array)
|
61
|
+
|
61
62
|
paths.compact
|
62
63
|
end
|
63
64
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Sorbet
|
6
|
-
#
|
6
|
+
# Ensures empty class/module definitions in RBI files are
|
7
7
|
# done on a single line rather than being split across multiple lines.
|
8
8
|
#
|
9
9
|
# @example
|
@@ -14,32 +14,26 @@ module RuboCop
|
|
14
14
|
#
|
15
15
|
# # good
|
16
16
|
# module SomeModule; end
|
17
|
-
class SingleLineRbiClassModuleDefinitions < RuboCop::Cop::
|
17
|
+
class SingleLineRbiClassModuleDefinitions < RuboCop::Cop::Base
|
18
|
+
extend AutoCorrector
|
19
|
+
|
18
20
|
MSG = "Empty class/module definitions in RBI files should be on a single line."
|
19
21
|
|
20
22
|
def on_module(node)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def on_class(node)
|
25
|
-
process_node(node)
|
26
|
-
end
|
23
|
+
return if node.body
|
24
|
+
return if node.single_line?
|
27
25
|
|
28
|
-
|
29
|
-
|
26
|
+
add_offense(node) do |corrector|
|
27
|
+
corrector.replace(node, convert_newlines_to_semicolons(node.source))
|
28
|
+
end
|
30
29
|
end
|
30
|
+
alias_method :on_class, :on_module
|
31
31
|
|
32
|
-
|
32
|
+
private
|
33
33
|
|
34
|
-
def
|
34
|
+
def convert_newlines_to_semicolons(source)
|
35
35
|
source.sub(/[\r\n]+\s*[\r\n]*/, "; ")
|
36
36
|
end
|
37
|
-
|
38
|
-
def process_node(node)
|
39
|
-
return if node.body
|
40
|
-
return if node.single_line?
|
41
|
-
add_offense(node)
|
42
|
-
end
|
43
37
|
end
|
44
38
|
end
|
45
39
|
end
|
@@ -25,10 +25,13 @@ module RuboCop
|
|
25
25
|
# def no_op; end
|
26
26
|
# end
|
27
27
|
#
|
28
|
-
class RedundantExtendTSig < RuboCop::Cop::
|
28
|
+
class RedundantExtendTSig < RuboCop::Cop::Base
|
29
|
+
extend AutoCorrector
|
30
|
+
|
29
31
|
MSG = "Do not redundantly `extend T::Sig` when it is already included in all modules."
|
30
32
|
RESTRICT_ON_SEND = [:extend].freeze
|
31
33
|
|
34
|
+
# @!method extend_t_sig?(node)
|
32
35
|
def_node_matcher :extend_t_sig?, <<~PATTERN
|
33
36
|
(send _ :extend (const (const {nil? | cbase} :T) :Sig))
|
34
37
|
PATTERN
|
@@ -36,11 +39,7 @@ module RuboCop
|
|
36
39
|
def on_send(node)
|
37
40
|
return unless extend_t_sig?(node)
|
38
41
|
|
39
|
-
add_offense(node)
|
40
|
-
end
|
41
|
-
|
42
|
-
def autocorrect(node)
|
43
|
-
lambda do |corrector|
|
42
|
+
add_offense(node) do |corrector|
|
44
43
|
corrector.remove(node)
|
45
44
|
end
|
46
45
|
end
|
@@ -5,7 +5,7 @@ require "rubocop"
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Sorbet
|
8
|
-
#
|
8
|
+
# Checks that the Sorbet sigil comes as the first magic comment in the file.
|
9
9
|
#
|
10
10
|
# The expected order for magic comments is: (en)?coding, typed, warn_indent then frozen_string_literal.
|
11
11
|
#
|
@@ -57,6 +57,7 @@ module RuboCop
|
|
57
57
|
(lines.min...lines.max).each do |line|
|
58
58
|
next if lines.include?(line)
|
59
59
|
next unless processed_source[line - 1].empty?
|
60
|
+
|
60
61
|
corrector.remove(source_range(processed_source.buffer, line, 0))
|
61
62
|
end
|
62
63
|
end
|
@@ -104,7 +105,7 @@ module RuboCop
|
|
104
105
|
add_offense(
|
105
106
|
token,
|
106
107
|
location: token.pos,
|
107
|
-
message: "Magic comments should be in the following order: #{PREFERRED_ORDER.values.join(", ")}."
|
108
|
+
message: "Magic comments should be in the following order: #{PREFERRED_ORDER.values.join(", ")}.",
|
108
109
|
)
|
109
110
|
end
|
110
111
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Sorbet
|
6
|
-
#
|
6
|
+
# Checks that there is only one Sorbet sigil in a given file
|
7
7
|
#
|
8
8
|
# For example, the following class with two sigils
|
9
9
|
#
|
@@ -28,8 +28,9 @@ module RuboCop
|
|
28
28
|
|
29
29
|
def investigate(processed_source)
|
30
30
|
return if processed_source.tokens.empty?
|
31
|
+
|
31
32
|
sigils = extract_all_sigils(processed_source)
|
32
|
-
return
|
33
|
+
return if sigils.empty?
|
33
34
|
|
34
35
|
sigils[1..sigils.size].each do |token|
|
35
36
|
add_offense(token, location: token.pos, message: "Files must only contain one sigil")
|
@@ -37,14 +38,14 @@ module RuboCop
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def autocorrect(_node)
|
40
|
-
->
|
41
|
+
->(corrector) do
|
41
42
|
sigils = extract_all_sigils(processed_source)
|
42
|
-
return
|
43
|
+
return if sigils.empty?
|
43
44
|
|
44
45
|
# The first sigil encountered represents the "real" strictness so remove any below
|
45
46
|
sigils[1..sigils.size].each do |token|
|
46
47
|
corrector.remove(
|
47
|
-
source_range(processed_source.buffer, token.line, (0..token.pos.last_column))
|
48
|
+
source_range(processed_source.buffer, token.line, (0..token.pos.last_column)),
|
48
49
|
)
|
49
50
|
end
|
50
51
|
end
|
@@ -6,7 +6,7 @@ require_relative "has_sigil"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Makes the Sorbet `false` sigil mandatory in all files.
|
10
10
|
class FalseSigil < HasSigil
|
11
11
|
def minimum_strictness
|
12
12
|
"false"
|
@@ -6,7 +6,7 @@ require_relative "has_sigil"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Makes the Sorbet `ignore` sigil mandatory in all files.
|
10
10
|
class IgnoreSigil < HasSigil
|
11
11
|
def minimum_strictness
|
12
12
|
"ignore"
|
@@ -6,7 +6,7 @@ require_relative "has_sigil"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Makes the Sorbet `strict` sigil mandatory in all files.
|
10
10
|
class StrictSigil < HasSigil
|
11
11
|
def minimum_strictness
|
12
12
|
"strict"
|
@@ -6,7 +6,7 @@ require_relative "has_sigil"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Makes the Sorbet `strong` sigil mandatory in all files.
|
10
10
|
class StrongSigil < HasSigil
|
11
11
|
def minimum_strictness
|
12
12
|
"strong"
|
@@ -6,7 +6,7 @@ require_relative "has_sigil"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Makes the Sorbet `true` sigil mandatory in all files.
|
10
10
|
class TrueSigil < HasSigil
|
11
11
|
def minimum_strictness
|
12
12
|
"true"
|