typeprof 0.15.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/Gemfile.lock +4 -4
  4. data/exe/typeprof +5 -1
  5. data/lib/typeprof/analyzer.rb +261 -66
  6. data/lib/typeprof/arguments.rb +1 -0
  7. data/lib/typeprof/builtin.rb +30 -22
  8. data/lib/typeprof/cli.rb +22 -2
  9. data/lib/typeprof/code-range.rb +177 -0
  10. data/lib/typeprof/config.rb +43 -18
  11. data/lib/typeprof/container-type.rb +10 -1
  12. data/lib/typeprof/export.rb +199 -20
  13. data/lib/typeprof/import.rb +30 -4
  14. data/lib/typeprof/iseq.rb +504 -200
  15. data/lib/typeprof/lsp.rb +865 -0
  16. data/lib/typeprof/method.rb +17 -13
  17. data/lib/typeprof/type.rb +46 -38
  18. data/lib/typeprof/utils.rb +18 -1
  19. data/lib/typeprof/version.rb +1 -1
  20. data/lib/typeprof.rb +3 -0
  21. data/smoke/array15.rb +1 -1
  22. data/smoke/array6.rb +2 -2
  23. data/smoke/array8.rb +1 -1
  24. data/smoke/block-args2.rb +3 -3
  25. data/smoke/block-args3.rb +4 -4
  26. data/smoke/break2.rb +1 -1
  27. data/smoke/gvar2.rb +0 -3
  28. data/smoke/hash-bot.rb +1 -1
  29. data/smoke/hash4.rb +1 -1
  30. data/smoke/identifier_keywords.rb +17 -0
  31. data/smoke/next2.rb +1 -1
  32. data/smoke/or_raise.rb +18 -0
  33. data/smoke/parameterizedd-self.rb +2 -2
  34. data/smoke/pattern-match1.rb +1 -6
  35. data/smoke/rbs-vars.rb +0 -3
  36. data/testbed/ao.rb +1 -1
  37. data/typeprof-lsp +3 -0
  38. data/typeprof.gemspec +1 -1
  39. data/vscode/.gitignore +5 -0
  40. data/vscode/.vscode/launch.json +16 -0
  41. data/vscode/.vscodeignore +7 -0
  42. data/vscode/README.md +22 -0
  43. data/vscode/development.md +31 -0
  44. data/vscode/package-lock.json +2211 -0
  45. data/vscode/package.json +71 -0
  46. data/vscode/sandbox/test.rb +24 -0
  47. data/vscode/src/extension.ts +285 -0
  48. data/vscode/tsconfig.json +15 -0
  49. metadata +20 -5
@@ -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|
@@ -69,14 +69,17 @@ module TypeProf
69
69
  end
70
70
 
71
71
  def show_gvars(scratch, gvars, output)
72
+ gvars = gvars.dump.filter_map do |gvar_name, entry|
73
+ if entry.type != Type.bot && !entry.rbs_declared
74
+ [gvar_name, entry]
75
+ end
76
+ end
72
77
  # A signature for global variables is not supported in RBS
73
- return if gvars.dump.empty?
78
+ return if gvars.empty?
74
79
 
75
80
  output.puts "# Global variables"
76
- gvars.dump.each do |gvar_name, entry|
77
- next if entry.type == Type.bot
78
- s = entry.rbs_declared ? "#" : ""
79
- output.puts s + "#{ gvar_name }: #{ entry.type.screen_name(scratch) }"
81
+ gvars.each do |gvar_name, entry|
82
+ output.puts "#{ gvar_name }: #{ entry.type.screen_name(scratch) }"
80
83
  end
81
84
  output.puts
82
85
  end
@@ -107,16 +110,17 @@ module TypeProf
107
110
  @scratch.namespace = class_def.name
108
111
 
109
112
  consts = {}
110
- class_def.consts.each do |name, (ty, absolute_path)|
113
+ class_def.consts.each do |name, (ty, loc)|
114
+ next unless loc
111
115
  next if ty.is_a?(Type::Class)
