steep-relaxed 2.0.0.0 → 2.1.0.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 +35 -0
- data/CLAUDE.md +2 -2
- data/README.md +3 -3
- data/Rakefile +352 -143
- data/Steepfile +2 -0
- data/bin/shape_warmup_bench.rb +402 -0
- data/bin/steep-check.rb +4 -4
- data/bin/subtyping_cache_bench.rb +436 -0
- data/bin/typecheck_bench.rb +145 -0
- data/doc/release.md +264 -0
- data/lib/steep/ast/types/helper.rb +0 -1
- data/lib/steep/ast/types/literal.rb +1 -1
- data/lib/steep/ast/types/name.rb +1 -1
- data/lib/steep/cli.rb +32 -5
- data/lib/steep/daemon/server.rb +1 -3
- data/lib/steep/daemon.rb +49 -9
- data/lib/steep/diagnostic/ruby.rb +4 -4
- data/lib/steep/drivers/check.rb +0 -1
- data/lib/steep/drivers/checkfile.rb +4 -12
- data/lib/steep/drivers/init.rb +4 -3
- data/lib/steep/drivers/langserver.rb +34 -5
- data/lib/steep/drivers/query.rb +40 -2
- data/lib/steep/drivers/stats.rb +0 -1
- data/lib/steep/drivers/worker.rb +0 -7
- data/lib/steep/interface/builder.rb +194 -28
- data/lib/steep/interface/shape.rb +28 -14
- data/lib/steep/node_helper.rb +2 -2
- data/lib/steep/server/base_worker.rb +27 -30
- data/lib/steep/server/change_buffer.rb +0 -32
- data/lib/steep/server/command_socket.rb +181 -0
- data/lib/steep/server/custom_methods.rb +22 -22
- data/lib/steep/server/goto_resolver.rb +200 -0
- data/lib/steep/server/interaction_worker.rb +32 -12
- data/lib/steep/server/master.rb +709 -261
- data/lib/steep/server/rbs_entry_builder.rb +237 -0
- data/lib/steep/server/type_check_controller.rb +73 -27
- data/lib/steep/server/type_check_database.rb +406 -0
- data/lib/steep/server/type_check_worker.rb +118 -352
- data/lib/steep/server/worker_process.rb +8 -22
- data/lib/steep/services/hover_provider/content.rb +1 -1
- data/lib/steep/services/hover_provider/ruby.rb +1 -1
- data/lib/steep/services/signature_help_provider.rb +1 -1
- data/lib/steep/services/stats_calculator.rb +16 -35
- data/lib/steep/services/symbol_provider.rb +191 -0
- data/lib/steep/services/type_check_service.rb +10 -21
- data/lib/steep/source.rb +9 -10
- data/lib/steep/subtyping/cache.rb +16 -4
- data/lib/steep/subtyping/check.rb +83 -16
- data/lib/steep/subtyping/stats.rb +356 -0
- data/lib/steep/tagged_logging.rb +4 -1
- data/lib/steep/type_construction.rb +39 -17
- data/lib/steep/type_inference/case_when.rb +2 -2
- data/lib/steep/type_inference/logic_type_interpreter.rb +6 -3
- data/lib/steep/type_inference/send_args.rb +9 -5
- data/lib/steep/typing.rb +3 -3
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +6 -1
- data/steep-relaxed.gemspec +2 -8
- metadata +15 -6
- data/lib/steep/services/goto_service.rb +0 -644
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Estimates the boot cost of a shape-warming zygote process: builds the shapes
|
|
3
|
+
# of every class/module/interface in the environment ahead of time, like a
|
|
4
|
+
# zygote would before forking type check workers, and measures wall-clock time
|
|
5
|
+
# and memory growth.
|
|
6
|
+
#
|
|
7
|
+
# Run it in the project directory to measure (the Steepfile is loaded from the
|
|
8
|
+
# current directory):
|
|
9
|
+
#
|
|
10
|
+
# bundle exec ruby "$(bundle info steep --path)/bin/shape_warmup_bench.rb"
|
|
11
|
+
#
|
|
12
|
+
# Env vars:
|
|
13
|
+
# TARGETS=app,test Steepfile targets to measure (default: app)
|
|
14
|
+
# VERBOSE_ERRORS=1 Include the messages of shape building errors and the number of
|
|
15
|
+
# project signature files in the report
|
|
16
|
+
# PHASES=1 Build all RBS definitions before the shapes, reporting the
|
|
17
|
+
# definition phase separately with time, memory, and GC time per
|
|
18
|
+
# phase -- the project/library times then measure the shape
|
|
19
|
+
# construction alone
|
|
20
|
+
# STACKPROF=cpu Profile the phases with stackprof (cpu, wall, or object) and
|
|
21
|
+
# print the report to stderr; STACKPROF_INTERVAL overrides the
|
|
22
|
+
# sampling interval, STACKPROF_OUT saves the raw dump to a file
|
|
23
|
+
# WORKING_SET_OUT=p Type check the target's source files instead of warming everything,
|
|
24
|
+
# and write the names of the types whose shapes were actually built to
|
|
25
|
+
# the file `p`; WORKING_SET_FILES=N checks only the first N files
|
|
26
|
+
# WORKING_SET_IN=p Warm only the types listed in the file `p`, instead of everything, to
|
|
27
|
+
# measure what a warmup limited to the working set costs
|
|
28
|
+
# DEFINITIONS_ONLY=1 Warm the RBS definitions but not the shapes, to compare how much a
|
|
29
|
+
# partial warmup shares (implies PHASES=1)
|
|
30
|
+
# FORK=N After warming, fork N children that type check their share of the
|
|
31
|
+
# source files, like the type check workers do, and report the memory
|
|
32
|
+
# each one ends up owning privately -- the number that decides how many
|
|
33
|
+
# workers fit in memory. FORK_FILES=N limits the files checked.
|
|
34
|
+
# NO_MAJOR_GC=1 Disable major GCs for the whole run with
|
|
35
|
+
# `GC.config(rgengc_allow_full_mark: false)` (Ruby 3.4+); the
|
|
36
|
+
# memory numbers then overestimate, because dead old objects
|
|
37
|
+
# are never collected
|
|
38
|
+
#
|
|
39
|
+
# Phases per target:
|
|
40
|
+
# 1. project types types declared in the signature files and inline sources of the
|
|
41
|
+
# target and its groups
|
|
42
|
+
# 2. library types everything else in the environment (gems, stdlib, core)
|
|
43
|
+
#
|
|
44
|
+
# The report contains no type names, so it is safe to share -- except with
|
|
45
|
+
# VERBOSE_ERRORS, whose error messages usually name the failing types. The stackprof
|
|
46
|
+
# report contains only Steep/RBS method names.
|
|
47
|
+
|
|
48
|
+
Encoding.default_external = Encoding::UTF_8
|
|
49
|
+
|
|
50
|
+
require "steep"
|
|
51
|
+
require "benchmark"
|
|
52
|
+
require "json"
|
|
53
|
+
require "objspace"
|
|
54
|
+
|
|
55
|
+
TARGET_NAMES = (ENV["TARGETS"] || "app").split(",").map(&:to_sym)
|
|
56
|
+
verbose_errors = ENV["VERBOSE_ERRORS"] == "1"
|
|
57
|
+
definitions_only = ENV["DEFINITIONS_ONLY"] == "1"
|
|
58
|
+
split_phases = ENV["PHASES"] == "1" || definitions_only
|
|
59
|
+
fork_count = ENV["FORK"]&.to_i
|
|
60
|
+
working_set_out = ENV["WORKING_SET_OUT"]
|
|
61
|
+
working_set_in = ENV["WORKING_SET_IN"]
|
|
62
|
+
stackprof_mode = ENV["STACKPROF"]&.to_sym
|
|
63
|
+
|
|
64
|
+
if stackprof_mode
|
|
65
|
+
begin
|
|
66
|
+
require "stackprof"
|
|
67
|
+
rescue LoadError
|
|
68
|
+
abort "STACKPROF needs the stackprof gem in the bundle"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if ENV["NO_MAJOR_GC"] == "1"
|
|
73
|
+
# Prototypes a boot that skips the major GCs while retaining almost everything --
|
|
74
|
+
# minor GCs keep collecting the young garbage
|
|
75
|
+
GC.respond_to?(:config) or abort "NO_MAJOR_GC needs Ruby 3.4+ (GC.config)"
|
|
76
|
+
GC.config(rgengc_allow_full_mark: false)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
GC.measure_total_time = true
|
|
80
|
+
gc_snap = -> { [GC.stat(:time), GC.stat(:major_gc_count), GC.stat(:minor_gc_count)] }
|
|
81
|
+
gc_delta = ->(snap) { gc_snap.().zip(snap).map {|now, was| now - was } }
|
|
82
|
+
|
|
83
|
+
if working_set_out
|
|
84
|
+
# The shapes built during a type check are the ones a warmup actually has to provide
|
|
85
|
+
TOUCHED = {} #: Hash[untyped, bool]
|
|
86
|
+
Steep::Interface::Builder.class_eval do
|
|
87
|
+
alias_method :object_shape_without_record, :object_shape
|
|
88
|
+
def object_shape(type_name)
|
|
89
|
+
TOUCHED[type_name] = true
|
|
90
|
+
object_shape_without_record(type_name)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
alias_method :singleton_shape_without_record, :singleton_shape
|
|
94
|
+
def singleton_shape(type_name)
|
|
95
|
+
TOUCHED[type_name] = true
|
|
96
|
+
singleton_shape_without_record(type_name)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
steepfile = Pathname.pwd + "Steepfile"
|
|
102
|
+
project = Steep::Project.new(steepfile_path: steepfile)
|
|
103
|
+
Steep::Project::DSL.parse(project, steepfile.read, filename: steepfile.to_s)
|
|
104
|
+
|
|
105
|
+
loader = Steep::Services::FileLoader.new(base_dir: project.base_dir)
|
|
106
|
+
|
|
107
|
+
service = nil
|
|
108
|
+
boot_time = Benchmark.realtime do
|
|
109
|
+
service = Steep::Services::TypeCheckService.new(project: project)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
changes = {}
|
|
113
|
+
project.targets.each do |target|
|
|
114
|
+
loader.each_path_in_target(target) do |path|
|
|
115
|
+
changes[path] ||= [Steep::Services::ContentChange.string((project.base_dir + path).read)]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
update_time = Benchmark.realtime { service.update(changes: changes) }
|
|
119
|
+
|
|
120
|
+
rss_mb = -> { File.read("/proc/self/status")[/VmRSS:\s+(\d+)/, 1].to_i / 1024.0 }
|
|
121
|
+
|
|
122
|
+
# `Private_Dirty` is the memory a forked child does not share with its parent any more,
|
|
123
|
+
# so it is what each additional worker really costs
|
|
124
|
+
smaps_mb = ->(key) do
|
|
125
|
+
if line = File.read("/proc/self/smaps_rollup")[/^#{key}:\s+(\d+) kB/]
|
|
126
|
+
$1.to_i / 1024.0
|
|
127
|
+
else
|
|
128
|
+
0.0
|
|
129
|
+
end
|
|
130
|
+
rescue Errno::ENOENT
|
|
131
|
+
0.0
|
|
132
|
+
end
|
|
133
|
+
gc_memsize = -> { 2.times { GC.start }; ObjectSpace.memsize_of_all }
|
|
134
|
+
|
|
135
|
+
result = {
|
|
136
|
+
boot: boot_time.round(2),
|
|
137
|
+
update: update_time.round(2),
|
|
138
|
+
targets: {},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
targets = project.targets.select {|t| TARGET_NAMES.include?(t.name) }
|
|
142
|
+
raise "no matching targets: #{TARGET_NAMES}" if targets.empty?
|
|
143
|
+
|
|
144
|
+
targets.each do |target|
|
|
145
|
+
signature_service = service.signature_services.fetch(target.name)
|
|
146
|
+
subtyping = signature_service.current_subtyping
|
|
147
|
+
|
|
148
|
+
unless subtyping
|
|
149
|
+
result[:targets][target.name] = { error: "signature has errors; no subtyping available" }
|
|
150
|
+
next
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
builder = subtyping.builder
|
|
154
|
+
env = signature_service.latest_env
|
|
155
|
+
|
|
156
|
+
# Signature and inline-source patterns of the groups don't appear in the target's own
|
|
157
|
+
# patterns, so both levels have to be enumerated, like FileLoader#each_path_in_target
|
|
158
|
+
sig_paths = [] #: Array[Pathname]
|
|
159
|
+
collect_paths = ->(pattern) { loader.each_path_in_patterns(pattern) {|path| sig_paths << path } }
|
|
160
|
+
target.groups.each do |group|
|
|
161
|
+
collect_paths.(group.signature_pattern)
|
|
162
|
+
collect_paths.(group.inline_source_pattern)
|
|
163
|
+
end
|
|
164
|
+
collect_paths.(target.signature_pattern)
|
|
165
|
+
collect_paths.(target.inline_source_pattern)
|
|
166
|
+
sig_paths.uniq!
|
|
167
|
+
|
|
168
|
+
project_names = signature_service.type_names(paths: Set.new(sig_paths), env: env).to_set
|
|
169
|
+
|
|
170
|
+
all_names = env.class_decls.keys + env.interface_decls.keys
|
|
171
|
+
|
|
172
|
+
if working_set_in
|
|
173
|
+
working_set = Set.new(File.readlines(working_set_in, chomp: true))
|
|
174
|
+
all_names = all_names.select {|name| working_set.include?(name.to_s) }
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
project_set, library_set = all_names.partition {|name| project_names.include?(name) }
|
|
178
|
+
|
|
179
|
+
warm = ->(names) do
|
|
180
|
+
errors = 0
|
|
181
|
+
error_tally = Hash.new(0)
|
|
182
|
+
time = Benchmark.realtime do
|
|
183
|
+
names.each do |name|
|
|
184
|
+
begin
|
|
185
|
+
if name.class?
|
|
186
|
+
builder.object_shape(name)
|
|
187
|
+
builder.singleton_shape(name)
|
|
188
|
+
elsif name.interface?
|
|
189
|
+
builder.object_shape(name)
|
|
190
|
+
end
|
|
191
|
+
rescue => exn
|
|
192
|
+
errors += 1
|
|
193
|
+
error_tally["#{exn.class}: #{exn.message}"[0, 200]] += 1 if verbose_errors
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
[time, errors, error_tally]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
warm_definitions = ->(names) do
|
|
201
|
+
definition_builder = builder.factory.definition_builder
|
|
202
|
+
errors = 0
|
|
203
|
+
time = Benchmark.realtime do
|
|
204
|
+
names.each do |name|
|
|
205
|
+
begin
|
|
206
|
+
if name.class?
|
|
207
|
+
definition_builder.build_instance(name)
|
|
208
|
+
definition_builder.build_singleton(name)
|
|
209
|
+
elsif name.interface?
|
|
210
|
+
definition_builder.build_interface(name)
|
|
211
|
+
end
|
|
212
|
+
rescue => _
|
|
213
|
+
errors += 1
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
[time, errors]
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Samples accumulate across the start/stop pairs, so the forced GCs of the memory
|
|
221
|
+
# measurements between the phases stay out of the profile
|
|
222
|
+
profile_phase = ->(callable) do
|
|
223
|
+
if stackprof_mode
|
|
224
|
+
interval = (ENV["STACKPROF_INTERVAL"] || (stackprof_mode == :object ? 100 : 1000)).to_i
|
|
225
|
+
StackProf.start(mode: stackprof_mode, raw: false, interval: interval)
|
|
226
|
+
begin
|
|
227
|
+
callable.call
|
|
228
|
+
ensure
|
|
229
|
+
StackProf.stop
|
|
230
|
+
end
|
|
231
|
+
else
|
|
232
|
+
callable.call
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
if working_set_out
|
|
237
|
+
source_paths = [] #: Array[Pathname]
|
|
238
|
+
loader.each_path_in_target(target) do |path|
|
|
239
|
+
source_paths << path if target.possible_source_file?(path)
|
|
240
|
+
end
|
|
241
|
+
if limit = ENV["WORKING_SET_FILES"]
|
|
242
|
+
source_paths = source_paths.first(limit.to_i)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
check_time = Benchmark.realtime do
|
|
246
|
+
source_paths.each do |path|
|
|
247
|
+
begin
|
|
248
|
+
service.typecheck_source(path: path, target: target)
|
|
249
|
+
rescue => _
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
File.write(working_set_out, TOUCHED.each_key.map(&:to_s).sort.join("\n"))
|
|
255
|
+
|
|
256
|
+
result[:targets][target.name] = {
|
|
257
|
+
checked_files: source_paths.size,
|
|
258
|
+
check_time: check_time.round(2),
|
|
259
|
+
environment_types: all_names.size,
|
|
260
|
+
types_touched: TOUCHED.size,
|
|
261
|
+
touched_ratio: (TOUCHED.size * 100.0 / all_names.size).round(1),
|
|
262
|
+
rss_mb: rss_mb.().round(0),
|
|
263
|
+
}
|
|
264
|
+
next
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
if split_phases
|
|
268
|
+
before_definitions = gc_memsize.()
|
|
269
|
+
gc0 = gc_snap.()
|
|
270
|
+
definitions_time, definitions_errors = profile_phase.(-> { warm_definitions.(all_names) })
|
|
271
|
+
definitions_gc = gc_delta.(gc0)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
before = gc_memsize.()
|
|
275
|
+
|
|
276
|
+
if definitions_only
|
|
277
|
+
project_time, project_errors, project_error_tally = 0.0, 0, {}
|
|
278
|
+
library_time, library_errors, library_error_tally = 0.0, 0, {}
|
|
279
|
+
project_gc = library_gc = [0, 0, 0]
|
|
280
|
+
after_project = after_library = before
|
|
281
|
+
else
|
|
282
|
+
gc0 = gc_snap.()
|
|
283
|
+
project_time, project_errors, project_error_tally = profile_phase.(-> { warm.(project_set) })
|
|
284
|
+
project_gc = gc_delta.(gc0)
|
|
285
|
+
after_project = gc_memsize.()
|
|
286
|
+
gc0 = gc_snap.()
|
|
287
|
+
library_time, library_errors, library_error_tally = profile_phase.(-> { warm.(library_set) })
|
|
288
|
+
library_gc = gc_delta.(gc0)
|
|
289
|
+
after_library = gc_memsize.()
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
compact_time = Benchmark.realtime { GC.compact }
|
|
293
|
+
|
|
294
|
+
result[:targets][target.name] = {
|
|
295
|
+
project_types: project_set.size,
|
|
296
|
+
project_time: project_time.round(2),
|
|
297
|
+
project_mb: ((after_project - before) / 1024.0 / 1024).round(1),
|
|
298
|
+
project_errors: project_errors,
|
|
299
|
+
library_types: library_set.size,
|
|
300
|
+
library_time: library_time.round(2),
|
|
301
|
+
library_mb: ((after_library - after_project) / 1024.0 / 1024).round(1),
|
|
302
|
+
library_errors: library_errors,
|
|
303
|
+
object_shape_cache: builder.object_shape_cache.size,
|
|
304
|
+
singleton_shape_cache: builder.singleton_shape_cache.size,
|
|
305
|
+
gc_compact_time: compact_time.round(2),
|
|
306
|
+
rss_mb: rss_mb.().round(0),
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if split_phases
|
|
310
|
+
gc_report = ->(prefix, (ms, major, minor)) do
|
|
311
|
+
{ :"#{prefix}_gc_ms" => ms, :"#{prefix}_major_gc" => major, :"#{prefix}_minor_gc" => minor }
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
result[:targets][target.name].merge!(
|
|
315
|
+
definitions_time: definitions_time.round(2),
|
|
316
|
+
definitions_mb: ((before - before_definitions) / 1024.0 / 1024).round(1),
|
|
317
|
+
definitions_errors: definitions_errors,
|
|
318
|
+
**gc_report.(:definitions, definitions_gc),
|
|
319
|
+
**gc_report.(:project, project_gc),
|
|
320
|
+
**gc_report.(:library, library_gc),
|
|
321
|
+
)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
if fork_count
|
|
325
|
+
# `Process.warmup` promotes everything to the old generation and compacts, so the pages
|
|
326
|
+
# the children inherit are as stable as they get
|
|
327
|
+
warmup_time = Benchmark.realtime { Process.warmup if Process.respond_to?(:warmup) }
|
|
328
|
+
|
|
329
|
+
source_paths = [] #: Array[Pathname]
|
|
330
|
+
loader.each_path_in_target(target) do |path|
|
|
331
|
+
source_paths << path if target.possible_source_file?(path)
|
|
332
|
+
end
|
|
333
|
+
if limit = ENV["FORK_FILES"]
|
|
334
|
+
source_paths = source_paths.first(limit.to_i)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
parent_rss = rss_mb.()
|
|
338
|
+
readers = source_paths.each_slice((source_paths.size.to_f / fork_count).ceil).map do |paths|
|
|
339
|
+
reader, writer = IO.pipe
|
|
340
|
+
|
|
341
|
+
fork do
|
|
342
|
+
reader.close
|
|
343
|
+
time = Benchmark.realtime do
|
|
344
|
+
paths.each do |path|
|
|
345
|
+
begin
|
|
346
|
+
service.typecheck_source(path: path, target: target)
|
|
347
|
+
rescue => _
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
writer.write(
|
|
352
|
+
JSON.generate(
|
|
353
|
+
files: paths.size,
|
|
354
|
+
check_time: time.round(2),
|
|
355
|
+
private_mb: smaps_mb.("Private_Dirty").round(1),
|
|
356
|
+
shared_mb: smaps_mb.("Shared_Clean").round(1),
|
|
357
|
+
rss_mb: rss_mb.().round(0)
|
|
358
|
+
)
|
|
359
|
+
)
|
|
360
|
+
writer.close
|
|
361
|
+
exit!(0)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
writer.close
|
|
365
|
+
reader
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
children = readers.map do |reader|
|
|
369
|
+
json = reader.read
|
|
370
|
+
reader.close
|
|
371
|
+
JSON.parse(json, symbolize_names: true)
|
|
372
|
+
end
|
|
373
|
+
Process.waitall
|
|
374
|
+
|
|
375
|
+
result[:targets][target.name].merge!(
|
|
376
|
+
warmup_call_time: warmup_time.round(2),
|
|
377
|
+
parent_rss_mb: parent_rss.round(0),
|
|
378
|
+
workers: children.size,
|
|
379
|
+
worker_check_time_max: children.map {|c| c[:check_time] }.max,
|
|
380
|
+
worker_private_mb_avg: (children.sum {|c| c[:private_mb] } / children.size).round(1),
|
|
381
|
+
worker_private_mb_max: children.map {|c| c[:private_mb] }.max,
|
|
382
|
+
worker_rss_mb_avg: (children.sum {|c| c[:rss_mb] } / children.size).round(0)
|
|
383
|
+
)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
if verbose_errors
|
|
387
|
+
result[:targets][target.name][:project_sig_paths] = sig_paths.size
|
|
388
|
+
top_errors = ->(tally) { tally.sort_by {|_, count| -count }.first(20).to_h }
|
|
389
|
+
result[:targets][target.name][:project_error_details] = top_errors.(project_error_tally)
|
|
390
|
+
result[:targets][target.name][:library_error_details] = top_errors.(library_error_tally)
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
if stackprof_mode
|
|
395
|
+
profile = StackProf.results
|
|
396
|
+
if profile
|
|
397
|
+
File.binwrite(ENV["STACKPROF_OUT"], Marshal.dump(profile)) if ENV["STACKPROF_OUT"]
|
|
398
|
+
StackProf::Report.new(profile).print_text(false, 30, nil, nil, nil, nil, $stderr)
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
puts JSON.pretty_generate(result)
|
data/bin/steep-check.rb
CHANGED
|
@@ -67,7 +67,7 @@ class Command
|
|
|
67
67
|
target.options.load_collection_lock
|
|
68
68
|
|
|
69
69
|
signature_service = Steep::Project::Target.construct_env_loader(options: target.options, project: project).yield_self do |loader|
|
|
70
|
-
Steep::Services::SignatureService.load_from(loader)
|
|
70
|
+
Steep::Services::SignatureService.load_from(loader, implicitly_returns_nil: target.implicitly_returns_nil)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
env = signature_service.latest_env
|
|
@@ -76,8 +76,8 @@ class Command
|
|
|
76
76
|
|
|
77
77
|
signature_files.each do |path, content|
|
|
78
78
|
buffer = RBS::Buffer.new(name: path.to_s, content: content)
|
|
79
|
-
|
|
80
|
-
env.
|
|
79
|
+
_, dirs, decls = RBS::Parser.parse_signature(buffer)
|
|
80
|
+
env.add_source(RBS::Source::RBS.new(buffer, dirs, decls))
|
|
81
81
|
new_decls.merge(decls)
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -94,7 +94,7 @@ class Command
|
|
|
94
94
|
|
|
95
95
|
definition_builder = RBS::DefinitionBuilder.new(env: env)
|
|
96
96
|
factory = AST::Types::Factory.new(builder: definition_builder)
|
|
97
|
-
builder = Interface::Builder.new(factory)
|
|
97
|
+
builder = Interface::Builder.new(factory, implicitly_returns_nil: target.implicitly_returns_nil)
|
|
98
98
|
subtyping = Subtyping::Check.new(builder: builder)
|
|
99
99
|
|
|
100
100
|
typings = {}
|