rubocop-sorbet 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|