typeprof 0.15.3 → 0.20.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/.github/workflows/main.yml +1 -1
- data/Gemfile.lock +4 -4
- data/exe/typeprof +5 -1
- data/lib/typeprof/analyzer.rb +228 -54
- data/lib/typeprof/arguments.rb +1 -0
- data/lib/typeprof/builtin.rb +23 -23
- data/lib/typeprof/cli.rb +22 -2
- data/lib/typeprof/code-range.rb +177 -0
- data/lib/typeprof/config.rb +43 -18
- data/lib/typeprof/container-type.rb +3 -0
- data/lib/typeprof/export.rb +191 -15
- data/lib/typeprof/import.rb +25 -4
- data/lib/typeprof/iseq.rb +218 -16
- data/lib/typeprof/lsp.rb +865 -0
- data/lib/typeprof/method.rb +15 -11
- data/lib/typeprof/type.rb +46 -38
- data/lib/typeprof/utils.rb +18 -1
- data/lib/typeprof/version.rb +1 -1
- data/lib/typeprof.rb +3 -0
- data/typeprof-lsp +3 -0
- data/typeprof.gemspec +1 -1
- data/vscode/.gitignore +5 -0
- data/vscode/.vscode/launch.json +16 -0
- data/vscode/.vscodeignore +7 -0
- data/vscode/README.md +22 -0
- data/vscode/development.md +31 -0
- data/vscode/package-lock.json +2211 -0
- data/vscode/package.json +71 -0
- data/vscode/sandbox/test.rb +24 -0
- data/vscode/src/extension.ts +285 -0
- data/vscode/tsconfig.json +15 -0
- metadata +18 -5
data/lib/typeprof/export.rb
CHANGED
@@ -26,7 +26,7 @@ module TypeProf
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def show_message(terminated, output)
|
29
|
-
if Config.options[:show_typeprof_version]
|
29
|
+
if Config.current.options[:show_typeprof_version]
|
30
30
|
output.puts "# TypeProf #{ VERSION }"
|
31
31
|
output.puts
|
32
32
|
end
|
@@ -38,7 +38,7 @@ module TypeProf
|
|
38
38
|
|
39
39
|
def show_error(errors, backward_edge, output)
|
40
40
|
return if errors.empty?
|
41
|
-
return unless Config.options[:show_errors]
|
41
|
+
return unless Config.current.options[:show_errors]
|
42
42
|
|
43
43
|
output.puts "# Errors"
|
44
44
|
errors.each do |ep, msg|
|
@@ -110,16 +110,17 @@ module TypeProf
|
|
110
110
|
@scratch.namespace = class_def.name
|
111
111
|
|
112
112
|
consts = {}
|
113
|
-
class_def.consts.each do |name, (ty,
|
113
|
+
class_def.consts.each do |name, (ty, loc)|
|
114
|
+
next unless loc
|
114
115
|
next if ty.is_a?(Type::Class)
|
115
|
-
next if
|
116
|
+
next if Config.current.check_dir_filter(loc[0]) == :exclude
|
116
117
|
consts[name] = ty.screen_name(@scratch)
|
117
118
|
end
|
118
119
|
|
119
120
|
modules = class_def.modules.to_h do |kind, mods|
|
120
121
|
mods = mods.to_h do |singleton, mods|
|
121
122
|
mods = mods.filter_map do |mod_def, _type_args, absolute_paths|
|
122
|
-
next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
|
123
|
+
next if absolute_paths.all? {|path| !path || Config.current.check_dir_filter(path) == :exclude }
|
123
124
|
Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
|
124
125
|
end
|
125
126
|
[singleton, mods]
|
@@ -142,7 +143,7 @@ module TypeProf
|
|
142
143
|
|
143
144
|
ctx = ctxs.find {|ctx| ctx.mid == mid } || ctxs.first
|
144
145
|
|
145
|
-
next if Config.check_dir_filter(ctx.iseq.absolute_path) == :exclude
|
146
|
+
next if Config.current.check_dir_filter(ctx.iseq.absolute_path) == :exclude
|
146
147
|
|
147
148
|
method_name = mid
|
148
149
|
method_name = "self.#{ method_name }" if singleton
|
@@ -152,7 +153,7 @@ module TypeProf
|
|
152
153
|
source_locations[key] ||= ctx.iseq.source_location(0)
|
153
154
|
(methods[key] ||= []) << @scratch.show_method_signature(ctx)
|
154
155
|
when AliasMethodDef
|
155
|
-
next if mdef.def_ep && Config.check_dir_filter(mdef.def_ep.source_location) == :exclude
|
156
|
+
next if mdef.def_ep && Config.current.check_dir_filter(mdef.def_ep.source_location) == :exclude
|
156
157
|
alias_name, orig_name = mid, mdef.orig_mid
|
157
158
|
if singleton
|
158
159
|
alias_name = "self.#{ alias_name }"
|
@@ -165,7 +166,7 @@ module TypeProf
|
|
165
166
|
when AttrMethodDef
|
166
167
|
next if !mdef.def_ep
|
167
168
|
absolute_path = mdef.def_ep.ctx.iseq.absolute_path
|
168
|
-
next if !absolute_path || Config.check_dir_filter(absolute_path) == :exclude
|
169
|
+
next if !absolute_path || Config.current.check_dir_filter(absolute_path) == :exclude
|
169
170
|
mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=")
|
170
171
|
method_name = mid
|
171
172
|
method_name = "self.#{ mid }" if singleton
|
@@ -188,13 +189,14 @@ module TypeProf
|
|
188
189
|
key = [:rbs, method_name]
|
189
190
|
methods[key] = sigs
|
190
191
|
visibilities[key] ||= mdef.pub_meth
|
192
|
+
source_locations[key] ||= mdef.iseq&.source_location(0)
|
191
193
|
end
|
192
194
|
end
|
193
195
|
end
|
194
196
|
end
|
195
197
|
|
196
198
|
ivars = ivars.map do |(singleton, var), entry|
|
197
|
-
next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
|
199
|
+
next if entry.absolute_paths.all? {|path| Config.current.check_dir_filter(path) == :exclude }
|
198
200
|
ty = entry.type
|
199
201
|
next unless var.to_s.start_with?("@")
|
200
202
|
var = "self.#{ var }" if singleton
|
@@ -204,12 +206,12 @@ module TypeProf
|
|
204
206
|
end.compact
|
205
207
|
|
206
208
|
cvars = cvars.map do |var, entry|
|
207
|
-
next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
|
209
|
+
next if entry.absolute_paths.all? {|path| Config.current.check_dir_filter(path) == :exclude }
|
208
210
|
next if entry.rbs_declared
|
209
211
|
[var, entry.type.screen_name(@scratch)]
|
210
212
|
end.compact
|
211
213
|
|
212
|
-
if !class_def.absolute_path || Config.check_dir_filter(class_def.absolute_path) == :exclude
|
214
|
+
if !class_def.absolute_path || Config.current.check_dir_filter(class_def.absolute_path) == :exclude
|
213
215
|
if methods.keys.all? {|type,| type == :rbs }
|
214
216
|
return nil if consts.empty? && modules[:before][true].empty? && modules[:before][false].empty? && modules[:after][true].empty? && modules[:after][false].empty? && ivars.empty? && cvars.empty? && inner_classes.empty?
|
215
217
|
end
|
@@ -232,8 +234,182 @@ module TypeProf
|
|
232
234
|
)
|
233
235
|
end
|
234
236
|
|
237
|
+
def conv_class_lsp(namespace, class_def)
|
238
|
+
@scratch.namespace = namespace
|
239
|
+
|
240
|
+
if class_def.klass_obj.superclass != :__root__ && class_def.klass_obj.superclass
|
241
|
+
omit = class_def.klass_obj.superclass == Type::Builtin[:obj] || class_def.klass_obj == Type::Builtin[:obj]
|
242
|
+
superclass = omit ? nil : @scratch.get_class_name(class_def.klass_obj.superclass)
|
243
|
+
type_args = class_def.klass_obj.superclass_type_args
|
244
|
+
if type_args && !type_args.empty?
|
245
|
+
superclass += "[#{ type_args.map {|ty| ty.screen_name(@scratch) }.join(", ") }]"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
@scratch.namespace = class_def.name
|
250
|
+
|
251
|
+
consts = {}
|
252
|
+
class_def.consts.each do |name, (ty, loc)|
|
253
|
+
next unless loc
|
254
|
+
next if ty.is_a?(Type::Class)
|
255
|
+
next if Config.current.check_dir_filter(loc[0]) == :exclude
|
256
|
+
consts[name] = ty.screen_name(@scratch)
|
257
|
+
end
|
258
|
+
|
259
|
+
modules = class_def.modules.to_h do |kind, mods|
|
260
|
+
mods = mods.to_h do |singleton, mods|
|
261
|
+
mods = mods.filter_map do |mod_def, _type_args, absolute_paths|
|
262
|
+
next if absolute_paths.all? {|path| !path || Config.current.check_dir_filter(path) == :exclude }
|
263
|
+
Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
|
264
|
+
end
|
265
|
+
[singleton, mods]
|
266
|
+
end
|
267
|
+
[kind, mods]
|
268
|
+
end
|
269
|
+
|
270
|
+
visibilities = {}
|
271
|
+
source_locations = {}
|
272
|
+
methods = {}
|
273
|
+
ivars = class_def.ivars.dump
|
274
|
+
cvars = class_def.cvars.dump
|
275
|
+
|
276
|
+
class_def.methods.each do |(singleton, mid), mdefs|
|
277
|
+
mdefs.each do |mdef|
|
278
|
+
case mdef
|
279
|
+
when ISeqMethodDef
|
280
|
+
ctxs = @iseq_method_to_ctxs[mdef]
|
281
|
+
next unless ctxs
|
282
|
+
|
283
|
+
ctx = ctxs.find {|ctx| ctx.mid == mid } || ctxs.first
|
284
|
+
|
285
|
+
next if Config.current.check_dir_filter(ctx.iseq.absolute_path) == :exclude
|
286
|
+
|
287
|
+
method_name = mid
|
288
|
+
method_name = "self.#{ method_name }" if singleton
|
289
|
+
|
290
|
+
key = [:iseq, method_name]
|
291
|
+
visibilities[key] ||= mdef.pub_meth
|
292
|
+
source_locations[key] ||= [ctx.iseq.source_location(0)]
|
293
|
+
sig = @scratch.show_method_signature(ctx)
|
294
|
+
(methods[key] ||= []) << sig if sig
|
295
|
+
when AliasMethodDef
|
296
|
+
alias_name, orig_name = mid, mdef.orig_mid
|
297
|
+
if singleton
|
298
|
+
alias_name = "self.#{ alias_name }"
|
299
|
+
orig_name = "self.#{ orig_name }"
|
300
|
+
end
|
301
|
+
key = [:alias, alias_name]
|
302
|
+
visibilities[key] ||= mdef.pub_meth
|
303
|
+
source_locations[key] ||= [mdef.def_ep&.source_location]
|
304
|
+
methods[key] = orig_name
|
305
|
+
when AttrMethodDef
|
306
|
+
next if !mdef.def_ep
|
307
|
+
absolute_path = mdef.def_ep.ctx.iseq.absolute_path
|
308
|
+
next if !absolute_path || Config.current.check_dir_filter(absolute_path) == :exclude
|
309
|
+
mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=")
|
310
|
+
method_name = mid
|
311
|
+
method_name = "self.#{ mid }" if singleton
|
312
|
+
method_name = [method_name, :"@#{ mid }" != mdef.ivar]
|
313
|
+
key = [:attr, method_name]
|
314
|
+
visibilities[key] ||= mdef.pub_meth
|
315
|
+
source_locations[key] ||= [mdef.def_ep.source_location]
|
316
|
+
if methods[key]
|
317
|
+
if methods[key][0] != mdef.kind
|
318
|
+
methods[key][0] = :accessor
|
319
|
+
end
|
320
|
+
else
|
321
|
+
entry = ivars[[singleton, mdef.ivar]]
|
322
|
+
ty = entry ? entry.type : Type.any
|
323
|
+
methods[key] = [mdef.kind, ty.screen_name(@scratch), ty.include_untyped?(@scratch)]
|
324
|
+
end
|
325
|
+
when TypedMethodDef
|
326
|
+
if mdef.rbs_source
|
327
|
+
method_name, sigs, rbs_code_range = mdef.rbs_source
|
328
|
+
key = [:rbs, method_name]
|
329
|
+
methods[key] = sigs
|
330
|
+
visibilities[key] ||= mdef.pub_meth
|
331
|
+
source_locations[key] ||= [mdef.iseq&.source_location(0), rbs_code_range]
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
ivars = ivars.map do |(singleton, var), entry|
|
338
|
+
next if entry.absolute_paths.all? {|path| Config.current.check_dir_filter(path) == :exclude }
|
339
|
+
ty = entry.type
|
340
|
+
next unless var.to_s.start_with?("@")
|
341
|
+
var = "self.#{ var }" if singleton
|
342
|
+
next if methods[[:attr, [singleton ? "self.#{ var.to_s[1..] }" : var.to_s[1..].to_sym, false]]]
|
343
|
+
next if entry.rbs_declared
|
344
|
+
[var, ty.screen_name(@scratch)]
|
345
|
+
end.compact
|
346
|
+
|
347
|
+
cvars = cvars.map do |var, entry|
|
348
|
+
next if entry.absolute_paths.all? {|path| Config.current.check_dir_filter(path) == :exclude }
|
349
|
+
next if entry.rbs_declared
|
350
|
+
[var, entry.type.screen_name(@scratch)]
|
351
|
+
end.compact
|
352
|
+
|
353
|
+
if !class_def.absolute_path || Config.current.check_dir_filter(class_def.absolute_path) == :exclude
|
354
|
+
if methods.keys.all? {|type,| type == :rbs }
|
355
|
+
return nil if consts.empty? && modules[:before][true].empty? && modules[:before][false].empty? && modules[:after][true].empty? && modules[:after][false].empty? && ivars.empty? && cvars.empty?
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
@scratch.namespace = nil
|
360
|
+
|
361
|
+
ClassData.new(
|
362
|
+
kind: class_def.kind,
|
363
|
+
name: class_def.name,
|
364
|
+
superclass: superclass,
|
365
|
+
consts: consts,
|
366
|
+
modules: modules,
|
367
|
+
ivars: ivars,
|
368
|
+
cvars: cvars,
|
369
|
+
methods: methods,
|
370
|
+
visibilities: visibilities,
|
371
|
+
source_locations: source_locations,
|
372
|
+
)
|
373
|
+
end
|
374
|
+
|
235
375
|
ClassData = Struct.new(:kind, :name, :superclass, :consts, :modules, :ivars, :cvars, :methods, :visibilities, :source_locations, :inner_classes, keyword_init: true)
|
236
376
|
|
377
|
+
def show_lsp
|
378
|
+
res = []
|
379
|
+
@class_defs.each_value do |class_def|
|
380
|
+
class_data = conv_class_lsp([], class_def)
|
381
|
+
next unless class_data
|
382
|
+
class_data.methods.each do |key, arg|
|
383
|
+
source_location, rbs_code_range = class_data.source_locations[key]
|
384
|
+
type, (method_name, hidden) = key
|
385
|
+
case type
|
386
|
+
when :attr
|
387
|
+
kind, ty, untyped = *arg
|
388
|
+
line = "attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" }: #{ ty }"
|
389
|
+
when :rbs
|
390
|
+
sigs = arg.sort.join(" | ")
|
391
|
+
line = "# def #{ method_name }: #{ sigs }"
|
392
|
+
when :iseq
|
393
|
+
sigs = []
|
394
|
+
untyped = false
|
395
|
+
arg.each do |sig, untyped0|
|
396
|
+
sigs << sig
|
397
|
+
untyped ||= untyped0
|
398
|
+
end
|
399
|
+
sigs = sigs.sort.join(" | ")
|
400
|
+
line = "def #{ method_name }: #{ sigs }"
|
401
|
+
when :alias
|
402
|
+
orig_name = arg
|
403
|
+
line = "alias #{ method_name } #{ orig_name }"
|
404
|
+
end
|
405
|
+
if source_location =~ /:(\d+)$/
|
406
|
+
res << [$`, $1.to_i, line, rbs_code_range, class_data.kind, class_data.name]
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
res
|
411
|
+
end
|
412
|
+
|
237
413
|
def show(stat_eps, output)
|
238
414
|
# make the class hierarchy
|
239
415
|
root = {}
|
@@ -270,7 +446,7 @@ module TypeProf
|
|
270
446
|
coverage = {}
|
271
447
|
stat_eps.each do |ep|
|
272
448
|
path = ep.ctx.iseq.path
|
273
|
-
lineno = ep.ctx.iseq.
|
449
|
+
lineno = ep.ctx.iseq.insns[ep.pc].lineno - 1
|
274
450
|
(coverage[path] ||= [])[lineno] ||= 0
|
275
451
|
(coverage[path] ||= [])[lineno] += 1
|
276
452
|
end
|
@@ -340,7 +516,7 @@ module TypeProf
|
|
340
516
|
prev_vis = vis
|
341
517
|
end
|
342
518
|
source_location = class_data.source_locations[key]
|
343
|
-
if Config.options[:show_source_locations] && source_location
|
519
|
+
if Config.current.options[:show_source_locations] && source_location
|
344
520
|
lines << nil
|
345
521
|
lines << (indent + " # #{ source_location }")
|
346
522
|
end
|
@@ -348,7 +524,7 @@ module TypeProf
|
|
348
524
|
case type
|
349
525
|
when :attr
|
350
526
|
kind, ty, untyped = *arg
|
351
|
-
exclude = Config.options[:exclude_untyped] && untyped ? "#" : " " # XXX
|
527
|
+
exclude = Config.current.options[:exclude_untyped] && untyped ? "#" : " " # XXX
|
352
528
|
lines << (indent + "#{ exclude } attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" }: #{ ty }")
|
353
529
|
when :rbs
|
354
530
|
sigs = arg.sort.join("\n" + indent + "#" + " " * (method_name.size + 5) + "| ")
|
@@ -361,7 +537,7 @@ module TypeProf
|
|
361
537
|
untyped ||= untyped0
|
362
538
|
end
|
363
539
|
sigs = sigs.sort.join("\n" + indent + " " * (method_name.size + 6) + "| ")
|
364
|
-
exclude = Config.options[:exclude_untyped] && untyped ? "#" : " " # XXX
|
540
|
+
exclude = Config.current.options[:exclude_untyped] && untyped ? "#" : " " # XXX
|
365
541
|
lines << (indent + "#{ exclude } def #{ method_name }: #{ sigs }")
|
366
542
|
when :alias
|
367
543
|
orig_name = arg
|
data/lib/typeprof/import.rb
CHANGED
@@ -4,9 +4,14 @@ module TypeProf
|
|
4
4
|
class RBSReader
|
5
5
|
def initialize
|
6
6
|
@repo = RBS::Repository.new
|
7
|
-
Config.gem_repo_dirs.each do |dir|
|
7
|
+
Config.current.gem_repo_dirs.each do |dir|
|
8
8
|
@repo.add(Pathname(dir))
|
9
9
|
end
|
10
|
+
collection_path = Config.current.collection_path
|
11
|
+
if collection_path&.exist?
|
12
|
+
collection_lock = RBS::Collection::Config.lockfile_of(collection_path)
|
13
|
+
@repo.add(collection_lock.repo_path)
|
14
|
+
end
|
10
15
|
@env, @builtin_env_json = RBSReader.get_builtin_env
|
11
16
|
end
|
12
17
|
|
@@ -66,6 +71,14 @@ module TypeProf
|
|
66
71
|
RBSReader.load_rbs(@env, new_decls)
|
67
72
|
end
|
68
73
|
|
74
|
+
def load_rbs_collection(collection_path)
|
75
|
+
loader = RBS::EnvironmentLoader.new(core_root: nil)
|
76
|
+
collection_lock = RBS::Collection::Config.lockfile_of(collection_path)
|
77
|
+
loader.add_collection(collection_lock)
|
78
|
+
new_decls = loader.load(env: @env).map {|decl,| decl }
|
79
|
+
RBSReader.load_rbs(@env, new_decls)
|
80
|
+
end
|
81
|
+
|
69
82
|
def self.load_rbs(env, new_decls)
|
70
83
|
all_env = env.resolve_type_names
|
71
84
|
resolver = RBS::TypeNameResolver.from_env(all_env)
|
@@ -157,7 +170,11 @@ module TypeProf
|
|
157
170
|
end
|
158
171
|
|
159
172
|
method_def = conv_method_def(method_types, visibility)
|
160
|
-
rbs_source = [
|
173
|
+
rbs_source = [
|
174
|
+
(member.kind == :singleton ? "self." : "") + member.name.to_s,
|
175
|
+
member.types.map {|type| type.location.source },
|
176
|
+
[member.location.name, CodeRange.from_rbs(member.location)],
|
177
|
+
]
|
161
178
|
if member.instance?
|
162
179
|
methods[[false, name]] = method_def
|
163
180
|
rbs_sources[[false, name]] = rbs_source
|
@@ -514,6 +531,10 @@ module TypeProf
|
|
514
531
|
Import.new(scratch, scratch.rbs_reader.load_rbs_string(rbs_name, rbs_code)).import(true)
|
515
532
|
end
|
516
533
|
|
534
|
+
def self.import_rbs_collection(scratch, collection_path)
|
535
|
+
Import.new(scratch, scratch.rbs_reader.load_rbs_collection(collection_path)).import(true)
|
536
|
+
end
|
537
|
+
|
517
538
|
def initialize(scratch, json)
|
518
539
|
@scratch = scratch
|
519
540
|
@json = json
|
@@ -529,7 +550,7 @@ module TypeProf
|
|
529
550
|
superclass = path_to_klass(superclass) if superclass
|
530
551
|
base_klass = path_to_klass(classpath[0..-2])
|
531
552
|
|
532
|
-
klass = @scratch.get_constant(base_klass, name)
|
553
|
+
klass, = @scratch.get_constant(base_klass, name)
|
533
554
|
if klass.is_a?(Type::Any)
|
534
555
|
klass = @scratch.new_class(base_klass, name, type_params, superclass, nil)
|
535
556
|
|
@@ -748,7 +769,7 @@ module TypeProf
|
|
748
769
|
def path_to_klass(path)
|
749
770
|
klass = Type::Builtin[:obj]
|
750
771
|
path.each do |name|
|
751
|
-
klass = @scratch.get_constant(klass, name)
|
772
|
+
klass, = @scratch.get_constant(klass, name)
|
752
773
|
if klass == Type.any
|
753
774
|
raise TypeProfError.new("A constant `#{ path.join("::") }' is used but not defined in RBS")
|
754
775
|
end
|