112
- next if !absolute_path || Config.check_dir_filter(absolute_path) == :exclude
116
+ next if Config.current.check_dir_filter(loc[0]) == :exclude
113
117
  consts[name] = ty.screen_name(@scratch)
114
118
  end
115
119
 
116
120
  modules = class_def.modules.to_h do |kind, mods|
117
121
  mods = mods.to_h do |singleton, mods|
118
122
  mods = mods.filter_map do |mod_def, _type_args, absolute_paths|
119
- 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 }
120
124
  Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
121
125
  end
122
126
  [singleton, mods]
@@ -139,7 +143,7 @@ module TypeProf
139
143
 
140
144
  ctx = ctxs.find {|ctx| ctx.mid == mid } || ctxs.first
141
145
 
142
- next if Config.check_dir_filter(ctx.iseq.absolute_path) == :exclude
146
+ next if Config.current.check_dir_filter(ctx.iseq.absolute_path) == :exclude
143
147
 
144
148
  method_name = mid
145
149
  method_name = "self.#{ method_name }" if singleton
@@ -149,7 +153,7 @@ module TypeProf
149
153
  source_locations[key] ||= ctx.iseq.source_location(0)
150
154
  (methods[key] ||= []) << @scratch.show_method_signature(ctx)
151
155
  when AliasMethodDef
152
- 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
153
157
  alias_name, orig_name = mid, mdef.orig_mid
154
158
  if singleton
155
159
  alias_name = "self.#{ alias_name }"
@@ -162,7 +166,7 @@ module TypeProf
162
166
  when AttrMethodDef
163
167
  next if !mdef.def_ep
164
168
  absolute_path = mdef.def_ep.ctx.iseq.absolute_path
165
- next if !absolute_path || Config.check_dir_filter(absolute_path) == :exclude
169
+ next if !absolute_path || Config.current.check_dir_filter(absolute_path) == :exclude
166
170
  mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=")
167
171
  method_name = mid
168
172
  method_name = "self.#{ mid }" if singleton
@@ -185,13 +189,14 @@ module TypeProf
185
189
  key = [:rbs, method_name]
186
190
  methods[key] = sigs
187
191
  visibilities[key] ||= mdef.pub_meth
192
+ source_locations[key] ||= mdef.iseq&.source_location(0)
188
193
  end
189
194
  end
190
195
  end
191
196
  end
192
197
 
193
198
  ivars = ivars.map do |(singleton, var), entry|
194
- 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 }
195
200
  ty = entry.type
196
201
  next unless var.to_s.start_with?("@")
197
202
  var = "self.#{ var }" if singleton
@@ -201,12 +206,12 @@ module TypeProf
201
206
  end.compact
202
207
 
203
208
  cvars = cvars.map do |var, entry|
204
- 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 }
205
210
  next if entry.rbs_declared
206
211
  [var, entry.type.screen_name(@scratch)]
207
212
  end.compact
208
213
 
209
- 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
210
215
  if methods.keys.all? {|type,| type == :rbs }
211
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?
212
217
  end
@@ -229,8 +234,182 @@ module TypeProf
229
234
  )
230
235
  end
231
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
+
232
375
  ClassData = Struct.new(:kind, :name, :superclass, :consts, :modules, :ivars, :cvars, :methods, :visibilities, :source_locations, :inner_classes, keyword_init: true)
233
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
+
234
413
  def show(stat_eps, output)
235
414
  # make the class hierarchy
236
415
  root = {}
@@ -267,7 +446,7 @@ module TypeProf
267
446
  coverage = {}
268
447
  stat_eps.each do |ep|
269
448
  path = ep.ctx.iseq.path
270
- lineno = ep.ctx.iseq.linenos[ep.pc] - 1
449
+ lineno = ep.ctx.iseq.insns[ep.pc].lineno - 1
271
450
  (coverage[path] ||= [])[lineno] ||= 0
272
451
  (coverage[path] ||= [])[lineno] += 1
