steep 0.27.0 → 0.31.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/CHANGELOG.md +38 -0
- data/bin/smoke_runner.rb +3 -4
- data/bin/steep-prof +1 -2
- data/lib/steep.rb +6 -4
- data/lib/steep/annotation_parser.rb +2 -4
- data/lib/steep/ast/builtin.rb +11 -21
- data/lib/steep/ast/types/factory.rb +234 -101
- data/lib/steep/ast/types/intersection.rb +12 -9
- data/lib/steep/ast/types/logic.rb +63 -0
- data/lib/steep/ast/types/name.rb +2 -58
- data/lib/steep/ast/types/union.rb +5 -6
- data/lib/steep/errors.rb +14 -0
- data/lib/steep/interface/interface.rb +5 -62
- data/lib/steep/interface/method_type.rb +346 -75
- data/lib/steep/interface/substitution.rb +16 -4
- data/lib/steep/module_helper.rb +25 -0
- data/lib/steep/project.rb +25 -0
- data/lib/steep/project/completion_provider.rb +57 -58
- data/lib/steep/project/file_loader.rb +7 -2
- data/lib/steep/project/hover_content.rb +92 -83
- data/lib/steep/project/signature_file.rb +33 -0
- data/lib/steep/project/{file.rb → source_file.rb} +24 -54
- data/lib/steep/project/target.rb +31 -12
- data/lib/steep/server/base_worker.rb +1 -0
- data/lib/steep/server/code_worker.rb +31 -45
- data/lib/steep/server/interaction_worker.rb +42 -38
- data/lib/steep/server/master.rb +23 -33
- data/lib/steep/server/utils.rb +46 -13
- data/lib/steep/server/worker_process.rb +4 -2
- data/lib/steep/signature/validator.rb +3 -3
- data/lib/steep/source.rb +60 -3
- data/lib/steep/subtyping/check.rb +34 -47
- data/lib/steep/subtyping/constraints.rb +8 -0
- data/lib/steep/type_construction.rb +366 -365
- data/lib/steep/type_inference/block_params.rb +5 -0
- data/lib/steep/type_inference/constant_env.rb +2 -5
- data/lib/steep/type_inference/logic_type_interpreter.rb +219 -0
- data/lib/steep/type_inference/type_env.rb +2 -2
- data/lib/steep/version.rb +1 -1
- data/smoke/alias/a.rb +1 -1
- data/smoke/case/a.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/toplevel/Steepfile +5 -0
- data/smoke/toplevel/a.rb +4 -0
- data/smoke/toplevel/a.rbs +3 -0
- data/smoke/type_case/a.rb +0 -7
- data/steep.gemspec +3 -3
- metadata +20 -16
- data/lib/steep/ast/method_type.rb +0 -126
- data/lib/steep/ast/namespace.rb +0 -80
- data/lib/steep/names.rb +0 -86
@@ -90,20 +90,32 @@ module Steep
|
|
90
90
|
|
91
91
|
def except(vars)
|
92
92
|
self.class.new(
|
93
|
-
dictionary: dictionary
|
93
|
+
dictionary: dictionary,
|
94
94
|
instance_type: instance_type,
|
95
95
|
module_type: module_type,
|
96
96
|
self_type: self_type
|
97
|
-
)
|
97
|
+
).except!(vars)
|
98
98
|
end
|
99
99
|
|
100
|
-
def
|
100
|
+
def except!(vars)
|
101
|
+
vars.each do |var|
|
102
|
+
dictionary.delete(var)
|
103
|
+
end
|
104
|
+
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
def merge!(s, overwrite: false)
|
101
109
|
dictionary.transform_values! {|ty| ty.subst(s) }
|
102
110
|
dictionary.merge!(s.dictionary) do |key, a, b|
|
103
111
|
if a == b
|
104
112
|
a
|
105
113
|
else
|
106
|
-
|
114
|
+
if overwrite
|
115
|
+
b
|
116
|
+
else
|
117
|
+
raise "Duplicated key on merge!: #{key}, #{a}, #{b} (#{self})"
|
118
|
+
end
|
107
119
|
end
|
108
120
|
end
|
109
121
|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Steep
|
2
|
+
module ModuleHelper
|
3
|
+
def module_name_from_node(node)
|
4
|
+
case node.type
|
5
|
+
when :const, :casgn
|
6
|
+
namespace = namespace_from_node(node.children[0]) or return
|
7
|
+
name = node.children[1]
|
8
|
+
RBS::TypeName.new(name: name, namespace: namespace)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def namespace_from_node(node)
|
13
|
+
case node&.type
|
14
|
+
when nil
|
15
|
+
RBS::Namespace.empty
|
16
|
+
when :cbase
|
17
|
+
RBS::Namespace.root
|
18
|
+
when :const
|
19
|
+
namespace_from_node(node.children[0])&.yield_self do |parent|
|
20
|
+
parent.append(node.children[1])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/steep/project.rb
CHANGED
@@ -24,6 +24,31 @@ module Steep
|
|
24
24
|
(base_dir + path).cleanpath
|
25
25
|
end
|
26
26
|
|
27
|
+
def target_for_source_path(path)
|
28
|
+
targets.find do |target|
|
29
|
+
target.possible_source_file?(path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def targets_for_path(path)
|
34
|
+
if target = target_for_source_path(path)
|
35
|
+
[target, []]
|
36
|
+
else
|
37
|
+
[
|
38
|
+
nil,
|
39
|
+
targets.select do |target|
|
40
|
+
target.possible_signature_file?(path)
|
41
|
+
end
|
42
|
+
]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def all_source_files
|
47
|
+
targets.each.with_object(Set[]) do |target, paths|
|
48
|
+
paths.merge(target.source_files.keys)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
27
52
|
def type_of_node(path:, line:, column:)
|
28
53
|
source_file = targets.map {|target| target.source_files[path] }.compact[0]
|
29
54
|
|
@@ -10,13 +10,9 @@ module Steep
|
|
10
10
|
|
11
11
|
InstanceVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
|
12
12
|
LocalVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
|
13
|
-
MethodNameItem = Struct.new(:identifier, :range, :
|
14
|
-
def method_type
|
15
|
-
def_type.type
|
16
|
-
end
|
17
|
-
|
13
|
+
MethodNameItem = Struct.new(:identifier, :range, :method_def, :method_type, :inherited_method, keyword_init: true) do
|
18
14
|
def comment
|
19
|
-
|
15
|
+
method_def&.comment
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
@@ -33,11 +29,13 @@ module Steep
|
|
33
29
|
@subtyping = subtyping
|
34
30
|
end
|
35
31
|
|
36
|
-
def type_check!(text)
|
32
|
+
def type_check!(text, line:, column:)
|
37
33
|
@modified_text = text
|
38
34
|
|
39
35
|
Steep.measure "parsing" do
|
40
|
-
@source = SourceFile
|
36
|
+
@source = SourceFile
|
37
|
+
.parse(text, path: path, factory: subtyping.factory)
|
38
|
+
.without_unrelated_defs(line: line, column: column)
|
41
39
|
end
|
42
40
|
|
43
41
|
Steep.measure "typechecking" do
|
@@ -57,7 +55,7 @@ module Steep
|
|
57
55
|
begin
|
58
56
|
Steep.logger.tagged "completion_provider#run(line: #{line}, column: #{column})" do
|
59
57
|
Steep.measure "type_check!" do
|
60
|
-
type_check!(source_text)
|
58
|
+
type_check!(source_text, line: line, column: column)
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
@@ -70,11 +68,11 @@ module Steep
|
|
70
68
|
case possible_trigger
|
71
69
|
when "."
|
72
70
|
source_text[index-1] = " "
|
73
|
-
type_check!(source_text)
|
71
|
+
type_check!(source_text, line: line, column: column)
|
74
72
|
items_for_dot(position: position)
|
75
73
|
when "@"
|
76
74
|
source_text[index-1] = " "
|
77
|
-
type_check!(source_text)
|
75
|
+
type_check!(source_text, line: line, column: column)
|
78
76
|
items_for_atmark(position: position)
|
79
77
|
else
|
80
78
|
[]
|
@@ -90,7 +88,9 @@ module Steep
|
|
90
88
|
end
|
91
89
|
|
92
90
|
def at_end?(pos, of:)
|
93
|
-
|
91
|
+
if of
|
92
|
+
of.last_line == pos.line && of.last_column == pos.column
|
93
|
+
end
|
94
94
|
end
|
95
95
|
|
96
96
|
def range_for(position, prefix: "")
|
@@ -194,21 +194,25 @@ module Steep
|
|
194
194
|
return [] unless node
|
195
195
|
|
196
196
|
if at_end?(shift_pos, of: node.loc)
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
197
|
+
begin
|
198
|
+
context = typing.context_at(line: position.line, column: position.column)
|
199
|
+
receiver_type = case (type = typing.type_of(node: node))
|
200
|
+
when AST::Types::Self
|
201
|
+
context.self_type
|
202
|
+
else
|
203
|
+
type
|
204
|
+
end
|
205
|
+
|
206
|
+
items = []
|
207
|
+
method_items_for_receiver_type(receiver_type,
|
208
|
+
include_private: false,
|
209
|
+
prefix: "",
|
210
|
+
position: position,
|
211
|
+
items: items)
|
212
|
+
items
|
213
|
+
rescue Typing::UnknownNodeError
|
214
|
+
[]
|
215
|
+
end
|
212
216
|
else
|
213
217
|
[]
|
214
218
|
end
|
@@ -217,50 +221,40 @@ module Steep
|
|
217
221
|
def items_for_atmark(position:)
|
218
222
|
# @ ←
|
219
223
|
shift_pos = position-1
|
220
|
-
node, *
|
224
|
+
node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
|
221
225
|
node ||= source.node
|
222
226
|
|
223
227
|
return [] unless node
|
224
228
|
|
225
229
|
context = typing.context_at(line: position.line, column: position.column)
|
226
230
|
items = []
|
227
|
-
instance_variable_items_for_context(context, prefix: "", position: position, items: items)
|
231
|
+
instance_variable_items_for_context(context, prefix: "@", position: position, items: items)
|
228
232
|
items
|
229
233
|
end
|
230
234
|
|
231
235
|
def method_items_for_receiver_type(type, include_private:, prefix:, position:, items:)
|
232
236
|
range = range_for(position, prefix: prefix)
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
if include_private || method.public?
|
250
|
-
if name.to_s.start_with?(prefix)
|
251
|
-
if word_name?(name.to_s)
|
252
|
-
method.defs.each do |def_type|
|
253
|
-
items << MethodNameItem.new(identifier: name,
|
254
|
-
range: range,
|
255
|
-
definition: method,
|
256
|
-
def_type: def_type,
|
257
|
-
inherited_method: inherited_method?(method, definition))
|
258
|
-
end
|
259
|
-
end
|
237
|
+
interface = subtyping.factory.interface(type, self_type: type, private: include_private)
|
238
|
+
|
239
|
+
interface.methods.each do |name, method_entry|
|
240
|
+
next if disallowed_method?(name)
|
241
|
+
|
242
|
+
if name.to_s.start_with?(prefix)
|
243
|
+
if word_name?(name.to_s)
|
244
|
+
method_entry.method_types.each do |method_type|
|
245
|
+
items << MethodNameItem.new(
|
246
|
+
identifier: name,
|
247
|
+
range: range,
|
248
|
+
method_def: method_type.method_def,
|
249
|
+
method_type: method_type.method_def&.type || subtyping.factory.method_type_1(method_type, self_type: type),
|
250
|
+
inherited_method: inherited_method?(method_type.method_def, type)
|
251
|
+
)
|
260
252
|
end
|
261
253
|
end
|
262
254
|
end
|
263
255
|
end
|
256
|
+
rescue
|
257
|
+
# nop
|
264
258
|
end
|
265
259
|
|
266
260
|
def word_name?(name)
|
@@ -304,8 +298,13 @@ module Steep
|
|
304
298
|
index
|
305
299
|
end
|
306
300
|
|
307
|
-
def inherited_method?(
|
308
|
-
|
301
|
+
def inherited_method?(method_def, type)
|
302
|
+
case type
|
303
|
+
when AST::Types::Name::Instance, AST::Types::Name::Singleton, AST::Types::Name::Interface
|
304
|
+
method_def.implemented_in != type.name
|
305
|
+
else
|
306
|
+
false
|
307
|
+
end
|
309
308
|
end
|
310
309
|
|
311
310
|
def disallowed_method?(name)
|
@@ -28,15 +28,20 @@ module Steep
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def load_sources(command_line_patterns)
|
31
|
+
loaded_paths = Set[]
|
32
|
+
|
31
33
|
project.targets.each do |target|
|
32
34
|
Steep.logger.tagged "target=#{target.name}" do
|
33
35
|
target_patterns = command_line_patterns.empty? ? target.source_patterns : command_line_patterns
|
34
36
|
|
35
|
-
each_path_in_patterns
|
37
|
+
each_path_in_patterns(target_patterns, ".rb") do |path|
|
36
38
|
if target.possible_source_file?(path)
|
37
|
-
|
39
|
+
if loaded_paths.include?(path)
|
40
|
+
Steep.logger.warn { "Skipping #{target} while loading #{path}... (Already loaded to another target.)" }
|
41
|
+
else
|
38
42
|
Steep.logger.info { "Adding source file: #{path}" }
|
39
43
|
target.add_source path, project.absolute_path(path).read
|
44
|
+
loaded_paths << path
|
40
45
|
end
|
41
46
|
end
|
42
47
|
end
|
@@ -21,9 +21,7 @@ module Steep
|
|
21
21
|
@project = project
|
22
22
|
end
|
23
23
|
|
24
|
-
def method_definition_for(factory,
|
25
|
-
type_name = factory.type_name_1(module_name)
|
26
|
-
|
24
|
+
def method_definition_for(factory, type_name, singleton_method: nil, instance_method: nil)
|
27
25
|
case
|
28
26
|
when instance_method
|
29
27
|
factory.definition_builder.build_instance(type_name).methods[instance_method]
|
@@ -38,102 +36,113 @@ module Steep
|
|
38
36
|
end
|
39
37
|
end
|
40
38
|
|
39
|
+
def typecheck(target, path:, line:, column:)
|
40
|
+
target.type_check(target_sources: [], validate_signatures: false)
|
41
|
+
|
42
|
+
case (status = target.status)
|
43
|
+
when Project::Target::TypeCheckStatus
|
44
|
+
subtyping = status.subtyping
|
45
|
+
source = SourceFile
|
46
|
+
.parse(target.source_files[path].content, path: path, factory: subtyping.factory)
|
47
|
+
.without_unrelated_defs(line: line, column: column)
|
48
|
+
SourceFile.type_check(source, subtyping: subtyping)
|
49
|
+
end
|
50
|
+
rescue
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
41
54
|
def content_for(path:, line:, column:)
|
42
|
-
target = project.
|
55
|
+
target = project.target_for_source_path(path)
|
43
56
|
|
44
57
|
if target
|
45
|
-
|
46
|
-
|
58
|
+
typing = typecheck(target, path: path, line: line, column: column) or return
|
59
|
+
|
60
|
+
node, *parents = typing.source.find_nodes(line: line, column: column)
|
47
61
|
|
48
|
-
|
49
|
-
|
50
|
-
|
62
|
+
if node
|
63
|
+
case node.type
|
64
|
+
when :lvar
|
65
|
+
var_name = node.children[0]
|
66
|
+
context = typing.context_at(line: line, column: column)
|
67
|
+
var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
|
51
68
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
|
69
|
+
VariableContent.new(node: node, name: var_name.name, type: var_type, location: node.location.name)
|
70
|
+
when :lvasgn
|
71
|
+
var_name, rhs = node.children
|
72
|
+
context = typing.context_at(line: line, column: column)
|
73
|
+
type = context.lvar_env[var_name.name] || typing.type_of(node: rhs)
|
58
74
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
context = status.typing.context_at(line: line, column: column)
|
63
|
-
type = context.lvar_env[var_name.name] || status.typing.type_of(node: rhs)
|
75
|
+
VariableContent.new(node: node, name: var_name.name, type: type, location: node.location.name)
|
76
|
+
when :send
|
77
|
+
receiver, method_name, *_ = node.children
|
64
78
|
|
65
|
-
VariableContent.new(node: node, name: var_name.name, type: type, location: node.location.name)
|
66
|
-
when :send
|
67
|
-
receiver, method_name, *_ = node.children
|
68
79
|
|
80
|
+
result_node = if parents[0]&.type == :block
|
81
|
+
parents[0]
|
82
|
+
else
|
83
|
+
node
|
84
|
+
end
|
69
85
|
|
70
|
-
|
71
|
-
|
86
|
+
context = typing.context_at(line: line, column: column)
|
87
|
+
|
88
|
+
receiver_type = if receiver
|
89
|
+
typing.type_of(node: receiver)
|
72
90
|
else
|
73
|
-
|
91
|
+
context.self_type
|
74
92
|
end
|
75
93
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
when AST::Types::Name::Instance
|
87
|
-
method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
|
88
|
-
if method_definition&.defined_in
|
89
|
-
owner_name = factory.type_name(method_definition.defined_in)
|
90
|
-
[
|
91
|
-
InstanceMethodName.new(owner_name, method_name),
|
92
|
-
method_definition
|
93
|
-
]
|
94
|
-
end
|
95
|
-
when AST::Types::Name::Class
|
96
|
-
method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
|
97
|
-
if method_definition&.defined_in
|
98
|
-
owner_name = factory.type_name(method_definition.defined_in)
|
99
|
-
[
|
100
|
-
SingletonMethodName.new(owner_name, method_name),
|
101
|
-
method_definition
|
102
|
-
]
|
103
|
-
end
|
104
|
-
else
|
105
|
-
nil
|
94
|
+
factory = context.type_env.subtyping.factory
|
95
|
+
method_name, definition = case receiver_type
|
96
|
+
when AST::Types::Name::Instance
|
97
|
+
method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
|
98
|
+
if method_definition&.defined_in
|
99
|
+
owner_name = method_definition.defined_in
|
100
|
+
[
|
101
|
+
InstanceMethodName.new(owner_name, method_name),
|
102
|
+
method_definition
|
103
|
+
]
|
106
104
|
end
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
105
|
+
when AST::Types::Name::Singleton
|
106
|
+
method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
|
107
|
+
if method_definition&.defined_in
|
108
|
+
owner_name = method_definition.defined_in
|
109
|
+
[
|
110
|
+
SingletonMethodName.new(owner_name, method_name),
|
111
|
+
method_definition
|
112
|
+
]
|
113
|
+
end
|
114
|
+
else
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
MethodCallContent.new(
|
119
|
+
node: node,
|
120
|
+
method_name: method_name,
|
121
|
+
type: typing.type_of(node: result_node),
|
122
|
+
definition: definition,
|
123
|
+
location: result_node.location.expression
|
124
|
+
)
|
125
|
+
when :def, :defs
|
126
|
+
context = typing.context_at(line: line, column: column)
|
127
|
+
method_context = context.method_context
|
128
|
+
|
129
|
+
if method_context && method_context.method
|
130
|
+
DefinitionContent.new(
|
132
131
|
node: node,
|
133
|
-
|
134
|
-
|
132
|
+
method_name: method_context.name,
|
133
|
+
method_type: method_context.method_type,
|
134
|
+
definition: method_context.method,
|
135
|
+
location: node.loc.expression
|
135
136
|
)
|
136
137
|
end
|
138
|
+
else
|
139
|
+
type = typing.type_of(node: node)
|
140
|
+
|
141
|
+
TypeContent.new(
|
142
|
+
node: node,
|
143
|
+
type: type,
|
144
|
+
location: node.location.expression
|
145
|
+
)
|
137
146
|
end
|
138
147
|
end
|
139
148
|
end
|