steep 0.29.0 → 0.33.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 +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
|