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
@@ -5,7 +5,7 @@ require "rubocop"
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Sorbet
|
8
|
-
#
|
8
|
+
# Checks that every Ruby file contains a valid Sorbet sigil.
|
9
9
|
# Adapted from: https://gist.github.com/clarkdave/85aca4e16f33fd52aceb6a0a29936e52
|
10
10
|
#
|
11
11
|
# Options:
|
@@ -13,9 +13,11 @@ module RuboCop
|
|
13
13
|
# * `RequireSigilOnAllFiles`: make offense if the Sorbet typed is not found in the file (default: false)
|
14
14
|
# * `SuggestedStrictness`: Sorbet strictness level suggested in offense messages (default: 'false')
|
15
15
|
# * `MinimumStrictness`: If set, make offense if the strictness level in the file is below this one
|
16
|
+
# * `ExactStrictness`: If set, make offense if the strictness level in the file is different than this one
|
16
17
|
#
|
17
|
-
# If
|
18
|
-
|
18
|
+
# If an `ExactStrictness` level is specified, it will be used in offense messages and autocorrect.
|
19
|
+
# Otherwise, if a `MinimumStrictness` level is specified, it will be used in offense messages and autocorrect.
|
20
|
+
class ValidSigil < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
|
19
21
|
@registry = Cop.registry # So we can properly subclass this cop
|
20
22
|
|
21
23
|
def investigate(processed_source)
|
@@ -36,7 +38,7 @@ module RuboCop
|
|
36
38
|
return unless extract_sigil(processed_source).nil?
|
37
39
|
|
38
40
|
token = processed_source.tokens.first
|
39
|
-
replace_with = suggested_strictness_level
|
41
|
+
replace_with = suggested_strictness_level
|
40
42
|
sigil = "# typed: #{replace_with}"
|
41
43
|
if token.text.start_with?("#!") # shebang line
|
42
44
|
corrector.insert_after(token.pos, "\n#{sigil}")
|
@@ -70,34 +72,37 @@ module RuboCop
|
|
70
72
|
|
71
73
|
token = processed_source.tokens.first
|
72
74
|
if require_sigil_on_all_files?
|
73
|
-
strictness = suggested_strictness_level
|
75
|
+
strictness = suggested_strictness_level
|
74
76
|
add_offense(
|
75
77
|
token,
|
76
78
|
location: token.pos,
|
77
79
|
message: "No Sorbet sigil found in file. " \
|
78
|
-
"Try a `typed: #{strictness}` to start (you can also use `rubocop -a` to automatically add this)."
|
80
|
+
"Try a `typed: #{strictness}` to start (you can also use `rubocop -a` to automatically add this).",
|
79
81
|
)
|
80
82
|
end
|
81
83
|
false
|
82
84
|
end
|
83
85
|
|
84
|
-
def suggested_strictness_level
|
86
|
+
def suggested_strictness_level
|
87
|
+
return exact_strictness if exact_strictness
|
85
88
|
# if no minimum strictness is set (eg. using Sorbet/HasSigil without config) then
|
86
89
|
# we always use the suggested strictness which defaults to `false`
|
87
90
|
return suggested_strictness unless minimum_strictness
|
88
91
|
|
89
92
|
# special case: if you're using Sorbet/IgnoreSigil without config, we should recommend `ignore`
|
90
|
-
return "ignore" if minimum_strictness == "ignore" && cop_config["SuggestedStrictness"].nil?
|
93
|
+
return "ignore" if minimum_strictness == "ignore" && cop_config["SuggestedStrictness"].nil? # rubocop:todo InternalAffairs/UndefinedConfig
|
91
94
|
|
92
95
|
# if a minimum strictness is set (eg. you're using Sorbet/FalseSigil)
|
93
96
|
# we want to compare the minimum strictness and suggested strictness. this is because
|
94
97
|
# the suggested strictness might be higher than the minimum (eg. if you want all new files
|
95
98
|
# at a higher strictness level, without having to migrate existing files at lower levels).
|
96
99
|
|
97
|
-
|
98
|
-
|
100
|
+
levels = [
|
101
|
+
STRICTNESS_LEVELS.index(suggested_strictness),
|
102
|
+
STRICTNESS_LEVELS.index(minimum_strictness),
|
103
|
+
]
|
99
104
|
|
100
|
-
|
105
|
+
STRICTNESS_LEVELS[levels.compact.max]
|
101
106
|
end
|
102
107
|
|
103
108
|
def check_strictness_not_empty(sigil, strictness)
|
@@ -106,7 +111,7 @@ module RuboCop
|
|
106
111
|
add_offense(
|
107
112
|
sigil,
|
108
113
|
location: sigil.pos,
|
109
|
-
message: "Sorbet sigil should not be empty."
|
114
|
+
message: "Sorbet sigil should not be empty.",
|
110
115
|
)
|
111
116
|
false
|
112
117
|
end
|
@@ -117,24 +122,38 @@ module RuboCop
|
|
117
122
|
add_offense(
|
118
123
|
sigil,
|
119
124
|
location: sigil.pos,
|
120
|
-
message: "Invalid Sorbet sigil `#{strictness}`."
|
125
|
+
message: "Invalid Sorbet sigil `#{strictness}`.",
|
121
126
|
)
|
122
127
|
false
|
123
128
|
end
|
124
129
|
|
125
130
|
def check_strictness_level(sigil, strictness)
|
126
|
-
return true
|
131
|
+
return true if !minimum_strictness && !exact_strictness
|
127
132
|
|
128
|
-
minimum_level = STRICTNESS_LEVELS.index(minimum_strictness)
|
129
133
|
current_level = STRICTNESS_LEVELS.index(strictness)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
134
|
+
|
135
|
+
if exact_strictness
|
136
|
+
exact_level = STRICTNESS_LEVELS.index(exact_strictness)
|
137
|
+
if current_level != exact_level
|
138
|
+
add_offense(
|
139
|
+
sigil,
|
140
|
+
location: sigil.pos,
|
141
|
+
message: "Sorbet sigil should be `#{exact_strictness}` got `#{strictness}`.",
|
142
|
+
)
|
143
|
+
return false
|
144
|
+
end
|
145
|
+
else
|
146
|
+
minimum_level = STRICTNESS_LEVELS.index(minimum_strictness)
|
147
|
+
if current_level < minimum_level
|
148
|
+
add_offense(
|
149
|
+
sigil,
|
150
|
+
location: sigil.pos,
|
151
|
+
message: "Sorbet sigil should be at least `#{minimum_strictness}` got `#{strictness}`.",
|
152
|
+
)
|
153
|
+
return false
|
154
|
+
end
|
137
155
|
end
|
156
|
+
|
138
157
|
true
|
139
158
|
end
|
140
159
|
|
@@ -142,18 +161,24 @@ module RuboCop
|
|
142
161
|
|
143
162
|
# Default is `false`
|
144
163
|
def require_sigil_on_all_files?
|
145
|
-
!!cop_config["RequireSigilOnAllFiles"]
|
164
|
+
!!cop_config["RequireSigilOnAllFiles"] # rubocop:todo InternalAffairs/UndefinedConfig
|
146
165
|
end
|
147
166
|
|
148
167
|
# Default is `'false'`
|
149
168
|
def suggested_strictness
|
150
|
-
config = cop_config["SuggestedStrictness"].to_s
|
169
|
+
config = cop_config["SuggestedStrictness"].to_s # rubocop:todo InternalAffairs/UndefinedConfig
|
151
170
|
STRICTNESS_LEVELS.include?(config) ? config : "false"
|
152
171
|
end
|
153
172
|
|
154
173
|
# Default is `nil`
|
155
174
|
def minimum_strictness
|
156
|
-
config = cop_config["MinimumStrictness"].to_s
|
175
|
+
config = cop_config["MinimumStrictness"].to_s # rubocop:todo InternalAffairs/UndefinedConfig
|
176
|
+
config if STRICTNESS_LEVELS.include?(config)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Default is `nil`
|
180
|
+
def exact_strictness
|
181
|
+
config = cop_config["ExactStrictness"].to_s # rubocop:todo InternalAffairs/UndefinedConfig
|
157
182
|
config if STRICTNESS_LEVELS.include?(config)
|
158
183
|
end
|
159
184
|
end
|
@@ -5,10 +5,10 @@ require "rubocop"
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Sorbet
|
8
|
-
#
|
8
|
+
# Disallows using `.override(allow_incompatible: true)`.
|
9
9
|
# Using `allow_incompatible` suggests a violation of the Liskov
|
10
10
|
# Substitution Principle, meaning that a subclass is not a valid
|
11
|
-
# subtype of
|
11
|
+
# subtype of its superclass. This Cop prevents these design smells
|
12
12
|
# from occurring.
|
13
13
|
#
|
14
14
|
# @example
|
@@ -18,40 +18,29 @@ module RuboCop
|
|
18
18
|
#
|
19
19
|
# # good
|
20
20
|
# sig.override
|
21
|
-
class AllowIncompatibleOverride < RuboCop::Cop::
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
nil?
|
26
|
-
:sig
|
27
|
-
...
|
28
|
-
)
|
29
|
-
PATTERN
|
21
|
+
class AllowIncompatibleOverride < RuboCop::Cop::Base
|
22
|
+
MSG = "Usage of `allow_incompatible` suggests a violation of the Liskov Substitution Principle. " \
|
23
|
+
"Instead, strive to write interfaces which respect subtyping principles and remove `allow_incompatible`"
|
24
|
+
RESTRICT_ON_SEND = [:override].freeze
|
30
25
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
def_node_search(:allow_incompatible?, <<-PATTERN)
|
36
|
-
(pair (sym :allow_incompatible) (true))
|
37
|
-
PATTERN
|
38
|
-
|
39
|
-
def_node_matcher(:allow_incompatible_override?, <<-PATTERN)
|
40
|
-
(
|
41
|
-
send
|
42
|
-
[#not_nil? #sig?]
|
26
|
+
# @!method allow_incompatible_override?(node)
|
27
|
+
def_node_matcher(:allow_incompatible_override?, <<~PATTERN)
|
28
|
+
(send
|
29
|
+
#sig?
|
43
30
|
:override
|
44
|
-
|
31
|
+
(hash <$(pair (sym :allow_incompatible) true) ...>)
|
45
32
|
)
|
46
33
|
PATTERN
|
47
34
|
|
35
|
+
# @!method sig?(node)
|
36
|
+
def_node_search :sig?, <<~PATTERN
|
37
|
+
(send _ :sig ...)
|
38
|
+
PATTERN
|
39
|
+
|
48
40
|
def on_send(node)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
message: "Usage of `allow_incompatible` suggests a violation of the Liskov Substitution Principle. "\
|
53
|
-
"Instead, strive to write interfaces which respect subtyping principles and remove `allow_incompatible`",
|
54
|
-
)
|
41
|
+
allow_incompatible_override?(node) do |allow_incompatible_pair|
|
42
|
+
add_offense(allow_incompatible_pair)
|
43
|
+
end
|
55
44
|
end
|
56
45
|
end
|
57
46
|
end
|
@@ -6,7 +6,7 @@ require_relative "signature_cop"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Disallows the usage of `checked(true)`. This usage could cause
|
10
10
|
# confusion; it could lead some people to believe that a method would be checked
|
11
11
|
# even if runtime checks have not been enabled on the class or globally.
|
12
12
|
# Additionally, in the event where checks are enabled, `checked(true)` would
|
@@ -22,6 +22,7 @@ module RuboCop
|
|
22
22
|
class CheckedTrueInSignature < SignatureCop
|
23
23
|
include(RuboCop::Cop::RangeHelp)
|
24
24
|
|
25
|
+
# @!method offending_node(node)
|
25
26
|
def_node_search(:offending_node, <<~PATTERN)
|
26
27
|
(send _ :checked (true))
|
27
28
|
PATTERN
|
@@ -44,7 +45,7 @@ module RuboCop
|
|
44
45
|
error.location.line,
|
45
46
|
(error.location.selector.begin_pos)..(error.location.end.begin_pos),
|
46
47
|
),
|
47
|
-
message: MESSAGE
|
48
|
+
message: MESSAGE,
|
48
49
|
)
|
49
50
|
end
|
50
51
|
end
|
@@ -5,7 +5,7 @@ require_relative "signature_cop"
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Sorbet
|
8
|
-
#
|
8
|
+
# Checks for blank lines after signatures.
|
9
9
|
#
|
10
10
|
# It also suggests an autocorrect
|
11
11
|
#
|
@@ -31,10 +31,10 @@ module RuboCop
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def autocorrect(node)
|
34
|
-
->
|
34
|
+
->(corrector) do
|
35
35
|
offending_range = node.source_range.with(
|
36
36
|
begin_pos: node.source_range.end_pos + 1,
|
37
|
-
end_pos: processed_source.buffer.line_range(next_method(node).line).begin_pos
|
37
|
+
end_pos: processed_source.buffer.line_range(next_method(node).line).begin_pos,
|
38
38
|
)
|
39
39
|
corrector.remove(offending_range)
|
40
40
|
clean_range = offending_range.source.split("\n").reject(&:empty?).join("\n")
|
@@ -7,7 +7,7 @@ require_relative "signature_cop"
|
|
7
7
|
module RuboCop
|
8
8
|
module Cop
|
9
9
|
module Sorbet
|
10
|
-
#
|
10
|
+
# Checks that every method definition and attribute accessor has a Sorbet signature.
|
11
11
|
#
|
12
12
|
# It also suggest an autocorrect with placeholders so the following code:
|
13
13
|
#
|
@@ -32,6 +32,7 @@ module RuboCop
|
|
32
32
|
@last_sig_for_scope = {}
|
33
33
|
end
|
34
34
|
|
35
|
+
# @!method accessor?(node)
|
35
36
|
def_node_matcher(:accessor?, <<-PATTERN)
|
36
37
|
(send nil? {:attr_reader :attr_writer :attr_accessor} ...)
|
37
38
|
PATTERN
|
@@ -67,13 +68,14 @@ module RuboCop
|
|
67
68
|
suggest.returns = "void" if method == :attr_writer
|
68
69
|
end
|
69
70
|
|
70
|
-
corrector.insert_before(node
|
71
|
+
corrector.insert_before(node, suggest.to_autocorrect)
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
74
75
|
def scope(node)
|
75
|
-
return
|
76
|
+
return unless node.parent
|
76
77
|
return node.parent if [:begin, :block, :class, :module].include?(node.parent.type)
|
78
|
+
|
77
79
|
scope(node.parent)
|
78
80
|
end
|
79
81
|
|
@@ -84,7 +86,7 @@ module RuboCop
|
|
84
86
|
unless @last_sig_for_scope[scope]
|
85
87
|
add_offense(
|
86
88
|
node,
|
87
|
-
message: "Each method is required to have a signature."
|
89
|
+
message: "Each method is required to have a signature.",
|
88
90
|
)
|
89
91
|
end
|
90
92
|
@last_sig_for_scope[scope] = nil
|
@@ -123,6 +125,7 @@ module RuboCop
|
|
123
125
|
|
124
126
|
def generate_params
|
125
127
|
return if @params.empty?
|
128
|
+
|
126
129
|
out = StringIO.new
|
127
130
|
out << "params("
|
128
131
|
out << @params.map do |param|
|
@@ -135,6 +138,7 @@ module RuboCop
|
|
135
138
|
def generate_return
|
136
139
|
return "returns(#{@return_placeholder})" if @returns.nil?
|
137
140
|
return @returns if @returns == "void"
|
141
|
+
|
138
142
|
"returns(#{@returns})"
|
139
143
|
end
|
140
144
|
end
|
@@ -6,7 +6,7 @@ require_relative "signature_cop"
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
8
8
|
module Sorbet
|
9
|
-
#
|
9
|
+
# Checks for the ordering of keyword arguments required by
|
10
10
|
# sorbet-runtime. The ordering requires that all keyword arguments
|
11
11
|
# are at the end of the parameters list, and all keyword arguments
|
12
12
|
# with a default value must be after those without default values.
|
@@ -24,6 +24,7 @@ module RuboCop
|
|
24
24
|
def on_signature(node)
|
25
25
|
method_node = node.parent.children[node.sibling_index + 1]
|
26
26
|
return if method_node.nil?
|
27
|
+
|
27
28
|
method_parameters = method_node.arguments
|
28
29
|
|
29
30
|
check_order_for_kwoptargs(method_parameters)
|
@@ -35,13 +36,13 @@ module RuboCop
|
|
35
36
|
out_of_kwoptarg = false
|
36
37
|
|
37
38
|
parameters.reverse.each do |param|
|
38
|
-
out_of_kwoptarg = true unless param.
|
39
|
+
out_of_kwoptarg = true unless param.kwoptarg_type? || param.blockarg_type? || param.kwrestarg_type?
|
39
40
|
|
40
|
-
next unless param.
|
41
|
+
next unless param.kwoptarg_type? && out_of_kwoptarg
|
41
42
|
|
42
43
|
add_offense(
|
43
44
|
param,
|
44
|
-
message: "Optional keyword arguments must be at the end of the parameter list."
|
45
|
+
message: "Optional keyword arguments must be at the end of the parameter list.",
|
45
46
|
)
|
46
47
|
end
|
47
48
|
end
|
@@ -27,13 +27,18 @@ module RuboCop
|
|
27
27
|
:on_failure,
|
28
28
|
].each_with_index.to_h.freeze
|
29
29
|
|
30
|
+
# @!method root_call(node)
|
30
31
|
def_node_search(:root_call, <<~PATTERN)
|
31
32
|
(send nil? {#{ORDER.keys.map(&:inspect).join(" ")}} ...)
|
32
33
|
PATTERN
|
33
34
|
|
34
35
|
def on_signature(node)
|
35
36
|
calls = call_chain(node.children[2]).map(&:method_name)
|
36
|
-
return
|
37
|
+
return if calls.empty?
|
38
|
+
|
39
|
+
# While the developer is typing, we may have an incomplete call statement, which means `ORDER[call]` will
|
40
|
+
# return `nil`. In that case, invoking `sort_by` will raise
|
41
|
+
return if calls.any? { |call| ORDER[call].nil? }
|
37
42
|
|
38
43
|
expected_order = calls.sort_by { |call| ORDER[call] }
|
39
44
|
return if expected_order == calls
|
@@ -52,7 +57,7 @@ module RuboCop
|
|
52
57
|
end
|
53
58
|
|
54
59
|
def autocorrect(node)
|
55
|
-
return
|
60
|
+
return unless can_autocorrect?
|
56
61
|
|
57
62
|
lambda do |corrector|
|
58
63
|
tree = call_chain(node_reparsed_with_modern_features(node))
|
@@ -62,7 +67,7 @@ module RuboCop
|
|
62
67
|
end
|
63
68
|
|
64
69
|
corrector.replace(
|
65
|
-
node
|
70
|
+
node,
|
66
71
|
Unparser.unparse(tree),
|
67
72
|
)
|
68
73
|
end
|
@@ -8,9 +8,10 @@ module RuboCop
|
|
8
8
|
# Abstract cop specific to Sorbet signatures
|
9
9
|
#
|
10
10
|
# You can subclass it to use the `on_signature` trigger and the `signature?` node matcher.
|
11
|
-
class SignatureCop < RuboCop::Cop::Cop
|
11
|
+
class SignatureCop < RuboCop::Cop::Cop # rubocop:todo InternalAffairs/InheritDeprecatedCopClass
|
12
12
|
@registry = Cop.registry # So we can properly subclass this cop
|
13
13
|
|
14
|
+
# @!method signature?(node)
|
14
15
|
def_node_matcher(:signature?, <<~PATTERN)
|
15
16
|
(block (send
|
16
17
|
{nil? #with_runtime? #without_runtime?}
|
@@ -19,10 +20,12 @@ module RuboCop
|
|
19
20
|
) (args) ...)
|
20
21
|
PATTERN
|
21
22
|
|
23
|
+
# @!method with_runtime?(node)
|
22
24
|
def_node_matcher(:with_runtime?, <<~PATTERN)
|
23
25
|
(const (const nil? :T) :Sig)
|
24
26
|
PATTERN
|
25
27
|
|
28
|
+
# @!method without_runtime?(node)
|
26
29
|
def_node_matcher(:without_runtime?, <<~PATTERN)
|
27
30
|
(const (const (const nil? :T) :Sig) :WithoutRuntime)
|
28
31
|
PATTERN
|
@@ -31,6 +34,8 @@ module RuboCop
|
|
31
34
|
on_signature(node) if signature?(node)
|
32
35
|
end
|
33
36
|
|
37
|
+
alias_method :on_numblock, :on_block
|
38
|
+
|
34
39
|
def on_signature(_)
|
35
40
|
# To be defined in subclasses
|
36
41
|
end
|
@@ -5,7 +5,7 @@ require "rubocop"
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Sorbet
|
8
|
-
#
|
8
|
+
# Ensures all constants used as `T.type_alias` are using CamelCase.
|
9
9
|
#
|
10
10
|
# @example
|
11
11
|
#
|
@@ -14,30 +14,23 @@ module RuboCop
|
|
14
14
|
#
|
15
15
|
# # good
|
16
16
|
# FooOrBar = T.type_alias { T.any(Foo, Bar) }
|
17
|
-
class TypeAliasName < RuboCop::Cop::
|
17
|
+
class TypeAliasName < RuboCop::Cop::Base
|
18
18
|
MSG = "Type alias constant name should be in CamelCase"
|
19
19
|
|
20
|
-
|
20
|
+
# @!method underscored_type_alias?(node)
|
21
|
+
def_node_matcher(:underscored_type_alias?, <<-PATTERN)
|
21
22
|
(casgn
|
22
23
|
_
|
23
|
-
_
|
24
|
+
/_/ # Name matches underscore
|
24
25
|
(block
|
25
|
-
(send
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
))
|
26
|
+
(send (const nil? :T) :type_alias)
|
27
|
+
...
|
28
|
+
)
|
29
|
+
)
|
30
30
|
PATTERN
|
31
31
|
|
32
32
|
def on_casgn(node)
|
33
|
-
|
34
|
-
|
35
|
-
name = node.children[1]
|
36
|
-
|
37
|
-
# From https://github.com/rubocop/rubocop/blob/master/lib/rubocop/cop/naming/class_and_module_camel_case.rb
|
38
|
-
return unless /_/.match?(name)
|
39
|
-
|
40
|
-
add_offense(node)
|
33
|
+
add_offense(node) if underscored_type_alias?(node)
|
41
34
|
end
|
42
35
|
end
|
43
36
|
end
|
@@ -1,15 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require_relative "sorbet/mixin/target_sorbet_version.rb"
|
4
|
+
|
5
|
+
require_relative "sorbet/binding_constant_without_type_alias"
|
3
6
|
require_relative "sorbet/constants_from_strings"
|
4
7
|
require_relative "sorbet/forbid_superclass_const_literal"
|
5
8
|
require_relative "sorbet/forbid_include_const_literal"
|
6
9
|
require_relative "sorbet/forbid_untyped_struct_props"
|
10
|
+
require_relative "sorbet/implicit_conversion_method"
|
7
11
|
require_relative "sorbet/one_ancestor_per_line"
|
8
12
|
require_relative "sorbet/callback_conditionals_binding"
|
9
13
|
require_relative "sorbet/forbid_t_unsafe"
|
10
14
|
require_relative "sorbet/forbid_t_untyped"
|
11
15
|
require_relative "sorbet/redundant_extend_t_sig"
|
12
16
|
require_relative "sorbet/type_alias_name"
|
17
|
+
require_relative "sorbet/obsolete_strict_memoization"
|
13
18
|
|
14
19
|
require_relative "sorbet/rbi/forbid_extend_t_sig_helpers_in_shims"
|
15
20
|
require_relative "sorbet/rbi/forbid_rbi_outside_of_allowed_paths"
|
@@ -7,13 +7,15 @@ module RuboCop
|
|
7
7
|
# Because RuboCop doesn't yet support plugins, we have to monkey patch in a
|
8
8
|
# bit of our configuration.
|
9
9
|
module Inject
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
class << self
|
11
|
+
def defaults!
|
12
|
+
path = CONFIG_DEFAULT.to_s
|
13
|
+
hash = ConfigLoader.send(:load_yaml_configuration, path)
|
14
|
+
config = Config.new(hash, path).tap(&:make_excludes_absolute)
|
15
|
+
puts "configuration from #{path}" if ConfigLoader.debug?
|
16
|
+
config = ConfigLoader.merge_with_default(config, path)
|
17
|
+
ConfigLoader.instance_variable_set(:@default_configuration, config)
|
18
|
+
end
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
data/lib/rubocop/sorbet.rb
CHANGED
data/manual/cops.md
CHANGED
@@ -24,7 +24,9 @@ In the following section you find all available cops:
|
|
24
24
|
* [Sorbet/ForbidUntypedStructProps](cops_sorbet.md#sorbetforbiduntypedstructprops)
|
25
25
|
* [Sorbet/HasSigil](cops_sorbet.md#sorbethassigil)
|
26
26
|
* [Sorbet/IgnoreSigil](cops_sorbet.md#sorbetignoresigil)
|
27
|
+
* [Sorbet/ImplicitConversionMethod](cops_sorbet.md#sorbetimplicitconversionmethod)
|
27
28
|
* [Sorbet/KeywordArgumentOrdering](cops_sorbet.md#sorbetkeywordargumentordering)
|
29
|
+
* [Sorbet/ObsoleteStrictMemoization](cops_sorbet.md#sorbetobsoletestrictmemoization)
|
28
30
|
* [Sorbet/OneAncestorPerLine](cops_sorbet.md#sorbetoneancestorperline)
|
29
31
|
* [Sorbet/RedundantExtendTSig](cops_sorbet.md#sorbetredundantextendtsig)
|
30
32
|
* [Sorbet/SignatureBuildOrder](cops_sorbet.md#sorbetsignaturebuildorder)
|