rubocop-sorbet 0.3.6 → 0.5.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/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.
|