steep 0.47.0 → 0.49.0
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/CHANGELOG.md +23 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +14 -16
- data/lib/steep/ast/types/any.rb +2 -0
- data/lib/steep/ast/types/boolean.rb +2 -0
- data/lib/steep/ast/types/bot.rb +2 -0
- data/lib/steep/ast/types/class.rb +2 -0
- data/lib/steep/ast/types/factory.rb +162 -138
- data/lib/steep/ast/types/helper.rb +8 -0
- data/lib/steep/ast/types/instance.rb +2 -0
- data/lib/steep/ast/types/intersection.rb +4 -0
- data/lib/steep/ast/types/literal.rb +2 -0
- data/lib/steep/ast/types/logic.rb +2 -0
- data/lib/steep/ast/types/name.rb +6 -0
- data/lib/steep/ast/types/nil.rb +2 -0
- data/lib/steep/ast/types/proc.rb +9 -0
- data/lib/steep/ast/types/record.rb +4 -0
- data/lib/steep/ast/types/self.rb +2 -0
- data/lib/steep/ast/types/top.rb +2 -0
- data/lib/steep/ast/types/tuple.rb +4 -0
- data/lib/steep/ast/types/union.rb +4 -0
- data/lib/steep/ast/types/var.rb +16 -3
- data/lib/steep/ast/types/void.rb +2 -0
- data/lib/steep/diagnostic/ruby.rb +23 -11
- data/lib/steep/diagnostic/signature.rb +56 -0
- data/lib/steep/interface/function.rb +4 -0
- data/lib/steep/interface/method_type.rb +14 -26
- data/lib/steep/interface/type_param.rb +103 -0
- data/lib/steep/server/base_worker.rb +1 -0
- data/lib/steep/server/interaction_worker.rb +1 -1
- data/lib/steep/server/type_check_worker.rb +2 -2
- data/lib/steep/services/signature_service.rb +2 -2
- data/lib/steep/services/type_check_service.rb +2 -1
- data/lib/steep/signature/validator.rb +221 -49
- data/lib/steep/subtyping/cache.rb +30 -0
- data/lib/steep/subtyping/check.rb +600 -705
- data/lib/steep/subtyping/constraints.rb +66 -30
- data/lib/steep/subtyping/relation.rb +60 -0
- data/lib/steep/subtyping/result.rb +190 -16
- data/lib/steep/type_construction.rb +543 -395
- data/lib/steep/type_inference/block_params.rb +31 -3
- data/lib/steep/type_inference/context.rb +37 -3
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +4 -4
- data/sample/lib/length.rb +35 -0
- data/sample/sig/length.rbs +34 -0
- data/smoke/diagnostics-rbs/nonregular-type-alias.rbs +3 -0
- data/smoke/diagnostics-rbs/recursive-type-alias.rbs +3 -0
- data/smoke/diagnostics-rbs/test_expectations.yml +57 -12
- data/smoke/tsort/a.rb +1 -1
- data/smoke/tsort/test_expectations.yml +1 -63
- data/steep.gemspec +1 -1
- metadata +13 -10
- data/lib/steep/drivers/trace_printer.rb +0 -29
- data/lib/steep/interface/method.rb +0 -78
- data/lib/steep/subtyping/trace.rb +0 -71
data/lib/steep/ast/types/nil.rb
CHANGED
data/lib/steep/ast/types/proc.rb
CHANGED
@@ -90,6 +90,15 @@ module Steep
|
|
90
90
|
def block_required?
|
91
91
|
block && !block.optional?
|
92
92
|
end
|
93
|
+
|
94
|
+
def each_child(&block)
|
95
|
+
if block_given?
|
96
|
+
type.each_child(&block)
|
97
|
+
self.block&.type&.each_child(&block)
|
98
|
+
else
|
99
|
+
enum_for :each_child
|
100
|
+
end
|
101
|
+
end
|
93
102
|
end
|
94
103
|
end
|
95
104
|
end
|
data/lib/steep/ast/types/self.rb
CHANGED
data/lib/steep/ast/types/top.rb
CHANGED
data/lib/steep/ast/types/var.rb
CHANGED
@@ -21,17 +21,21 @@ module Steep
|
|
21
21
|
|
22
22
|
alias eql? ==
|
23
23
|
|
24
|
-
def self.
|
24
|
+
def self.fresh_name(name)
|
25
25
|
@mutex ||= Mutex.new
|
26
26
|
|
27
27
|
@mutex.synchronize do
|
28
28
|
@max ||= 0
|
29
29
|
@max += 1
|
30
30
|
|
31
|
-
|
31
|
+
:"#{name}(#{@max})"
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
def self.fresh(name, location: nil)
|
36
|
+
new(name: fresh_name(name), location: location)
|
37
|
+
end
|
38
|
+
|
35
39
|
def to_s
|
36
40
|
name.to_s
|
37
41
|
end
|
@@ -48,12 +52,21 @@ module Steep
|
|
48
52
|
@fvs ||= Set.new([name])
|
49
53
|
end
|
50
54
|
|
55
|
+
include Helper::NoChild
|
56
|
+
|
51
57
|
def level
|
52
58
|
[0]
|
53
59
|
end
|
54
60
|
|
61
|
+
def update(name: self.name, location: self.location)
|
62
|
+
self.class.new(
|
63
|
+
name: name,
|
64
|
+
location: location
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
55
68
|
def with_location(new_location)
|
56
|
-
|
69
|
+
update(location: new_location)
|
57
70
|
end
|
58
71
|
end
|
59
72
|
end
|
data/lib/steep/ast/types/void.rb
CHANGED
@@ -26,20 +26,32 @@ module Steep
|
|
26
26
|
end
|
27
27
|
|
28
28
|
module ResultPrinter
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
29
|
+
def relation_message(relation)
|
30
|
+
case
|
31
|
+
when relation.type?
|
32
|
+
relation.to_s
|
33
|
+
when relation.method?
|
34
|
+
if relation.super_type.is_a?(Interface::MethodType) && relation.sub_type.is_a?(Interface::MethodType)
|
35
|
+
relation.to_s
|
36
|
+
end
|
37
|
+
when relation.interface?
|
38
|
+
nil
|
39
|
+
when relation.block?
|
40
|
+
nil
|
41
|
+
when relation.function?
|
42
|
+
nil
|
43
|
+
when relation.params?
|
44
|
+
nil
|
45
|
+
end
|
38
46
|
end
|
39
47
|
|
40
48
|
def detail_lines
|
41
49
|
StringIO.new.tap do |io|
|
42
|
-
|
50
|
+
result.failure_path&.reverse_each.map do |result|
|
51
|
+
relation_message(result.relation)
|
52
|
+
end.compact.each.with_index(1) do |message, index|
|
53
|
+
io.puts "#{" " * (index)}#{message}"
|
54
|
+
end
|
43
55
|
end.string.chomp
|
44
56
|
end
|
45
57
|
end
|
@@ -440,7 +452,7 @@ module Steep
|
|
440
452
|
super(node: node)
|
441
453
|
@interface_method = interface_method
|
442
454
|
@annotation_method = annotation_method
|
443
|
-
@result =
|
455
|
+
@result = relation
|
444
456
|
end
|
445
457
|
end
|
446
458
|
|
@@ -101,6 +101,23 @@ module Steep
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
+
class UnsatisfiableTypeApplication < Base
|
105
|
+
attr_reader :type_name
|
106
|
+
attr_reader :type_arg
|
107
|
+
attr_reader :type_param
|
108
|
+
|
109
|
+
def initialize(type_name:, type_arg:, type_param:, location:)
|
110
|
+
super(location: location)
|
111
|
+
@type_name = type_name
|
112
|
+
@type_arg = type_arg
|
113
|
+
@type_param = type_param
|
114
|
+
end
|
115
|
+
|
116
|
+
def header_line
|
117
|
+
"Type application of `#{type_name}` doesn't satisfy the constraints: #{type_arg} <: #{type_param.upper_bound}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
104
121
|
class InvalidMethodOverload < Base
|
105
122
|
attr_reader :class_name
|
106
123
|
attr_reader :method_name
|
@@ -307,6 +324,34 @@ module Steep
|
|
307
324
|
end
|
308
325
|
end
|
309
326
|
|
327
|
+
class RecursiveTypeAlias < Base
|
328
|
+
attr_reader :alias_names
|
329
|
+
|
330
|
+
def initialize(alias_names:, location:)
|
331
|
+
@alias_names = alias_names
|
332
|
+
super(location: location)
|
333
|
+
end
|
334
|
+
|
335
|
+
def header_line
|
336
|
+
"Type aliases cannot be *directly recursive*: #{alias_names.join(", ")}"
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class NonregularTypeAlias < Base
|
341
|
+
attr_reader :type_name
|
342
|
+
attr_reader :nonregular_type
|
343
|
+
|
344
|
+
def initialize(type_name:, nonregular_type:, location:)
|
345
|
+
@type_name = type_name
|
346
|
+
@nonregular_type = nonregular_type
|
347
|
+
@location = location
|
348
|
+
end
|
349
|
+
|
350
|
+
def header_line
|
351
|
+
"Type alias #{type_name} is defined *non-regular*: #{nonregular_type}"
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
310
355
|
def self.from_rbs_error(error, factory:)
|
311
356
|
case error
|
312
357
|
when RBS::ParsingError
|
@@ -388,6 +433,17 @@ module Steep
|
|
388
433
|
type_name: error.type_name,
|
389
434
|
member: error.member,
|
390
435
|
)
|
436
|
+
when RBS::RecursiveTypeAliasError
|
437
|
+
Diagnostic::Signature::RecursiveTypeAlias.new(
|
438
|
+
alias_names: error.alias_names,
|
439
|
+
location: error.location
|
440
|
+
)
|
441
|
+
when RBS::NonregularTypeAliasError
|
442
|
+
Diagnostic::Signature::NonregularTypeAlias.new(
|
443
|
+
type_name: error.diagnostic.type_name,
|
444
|
+
nonregular_type: factory.type(error.diagnostic.nonregular_type),
|
445
|
+
location: error.location
|
446
|
+
)
|
391
447
|
else
|
392
448
|
raise error
|
393
449
|
end
|
@@ -77,7 +77,7 @@ module Steep
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def to_s
|
80
|
-
type_params = !self.type_params.empty? ? "[#{self.type_params.
|
80
|
+
type_params = !self.type_params.empty? ? "[#{self.type_params.join(", ")}] " : ""
|
81
81
|
params = type.params.to_s
|
82
82
|
return_type = type.return_type
|
83
83
|
block = self.block ? " #{self.block}" : ""
|
@@ -95,11 +95,9 @@ module Steep
|
|
95
95
|
# Returns a new method type which can be used for the method implementation type of both `self` and `other`.
|
96
96
|
#
|
97
97
|
def unify_overload(other)
|
98
|
-
|
99
|
-
|
100
|
-
type_params
|
101
|
-
s2 = Substitution.build(other.type_params)
|
102
|
-
type_params.push(*s2.dictionary.values.map(&:name))
|
98
|
+
type_params_1, s1 = TypeParam.rename(self.type_params)
|
99
|
+
type_params_2, s2 = TypeParam.rename(other.type_params)
|
100
|
+
type_params = type_params_1 + type_params_2
|
103
101
|
|
104
102
|
block = case
|
105
103
|
when self.block && other.block
|
@@ -134,17 +132,12 @@ module Steep
|
|
134
132
|
# Returns nil if self and other are incompatible.
|
135
133
|
#
|
136
134
|
def |(other)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
unless (common_type_params = (self_type_params & other_type_params).to_a).empty?
|
141
|
-
fresh_types = common_type_params.map {|name| AST::Types::Var.fresh(name) }
|
142
|
-
fresh_names = fresh_types.map(&:name)
|
143
|
-
subst = Substitution.build(common_type_params, fresh_types)
|
144
|
-
other = other.instantiate(subst)
|
145
|
-
type_params = (self_type_params + (other_type_params - common_type_params + Set.new(fresh_names))).to_a
|
135
|
+
if other.type_params.empty?
|
136
|
+
type_params = self.type_params
|
146
137
|
else
|
147
|
-
|
138
|
+
other_params, s2 = TypeParam.rename(other.type_params)
|
139
|
+
other = other.instantiate(s2)
|
140
|
+
type_params = self.type_params + other_params
|
148
141
|
end
|
149
142
|
|
150
143
|
params = self.type.params & other.type.params or return
|
@@ -182,17 +175,12 @@ module Steep
|
|
182
175
|
# Returns nil if self and other are incompatible.
|
183
176
|
#
|
184
177
|
def &(other)
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
unless (common_type_params = (self_type_params & other_type_params).to_a).empty?
|
189
|
-
fresh_types = common_type_params.map {|name| AST::Types::Var.fresh(name) }
|
190
|
-
fresh_names = fresh_types.map(&:name)
|
191
|
-
subst = Substitution.build(common_type_params, fresh_types)
|
192
|
-
other = other.subst(subst)
|
193
|
-
type_params = (self_type_params + (other_type_params - common_type_params + Set.new(fresh_names))).to_a
|
178
|
+
if other.type_params.empty?
|
179
|
+
type_params = self.type_params
|
194
180
|
else
|
195
|
-
|
181
|
+
other_params, s2 = TypeParam.rename(other.type_params)
|
182
|
+
other = other.instantiate(s2)
|
183
|
+
type_params = self.type_params + other_params
|
196
184
|
end
|
197
185
|
|
198
186
|
params = self.type.params | other.type.params
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Steep
|
2
|
+
module Interface
|
3
|
+
class TypeParam
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :upper_bound
|
6
|
+
attr_reader :variance
|
7
|
+
attr_reader :unchecked
|
8
|
+
attr_reader :location
|
9
|
+
|
10
|
+
def initialize(name:, upper_bound:, variance:, unchecked:, location: nil)
|
11
|
+
@name = name
|
12
|
+
@upper_bound = upper_bound
|
13
|
+
@variance = variance
|
14
|
+
@unchecked = unchecked
|
15
|
+
@location = location
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
other.is_a?(TypeParam) &&
|
20
|
+
other.name == name &&
|
21
|
+
other.upper_bound == upper_bound &&
|
22
|
+
other.variance == variance &&
|
23
|
+
other.unchecked == unchecked
|
24
|
+
end
|
25
|
+
|
26
|
+
alias eql? ==
|
27
|
+
|
28
|
+
def hash
|
29
|
+
name.hash ^ upper_bound.hash ^ variance.hash ^ unchecked.hash
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.rename(params, conflicting_names = params.map(&:name))
|
33
|
+
unless conflicting_names.empty?
|
34
|
+
new_names = conflicting_names.map {|n| AST::Types::Var.fresh_name(n) }
|
35
|
+
hash = conflicting_names.zip(new_names).to_h
|
36
|
+
new_types = new_names.map {|n| AST::Types::Var.new(name: n) }
|
37
|
+
|
38
|
+
subst = Substitution.build(conflicting_names, new_types)
|
39
|
+
|
40
|
+
[
|
41
|
+
params.map do |param|
|
42
|
+
if hash.key?(param.name) || param.upper_bound
|
43
|
+
TypeParam.new(
|
44
|
+
name: hash[param.name] || param.name,
|
45
|
+
upper_bound: param.upper_bound&.subst(subst),
|
46
|
+
variance: param.variance,
|
47
|
+
unchecked: param.unchecked,
|
48
|
+
location: param.location
|
49
|
+
)
|
50
|
+
else
|
51
|
+
param
|
52
|
+
end
|
53
|
+
end,
|
54
|
+
subst
|
55
|
+
]
|
56
|
+
else
|
57
|
+
[params, Substitution.empty]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
buf = ""
|
63
|
+
|
64
|
+
if unchecked
|
65
|
+
buf << "unchecked "
|
66
|
+
end
|
67
|
+
|
68
|
+
case variance
|
69
|
+
when :covariant
|
70
|
+
buf << "out "
|
71
|
+
when :contravariant
|
72
|
+
buf << "in "
|
73
|
+
end
|
74
|
+
|
75
|
+
buf << name.to_s
|
76
|
+
|
77
|
+
if upper_bound
|
78
|
+
buf << " < #{upper_bound}"
|
79
|
+
end
|
80
|
+
|
81
|
+
buf
|
82
|
+
end
|
83
|
+
|
84
|
+
def update(name: self.name, upper_bound: self.upper_bound, variance: self.variance, unchecked: self.unchecked, location: self.location)
|
85
|
+
TypeParam.new(
|
86
|
+
name: name,
|
87
|
+
upper_bound: upper_bound,
|
88
|
+
variance: variance,
|
89
|
+
unchecked: unchecked,
|
90
|
+
location: location
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def subst(s)
|
95
|
+
if u = upper_bound
|
96
|
+
update(upper_bound: u.subst(s))
|
97
|
+
else
|
98
|
+
self
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -144,7 +144,7 @@ module Steep
|
|
144
144
|
if job.guid == current_type_check_guid
|
145
145
|
Steep.logger.info { "Processing ValidateAppSignature for guid=#{job.guid}, path=#{job.path}" }
|
146
146
|
service.validate_signature(path: project.relative_path(job.path)) do |path, diagnostics|
|
147
|
-
formatter = Diagnostic::LSPFormatter.new({})
|
147
|
+
formatter = Diagnostic::LSPFormatter.new({}, **{})
|
148
148
|
|
149
149
|
writer.write(
|
150
150
|
method: :"textDocument/publishDiagnostics",
|
@@ -162,7 +162,7 @@ module Steep
|
|
162
162
|
if job.guid == current_type_check_guid
|
163
163
|
Steep.logger.info { "Processing ValidateLibrarySignature for guid=#{job.guid}, path=#{job.path}" }
|
164
164
|
service.validate_signature(path: job.path) do |path, diagnostics|
|
165
|
-
formatter = Diagnostic::LSPFormatter.new({})
|
165
|
+
formatter = Diagnostic::LSPFormatter.new({}, **{})
|
166
166
|
|
167
167
|
writer.write(
|
168
168
|
method: :"textDocument/publishDiagnostics",
|
@@ -224,7 +224,7 @@ module Steep
|
|
224
224
|
Steep.measure "Loading new decls" do
|
225
225
|
updated_files.each_value do |content|
|
226
226
|
case decls = content.decls
|
227
|
-
when RBS::
|
227
|
+
when RBS::BaseError
|
228
228
|
errors << content.decls
|
229
229
|
else
|
230
230
|
begin
|
@@ -283,7 +283,7 @@ module Steep
|
|
283
283
|
def rescue_rbs_error(errors)
|
284
284
|
begin
|
285
285
|
yield
|
286
|
-
rescue RBS::
|
286
|
+
rescue RBS::BaseError => exn
|
287
287
|
errors << exn
|
288
288
|
end
|
289
289
|
end
|
@@ -366,7 +366,8 @@ module Steep
|
|
366
366
|
self_type: AST::Builtin::Object.instance_type,
|
367
367
|
type_env: type_env,
|
368
368
|
lvar_env: lvar_env,
|
369
|
-
call_context: TypeInference::MethodCall::TopLevelContext.new
|
369
|
+
call_context: TypeInference::MethodCall::TopLevelContext.new,
|
370
|
+
variable_context: TypeInference::Context::TypeVariableContext.empty
|
370
371
|
)
|
371
372
|
|
372
373
|
typing = Typing.new(source: source, root_context: context)
|