steep 0.24.0 → 0.30.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 +48 -0
- data/bin/smoke_runner.rb +3 -4
- 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.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 +251 -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.rb +25 -0
- data/lib/steep/project/completion_provider.rb +48 -51
- data/lib/steep/project/file_loader.rb +7 -2
- data/lib/steep/project/hover_content.rb +4 -6
- 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 +36 -14
- data/lib/steep/server/base_worker.rb +5 -3
- data/lib/steep/server/code_worker.rb +31 -45
- data/lib/steep/server/master.rb +23 -31
- 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 +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/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 +2 -2
- metadata +15 -11
- data/lib/steep/ast/method_type.rb +0 -126
- data/lib/steep/ast/namespace.rb +0 -80
- data/lib/steep/names.rb +0 -86
data/lib/steep/project/target.rb
CHANGED
@@ -29,9 +29,15 @@ module Steep
|
|
29
29
|
@signature_files = {}
|
30
30
|
end
|
31
31
|
|
32
|
-
def add_source(path, content)
|
32
|
+
def add_source(path, content = "")
|
33
33
|
file = SourceFile.new(path: path)
|
34
|
-
|
34
|
+
|
35
|
+
if block_given?
|
36
|
+
file.content = yield
|
37
|
+
else
|
38
|
+
file.content = content
|
39
|
+
end
|
40
|
+
|
35
41
|
source_files[path] = file
|
36
42
|
end
|
37
43
|
|
@@ -39,14 +45,22 @@ module Steep
|
|
39
45
|
source_files.delete(path)
|
40
46
|
end
|
41
47
|
|
42
|
-
def update_source(path, content)
|
48
|
+
def update_source(path, content = nil)
|
43
49
|
file = source_files[path]
|
44
|
-
|
50
|
+
if block_given?
|
51
|
+
file.content = yield(file.content)
|
52
|
+
else
|
53
|
+
file.content = content || file.content
|
54
|
+
end
|
45
55
|
end
|
46
56
|
|
47
|
-
def add_signature(path, content)
|
57
|
+
def add_signature(path, content = "")
|
48
58
|
file = SignatureFile.new(path: path)
|
49
|
-
|
59
|
+
if block_given?
|
60
|
+
file.content = yield
|
61
|
+
else
|
62
|
+
file.content = content
|
63
|
+
end
|
50
64
|
signature_files[path] = file
|
51
65
|
end
|
52
66
|
|
@@ -54,9 +68,13 @@ module Steep
|
|
54
68
|
signature_files.delete(path)
|
55
69
|
end
|
56
70
|
|
57
|
-
def update_signature(path, content)
|
71
|
+
def update_signature(path, content = nil)
|
58
72
|
file = signature_files[path]
|
59
|
-
|
73
|
+
if block_given?
|
74
|
+
file.content = yield(file.content)
|
75
|
+
else
|
76
|
+
file.content = content || file.content
|
77
|
+
end
|
60
78
|
end
|
61
79
|
|
62
80
|
def source_file?(path)
|
@@ -115,6 +133,7 @@ module Steep
|
|
115
133
|
when TypeCheckStatus
|
116
134
|
status.timestamp
|
117
135
|
end
|
136
|
+
now = Time.now
|
118
137
|
|
119
138
|
updated_files = []
|
120
139
|
|
@@ -151,11 +170,11 @@ module Steep
|
|
151
170
|
validator.validate()
|
152
171
|
|
153
172
|
if validator.no_error?
|
154
|
-
yield env, check,
|
173
|
+
yield env, check, now
|
155
174
|
else
|
156
175
|
@status = SignatureValidationErrorStatus.new(
|
157
176
|
errors: validator.each_error.to_a,
|
158
|
-
timestamp:
|
177
|
+
timestamp: now
|
159
178
|
)
|
160
179
|
end
|
161
180
|
else
|
@@ -169,10 +188,11 @@ module Steep
|
|
169
188
|
location: exn.decls[0].location
|
170
189
|
)
|
171
190
|
],
|
172
|
-
timestamp:
|
191
|
+
timestamp: now
|
173
192
|
)
|
174
193
|
rescue => exn
|
175
|
-
|
194
|
+
Steep.log_error exn
|
195
|
+
@status = SignatureOtherErrorStatus.new(error: exn, timestamp: now)
|
176
196
|
end
|
177
197
|
end
|
178
198
|
|
@@ -194,8 +214,10 @@ module Steep
|
|
194
214
|
type_check_sources = []
|
195
215
|
|
196
216
|
target_sources.each do |file|
|
197
|
-
|
198
|
-
|
217
|
+
Steep.logger.tagged("path=#{file.path}") do
|
218
|
+
if file.type_check(check, timestamp)
|
219
|
+
type_check_sources << file
|
220
|
+
end
|
199
221
|
end
|
200
222
|
end
|
201
223
|
|
@@ -12,6 +12,7 @@ module Steep
|
|
12
12
|
@project = project
|
13
13
|
@reader = reader
|
14
14
|
@writer = writer
|
15
|
+
@shutdown = false
|
15
16
|
end
|
16
17
|
|
17
18
|
def handle_request(request)
|
@@ -28,7 +29,7 @@ module Steep
|
|
28
29
|
Steep.logger.formatter.push_tags(*tags)
|
29
30
|
Steep.logger.tagged "background" do
|
30
31
|
while job = queue.pop
|
31
|
-
handle_job(job)
|
32
|
+
handle_job(job) unless @shutdown
|
32
33
|
end
|
33
34
|
end
|
34
35
|
end
|
@@ -38,11 +39,12 @@ module Steep
|
|
38
39
|
reader.read do |request|
|
39
40
|
case request[:method]
|
40
41
|
when "shutdown"
|
41
|
-
|
42
|
+
@shutdown = true
|
43
|
+
writer.write(id: request[:id], result: nil)
|
42
44
|
when "exit"
|
43
45
|
break
|
44
46
|
else
|
45
|
-
handle_request(request)
|
47
|
+
handle_request(request) unless @shutdown
|
46
48
|
end
|
47
49
|
end
|
48
50
|
ensure
|
@@ -5,47 +5,32 @@ module Steep
|
|
5
5
|
|
6
6
|
include Utils
|
7
7
|
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :typecheck_paths
|
9
9
|
attr_reader :queue
|
10
10
|
|
11
11
|
def initialize(project:, reader:, writer:, queue: Queue.new)
|
12
12
|
super(project: project, reader: reader, writer: writer)
|
13
13
|
|
14
|
-
@
|
14
|
+
@typecheck_paths = Set[]
|
15
15
|
@queue = queue
|
16
16
|
end
|
17
17
|
|
18
|
-
def enqueue_type_check(target:, path
|
19
|
-
Steep.logger.info "Enqueueing type check: #{
|
20
|
-
|
21
|
-
queue << [path, version, target]
|
22
|
-
end
|
23
|
-
|
24
|
-
def each_type_check_subject(path:, version:)
|
25
|
-
case
|
26
|
-
when !(updated_targets = project.targets.select {|target| target.signature_file?(path) }).empty?
|
27
|
-
updated_targets.each do |target|
|
28
|
-
target_files.each_key do |path|
|
29
|
-
if target.source_file?(path)
|
30
|
-
yield target, path, target_files[path]
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
when target = project.targets.find {|target| target.source_file?(path) }
|
36
|
-
if target_files.key?(path)
|
37
|
-
yield target, path, version
|
38
|
-
end
|
39
|
-
end
|
18
|
+
def enqueue_type_check(target:, path:)
|
19
|
+
Steep.logger.info "Enqueueing type check: #{target.name}::#{path}..."
|
20
|
+
queue << [target, path]
|
40
21
|
end
|
41
22
|
|
42
23
|
def typecheck_file(path, target)
|
43
|
-
Steep.logger.info "Starting type checking: #{
|
24
|
+
Steep.logger.info "Starting type checking: #{target.name}::#{path}..."
|
44
25
|
|
45
26
|
source = target.source_files[path]
|
46
27
|
target.type_check(target_sources: [source], validate_signatures: false)
|
47
28
|
|
48
|
-
|
29
|
+
if target.status.is_a?(Project::Target::TypeCheckStatus) && target.status.type_check_sources.empty?
|
30
|
+
Steep.logger.debug "Skipped type checking: #{target.name}::#{path}"
|
31
|
+
else
|
32
|
+
Steep.logger.info "Finished type checking: #{target.name}::#{path}"
|
33
|
+
end
|
49
34
|
|
50
35
|
diagnostics = source_diagnostics(source, target.options)
|
51
36
|
|
@@ -109,8 +94,8 @@ module Steep
|
|
109
94
|
# Don't respond to initialize request, but start type checking.
|
110
95
|
project.targets.each do |target|
|
111
96
|
target.source_files.each_key do |path|
|
112
|
-
if
|
113
|
-
enqueue_type_check(target: target, path: path
|
97
|
+
if typecheck_paths.include?(path)
|
98
|
+
enqueue_type_check(target: target, path: path)
|
114
99
|
end
|
115
100
|
end
|
116
101
|
end
|
@@ -118,33 +103,34 @@ module Steep
|
|
118
103
|
when "workspace/executeCommand"
|
119
104
|
if request[:params][:command] == "steep/registerSourceToWorker"
|
120
105
|
paths = request[:params][:arguments].map {|arg| source_path(URI.parse(arg)) }
|
121
|
-
paths
|
122
|
-
target_files[path] = 0
|
123
|
-
end
|
106
|
+
typecheck_paths.merge(paths)
|
124
107
|
end
|
125
108
|
|
126
109
|
when "textDocument/didChange"
|
127
|
-
update_source(request) do |path,
|
128
|
-
|
129
|
-
|
110
|
+
update_source(request) do |path, _|
|
111
|
+
source_target, signature_targets = project.targets_for_path(path)
|
112
|
+
|
113
|
+
if source_target
|
114
|
+
if typecheck_paths.include?(path)
|
115
|
+
enqueue_type_check(target: source_target, path: path)
|
116
|
+
end
|
130
117
|
end
|
131
|
-
end
|
132
118
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
119
|
+
signature_targets.each do |target|
|
120
|
+
target.source_files.each_key do |source_path|
|
121
|
+
if typecheck_paths.include?(source_path)
|
122
|
+
enqueue_type_check(target: target, path: source_path)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
137
126
|
end
|
138
127
|
end
|
139
128
|
end
|
140
129
|
|
141
130
|
def handle_job(job)
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
else
|
146
|
-
Steep.logger.info "Skipping type check: #{path}@#{target.name}, queued version=#{version}, latest version=#{target_files[path]}"
|
147
|
-
end
|
131
|
+
target, path = job
|
132
|
+
|
133
|
+
typecheck_file(path, target)
|
148
134
|
end
|
149
135
|
end
|
150
136
|
end
|
data/lib/steep/server/master.rb
CHANGED
@@ -14,6 +14,8 @@ module Steep
|
|
14
14
|
attr_reader :signature_worker
|
15
15
|
attr_reader :code_workers
|
16
16
|
|
17
|
+
include Utils
|
18
|
+
|
17
19
|
def initialize(project:, reader:, writer:, interaction_worker:, signature_worker:, code_workers:, queue: Queue.new)
|
18
20
|
@project = project
|
19
21
|
@reader = reader
|
@@ -23,22 +25,28 @@ module Steep
|
|
23
25
|
@signature_worker = signature_worker
|
24
26
|
@code_workers = code_workers
|
25
27
|
@worker_to_paths = {}
|
28
|
+
@shutdown_request_id = nil
|
26
29
|
end
|
27
30
|
|
28
31
|
def start
|
29
|
-
source_paths = project.
|
32
|
+
source_paths = project.all_source_files
|
30
33
|
bin_size = (source_paths.size / code_workers.size) + 1
|
31
34
|
source_paths.each_slice(bin_size).with_index do |paths, index|
|
32
35
|
register_code_to_worker(paths, worker: code_workers[index])
|
33
36
|
end
|
34
37
|
|
38
|
+
tags = Steep.logger.formatter.current_tags.dup
|
39
|
+
tags << "master"
|
40
|
+
|
35
41
|
Thread.new do
|
42
|
+
Steep.logger.formatter.push_tags(*tags, "from-worker@interaction")
|
36
43
|
interaction_worker.reader.read do |message|
|
37
44
|
process_message_from_worker(message)
|
38
45
|
end
|
39
46
|
end
|
40
47
|
|
41
48
|
Thread.new do
|
49
|
+
Steep.logger.formatter.push_tags(*tags, "from-worker@signature")
|
42
50
|
signature_worker.reader.read do |message|
|
43
51
|
process_message_from_worker(message)
|
44
52
|
end
|
@@ -46,6 +54,7 @@ module Steep
|
|
46
54
|
|
47
55
|
code_workers.each do |worker|
|
48
56
|
Thread.new do
|
57
|
+
Steep.logger.formatter.push_tags(*tags, "from-worker@#{worker.name}")
|
49
58
|
worker.reader.read do |message|
|
50
59
|
process_message_from_worker(message)
|
51
60
|
end
|
@@ -53,13 +62,21 @@ module Steep
|
|
53
62
|
end
|
54
63
|
|
55
64
|
Thread.new do
|
65
|
+
Steep.logger.formatter.push_tags(*tags, "from-client")
|
56
66
|
reader.read do |request|
|
57
67
|
process_message_from_client(request)
|
58
68
|
end
|
59
69
|
end
|
60
70
|
|
61
71
|
while job = queue.pop
|
62
|
-
|
72
|
+
if @shutdown_request_id
|
73
|
+
if job[:id] == @shutdown_request_id
|
74
|
+
writer.write(job)
|
75
|
+
break
|
76
|
+
end
|
77
|
+
else
|
78
|
+
writer.write(job)
|
79
|
+
end
|
63
80
|
end
|
64
81
|
|
65
82
|
writer.io.close
|
@@ -89,7 +106,7 @@ module Steep
|
|
89
106
|
result: LSP::Interface::InitializeResult.new(
|
90
107
|
capabilities: LSP::Interface::ServerCapabilities.new(
|
91
108
|
text_document_sync: LSP::Interface::TextDocumentSyncOptions.new(
|
92
|
-
change: LSP::Constant::TextDocumentSyncKind::
|
109
|
+
change: LSP::Constant::TextDocumentSyncKind::INCREMENTAL
|
93
110
|
),
|
94
111
|
hover_provider: true,
|
95
112
|
completion_provider: LSP::Interface::CompletionOptions.new(
|
@@ -104,36 +121,10 @@ module Steep
|
|
104
121
|
end
|
105
122
|
|
106
123
|
when "textDocument/didChange"
|
124
|
+
update_source(message)
|
125
|
+
|
107
126
|
uri = URI.parse(message[:params][:textDocument][:uri])
|
108
127
|
path = project.relative_path(Pathname(uri.path))
|
109
|
-
text = message[:params][:contentChanges][0][:text]
|
110
|
-
|
111
|
-
project.targets.each do |target|
|
112
|
-
case
|
113
|
-
when target.source_file?(path)
|
114
|
-
if text.empty? && !path.file?
|
115
|
-
Steep.logger.info { "Deleting source file: #{path}..." }
|
116
|
-
target.remove_source(path)
|
117
|
-
else
|
118
|
-
Steep.logger.info { "Updating source file: #{path}..." }
|
119
|
-
target.update_source(path, text)
|
120
|
-
end
|
121
|
-
when target.possible_source_file?(path)
|
122
|
-
Steep.logger.info { "Adding source file: #{path}..." }
|
123
|
-
target.add_source(path, text)
|
124
|
-
when target.signature_file?(path)
|
125
|
-
if text.empty? && !path.file?
|
126
|
-
Steep.logger.info { "Deleting signature file: #{path}..." }
|
127
|
-
target.remove_signature(path)
|
128
|
-
else
|
129
|
-
Steep.logger.info { "Updating signature file: #{path}..." }
|
130
|
-
target.update_signature(path, text)
|
131
|
-
end
|
132
|
-
when target.possible_signature_file?(path)
|
133
|
-
Steep.logger.info { "Adding signature file: #{path}..." }
|
134
|
-
target.add_signature(path, text)
|
135
|
-
end
|
136
|
-
end
|
137
128
|
|
138
129
|
unless registered_path?(path)
|
139
130
|
register_code_to_worker [path], worker: least_busy_worker()
|
@@ -154,6 +145,7 @@ module Steep
|
|
154
145
|
|
155
146
|
when "shutdown"
|
156
147
|
queue << { id: id, result: nil }
|
148
|
+
@shutdown_request_id = id
|
157
149
|
|
158
150
|
when "exit"
|
159
151
|
queue << nil
|
data/lib/steep/server/utils.rb
CHANGED
@@ -7,23 +7,56 @@ module Steep
|
|
7
7
|
project.relative_path(Pathname(uri.path))
|
8
8
|
end
|
9
9
|
|
10
|
+
def apply_change(change, text)
|
11
|
+
range = change[:range]
|
12
|
+
|
13
|
+
if range
|
14
|
+
text = text.dup
|
15
|
+
|
16
|
+
buf = AST::Buffer.new(name: :_, content: text)
|
17
|
+
|
18
|
+
start_pos = buf.loc_to_pos(range[:start].yield_self {|pos| [pos[:line]+1, pos[:character]] })
|
19
|
+
end_pos = buf.loc_to_pos(range[:end].yield_self {|pos| [pos[:line]+1, pos[:character]] })
|
20
|
+
|
21
|
+
text[start_pos...end_pos] = change[:text]
|
22
|
+
text
|
23
|
+
else
|
24
|
+
change[:text]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
10
28
|
def update_source(request)
|
11
29
|
path = source_path(URI.parse(request[:params][:textDocument][:uri]))
|
12
|
-
text = request[:params][:contentChanges][0][:text]
|
13
30
|
version = request[:params][:textDocument][:version]
|
31
|
+
Steep.logger.info { "Updating source: path=#{path}, version=#{version}..." }
|
32
|
+
|
33
|
+
changes = request[:params][:contentChanges]
|
34
|
+
|
35
|
+
source_target, signature_targets = project.targets_for_path(path)
|
36
|
+
|
37
|
+
if source_target
|
38
|
+
changes.each do |change|
|
39
|
+
case
|
40
|
+
when source_target.source_file?(path)
|
41
|
+
Steep.logger.debug { "Updating source in #{source_target.name}: path=#{path}" }
|
42
|
+
source_target.update_source(path) {|text| apply_change(change, text) }
|
43
|
+
when source_target.possible_source_file?(path)
|
44
|
+
Steep.logger.debug { "Adding source to #{source_target.name}: path=#{path}" }
|
45
|
+
source_target.add_source(path, change[:text])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
14
49
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
when target.possible_signature_file?(path)
|
26
|
-
target.add_signature path, text
|
50
|
+
signature_targets.each do |target|
|
51
|
+
changes.each do |change|
|
52
|
+
case
|
53
|
+
when target.signature_file?(path)
|
54
|
+
Steep.logger.debug { "Updating signature in #{target.name}: path=#{path}" }
|
55
|
+
target.update_signature(path) {|text| apply_change(change, text) }
|
56
|
+
when target.possible_signature_file?(path)
|
57
|
+
Steep.logger.debug { "Adding signature to #{target.name}: path=#{path}" }
|
58
|
+
target.add_signature(path, change[:text])
|
59
|
+
end
|
27
60
|
end
|
28
61
|
end
|
29
62
|
|