273
452
  end
@@ -337,7 +516,7 @@ module TypeProf
337
516
  prev_vis = vis
338
517
  end
339
518
  source_location = class_data.source_locations[key]
340
- if Config.options[:show_source_locations] && source_location
519
+ if Config.current.options[:show_source_locations] && source_location
341
520
  lines << nil
342
521
  lines << (indent + " # #{ source_location }")
343
522
  end
@@ -345,7 +524,7 @@ module TypeProf
345
524
  case type
346
525
  when :attr
347
526
  kind, ty, untyped = *arg
348
- exclude = Config.options[:exclude_untyped] && untyped ? "#" : " " # XXX
527
+ exclude = Config.current.options[:exclude_untyped] && untyped ? "#" : " " # XXX
349
528
  lines << (indent + "#{ exclude } attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" }: #{ ty }")
350
529
  when :rbs
351
530
  sigs = arg.sort.join("\n" + indent + "#" + " " * (method_name.size + 5) + "| ")
@@ -358,7 +537,7 @@ module TypeProf
358
537
  untyped ||= untyped0
359
538
  end
360
539
  sigs = sigs.sort.join("\n" + indent + " " * (method_name.size + 6) + "| ")
361
- exclude = Config.options[:exclude_untyped] && untyped ? "#" : " " # XXX
540
+ exclude = Config.current.options[:exclude_untyped] && untyped ? "#" : " " # XXX
362
541
  lines << (indent + "#{ exclude } def #{ method_name }: #{ sigs }")
363
542
  when :alias
364
543
  orig_name = arg
@@ -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 = [(member.kind == :singleton ? "self." : "") + member.name.to_s, member.types.map {|type| type.location.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
@@ -459,6 +476,8 @@ module TypeProf
459
476
  end
460
477
  when RBS::Types::Union
461
478
  [:union, ty.types.map {|ty2| conv_type(ty2) }.compact]
479
+ when RBS::Types::Intersection
480
+ [:intersection, ty.types.map {|ty2| conv_type(ty2) }.compact]
462
481
  when RBS::Types::Optional
463
482
  [:optional, conv_type(ty.type)]
464
483
  when RBS::Types::Interface
@@ -512,6 +531,10 @@ module TypeProf
512
531
  Import.new(scratch, scratch.rbs_reader.load_rbs_string(rbs_name, rbs_code)).import(true)
513
532
  end
514
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
+
515
538
  def initialize(scratch, json)
516
539
  @scratch = scratch
517
540
  @json = json
@@ -527,7 +550,7 @@ module TypeProf
527
550
  superclass = path_to_klass(superclass) if superclass
528
551
  base_klass = path_to_klass(classpath[0..-2])
529
552
 
530
- klass = @scratch.get_constant(base_klass, name)
553
+ klass, = @scratch.get_constant(base_klass, name)
531
554
  if klass.is_a?(Type::Any)
532
555
  klass = @scratch.new_class(base_klass, name, type_params, superclass, nil)
533
556
 
@@ -729,6 +752,9 @@ module TypeProf
729
752
  when :union
730
753
  tys = ty[1]
731
754
  Type::Union.create(Utils::Set[*tys.map {|ty2| conv_type(ty2) }], nil) # XXX: Array and Hash support
755
+ when :intersection
756
+ tys = ty[1]
757
+ conv_type(tys.first) # XXX: This is wrong! We need to support intersection type
732
758
  when :var
733
759
  Type::Var.new(ty[1])
734
760
  when :proc
@@ -743,7 +769,7 @@ module TypeProf
743
769
  def path_to_klass(path)
744
770
  klass = Type::Builtin[:obj]
745
771
  path.each do |name|
746
- klass = @scratch.get_constant(klass, name)
772
+ klass, = @scratch.get_constant(klass, name)
747
773
  if klass == Type.any
748
774
  raise TypeProfError.new("A constant `#{ path.join("::") }' is used but not defined in RBS")
749
775
  end