rubocop-sorbet 0.3.6 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +2 -0
- data/.github/probots.yml +1 -1
- data/.github/stale.yml +20 -0
- data/.gitignore +0 -2
- data/.rspec +3 -0
- data/.rubocop.yml +2 -12
- data/.travis.yml +3 -1
- data/Gemfile +6 -7
- data/Gemfile.lock +27 -42
- data/README.md +64 -6
- data/Rakefile +34 -5
- data/config/default.yml +124 -2
- data/lib/rubocop-sorbet.rb +9 -1
- data/lib/rubocop/cop/sorbet/binding_constants_without_type_alias.rb +24 -1
- data/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb +0 -40
- data/lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb +0 -17
- data/lib/rubocop/cop/sorbet/forbid_untyped_struct_props.rb +58 -0
- data/lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb +11 -1
- data/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb +6 -1
- data/lib/rubocop/cop/sorbet/signatures/parameters_ordering_in_signature.rb +14 -6
- data/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb +13 -3
- data/lib/rubocop/cop/sorbet_cops.rb +22 -0
- data/lib/rubocop/sorbet.rb +15 -0
- data/lib/rubocop/sorbet/inject.rb +20 -0
- data/lib/rubocop/sorbet/version.rb +6 -0
- data/manual/cops.md +29 -0
- data/manual/cops_sorbet.md +340 -0
- data/rubocop-sorbet.gemspec +18 -18
- data/service.yml +1 -1
- data/tasks/cops_documentation.rake +317 -0
- metadata +23 -12
- data/lib/rubocop_sorbet.rb +0 -22
data/lib/rubocop-sorbet.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'rubocop'
|
4
|
+
|
5
|
+
require_relative 'rubocop/sorbet'
|
6
|
+
require_relative 'rubocop/sorbet/version'
|
7
|
+
require_relative 'rubocop/sorbet/inject'
|
8
|
+
|
9
|
+
RuboCop::Sorbet::Inject.defaults!
|
10
|
+
|
11
|
+
require_relative 'rubocop/cop/sorbet_cops'
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
# FooOrBar = T.type_alias { T.any(Foo, Bar) }
|
18
18
|
class BindingConstantWithoutTypeAlias < RuboCop::Cop::Cop
|
19
19
|
def_node_matcher(:binding_unaliased_type?, <<-PATTERN)
|
20
|
-
(casgn _ _ [#not_nil? #not_t_let? #method_needing_aliasing_on_t?])
|
20
|
+
(casgn _ _ [#not_nil? #not_t_let? #not_dynamic_type_creation_with_block? #not_generic_parameter_decl? #method_needing_aliasing_on_t?])
|
21
21
|
PATTERN
|
22
22
|
|
23
23
|
def_node_matcher(:using_type_alias?, <<-PATTERN)
|
@@ -48,6 +48,21 @@ module RuboCop
|
|
48
48
|
)
|
49
49
|
PATTERN
|
50
50
|
|
51
|
+
def_node_matcher(:dynamic_type_creation_with_block?, <<-PATTERN)
|
52
|
+
(block
|
53
|
+
(send
|
54
|
+
const :new ...)
|
55
|
+
_
|
56
|
+
_
|
57
|
+
)
|
58
|
+
PATTERN
|
59
|
+
|
60
|
+
def_node_matcher(:generic_parameter_decl?, <<-PATTERN)
|
61
|
+
(
|
62
|
+
send nil? {:type_template :type_member} ...
|
63
|
+
)
|
64
|
+
PATTERN
|
65
|
+
|
51
66
|
def_node_search(:method_needing_aliasing_on_t?, <<-PATTERN)
|
52
67
|
(
|
53
68
|
send
|
@@ -61,6 +76,14 @@ module RuboCop
|
|
61
76
|
!t_let?(node)
|
62
77
|
end
|
63
78
|
|
79
|
+
def not_dynamic_type_creation_with_block?(node)
|
80
|
+
!dynamic_type_creation_with_block?(node)
|
81
|
+
end
|
82
|
+
|
83
|
+
def not_generic_parameter_decl?(node)
|
84
|
+
!generic_parameter_decl?(node)
|
85
|
+
end
|
86
|
+
|
64
87
|
def not_nil?(node)
|
65
88
|
!node.nil?
|
66
89
|
end
|
@@ -14,15 +14,6 @@ require 'rubocop'
|
|
14
14
|
# end
|
15
15
|
# ```
|
16
16
|
#
|
17
|
-
# This cop replaces them by:
|
18
|
-
#
|
19
|
-
# ```ruby
|
20
|
-
# class MyClass
|
21
|
-
# MyClassInclude = send_expr
|
22
|
-
# include MyClassInclude
|
23
|
-
# end
|
24
|
-
# ```
|
25
|
-
#
|
26
17
|
# Multiple occurences of this can be found in Shopify's code base like:
|
27
18
|
#
|
28
19
|
# ```ruby
|
@@ -61,37 +52,6 @@ module RuboCop
|
|
61
52
|
return unless [:module, :class, :sclass].include?(parent.type)
|
62
53
|
add_offense(node)
|
63
54
|
end
|
64
|
-
|
65
|
-
def autocorrect(node)
|
66
|
-
lambda do |corrector|
|
67
|
-
# Find parent class node
|
68
|
-
parent = node.parent
|
69
|
-
parent = parent.parent if parent.type == :begin
|
70
|
-
|
71
|
-
# Build include variable name
|
72
|
-
class_name = (parent.child_nodes.first.const_name || 'Anon').split('::').last
|
73
|
-
include_name = find_free_name("#{class_name}Include")
|
74
|
-
used_names << include_name
|
75
|
-
|
76
|
-
# Apply fix
|
77
|
-
indent = ' ' * node.loc.column
|
78
|
-
fix = "#{include_name} = #{node.child_nodes.first.source}\n#{indent}"
|
79
|
-
corrector.insert_before(node.loc.expression, fix)
|
80
|
-
corrector.replace(node.child_nodes.first.loc.expression, include_name)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# Find a free local variable name
|
85
|
-
#
|
86
|
-
# Since each include uses its own local variable to store the send result,
|
87
|
-
# we need to ensure that we don't use the same name twice in the same
|
88
|
-
# module.
|
89
|
-
def find_free_name(base_name)
|
90
|
-
return base_name unless used_names.include?(base_name)
|
91
|
-
i = 2
|
92
|
-
i += 1 while used_names.include?("#{base_name}#{i}")
|
93
|
-
"#{base_name}#{i}"
|
94
|
-
end
|
95
55
|
end
|
96
56
|
end
|
97
57
|
end
|
@@ -12,13 +12,6 @@ require 'rubocop'
|
|
12
12
|
# class Foo < send_expr; end
|
13
13
|
# ```
|
14
14
|
#
|
15
|
-
# This cop replaces them by:
|
16
|
-
#
|
17
|
-
# ```ruby
|
18
|
-
# FooParent = send_expr
|
19
|
-
# class Foo < FooParent; end
|
20
|
-
# ```
|
21
|
-
#
|
22
15
|
# Multiple occurences of this can be found in Shopify's code base like:
|
23
16
|
#
|
24
17
|
# ```ruby
|
@@ -46,16 +39,6 @@ module RuboCop
|
|
46
39
|
return unless not_lit_const_superclass?(node)
|
47
40
|
add_offense(node.child_nodes[1])
|
48
41
|
end
|
49
|
-
|
50
|
-
def autocorrect(node)
|
51
|
-
lambda do |corrector|
|
52
|
-
class_name = node.parent.child_nodes.first.const_name
|
53
|
-
parent_name = "#{class_name}Parent"
|
54
|
-
indent = ' ' * node.parent.loc.column
|
55
|
-
corrector.insert_before(node.parent.loc.expression, "#{parent_name} = #{node.source}\n#{indent}")
|
56
|
-
corrector.replace(node.loc.expression, parent_name)
|
57
|
-
end
|
58
|
-
end
|
59
42
|
end
|
60
43
|
end
|
61
44
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'rubocop'
|
5
|
+
|
6
|
+
module RuboCop
|
7
|
+
module Cop
|
8
|
+
module Sorbet
|
9
|
+
# This cop disallows use of `T.untyped` or `T.nilable(T.untyped)`
|
10
|
+
# as a prop type for `T::Struct`.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# class SomeClass
|
16
|
+
# const :foo, T.untyped
|
17
|
+
# prop :bar, T.nilable(T.untyped)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# class SomeClass
|
22
|
+
# const :foo, Integer
|
23
|
+
# prop :bar, T.nilable(String)
|
24
|
+
# end
|
25
|
+
class ForbidUntypedStructProps < RuboCop::Cop::Cop
|
26
|
+
MSG = 'Struct props cannot be T.untyped'
|
27
|
+
|
28
|
+
def_node_matcher :t_struct, <<~PATTERN
|
29
|
+
(const (const nil? :T) :Struct)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def_node_matcher :t_untyped, <<~PATTERN
|
33
|
+
(send (const nil? :T) :untyped)
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def_node_matcher :t_nilable_untyped, <<~PATTERN
|
37
|
+
(send (const nil? :T) :nilable {#t_untyped #t_nilable_untyped})
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def_node_matcher :subclass_of_t_struct?, <<~PATTERN
|
41
|
+
(class (const ...) #t_struct ...)
|
42
|
+
PATTERN
|
43
|
+
|
44
|
+
def_node_search :untyped_props, <<~PATTERN
|
45
|
+
(send nil? {:prop :const} _ {#t_untyped #t_nilable_untyped} ...)
|
46
|
+
PATTERN
|
47
|
+
|
48
|
+
def on_class(node)
|
49
|
+
return unless subclass_of_t_struct?(node)
|
50
|
+
|
51
|
+
untyped_props(node).each do |untyped_prop|
|
52
|
+
add_offense(untyped_prop.child_nodes[1])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -28,6 +28,8 @@ module RuboCop
|
|
28
28
|
# Only `typed`, `(en)?coding`, `warn_indent` and `frozen_string_literal` magic comments are considered,
|
29
29
|
# other comments or magic comments are left in the same place.
|
30
30
|
class EnforceSigilOrder < ValidSigil
|
31
|
+
include RangeHelp
|
32
|
+
|
31
33
|
def investigate(processed_source)
|
32
34
|
return if processed_source.tokens.empty?
|
33
35
|
|
@@ -49,6 +51,14 @@ module RuboCop
|
|
49
51
|
tokens.each_with_index do |token, index|
|
50
52
|
corrector.replace(token.pos, expected[index].text)
|
51
53
|
end
|
54
|
+
|
55
|
+
# Remove blank lines between the magic comments
|
56
|
+
lines = tokens.map(&:line).to_set
|
57
|
+
(lines.min...lines.max).each do |line|
|
58
|
+
next if lines.include?(line)
|
59
|
+
next unless processed_source[line - 1].empty?
|
60
|
+
corrector.remove(source_range(processed_source.buffer, line, 0))
|
61
|
+
end
|
52
62
|
end
|
53
63
|
end
|
54
64
|
|
@@ -59,8 +69,8 @@ module RuboCop
|
|
59
69
|
FROZEN_REGEX = /#\s+frozen_string_literal:(?:\s+([\w]+))?/
|
60
70
|
|
61
71
|
PREFERRED_ORDER = {
|
62
|
-
SIGIL_REGEX => 'typed',
|
63
72
|
CODING_REGEX => 'encoding',
|
73
|
+
SIGIL_REGEX => 'typed',
|
64
74
|
INDENT_REGEX => 'warn_indent',
|
65
75
|
FROZEN_REGEX => 'frozen_string_literal',
|
66
76
|
}.freeze
|
@@ -37,7 +37,12 @@ module RuboCop
|
|
37
37
|
|
38
38
|
token = processed_source.tokens.first
|
39
39
|
replace_with = suggested_strictness_level(minimum_strictness, suggested_strictness)
|
40
|
-
|
40
|
+
sigil = "# typed: #{replace_with}"
|
41
|
+
if token.text.start_with?("#!") # shebang line
|
42
|
+
corrector.insert_after(token.pos, "\n#{sigil}")
|
43
|
+
else
|
44
|
+
corrector.insert_before(token.pos, "#{sigil}\n")
|
45
|
+
end
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
@@ -26,13 +26,9 @@ module RuboCop
|
|
26
26
|
|
27
27
|
def on_signature(node)
|
28
28
|
sig_params = signature_params(node).first
|
29
|
-
sig_params_order =
|
30
|
-
if sig_params.nil?
|
31
|
-
[]
|
32
|
-
else
|
33
|
-
sig_params.arguments.first.keys.map(&:value)
|
34
|
-
end
|
35
29
|
|
30
|
+
sig_params_order = extract_parameters(sig_params)
|
31
|
+
return if sig_params_order.nil?
|
36
32
|
method_node = node.parent.children[node.sibling_index + 1]
|
37
33
|
return if method_node.nil? || method_node.type != :def
|
38
34
|
method_parameters = method_node.arguments
|
@@ -42,6 +38,18 @@ module RuboCop
|
|
42
38
|
|
43
39
|
private
|
44
40
|
|
41
|
+
def extract_parameters(sig_params)
|
42
|
+
return [] if sig_params.nil?
|
43
|
+
|
44
|
+
arguments = sig_params.arguments.first
|
45
|
+
return arguments.keys.map(&:value) if RuboCop::AST::HashNode === arguments
|
46
|
+
|
47
|
+
add_offense(
|
48
|
+
sig_params,
|
49
|
+
message: "Invalid signature."
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
45
53
|
def check_for_inconsistent_param_ordering(sig_params_order, parameters)
|
46
54
|
parameters.each_with_index do |param, index|
|
47
55
|
param_name = param.children[0]
|
@@ -55,9 +55,9 @@ module RuboCop
|
|
55
55
|
return nil unless can_autocorrect?
|
56
56
|
|
57
57
|
lambda do |corrector|
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
tree = call_chain(node_with_index_sends(node))
|
59
|
+
.sort_by { |call| ORDER[call.method_name] }
|
60
|
+
.reduce(nil) do |receiver, caller|
|
61
61
|
caller.updated(nil, [receiver] + caller.children.drop(1))
|
62
62
|
end
|
63
63
|
|
@@ -70,6 +70,16 @@ module RuboCop
|
|
70
70
|
|
71
71
|
private
|
72
72
|
|
73
|
+
def node_with_index_sends(node)
|
74
|
+
# This is really dirty hack to reparse the current node with index send
|
75
|
+
# emitting enabled, which is necessary to unparse them back as index accessors.
|
76
|
+
emit_index_value = RuboCop::AST::Builder.emit_index
|
77
|
+
RuboCop::AST::Builder.emit_index = true
|
78
|
+
RuboCop::AST::ProcessedSource.new(node.source, target_ruby_version, processed_source.path).ast
|
79
|
+
ensure
|
80
|
+
RuboCop::AST::Builder.emit_index = emit_index_value
|
81
|
+
end
|
82
|
+
|
73
83
|
def can_autocorrect?
|
74
84
|
defined?(::Unparser)
|
75
85
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'sorbet/binding_constants_without_type_alias'
|
3
|
+
require_relative 'sorbet/constants_from_strings'
|
4
|
+
require_relative 'sorbet/forbid_superclass_const_literal'
|
5
|
+
require_relative 'sorbet/forbid_include_const_literal'
|
6
|
+
require_relative 'sorbet/forbid_untyped_struct_props'
|
7
|
+
|
8
|
+
require_relative 'sorbet/signatures/allow_incompatible_override'
|
9
|
+
require_relative 'sorbet/signatures/checked_true_in_signature'
|
10
|
+
require_relative 'sorbet/signatures/keyword_argument_ordering'
|
11
|
+
require_relative 'sorbet/signatures/parameters_ordering_in_signature'
|
12
|
+
require_relative 'sorbet/signatures/signature_build_order'
|
13
|
+
require_relative 'sorbet/signatures/enforce_signatures'
|
14
|
+
|
15
|
+
require_relative 'sorbet/sigils/valid_sigil'
|
16
|
+
require_relative 'sorbet/sigils/has_sigil'
|
17
|
+
require_relative 'sorbet/sigils/ignore_sigil'
|
18
|
+
require_relative 'sorbet/sigils/false_sigil'
|
19
|
+
require_relative 'sorbet/sigils/true_sigil'
|
20
|
+
require_relative 'sorbet/sigils/strict_sigil'
|
21
|
+
require_relative 'sorbet/sigils/strong_sigil'
|
22
|
+
require_relative 'sorbet/sigils/enforce_sigil_order'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "rubocop/sorbet/version"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Sorbet
|
7
|
+
class Error < StandardError; end
|
8
|
+
|
9
|
+
PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
|
10
|
+
CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
|
11
|
+
CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
|
12
|
+
|
13
|
+
private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The original code is from https://github.com/rubocop-hq/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
|
4
|
+
# See https://github.com/rubocop-hq/rubocop-rspec/blob/master/MIT-LICENSE.md
|
5
|
+
module RuboCop
|
6
|
+
module Sorbet
|
7
|
+
# Because RuboCop doesn't yet support plugins, we have to monkey patch in a
|
8
|
+
# bit of our configuration.
|
9
|
+
module Inject
|
10
|
+
def self.defaults!
|
11
|
+
path = CONFIG_DEFAULT.to_s
|
12
|
+
hash = ConfigLoader.send(:load_yaml_configuration, path)
|
13
|
+
config = Config.new(hash, path).tap(&:make_excludes_absolute)
|
14
|
+
puts "configuration from #{path}" if ConfigLoader.debug?
|
15
|
+
config = ConfigLoader.merge_with_default(config, path)
|
16
|
+
ConfigLoader.instance_variable_set(:@default_configuration, config)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/manual/cops.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
## Available cops
|
2
|
+
|
3
|
+
In the following section you find all available cops:
|
4
|
+
|
5
|
+
<!-- START_COP_LIST -->
|
6
|
+
#### Department [Sorbet](cops_sorbet.md)
|
7
|
+
|
8
|
+
* [Sorbet/AllowIncompatibleOverride](cops_sorbet.md#sorbetallowincompatibleoverride)
|
9
|
+
* [Sorbet/BindingConstantWithoutTypeAlias](cops_sorbet.md#sorbetbindingconstantwithouttypealias)
|
10
|
+
* [Sorbet/CheckedTrueInSignature](cops_sorbet.md#sorbetcheckedtrueinsignature)
|
11
|
+
* [Sorbet/ConstantsFromStrings](cops_sorbet.md#sorbetconstantsfromstrings)
|
12
|
+
* [Sorbet/EnforceSigilOrder](cops_sorbet.md#sorbetenforcesigilorder)
|
13
|
+
* [Sorbet/EnforceSignatures](cops_sorbet.md#sorbetenforcesignatures)
|
14
|
+
* [Sorbet/FalseSigil](cops_sorbet.md#sorbetfalsesigil)
|
15
|
+
* [Sorbet/ForbidIncludeConstLiteral](cops_sorbet.md#sorbetforbidincludeconstliteral)
|
16
|
+
* [Sorbet/ForbidSuperclassConstLiteral](cops_sorbet.md#sorbetforbidsuperclassconstliteral)
|
17
|
+
* [Sorbet/ForbidUntypedStructProps](cops_sorbet.md#sorbetforbiduntypedstructprops)
|
18
|
+
* [Sorbet/HasSigil](cops_sorbet.md#sorbethassigil)
|
19
|
+
* [Sorbet/IgnoreSigil](cops_sorbet.md#sorbetignoresigil)
|
20
|
+
* [Sorbet/KeywordArgumentOrdering](cops_sorbet.md#sorbetkeywordargumentordering)
|
21
|
+
* [Sorbet/ParametersOrderingInSignature](cops_sorbet.md#sorbetparametersorderinginsignature)
|
22
|
+
* [Sorbet/SignatureBuildOrder](cops_sorbet.md#sorbetsignaturebuildorder)
|
23
|
+
* [Sorbet/SignatureCop](cops_sorbet.md#sorbetsignaturecop)
|
24
|
+
* [Sorbet/StrictSigil](cops_sorbet.md#sorbetstrictsigil)
|
25
|
+
* [Sorbet/StrongSigil](cops_sorbet.md#sorbetstrongsigil)
|
26
|
+
* [Sorbet/TrueSigil](cops_sorbet.md#sorbettruesigil)
|
27
|
+
* [Sorbet/ValidSigil](cops_sorbet.md#sorbetvalidsigil)
|
28
|
+
|
29
|
+
<!-- END_COP_LIST -->
|
@@ -0,0 +1,340 @@
|
|
1
|
+
# Sorbet
|
2
|
+
|
3
|
+
## Sorbet/AllowIncompatibleOverride
|
4
|
+
|
5
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
6
|
+
--- | --- | --- | --- | ---
|
7
|
+
Enabled | Yes | No | 0.2.0 | -
|
8
|
+
|
9
|
+
This cop disallows using `.override(allow_incompatible: true)`.
|
10
|
+
Using `allow_incompatible` suggests a violation of the Liskov
|
11
|
+
Substitution Principle, meaning that a subclass is not a valid
|
12
|
+
subtype of it's superclass. This Cop prevents these design smells
|
13
|
+
from occurring.
|
14
|
+
|
15
|
+
### Examples
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
# bad
|
19
|
+
sig.override(allow_incompatible: true)
|
20
|
+
|
21
|
+
# good
|
22
|
+
sig.override
|
23
|
+
```
|
24
|
+
|
25
|
+
## Sorbet/BindingConstantWithoutTypeAlias
|
26
|
+
|
27
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
28
|
+
--- | --- | --- | --- | ---
|
29
|
+
Enabled | Yes | Yes | 0.2.0 | -
|
30
|
+
|
31
|
+
This cop disallows binding the return value of `T.any`, `T.all`, `T.enum`
|
32
|
+
to a constant directly. To bind the value, one must use `T.type_alias`.
|
33
|
+
|
34
|
+
### Examples
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# bad
|
38
|
+
FooOrBar = T.any(Foo, Bar)
|
39
|
+
|
40
|
+
# good
|
41
|
+
FooOrBar = T.type_alias { T.any(Foo, Bar) }
|
42
|
+
```
|
43
|
+
|
44
|
+
## Sorbet/CheckedTrueInSignature
|
45
|
+
|
46
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
47
|
+
--- | --- | --- | --- | ---
|
48
|
+
Enabled | Yes | No | 0.2.0 | -
|
49
|
+
|
50
|
+
This cop disallows the usage of `checked(true)`. This usage could cause
|
51
|
+
confusion; it could lead some people to believe that a method would be checked
|
52
|
+
even if runtime checks have not been enabled on the class or globally.
|
53
|
+
Additionally, in the event where checks are enabled, `checked(true)` would
|
54
|
+
be redundant; only `checked(false)` or `soft` would change the behaviour.
|
55
|
+
|
56
|
+
### Examples
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
# bad
|
60
|
+
sig { void.checked(true) }
|
61
|
+
|
62
|
+
# good
|
63
|
+
sig { void }
|
64
|
+
```
|
65
|
+
|
66
|
+
## Sorbet/ConstantsFromStrings
|
67
|
+
|
68
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
69
|
+
--- | --- | --- | --- | ---
|
70
|
+
Enabled | Yes | No | 0.2.0 | -
|
71
|
+
|
72
|
+
This cop disallows the calls that are used to get constants fom Strings
|
73
|
+
such as +constantize+, +const_get+, and +constants+.
|
74
|
+
|
75
|
+
The goal of this cop is to make the code easier to statically analyze,
|
76
|
+
more IDE-friendly, and more predictable. It leads to code that clearly
|
77
|
+
expresses which values the constant can have.
|
78
|
+
|
79
|
+
### Examples
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
# bad
|
83
|
+
class_name.constantize
|
84
|
+
|
85
|
+
# bad
|
86
|
+
constants.detect { |c| c.name == "User" }
|
87
|
+
|
88
|
+
# bad
|
89
|
+
const_get(class_name)
|
90
|
+
|
91
|
+
# good
|
92
|
+
case class_name
|
93
|
+
when "User"
|
94
|
+
User
|
95
|
+
else
|
96
|
+
raise ArgumentError
|
97
|
+
end
|
98
|
+
|
99
|
+
# good
|
100
|
+
{ "User" => User }.fetch(class_name)
|
101
|
+
```
|
102
|
+
|
103
|
+
## Sorbet/EnforceSigilOrder
|
104
|
+
|
105
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
106
|
+
--- | --- | --- | --- | ---
|
107
|
+
Enabled | Yes | Yes | 0.3.4 | -
|
108
|
+
|
109
|
+
This cop checks that the Sorbet sigil comes as the first magic comment in the file.
|
110
|
+
|
111
|
+
The expected order for magic comments is: typed, (en)?coding, warn_indent then frozen_string_literal.
|
112
|
+
|
113
|
+
For example, the following bad ordering:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
# frozen_string_literal: true
|
117
|
+
# typed: true
|
118
|
+
class Foo; end
|
119
|
+
```
|
120
|
+
|
121
|
+
Will be corrected as:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# typed: true
|
125
|
+
# frozen_string_literal: true
|
126
|
+
class Foo; end
|
127
|
+
```
|
128
|
+
|
129
|
+
Only `typed`, `(en)?coding`, `warn_indent` and `frozen_string_literal` magic comments are considered,
|
130
|
+
other comments or magic comments are left in the same place.
|
131
|
+
|
132
|
+
## Sorbet/EnforceSignatures
|
133
|
+
|
134
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
135
|
+
--- | --- | --- | --- | ---
|
136
|
+
Disabled | Yes | Yes | 0.3.4 | -
|
137
|
+
|
138
|
+
This cop checks that every method definition and attribute accessor has a Sorbet signature.
|
139
|
+
|
140
|
+
It also suggest an autocorrect with placeholders so the following code:
|
141
|
+
|
142
|
+
```
|
143
|
+
def foo(a, b, c); end
|
144
|
+
```
|
145
|
+
|
146
|
+
Will be corrected as:
|
147
|
+
|
148
|
+
```
|
149
|
+
sig { params(a: T.untyped, b: T.untyped, c: T.untyped).returns(T.untyped)
|
150
|
+
def foo(a, b, c); end
|
151
|
+
```
|
152
|
+
|
153
|
+
You can configure the placeholders used by changing the following options:
|
154
|
+
|
155
|
+
* `ParameterTypePlaceholder`: placeholders used for parameter types (default: 'T.untyped')
|
156
|
+
* `ReturnTypePlaceholder`: placeholders used for return types (default: 'T.untyped')
|
157
|
+
|
158
|
+
## Sorbet/FalseSigil
|
159
|
+
|
160
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
161
|
+
--- | --- | --- | --- | ---
|
162
|
+
Enabled | Yes | Yes | 0.3.3 | -
|
163
|
+
|
164
|
+
This cop makes the Sorbet `false` sigil mandatory in all files.
|
165
|
+
|
166
|
+
### Configurable attributes
|
167
|
+
|
168
|
+
Name | Default value | Configurable values
|
169
|
+
--- | --- | ---
|
170
|
+
SuggestedStrictness | `true` | Boolean
|
171
|
+
Include | `**/*.rb`, `**/*.rbi`, `**/*.rake`, `**/*.ru` | Array
|
172
|
+
Exclude | `bin/**/*`, `db/**/*.rb`, `script/**/*` | Array
|
173
|
+
|
174
|
+
## Sorbet/ForbidIncludeConstLiteral
|
175
|
+
|
176
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
177
|
+
--- | --- | --- | --- | ---
|
178
|
+
Disabled | Yes | No | 0.2.0 | 0.5.0
|
179
|
+
|
180
|
+
No documentation
|
181
|
+
|
182
|
+
## Sorbet/ForbidSuperclassConstLiteral
|
183
|
+
|
184
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
185
|
+
--- | --- | --- | --- | ---
|
186
|
+
Disabled | Yes | No | 0.2.0 | 0.5.0
|
187
|
+
|
188
|
+
No documentation
|
189
|
+
|
190
|
+
## Sorbet/ForbidUntypedStructProps
|
191
|
+
|
192
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
193
|
+
--- | --- | --- | --- | ---
|
194
|
+
Enabled | Yes | No | 0.3.8 | -
|
195
|
+
|
196
|
+
This cop disallows use of `T.untyped` or `T.nilable(T.untyped)`
|
197
|
+
as a prop type for `T::Struct`.
|
198
|
+
|
199
|
+
### Examples
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
# bad
|
203
|
+
class SomeClass
|
204
|
+
const :foo, T.untyped
|
205
|
+
prop :bar, T.nilable(T.untyped)
|
206
|
+
end
|
207
|
+
|
208
|
+
# good
|
209
|
+
class SomeClass
|
210
|
+
const :foo, Integer
|
211
|
+
prop :bar, T.nilable(String)
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
## Sorbet/HasSigil
|
216
|
+
|
217
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
218
|
+
--- | --- | --- | --- | ---
|
219
|
+
Disabled | Yes | Yes | 0.3.3 | -
|
220
|
+
|
221
|
+
This cop makes the Sorbet typed sigil mandatory in all files.
|
222
|
+
|
223
|
+
Options:
|
224
|
+
|
225
|
+
* `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false')
|
226
|
+
* `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one
|
227
|
+
|
228
|
+
If a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
|
229
|
+
|
230
|
+
## Sorbet/IgnoreSigil
|
231
|
+
|
232
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
233
|
+
--- | --- | --- | --- | ---
|
234
|
+
Disabled | Yes | Yes | 0.3.3 | -
|
235
|
+
|
236
|
+
This cop makes the Sorbet `ignore` sigil mandatory in all files.
|
237
|
+
|
238
|
+
## Sorbet/KeywordArgumentOrdering
|
239
|
+
|
240
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
241
|
+
--- | --- | --- | --- | ---
|
242
|
+
Enabled | Yes | No | 0.2.0 | -
|
243
|
+
|
244
|
+
This cop checks for the ordering of keyword arguments required by
|
245
|
+
sorbet-runtime. The ordering requires that all keyword arguments
|
246
|
+
are at the end of the parameters list, and all keyword arguments
|
247
|
+
with a default value must be after those without default values.
|
248
|
+
|
249
|
+
### Examples
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
# bad
|
253
|
+
sig { params(a: Integer, b: String).void }
|
254
|
+
def foo(a: 1, b:); end
|
255
|
+
|
256
|
+
# good
|
257
|
+
sig { params(b: String, a: Integer).void }
|
258
|
+
def foo(b:, a: 1); end
|
259
|
+
```
|
260
|
+
|
261
|
+
## Sorbet/ParametersOrderingInSignature
|
262
|
+
|
263
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
264
|
+
--- | --- | --- | --- | ---
|
265
|
+
Enabled | Yes | No | 0.2.0 | -
|
266
|
+
|
267
|
+
This cop checks for inconsistent ordering of parameters between the
|
268
|
+
signature and the method definition. The sorbet-runtime gem raises
|
269
|
+
when such inconsistency occurs.
|
270
|
+
|
271
|
+
### Examples
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
# bad
|
275
|
+
sig { params(a: Integer, b: String).void }
|
276
|
+
def foo(b:, a:); end
|
277
|
+
|
278
|
+
# good
|
279
|
+
sig { params(a: Integer, b: String).void }
|
280
|
+
def foo(a:, b:); end
|
281
|
+
```
|
282
|
+
|
283
|
+
## Sorbet/SignatureBuildOrder
|
284
|
+
|
285
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
286
|
+
--- | --- | --- | --- | ---
|
287
|
+
Enabled | Yes | Yes | 0.3.0 | -
|
288
|
+
|
289
|
+
No documentation
|
290
|
+
|
291
|
+
## Sorbet/SignatureCop
|
292
|
+
|
293
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
294
|
+
--- | --- | --- | --- | ---
|
295
|
+
Enabled | Yes | No | - | -
|
296
|
+
|
297
|
+
Abstract cop specific to Sorbet signatures
|
298
|
+
|
299
|
+
You can subclass it to use the `on_signature` trigger and the `signature?` node matcher.
|
300
|
+
|
301
|
+
## Sorbet/StrictSigil
|
302
|
+
|
303
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
304
|
+
--- | --- | --- | --- | ---
|
305
|
+
Disabled | Yes | Yes | 0.3.3 | -
|
306
|
+
|
307
|
+
This cop makes the Sorbet `strict` sigil mandatory in all files.
|
308
|
+
|
309
|
+
## Sorbet/StrongSigil
|
310
|
+
|
311
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
312
|
+
--- | --- | --- | --- | ---
|
313
|
+
Disabled | Yes | Yes | 0.3.3 | -
|
314
|
+
|
315
|
+
This cop makes the Sorbet `strong` sigil mandatory in all files.
|
316
|
+
|
317
|
+
## Sorbet/TrueSigil
|
318
|
+
|
319
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
320
|
+
--- | --- | --- | --- | ---
|
321
|
+
Disabled | Yes | Yes | 0.3.3 | -
|
322
|
+
|
323
|
+
This cop makes the Sorbet `true` sigil mandatory in all files.
|
324
|
+
|
325
|
+
## Sorbet/ValidSigil
|
326
|
+
|
327
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
328
|
+
--- | --- | --- | --- | ---
|
329
|
+
Enabled | Yes | Yes | 0.3.3 | -
|
330
|
+
|
331
|
+
This cop checks that every Ruby file contains a valid Sorbet sigil.
|
332
|
+
Adapted from: https://gist.github.com/clarkdave/85aca4e16f33fd52aceb6a0a29936e52
|
333
|
+
|
334
|
+
Options:
|
335
|
+
|
336
|
+
* `RequireSigilOnAllFiles`: make offense if the Sorbet typed is not found in the file (default: false)
|
337
|
+
* `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false')
|
338
|
+
* `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one
|
339
|
+
|
340
|
+
If a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
|