steep 0.29.0 → 0.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/bin/steep-prof +1 -2
- data/lib/steep.rb +2 -3
- data/lib/steep/ast/types/factory.rb +43 -28
- data/lib/steep/project.rb +25 -0
- data/lib/steep/project/completion_provider.rb +9 -7
- data/lib/steep/project/file_loader.rb +7 -2
- data/lib/steep/project/hover_content.rb +91 -80
- data/lib/steep/project/signature_file.rb +33 -0
- data/lib/steep/project/{file.rb → source_file.rb} +21 -51
- data/lib/steep/project/target.rb +31 -12
- data/lib/steep/server/code_worker.rb +30 -46
- data/lib/steep/server/interaction_worker.rb +42 -38
- data/lib/steep/server/master.rb +13 -30
- data/lib/steep/server/utils.rb +46 -13
- data/lib/steep/server/worker_process.rb +4 -2
- data/lib/steep/source.rb +60 -3
- data/lib/steep/type_inference/context_array.rb +1 -1
- data/lib/steep/type_inference/logic_type_interpreter.rb +6 -0
- data/lib/steep/version.rb +1 -1
- data/smoke/regression/fun.rb +8 -0
- data/smoke/regression/fun.rbs +4 -0
- data/smoke/toplevel/Steepfile +5 -0
- data/smoke/toplevel/a.rb +4 -0
- data/smoke/toplevel/a.rbs +3 -0
- data/steep.gemspec +1 -1
- metadata +11 -7
- data/lib/steep/ast/buffer.rb +0 -51
- data/lib/steep/ast/location.rb +0 -86
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e93086efe3704ac8b59a1450fda301b7ab91f409c13c53a679a2ed3b3419ca84
|
4
|
+
data.tar.gz: b00e2030a9ea2171b6f967c78f469204c903cb937d421e5dcab896021fce2919
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06023d31b509f58efdea8c8f0e13c49b38f3a4936debbe3a0d88bfd84b356e7a761506a8f105c035bd454a4b2f39ae6af13d16b84d29e6d0050d9d58d7062e82
|
7
|
+
data.tar.gz: '0601459d31ad8ca758c95c29215eb237b5bc50490b5133e574057ec1836396d9189c3dd22628244c1d8d432e0f945a746c280ae96ca20b768c063fbae0c696af'
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.33.0 (2020-10-13)
|
6
|
+
|
7
|
+
* Make `!` typing flow sensitive ([#240](https://github.com/soutaro/steep/pull/240))
|
8
|
+
|
9
|
+
## 0.32.0 (2020-10-09)
|
10
|
+
|
11
|
+
* Let type-case support interface types ([#237](https://github.com/soutaro/steep/pull/237))
|
12
|
+
|
13
|
+
## 0.31.1 (2020-10-07)
|
14
|
+
|
15
|
+
* Fix `if-then-else` parsing ([#236](https://github.com/soutaro/steep/pull/236))
|
16
|
+
|
17
|
+
## 0.31.0 (2020-10-04)
|
18
|
+
|
19
|
+
* Fix type checking performance ([#230](https://github.com/soutaro/steep/pull/230))
|
20
|
+
* Improve LSP completion/hover performance ([#232](https://github.com/soutaro/steep/pull/232))
|
21
|
+
* Fix instance variable completion ([#234](https://github.com/soutaro/steep/pull/234))
|
22
|
+
* Relax version requirements on Listen to allow installing on Ruby 3 ([#235](https://github.com/soutaro/steep/pull/235))
|
23
|
+
|
24
|
+
## 0.30.0 (2020-10-03)
|
25
|
+
|
26
|
+
* Let top-level defs be methods of Object ([#227](https://github.com/soutaro/steep/pull/227))
|
27
|
+
* Fix error caused by attribute definitions ([#228](https://github.com/soutaro/steep/pull/228))
|
28
|
+
* LSP worker improvements ([#222](https://github.com/soutaro/steep/pull/222), [#223](https://github.com/soutaro/steep/pull/223), [#226](https://github.com/soutaro/steep/pull/226), [#229](https://github.com/soutaro/steep/pull/229))
|
29
|
+
|
5
30
|
## 0.29.0 (2020-09-28)
|
6
31
|
|
7
32
|
* Implement reasoning on `is_a?`, `nil?`, and `===` methods. ([#218](https://github.com/soutaro/steep/pull/218))
|
data/bin/steep-prof
CHANGED
data/lib/steep.rb
CHANGED
@@ -16,7 +16,6 @@ require 'uri'
|
|
16
16
|
|
17
17
|
require "rbs"
|
18
18
|
|
19
|
-
require "steep/ast/location"
|
20
19
|
require "steep/ast/types/helper"
|
21
20
|
require "steep/ast/types/any"
|
22
21
|
require "steep/ast/types/instance"
|
@@ -39,7 +38,6 @@ require "steep/ast/types/logic"
|
|
39
38
|
require "steep/ast/type_params"
|
40
39
|
require "steep/ast/annotation"
|
41
40
|
require "steep/ast/annotation/collection"
|
42
|
-
require "steep/ast/buffer"
|
43
41
|
require "steep/ast/builtin"
|
44
42
|
require "steep/ast/types/factory"
|
45
43
|
|
@@ -83,7 +81,8 @@ require "steep/server/interaction_worker"
|
|
83
81
|
require "steep/server/master"
|
84
82
|
|
85
83
|
require "steep/project"
|
86
|
-
require "steep/project/
|
84
|
+
require "steep/project/signature_file"
|
85
|
+
require "steep/project/source_file"
|
87
86
|
require "steep/project/options"
|
88
87
|
require "steep/project/target"
|
89
88
|
require "steep/project/dsl"
|
@@ -7,11 +7,14 @@ module Steep
|
|
7
7
|
attr_reader :type_name_cache
|
8
8
|
attr_reader :type_cache
|
9
9
|
|
10
|
+
attr_reader :type_interface_cache
|
11
|
+
|
10
12
|
def initialize(builder:)
|
11
13
|
@definition_builder = builder
|
12
14
|
|
13
15
|
@type_name_cache = {}
|
14
16
|
@type_cache = {}
|
17
|
+
@type_interface_cache = {}
|
15
18
|
end
|
16
19
|
|
17
20
|
def type_name_resolver
|
@@ -339,46 +342,48 @@ module Steep
|
|
339
342
|
end
|
340
343
|
end
|
341
344
|
|
345
|
+
NilClassName = TypeName("::NilClass")
|
346
|
+
|
342
347
|
def setup_primitives(method_name, method_type)
|
343
348
|
if method_def = method_type.method_def
|
344
349
|
defined_in = method_def.defined_in
|
345
350
|
member = method_def.member
|
346
351
|
|
347
|
-
|
348
|
-
when defined_in == RBS::BuiltinNames::Object.name && member.instance?
|
352
|
+
if member.is_a?(RBS::AST::Members::MethodDefinition)
|
349
353
|
case method_name
|
350
354
|
when :is_a?, :kind_of?, :instance_of?
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
|
357
|
-
)
|
358
|
-
end
|
355
|
+
if defined_in == RBS::BuiltinNames::Object.name && member.instance?
|
356
|
+
return method_type.with(
|
357
|
+
return_type: AST::Types::Logic::ReceiverIsArg.new(location: method_type.return_type.location)
|
358
|
+
)
|
359
|
+
end
|
359
360
|
|
360
|
-
when defined_in == AST::Builtin::NilClass.module_name && member.instance?
|
361
|
-
case method_name
|
362
361
|
when :nil?
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
362
|
+
case defined_in
|
363
|
+
when RBS::BuiltinNames::Object.name,
|
364
|
+
NilClassName
|
365
|
+
return method_type.with(
|
366
|
+
return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
|
367
|
+
)
|
368
|
+
end
|
367
369
|
|
368
|
-
when defined_in == RBS::BuiltinNames::BasicObject.name && member.instance?
|
369
|
-
case method_name
|
370
370
|
when :!
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
371
|
+
case defined_in
|
372
|
+
when RBS::BuiltinNames::BasicObject.name,
|
373
|
+
RBS::BuiltinNames::TrueClass.name,
|
374
|
+
RBS::BuiltinNames::FalseClass.name
|
375
|
+
return method_type.with(
|
376
|
+
return_type: AST::Types::Logic::Not.new(location: method_type.return_type.location)
|
377
|
+
)
|
378
|
+
end
|
375
379
|
|
376
|
-
when defined_in == RBS::BuiltinNames::Module.name && member.instance?
|
377
|
-
case method_name
|
378
380
|
when :===
|
379
|
-
|
380
|
-
|
381
|
-
|
381
|
+
case defined_in
|
382
|
+
when RBS::BuiltinNames::Module.name
|
383
|
+
return method_type.with(
|
384
|
+
return_type: AST::Types::Logic::ArgIsReceiver.new(location: method_type.return_type.location)
|
385
|
+
)
|
386
|
+
end
|
382
387
|
end
|
383
388
|
end
|
384
389
|
end
|
@@ -388,15 +393,23 @@ module Steep
|
|
388
393
|
|
389
394
|
def interface(type, private:, self_type: type)
|
390
395
|
Steep.logger.debug { "Factory#interface: #{type}, private=#{private}, self_type=#{self_type}" }
|
391
|
-
|
396
|
+
|
397
|
+
cache_key = [type, self_type, private]
|
398
|
+
if type_interface_cache.key?(cache_key)
|
399
|
+
return type_interface_cache[cache_key]
|
400
|
+
end
|
392
401
|
|
393
402
|
case type
|
403
|
+
when Name::Alias
|
404
|
+
interface(expand_alias(type), private: private, self_type: self_type)
|
405
|
+
|
394
406
|
when Self
|
395
407
|
if self_type != type
|
396
408
|
interface self_type, private: private, self_type: Self.new
|
397
409
|
else
|
398
410
|
raise "Unexpected `self` type interface"
|
399
411
|
end
|
412
|
+
|
400
413
|
when Name::Instance
|
401
414
|
Interface::Interface.new(type: self_type, private: private).tap do |interface|
|
402
415
|
definition = definition_builder.build_instance(type.name)
|
@@ -687,6 +700,8 @@ module Steep
|
|
687
700
|
|
688
701
|
else
|
689
702
|
raise "Unexpected type for interface: #{type}"
|
703
|
+
end.tap do |interface|
|
704
|
+
type_interface_cache[cache_key] = interface
|
690
705
|
end
|
691
706
|
end
|
692
707
|
|
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
|
|
@@ -29,11 +29,13 @@ module Steep
|
|
29
29
|
@subtyping = subtyping
|
30
30
|
end
|
31
31
|
|
32
|
-
def type_check!(text)
|
32
|
+
def type_check!(text, line:, column:)
|
33
33
|
@modified_text = text
|
34
34
|
|
35
35
|
Steep.measure "parsing" do
|
36
|
-
@source = SourceFile
|
36
|
+
@source = SourceFile
|
37
|
+
.parse(text, path: path, factory: subtyping.factory)
|
38
|
+
.without_unrelated_defs(line: line, column: column)
|
37
39
|
end
|
38
40
|
|
39
41
|
Steep.measure "typechecking" do
|
@@ -53,7 +55,7 @@ module Steep
|
|
53
55
|
begin
|
54
56
|
Steep.logger.tagged "completion_provider#run(line: #{line}, column: #{column})" do
|
55
57
|
Steep.measure "type_check!" do
|
56
|
-
type_check!(source_text)
|
58
|
+
type_check!(source_text, line: line, column: column)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
@@ -66,11 +68,11 @@ module Steep
|
|
66
68
|
case possible_trigger
|
67
69
|
when "."
|
68
70
|
source_text[index-1] = " "
|
69
|
-
type_check!(source_text)
|
71
|
+
type_check!(source_text, line: line, column: column)
|
70
72
|
items_for_dot(position: position)
|
71
73
|
when "@"
|
72
74
|
source_text[index-1] = " "
|
73
|
-
type_check!(source_text)
|
75
|
+
type_check!(source_text, line: line, column: column)
|
74
76
|
items_for_atmark(position: position)
|
75
77
|
else
|
76
78
|
[]
|
@@ -219,14 +221,14 @@ module Steep
|
|
219
221
|
def items_for_atmark(position:)
|
220
222
|
# @ ←
|
221
223
|
shift_pos = position-1
|
222
|
-
node, *
|
224
|
+
node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
|
223
225
|
node ||= source.node
|
224
226
|
|
225
227
|
return [] unless node
|
226
228
|
|
227
229
|
context = typing.context_at(line: position.line, column: position.column)
|
228
230
|
items = []
|
229
|
-
instance_variable_items_for_context(context, prefix: "", position: position, items: items)
|
231
|
+
instance_variable_items_for_context(context, prefix: "@", position: position, items: items)
|
230
232
|
items
|
231
233
|
end
|
232
234
|
|
@@ -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
|
@@ -36,102 +36,113 @@ module Steep
|
|
36
36
|
end
|
37
37
|
end
|
38
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
|
+
|
39
54
|
def content_for(path:, line:, column:)
|
40
|
-
target = project.
|
55
|
+
target = project.target_for_source_path(path)
|
41
56
|
|
42
57
|
if target
|
43
|
-
|
44
|
-
|
58
|
+
typing = typecheck(target, path: path, line: line, column: column) or return
|
59
|
+
|
60
|
+
node, *parents = typing.source.find_nodes(line: line, column: column)
|
61
|
+
|
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)
|
45
68
|
|
46
|
-
|
47
|
-
|
48
|
-
|
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)
|
49
74
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
var_name = node.children[0]
|
54
|
-
context = status.typing.context_at(line: line, column: column)
|
55
|
-
var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
|
75
|
+
VariableContent.new(node: node, name: var_name.name, type: type, location: node.location.name)
|
76
|
+
when :send
|
77
|
+
receiver, method_name, *_ = node.children
|
56
78
|
|
57
|
-
VariableContent.new(node: node, name: var_name.name, type: var_type, location: node.location.name)
|
58
|
-
when :lvasgn
|
59
|
-
var_name, rhs = node.children
|
60
|
-
context = status.typing.context_at(line: line, column: column)
|
61
|
-
type = context.lvar_env[var_name.name] || status.typing.type_of(node: rhs)
|
62
79
|
|
63
|
-
|
64
|
-
|
65
|
-
|
80
|
+
result_node = if parents[0]&.type == :block
|
81
|
+
parents[0]
|
82
|
+
else
|
83
|
+
node
|
84
|
+
end
|
66
85
|
|
86
|
+
context = typing.context_at(line: line, column: column)
|
67
87
|
|
68
|
-
|
69
|
-
|
88
|
+
receiver_type = if receiver
|
89
|
+
typing.type_of(node: receiver)
|
70
90
|
else
|
71
|
-
|
91
|
+
context.self_type
|
72
92
|
end
|
73
93
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
when AST::Types::Name::Instance
|
85
|
-
method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
|
86
|
-
if method_definition&.defined_in
|
87
|
-
owner_name = method_definition.defined_in
|
88
|
-
[
|
89
|
-
InstanceMethodName.new(owner_name, method_name),
|
90
|
-
method_definition
|
91
|
-
]
|
92
|
-
end
|
93
|
-
when AST::Types::Name::Singleton
|
94
|
-
method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
|
95
|
-
if method_definition&.defined_in
|
96
|
-
owner_name = method_definition.defined_in
|
97
|
-
[
|
98
|
-
SingletonMethodName.new(owner_name, method_name),
|
99
|
-
method_definition
|
100
|
-
]
|
101
|
-
end
|
102
|
-
else
|
103
|
-
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
|
+
]
|
104
104
|
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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(
|
130
131
|
node: node,
|
131
|
-
|
132
|
-
|
132
|
+
method_name: method_context.name,
|
133
|
+
method_type: method_context.method_type,
|
134
|
+
definition: method_context.method,
|
135
|
+
location: node.loc.expression
|
133
136
|
)
|
134
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
|
+
)
|
135
146
|
end
|
136
147
|
end
|
137
148
|
end
|