steep 0.23.0 → 0.29.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 +46 -0
- data/bin/smoke_runner.rb +3 -4
- data/lib/steep.rb +4 -3
- data/lib/steep/annotation_parser.rb +2 -4
- data/lib/steep/ast/builtin.rb +11 -21
- data/lib/steep/ast/types.rb +5 -3
- data/lib/steep/ast/types/any.rb +1 -3
- data/lib/steep/ast/types/boolean.rb +1 -3
- data/lib/steep/ast/types/bot.rb +1 -3
- data/lib/steep/ast/types/class.rb +2 -2
- data/lib/steep/ast/types/factory.rb +249 -89
- data/lib/steep/ast/types/helper.rb +6 -0
- data/lib/steep/ast/types/instance.rb +2 -2
- data/lib/steep/ast/types/intersection.rb +20 -13
- data/lib/steep/ast/types/literal.rb +1 -3
- data/lib/steep/ast/types/logic.rb +63 -0
- data/lib/steep/ast/types/name.rb +15 -67
- data/lib/steep/ast/types/nil.rb +1 -3
- data/lib/steep/ast/types/proc.rb +5 -2
- data/lib/steep/ast/types/record.rb +9 -4
- data/lib/steep/ast/types/self.rb +1 -1
- data/lib/steep/ast/types/top.rb +1 -3
- data/lib/steep/ast/types/tuple.rb +5 -3
- data/lib/steep/ast/types/union.rb +16 -9
- data/lib/steep/ast/types/var.rb +2 -2
- data/lib/steep/ast/types/void.rb +1 -3
- data/lib/steep/drivers/check.rb +4 -0
- data/lib/steep/errors.rb +14 -0
- data/lib/steep/interface/interface.rb +5 -62
- data/lib/steep/interface/method_type.rb +394 -93
- data/lib/steep/interface/substitution.rb +48 -6
- data/lib/steep/module_helper.rb +25 -0
- data/lib/steep/project/completion_provider.rb +48 -51
- data/lib/steep/project/file.rb +3 -3
- data/lib/steep/project/hover_content.rb +4 -6
- data/lib/steep/project/target.rb +5 -2
- data/lib/steep/server/base_worker.rb +5 -3
- data/lib/steep/server/code_worker.rb +2 -0
- data/lib/steep/server/master.rb +10 -1
- data/lib/steep/signature/validator.rb +3 -3
- data/lib/steep/source.rb +4 -3
- data/lib/steep/subtyping/check.rb +46 -59
- data/lib/steep/subtyping/constraints.rb +8 -0
- data/lib/steep/type_construction.rb +771 -513
- data/lib/steep/type_inference/block_params.rb +5 -0
- data/lib/steep/type_inference/constant_env.rb +3 -6
- data/lib/steep/type_inference/context.rb +8 -0
- data/lib/steep/type_inference/context_array.rb +4 -3
- data/lib/steep/type_inference/logic.rb +31 -0
- data/lib/steep/type_inference/logic_type_interpreter.rb +219 -0
- data/lib/steep/type_inference/type_env.rb +2 -2
- data/lib/steep/typing.rb +7 -0
- data/lib/steep/version.rb +1 -1
- data/smoke/alias/a.rb +1 -1
- data/smoke/case/a.rb +1 -1
- data/smoke/hash/d.rb +1 -1
- data/smoke/if/a.rb +1 -1
- data/smoke/module/a.rb +1 -1
- data/smoke/rescue/a.rb +4 -13
- data/smoke/type_case/a.rb +0 -7
- data/steep.gemspec +2 -2
- metadata +10 -10
- data/lib/steep/ast/method_type.rb +0 -126
- data/lib/steep/ast/namespace.rb +0 -80
- data/lib/steep/names.rb +0 -86
@@ -6,7 +6,7 @@ module Steep
|
|
6
6
|
attr_reader :factory
|
7
7
|
attr_reader :table
|
8
8
|
|
9
|
-
# ConstantEnv receives an
|
9
|
+
# ConstantEnv receives an TypeName as a context, not a Namespace, because this is a simulation of Ruby.
|
10
10
|
# Any namespace is a module or class.
|
11
11
|
def initialize(factory:, context:)
|
12
12
|
@cache = {}
|
@@ -17,16 +17,13 @@ module Steep
|
|
17
17
|
|
18
18
|
def lookup(name)
|
19
19
|
cache[name] ||= begin
|
20
|
-
constant = table.resolve_constant_reference(
|
21
|
-
factory.type_name_1(name),
|
22
|
-
context: context.map {|namespace| factory.namespace_1(namespace) }
|
23
|
-
)
|
20
|
+
constant = table.resolve_constant_reference(name, context: context)
|
24
21
|
|
25
22
|
if constant
|
26
23
|
factory.type(constant.type)
|
27
24
|
end
|
28
25
|
rescue => exn
|
29
|
-
Steep.logger.
|
26
|
+
Steep.logger.debug "Looking up a constant failed: name=#{name}, context=[#{context.join(", ")}], error=#{exn.inspect}"
|
30
27
|
nil
|
31
28
|
end
|
32
29
|
end
|
@@ -70,6 +70,14 @@ module Steep
|
|
70
70
|
def const_context
|
71
71
|
const_env.context
|
72
72
|
end
|
73
|
+
|
74
|
+
def class_variables
|
75
|
+
if module_definition
|
76
|
+
@class_variables ||= module_definition.class_variables.transform_values do |var_def|
|
77
|
+
var_def.type
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
73
81
|
end
|
74
82
|
|
75
83
|
attr_reader :method_context
|
@@ -35,12 +35,13 @@ module Steep
|
|
35
35
|
|
36
36
|
def insert_context(range, context:, entry: self.root)
|
37
37
|
entry.sub_entries.each do |sub|
|
38
|
-
next if sub.range.begin
|
39
|
-
next if range.begin
|
38
|
+
next if sub.range.begin <= range.begin && range.end <= sub.range.end
|
39
|
+
next if range.begin <= sub.range.begin && sub.range.end <= range.end
|
40
40
|
next if range.end <= sub.range.begin
|
41
41
|
next if sub.range.end <= range.begin
|
42
42
|
|
43
|
-
|
43
|
+
Steep.logger.error { "Range crossing: sub range=#{sub.range}, new range=#{range}" }
|
44
|
+
raise
|
44
45
|
end
|
45
46
|
|
46
47
|
sup = entry.sub_entries.find do |sub|
|
@@ -47,6 +47,37 @@ module Steep
|
|
47
47
|
f.merge([node])
|
48
48
|
]
|
49
49
|
|
50
|
+
when :masgn
|
51
|
+
lhs, rhs = node.children
|
52
|
+
|
53
|
+
lt, lf = nodes(node: lhs)
|
54
|
+
rt, rf = nodes(node: rhs)
|
55
|
+
|
56
|
+
[
|
57
|
+
(lt + rt).merge([node]),
|
58
|
+
(lf + rf).merge([node])
|
59
|
+
]
|
60
|
+
|
61
|
+
when :mlhs
|
62
|
+
nodes = [node]
|
63
|
+
|
64
|
+
node.children.each do |child|
|
65
|
+
case child.type
|
66
|
+
when :lvasgn
|
67
|
+
nodes << child
|
68
|
+
when :splat
|
69
|
+
if node.children[0].type == :lvasgn
|
70
|
+
nodes << child
|
71
|
+
nodes << child.children[0]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
[
|
77
|
+
Result.new(nodes),
|
78
|
+
Result.new(nodes)
|
79
|
+
]
|
80
|
+
|
50
81
|
when :and
|
51
82
|
lhs, rhs = node.children
|
52
83
|
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module Steep
|
2
|
+
module TypeInference
|
3
|
+
class LogicTypeInterpreter
|
4
|
+
attr_reader :subtyping
|
5
|
+
attr_reader :typing
|
6
|
+
|
7
|
+
def initialize(subtyping:, typing:)
|
8
|
+
@subtyping = subtyping
|
9
|
+
@typing = typing
|
10
|
+
end
|
11
|
+
|
12
|
+
def factory
|
13
|
+
subtyping.factory
|
14
|
+
end
|
15
|
+
|
16
|
+
def guess_type_from_method(node)
|
17
|
+
if node.type == :send
|
18
|
+
method = node.children[1]
|
19
|
+
case method
|
20
|
+
when :is_a?, :kind_of?, :instance_of?
|
21
|
+
AST::Types::Logic::ReceiverIsArg.new
|
22
|
+
when :nil?
|
23
|
+
AST::Types::Logic::ReceiverIsNil.new
|
24
|
+
when :!
|
25
|
+
AST::Types::Logic::Not.new
|
26
|
+
when :===
|
27
|
+
AST::Types::Logic::ArgIsReceiver.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def eval(env:, type:, node:)
|
33
|
+
value_node, vars = decompose_value(node)
|
34
|
+
|
35
|
+
truthy_env = env
|
36
|
+
falsy_env = env
|
37
|
+
|
38
|
+
if type.is_a?(AST::Types::Any)
|
39
|
+
type = guess_type_from_method(node) || type
|
40
|
+
end
|
41
|
+
|
42
|
+
if type.is_a?(AST::Types::Logic::Base)
|
43
|
+
vars.each do |var_name|
|
44
|
+
truthy_env = truthy_env.assign!(var_name, node: node, type: AST::Builtin.true_type)
|
45
|
+
falsy_env = truthy_env.assign!(var_name, node: node, type: AST::Builtin.false_type)
|
46
|
+
end
|
47
|
+
|
48
|
+
case type
|
49
|
+
when AST::Types::Logic::ReceiverIsNil
|
50
|
+
case value_node.type
|
51
|
+
when :send
|
52
|
+
receiver = value_node.children[0]
|
53
|
+
|
54
|
+
if receiver
|
55
|
+
_, receiver_vars = decompose_value(receiver)
|
56
|
+
|
57
|
+
receiver_vars.each do |receiver_var|
|
58
|
+
var_type = env[receiver_var]
|
59
|
+
truthy_type, falsy_type = factory.unwrap_optional(var_type)
|
60
|
+
|
61
|
+
truthy_env = truthy_env.assign!(receiver_var, node: node, type: falsy_type || AST::Builtin.nil_type)
|
62
|
+
falsy_env = falsy_env.assign!(receiver_var, node: node, type: truthy_type)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
when AST::Types::Logic::ReceiverIsArg
|
67
|
+
case value_node.type
|
68
|
+
when :send
|
69
|
+
receiver, _, arg = value_node.children
|
70
|
+
|
71
|
+
if receiver
|
72
|
+
_, receiver_vars = decompose_value(receiver)
|
73
|
+
arg_type = typing.type_of(node: arg)
|
74
|
+
|
75
|
+
if arg_type.is_a?(AST::Types::Name::Singleton)
|
76
|
+
receiver_vars.each do |var_name|
|
77
|
+
var_type = env[var_name]
|
78
|
+
truthy_type, falsy_type = type_case_select(var_type, arg_type.name)
|
79
|
+
|
80
|
+
truthy_env = truthy_env.assign!(var_name, node: node, type: truthy_type)
|
81
|
+
falsy_env = falsy_env.assign!(var_name, node: node, type: falsy_type)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
when AST::Types::Logic::ArgIsReceiver
|
87
|
+
case value_node.type
|
88
|
+
when :send
|
89
|
+
receiver, _, arg = value_node.children
|
90
|
+
|
91
|
+
if receiver
|
92
|
+
_, arg_vars = decompose_value(arg)
|
93
|
+
receiver_type = typing.type_of(node: receiver)
|
94
|
+
|
95
|
+
if receiver_type.is_a?(AST::Types::Name::Singleton)
|
96
|
+
arg_vars.each do |var_name|
|
97
|
+
var_type = env[var_name]
|
98
|
+
truthy_type, falsy_type = type_case_select(var_type, receiver_type.name)
|
99
|
+
|
100
|
+
truthy_env = truthy_env.assign!(var_name, node: node, type: truthy_type)
|
101
|
+
falsy_env = falsy_env.assign!(var_name, node: node, type: falsy_type)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
when AST::Types::Logic::Not
|
107
|
+
receiver, * = value_node.children
|
108
|
+
receiver_type = typing.type_of(node: receiver)
|
109
|
+
falsy_env, truthy_env = eval(env: env, type: receiver_type, node: receiver)
|
110
|
+
end
|
111
|
+
else
|
112
|
+
_, vars = decompose_value(node)
|
113
|
+
|
114
|
+
vars.each do |var_name|
|
115
|
+
var_type = env[var_name]
|
116
|
+
truthy_type, falsy_type = factory.unwrap_optional(var_type)
|
117
|
+
|
118
|
+
if falsy_type
|
119
|
+
truthy_env = truthy_env.assign!(var_name, node: node, type: truthy_type)
|
120
|
+
falsy_env = falsy_env.assign!(var_name, node: node, type: falsy_type)
|
121
|
+
else
|
122
|
+
truthy_env = truthy_env.assign!(var_name, node: node, type: truthy_type)
|
123
|
+
falsy_env = falsy_env.assign!(var_name, node: node, type: truthy_type)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
[truthy_env, falsy_env]
|
129
|
+
end
|
130
|
+
|
131
|
+
def decompose_value(node)
|
132
|
+
case node.type
|
133
|
+
when :lvar
|
134
|
+
[node, Set[node.children[0].name]]
|
135
|
+
when :masgn
|
136
|
+
lhs, rhs = node.children
|
137
|
+
lhs_vars = lhs.children.select {|m| m.type == :lvasgn }.map {|m| m.children[0].name }
|
138
|
+
val, vars = decompose_value(rhs)
|
139
|
+
[val, vars + lhs_vars]
|
140
|
+
when :lvasgn
|
141
|
+
var, rhs = node.children
|
142
|
+
val, vars = decompose_value(rhs)
|
143
|
+
[val, vars + [var.name]]
|
144
|
+
when :begin
|
145
|
+
decompose_value(node.children.last)
|
146
|
+
when :and
|
147
|
+
left, right = node.children
|
148
|
+
_, left_vars = decompose_value(left)
|
149
|
+
val, right_vars = decompose_value(right)
|
150
|
+
[val, left_vars + right_vars]
|
151
|
+
else
|
152
|
+
[node, Set[]]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def type_case_select(type, klass)
|
157
|
+
truth_types, false_types = type_case_select0(type, klass)
|
158
|
+
|
159
|
+
[
|
160
|
+
AST::Types::Union.build(types: truth_types),
|
161
|
+
AST::Types::Union.build(types: false_types)
|
162
|
+
]
|
163
|
+
end
|
164
|
+
|
165
|
+
def type_case_select0(type, klass)
|
166
|
+
instance_type = factory.instance_type(klass)
|
167
|
+
|
168
|
+
case type
|
169
|
+
when AST::Types::Union
|
170
|
+
truthy_types = []
|
171
|
+
falsy_types = []
|
172
|
+
|
173
|
+
type.types.each do |ty|
|
174
|
+
truths, falses = type_case_select0(ty, klass)
|
175
|
+
|
176
|
+
if truths.empty?
|
177
|
+
falsy_types.push(ty)
|
178
|
+
else
|
179
|
+
truthy_types.push(*truths)
|
180
|
+
falsy_types.push(*falses)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
[truthy_types, falsy_types]
|
185
|
+
|
186
|
+
when AST::Types::Name::Instance
|
187
|
+
relation = Subtyping::Relation.new(sub_type: type, super_type: instance_type)
|
188
|
+
if subtyping.check(relation, constraints: Subtyping::Constraints.empty, self_type: AST::Types::Self.new).success?
|
189
|
+
[
|
190
|
+
[type],
|
191
|
+
[]
|
192
|
+
]
|
193
|
+
else
|
194
|
+
[
|
195
|
+
[],
|
196
|
+
[type]
|
197
|
+
]
|
198
|
+
end
|
199
|
+
|
200
|
+
when AST::Types::Name::Alias
|
201
|
+
ty = factory.expand_alias(type)
|
202
|
+
type_case_select0(ty, klass)
|
203
|
+
|
204
|
+
when AST::Types::Any, AST::Types::Top
|
205
|
+
[
|
206
|
+
[instance_type],
|
207
|
+
[type]
|
208
|
+
]
|
209
|
+
|
210
|
+
else
|
211
|
+
[
|
212
|
+
[],
|
213
|
+
[type]
|
214
|
+
]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -57,7 +57,7 @@ module Steep
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
# @type method assert: (const:
|
60
|
+
# @type method assert: (const: TypeName) { () -> void } -> AST::Type
|
61
61
|
# | (gvar: Symbol) { () -> void } -> AST::Type
|
62
62
|
# | (ivar: Symbol) { () -> void } -> AST::Type
|
63
63
|
def get(const: nil, gvar: nil, ivar: nil)
|
@@ -98,7 +98,7 @@ module Steep
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
# @type method assign: (const:
|
101
|
+
# @type method assign: (const: TypeName, type: AST::Type) { (Subtyping::Result::Failure | nil) -> void } -> AST::Type
|
102
102
|
# | (gvar: Symbol, type: AST::Type) { (Subtyping::Result::Failure | nil) -> void } -> AST::Type
|
103
103
|
# | (ivar: Symbol, type: AST::Type) { (Subtyping::Result::Failure | nil) -> void } -> AST::Type
|
104
104
|
def assign(const: nil, gvar: nil, ivar: nil, type:, self_type:, &block)
|
data/lib/steep/typing.rb
CHANGED
@@ -126,6 +126,13 @@ module Steep
|
|
126
126
|
end_pos = node.loc.end.begin_pos
|
127
127
|
add_context(begin_pos..end_pos, context: context)
|
128
128
|
|
129
|
+
when :for
|
130
|
+
_, collection, _ = node.children
|
131
|
+
|
132
|
+
begin_pos = collection.loc.expression.end_pos
|
133
|
+
end_pos = node.loc.end.begin_pos
|
134
|
+
|
135
|
+
add_context(begin_pos..end_pos, context: context)
|
129
136
|
else
|
130
137
|
raise "Unexpected node for insert_context: #{node.type}"
|
131
138
|
end
|
data/lib/steep/version.rb
CHANGED
data/smoke/alias/a.rb
CHANGED
data/smoke/case/a.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# @type var a: Integer
|
2
2
|
|
3
|
-
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Array[::String] |
|
3
|
+
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Integer | ::Array[::String] | nil | ::String)
|
4
4
|
a = case 1
|
5
5
|
when 2
|
6
6
|
1
|
data/smoke/hash/d.rb
CHANGED
@@ -2,5 +2,5 @@
|
|
2
2
|
|
3
3
|
params = { id: 30, name: "Matz" }
|
4
4
|
|
5
|
-
# !expects IncompatibleAssignment: lhs_type={ :
|
5
|
+
# !expects IncompatibleAssignment: lhs_type={ :id => ::Integer, :name => ::String }, rhs_type={ :email => ::String, :id => ::String, :name => ::String }
|
6
6
|
params = { id: "30", name: "foo", email: "matsumoto@soutaro.com" }
|
data/smoke/if/a.rb
CHANGED
data/smoke/module/a.rb
CHANGED
@@ -13,7 +13,7 @@ module A
|
|
13
13
|
# !expects IncompatibleAssignment: lhs_type=::String, rhs_type=::Integer
|
14
14
|
s = n
|
15
15
|
|
16
|
-
# !expects NoMethodError: type=(::
|
16
|
+
# !expects NoMethodError: type=(::Object & ::_Each2[::Integer, ::A] & ::A), method=foo
|
17
17
|
foo()
|
18
18
|
|
19
19
|
n
|
data/smoke/rescue/a.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# @type var a: Integer
|
2
2
|
|
3
|
-
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::
|
3
|
+
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::String | ::Integer)
|
4
4
|
a = begin
|
5
5
|
'foo'
|
6
6
|
rescue
|
@@ -9,12 +9,12 @@ a = begin
|
|
9
9
|
|
10
10
|
# @type var b: Integer
|
11
11
|
|
12
|
-
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::
|
12
|
+
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::String | ::Integer)
|
13
13
|
b = 'foo' rescue 1
|
14
14
|
|
15
15
|
# @type var c: Integer
|
16
16
|
|
17
|
-
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::
|
17
|
+
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::String | ::Symbol | ::Integer)
|
18
18
|
c = begin
|
19
19
|
'foo'
|
20
20
|
rescue RuntimeError
|
@@ -23,18 +23,9 @@ c = begin
|
|
23
23
|
1
|
24
24
|
end
|
25
25
|
|
26
|
-
# @type var d: Integer
|
27
|
-
|
28
|
-
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=::String
|
29
|
-
d = begin
|
30
|
-
1
|
31
|
-
else
|
32
|
-
'foo'
|
33
|
-
end
|
34
|
-
|
35
26
|
# @type var e: Integer
|
36
27
|
|
37
|
-
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Array[::Integer] | ::
|
28
|
+
# !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Array[::Integer] | ::Symbol | ::Integer)
|
38
29
|
e = begin
|
39
30
|
'foo'
|
40
31
|
rescue RuntimeError
|