typeprof 0.3.0 → 0.4.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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/doc/demo.md +398 -0
  4. data/doc/doc.ja.md +4 -0
  5. data/doc/doc.md +4 -0
  6. data/lib/typeprof.rb +8 -0
  7. data/lib/typeprof/analyzer.rb +229 -245
  8. data/lib/typeprof/arguments.rb +397 -0
  9. data/lib/typeprof/block.rb +133 -0
  10. data/lib/typeprof/builtin.rb +14 -10
  11. data/lib/typeprof/container-type.rb +94 -17
  12. data/lib/typeprof/export.rb +185 -108
  13. data/lib/typeprof/import.rb +76 -54
  14. data/lib/typeprof/iseq.rb +27 -2
  15. data/lib/typeprof/method.rb +87 -73
  16. data/lib/typeprof/type.rb +125 -309
  17. data/lib/typeprof/version.rb +1 -1
  18. data/smoke/arguments2.rb +1 -1
  19. data/smoke/array-each3.rb +1 -4
  20. data/smoke/array12.rb +1 -1
  21. data/smoke/array6.rb +1 -0
  22. data/smoke/block-ambiguous.rb +36 -0
  23. data/smoke/block-args1-rest.rb +62 -0
  24. data/smoke/block-args1.rb +59 -0
  25. data/smoke/block-args2-rest.rb +62 -0
  26. data/smoke/block-args2.rb +59 -0
  27. data/smoke/block-args3-rest.rb +73 -0
  28. data/smoke/block-args3.rb +70 -0
  29. data/smoke/block-blockarg.rb +27 -0
  30. data/smoke/block-kwarg.rb +52 -0
  31. data/smoke/block11.rb +1 -1
  32. data/smoke/block14.rb +17 -0
  33. data/smoke/block4.rb +2 -2
  34. data/smoke/block5.rb +1 -0
  35. data/smoke/block6.rb +1 -1
  36. data/smoke/block7.rb +0 -2
  37. data/smoke/block8.rb +2 -2
  38. data/smoke/block9.rb +1 -1
  39. data/smoke/blown.rb +1 -1
  40. data/smoke/class-hierarchy.rb +54 -0
  41. data/smoke/class-hierarchy2.rb +27 -0
  42. data/smoke/constant1.rb +11 -6
  43. data/smoke/constant2.rb +2 -0
  44. data/smoke/cvar.rb +1 -0
  45. data/smoke/demo10.rb +1 -1
  46. data/smoke/demo8.rb +2 -2
  47. data/smoke/demo9.rb +1 -3
  48. data/smoke/flow7.rb +1 -7
  49. data/smoke/flow8.rb +13 -0
  50. data/smoke/instance_eval.rb +1 -1
  51. data/smoke/int_times.rb +1 -1
  52. data/smoke/multiple-superclass.rb +4 -0
  53. data/smoke/next2.rb +1 -1
  54. data/smoke/optional3.rb +10 -0
  55. data/smoke/proc4.rb +1 -1
  56. data/smoke/rbs-proc1.rb +9 -0
  57. data/smoke/rbs-proc1.rbs +3 -0
  58. data/smoke/rbs-proc2.rb +20 -0
  59. data/smoke/rbs-proc2.rbs +3 -0
  60. data/smoke/rbs-proc3.rb +13 -0
  61. data/smoke/rbs-proc3.rbs +4 -0
  62. data/smoke/rbs-record.rb +17 -0
  63. data/smoke/rbs-record.rbs +4 -0
  64. data/smoke/rbs-tyvar3.rb +25 -0
  65. data/smoke/rbs-tyvar3.rbs +4 -0
  66. data/smoke/rest2.rb +1 -1
  67. data/smoke/rest5.rb +1 -1
  68. data/smoke/return.rb +1 -1
  69. data/smoke/singleton_method.rb +3 -0
  70. data/smoke/struct.rb +4 -3
  71. data/smoke/struct3.rb +14 -0
  72. data/smoke/symbol-proc.rb +24 -0
  73. metadata +31 -3
  74. data/smoke/variadic1.rb.notyet +0 -5
@@ -59,14 +59,13 @@ module TypeProf
59
59
  end
60
60
 
61
61
  def proc_call(recv, mid, aargs, ep, env, scratch, &ctn)
62
- given_block = env.static_env.blk_ty == recv
63
- scratch.do_invoke_block(given_block, recv, aargs, ep, env, &ctn)
62
+ scratch.do_invoke_block(recv, aargs, ep, env, &ctn)
64
63
  end
65
64
 
66
65
  def object_s_new(recv, mid, aargs, ep, env, scratch, &ctn)
67
66
  ty = Type::Instance.new(recv)
68
67
  if recv.type_params.size >= 1
69
- ty = Type::Cell.new([Type.bot] * recv.type_params.size, ty)
68
+ ty = Type::Cell.new(Type::Cell::Elements.new([Type.bot] * recv.type_params.size), ty)
70
69
  env, ty = scratch.localize_type(ty, env, ep, AllocationSite.new(ep).add_id(:object_s_new))
71
70
  end
72
71
  meths = scratch.get_method(recv, false, :initialize)
@@ -142,9 +141,8 @@ module TypeProf
142
141
  ctn[type.any, ep, env]
143
142
  return
144
143
  end
145
- naargs = ActualArguments.new([recv], Type.nil, {}, Type.nil)
146
- given_block = env.static_env.blk_ty == recv
147
- scratch.do_invoke_block(given_block, aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
144
+ naargs = ActualArguments.new([recv], nil, {}, Type.nil)
145
+ scratch.do_invoke_block(aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
148
146
  ctn[recv, ep, scratch.return_envs[ep]]
149
147
  end
150
148
  end
@@ -263,7 +261,7 @@ module TypeProf
263
261
 
264
262
  ty = aargs.lead_tys.last
265
263
 
266
- env = scratch.update_container_elem_types(env, ep, recv.id) do |elems|
264
+ env = scratch.update_container_elem_types(env, ep, recv.id, recv.base_type) do |elems|
267
265
  elems.update(idx, ty)
268
266
  end
269
267
 
@@ -314,7 +312,7 @@ module TypeProf
314
312
  return ctn[ty, ep, env]
315
313
  end
316
314
 
317
- env = scratch.update_container_elem_types(env, ep, recv.id) do |elems|
315
+ env = scratch.update_container_elem_types(env, ep, recv.id, recv.base_type) do |elems|
318
316
  elems.update(idx, ty)
319
317
  end
320
318
 
@@ -486,8 +484,8 @@ module TypeProf
486
484
  def self.setup_initial_global_env(scratch)
487
485
  klass_basic_obj = scratch.new_class(nil, :BasicObject, [], :__root__, nil) # cbase, name, superclass
488
486
  klass_obj = scratch.new_class(nil, :Object, [], klass_basic_obj, nil)
489
- scratch.add_constant(klass_obj, :Object, klass_obj)
490
- scratch.add_constant(klass_obj, :BasicObject, klass_basic_obj)
487
+ scratch.add_constant(klass_obj, :Object, klass_obj, nil)
488
+ scratch.add_constant(klass_obj, :BasicObject, klass_basic_obj, nil)
491
489
 
492
490
  Type::Builtin[:basic_obj] = klass_basic_obj
493
491
  Type::Builtin[:obj] = klass_obj
@@ -533,6 +531,7 @@ module TypeProf
533
531
  scratch.set_custom_method(klass_obj, :class, Builtin.method(:object_class))
534
532
  scratch.set_custom_method(klass_obj, :send, Builtin.method(:object_send))
535
533
  scratch.set_custom_method(klass_obj, :instance_eval, Builtin.method(:object_instance_eval))
534
+ scratch.set_custom_method(klass_obj, :proc, Builtin.method(:lambda))
536
535
 
537
536
  scratch.set_custom_method(klass_module, :include, Builtin.method(:module_include))
538
537
  scratch.set_custom_method(klass_module, :extend, Builtin.method(:module_extend))
@@ -554,6 +553,11 @@ module TypeProf
554
553
  scratch.set_custom_method(klass_obj, :require, Builtin.method(:kernel_require))
555
554
  scratch.set_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
556
555
  scratch.set_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
556
+
557
+ # ENV: Hash[String, String]
558
+ str_ty = Type::Instance.new(Type::Builtin[:str])
559
+ env_ty = Type.gen_hash {|h| h[str_ty] = Type.optional(str_ty) }
560
+ scratch.add_constant(klass_obj, :ENV, env_ty, false)
557
561
  end
558
562
  end
559
563
  end
@@ -22,7 +22,8 @@ module TypeProf
22
22
  # The most basic container type for default type parameter class
23
23
  class Cell < Type
24
24
  def initialize(elems, base_type)
25
- @elems = elems # Array[Symbol]
25
+ raise if !elems.is_a?(Cell::Elements)
26
+ @elems = elems # Cell::Elements
26
27
  raise unless base_type
27
28
  @base_type = base_type
28
29
  end
@@ -34,22 +35,23 @@ module TypeProf
34
35
  end
35
36
 
36
37
  def screen_name(scratch)
37
- "#{ @base_type.screen_name(scratch) }[#{ @elems.map {|elem| elem.screen_name(scratch) }.join(", ") }]"
38
+ str = @elems.screen_name(scratch)
39
+ if str.start_with?("*")
40
+ str = @base_type.screen_name(scratch) + str[1..]
41
+ end
42
+ str
38
43
  end
39
44
 
40
45
  def localize(env, alloc_site, depth)
41
46
  return env, Type.any if depth <= 0
42
47
  alloc_site = alloc_site.add_id(:cell)
43
- elems = @elems.map.with_index do |elem, idx|
44
- env, elem = elem.localize(env, alloc_site.add_id(idx), depth - 1)
45
- elem
46
- end
47
- env.deploy_cell_type(alloc_site, elems, @base_type)
48
+ env, elems = @elems.localize(env, alloc_site, depth)
49
+ env.deploy_type(LocalCell, alloc_site, elems, @base_type)
48
50
  end
49
51
 
50
52
  def limit_size(limit)
51
53
  return Type.any if limit <= 0
52
- Cell.new(@elems.map {|elem| elem.limit_size(limit - 1) }, @base_type)
54
+ Cell.new(@elems.limit_size(limit - 1), @base_type)
53
55
  end
54
56
 
55
57
  def get_method(mid, scratch)
@@ -76,9 +78,81 @@ module TypeProf
76
78
 
77
79
  def substitute(subst, depth)
78
80
  return Type.any if depth <= 0
79
- elems = @elems.map {|elem| elem.substitute(subst, depth - 1) }
81
+ elems = @elems.substitute(subst, depth)
80
82
  Cell.new(elems, @base_type)
81
83
  end
84
+
85
+ class Elements
86
+ include Utils::StructuralEquality
87
+
88
+ def initialize(elems)
89
+ @elems = elems
90
+ end
91
+
92
+ attr_reader :elems
93
+
94
+ def to_local_type(id, base_ty)
95
+ Type::LocalCell.new(id, base_ty)
96
+ end
97
+
98
+ def globalize(env, visited, depth)
99
+ Elements.new(@elems.map {|ty| ty.globalize(env, visited, depth) })
100
+ end
101
+
102
+ def localize(env, alloc_site, depth)
103
+ elems = @elems.map.with_index do |ty, i|
104
+ alloc_site2 = alloc_site.add_id(i)
105
+ env, ty = ty.localize(env, alloc_site2, depth)
106
+ ty
107
+ end
108
+ return env, Elements.new(elems)
109
+ end
110
+
111
+ def limit_size(limit)
112
+ Elements.new(@elems.map {|ty| ty.limit_size(limit) })
113
+ end
114
+
115
+ def screen_name(scratch)
116
+ "*[#{ @elems.map {|ty| ty.screen_name(scratch) }.join(", ") }]"
117
+ end
118
+
119
+ def pretty_print(q)
120
+ q.group(9, "Elements[", "]") do
121
+ q.seplist(@elems) do |elem|
122
+ q.pp elem
123
+ end
124
+ end
125
+ end
126
+
127
+ def consistent?(other, subst)
128
+ false if @elems.size != other.elems.size
129
+ @elems.zip(other.elems) do |ty0, ty1|
130
+ return false unless ty0.consistent?(ty1, subst)
131
+ end
132
+ return true
133
+ end
134
+
135
+ def substitute(subst, depth)
136
+ Elements.new(@elems.map {|ty| ty.substitute(subst, depth) })
137
+ end
138
+
139
+ def [](idx)
140
+ @elems[idx]
141
+ end
142
+
143
+ def update(idx, ty)
144
+ Elements.new(Utils.array_update(@elems, idx, @elems[idx].union(ty)))
145
+ end
146
+
147
+ def union(other)
148
+ return self if self == other
149
+ elems = []
150
+ @elems.zip(other.elems) do |ty0, ty1|
151
+ elems << ty0.union(ty1)
152
+ end
153
+ Elements.new(elems)
154
+ end
155
+ end
82
156
  end
83
157
 
84
158
  class LocalCell < Type
@@ -106,9 +180,9 @@ module TypeProf
106
180
  visited[self] = true
107
181
  elems = env.get_container_elem_types(@id)
108
182
  if elems
109
- elems = elems.map {|elem| elem.globalize(env, visited, depth - 1) }
183
+ elems = elems.globalize(env, visited, depth - 1)
110
184
  else
111
- elems = [] # XXX
185
+ elems = Cell::Elements.new([]) # XXX
112
186
  end
113
187
  Cell.new(elems, @base_type)
114
188
  end
@@ -151,7 +225,7 @@ module TypeProf
151
225
  return env, Type.any if depth <= 0
152
226
  alloc_site = alloc_site.add_id(:ary)
153
227
  env, elems = @elems.localize(env, alloc_site, depth - 1)
154
- env.deploy_array_type(alloc_site, elems, @base_type)
228
+ env.deploy_type(LocalArray, alloc_site, elems, @base_type)
155
229
  end
156
230
 
157
231
  def limit_size(limit)
@@ -196,8 +270,7 @@ module TypeProf
196
270
 
197
271
  attr_reader :lead_tys, :rest_ty
198
272
 
199
- def to_local_type(id)
200
- base_ty = Type::Instance.new(Type::Builtin[:ary])
273
+ def to_local_type(id, base_ty)
201
274
  Type::LocalArray.new(id, base_ty)
202
275
  end
203
276
 
@@ -265,6 +338,11 @@ module TypeProf
265
338
  @lead_tys.inject(@rest_ty) {|ty1, ty2| ty1.union(ty2) } #.union(Type.nil) # is this needed?
266
339
  end
267
340
 
341
+ def squash_or_any
342
+ ty = squash
343
+ ty == Type.bot ? Type.any : ty
344
+ end
345
+
268
346
  def [](idx)
269
347
  if idx >= 0
270
348
  if idx < @lead_tys.size
@@ -463,7 +541,7 @@ module TypeProf
463
541
  return env, Type.any if depth <= 0
464
542
  alloc_site = alloc_site.add_id(:hash)
465
543
  env, elems = @elems.localize(env, alloc_site, depth - 1)
466
- env.deploy_hash_type(alloc_site, elems, @base_type)
544
+ env.deploy_type(LocalHash, alloc_site, elems, @base_type)
467
545
  end
468
546
 
469
547
  def limit_size(limit)
@@ -515,8 +593,7 @@ module TypeProf
515
593
 
516
594
  attr_reader :map_tys
517
595
 
518
- def to_local_type(id)
519
- base_ty = Type::Instance.new(Type::Builtin[:hash])
596
+ def to_local_type(id, base_ty)
520
597
  Type::LocalHash.new(id, base_ty)
521
598
  end
522
599
 
@@ -81,141 +81,148 @@ module TypeProf
81
81
  class RubySignatureExporter
82
82
  def initialize(
83
83
  scratch,
84
- class_defs, iseq_method_to_ctxs, sig_fargs, sig_ret, yields
84
+ class_defs, iseq_method_to_ctxs
85
85
  )
86
86
  @scratch = scratch
87
87
  @class_defs = class_defs
88
88
  @iseq_method_to_ctxs = iseq_method_to_ctxs
89
- @sig_fargs = sig_fargs
90
- @sig_ret = sig_ret
91
- @yields = yields
92
89
  end
93
90
 
94
- def show(stat_eps, output)
95
- output.puts "# Classes" # and Modules
91
+ def conv_class(namespace, class_def, inner_classes)
92
+ @scratch.namespace = namespace
96
93
 
97
- first = true
94
+ if class_def.superclass
95
+ omit = @class_defs[class_def.superclass].klass_obj == Type::Builtin[:obj] || class_def.klass_obj == Type::Builtin[:obj]
96
+ superclass = omit ? nil : @scratch.get_class_name(@class_defs[class_def.superclass].klass_obj)
97
+ end
98
98
 
99
- @class_defs.each_value do |class_def|
100
- included_mods = class_def.modules[false].filter_map do |mod_def, absolute_paths|
101
- next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
102
- mod_def.name
103
- end
99
+ @scratch.namespace = class_def.name
104
100
 
105
- extended_mods = class_def.modules[true].filter_map do |mod_def, absolute_paths|
106
- next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
107
- mod_def.name
108
- end
101
+ consts = {}
102
+ class_def.consts.each do |name, (ty, absolute_path)|
103
+ next if ty.is_a?(Type::Class)
104
+ next if !absolute_path || Config.check_dir_filter(absolute_path) == :exclude
105
+ consts[name] = ty.screen_name(@scratch)
106
+ end
109
107
 
110
- explicit_methods = {}
111
- iseq_methods = {}
112
- attr_methods = {}
113
- ivars = class_def.ivars.dump
114
- cvars = class_def.cvars.dump
115
- class_def.methods.each do |(singleton, mid), mdefs|
116
- mdefs.each do |mdef|
117
- case mdef
118
- when ISeqMethodDef
119
- ctxs = @iseq_method_to_ctxs[mdef]
120
- next unless ctxs
121
-
122
- ctxs.each do |ctx|
123
- next if mid != ctx.mid
124
- next if Config.check_dir_filter(ctx.iseq.absolute_path) == :exclude
125
-
126
- method_name = ctx.mid
127
- method_name = "self.#{ method_name }" if singleton
128
-
129
- fargs = @sig_fargs[ctx]
130
- ret_tys = @sig_ret[ctx]
131
-
132
- iseq_methods[method_name] ||= []
133
- iseq_methods[method_name] << @scratch.show_signature(fargs, @yields[ctx], ret_tys)
134
- end
135
- when AttrMethodDef
136
- next if Config.check_dir_filter(mdef.absolute_path) == :exclude
137
- mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=")
138
- method_name = mid
139
- method_name = "self.#{ mid }" if singleton
140
- method_name = [method_name, :"@#{ mid }" != mdef.ivar]
141
- if attr_methods[method_name]
142
- if attr_methods[method_name][0] != mdef.kind
143
- attr_methods[method_name][0] = :accessor
144
- end
145
- else
146
- entry = ivars[[singleton, mdef.ivar]]
147
- ty = entry ? entry.type : Type.any
148
- attr_methods[method_name] = [mdef.kind, ty.screen_name(@scratch)]
149
- end
150
- when TypedMethodDef
151
- if mdef.rbs_source
152
- method_name, sigs = mdef.rbs_source
153
- explicit_methods[method_name] = sigs
108
+ included_mods = class_def.modules[false].filter_map do |mod_def, absolute_paths|
109
+ next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
110
+ Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
111
+ end
112
+
113
+ extended_mods = class_def.modules[true].filter_map do |mod_def, absolute_paths|
114
+ next if absolute_paths.all? {|path| !path || Config.check_dir_filter(path) == :exclude }
115
+ Type::Instance.new(mod_def.klass_obj).screen_name(@scratch)
116
+ end
117
+
118
+ explicit_methods = {}
119
+ iseq_methods = {}
120
+ attr_methods = {}
121
+ ivars = class_def.ivars.dump
122
+ cvars = class_def.cvars.dump
123
+
124
+ class_def.methods.each do |(singleton, mid), mdefs|
125
+ mdefs.each do |mdef|
126
+ case mdef
127
+ when ISeqMethodDef
128
+ ctxs = @iseq_method_to_ctxs[mdef]
129
+ next unless ctxs
130
+
131
+ ctxs.each do |ctx|
132
+ next if mid != ctx.mid
133
+ next if Config.check_dir_filter(ctx.iseq.absolute_path) == :exclude
134
+
135
+ method_name = ctx.mid
136
+ method_name = "self.#{ method_name }" if singleton
137
+
138
+ iseq_methods[method_name] ||= []
139
+ iseq_methods[method_name] << @scratch.show_method_signature(ctx)
140
+ end
141
+ when AttrMethodDef
142
+ next if Config.check_dir_filter(mdef.absolute_path) == :exclude
143
+ mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=")
144
+ method_name = mid
145
+ method_name = "self.#{ mid }" if singleton
146
+ method_name = [method_name, :"@#{ mid }" != mdef.ivar]
147
+ if attr_methods[method_name]
148
+ if attr_methods[method_name][0] != mdef.kind
149
+ attr_methods[method_name][0] = :accessor
154
150
  end
151
+ else
152
+ entry = ivars[[singleton, mdef.ivar]]
153
+ ty = entry ? entry.type : Type.any
154
+ attr_methods[method_name] = [mdef.kind, ty.screen_name(@scratch)]
155
+ end
156
+ when TypedMethodDef
157
+ if mdef.rbs_source
158
+ method_name, sigs = mdef.rbs_source
159
+ explicit_methods[method_name] = sigs
155
160
  end
156
161
  end
157
162
  end
163
+ end
158
164
 
159
- ivars = ivars.map do |(singleton, var), entry|
160
- next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
161
- ty = entry.type
162
- next unless var.to_s.start_with?("@")
163
- var = "self.#{ var }" if singleton
164
- next if attr_methods[[singleton ? "self.#{ var.to_s[1..] }" : var.to_s[1..].to_sym, false]]
165
- [var, ty.screen_name(@scratch), entry.rbs_declared]
166
- end.compact
167
-
168
- cvars = cvars.map do |var, entry|
169
- next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
170
- [var, entry.type.screen_name(@scratch), entry.rbs_declared]
171
- end
165
+ ivars = ivars.map do |(singleton, var), entry|
166
+ next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
167
+ ty = entry.type
168
+ next unless var.to_s.start_with?("@")
169
+ var = "self.#{ var }" if singleton
170
+ next if attr_methods[[singleton ? "self.#{ var.to_s[1..] }" : var.to_s[1..].to_sym, false]]
171
+ [var, ty.screen_name(@scratch), entry.rbs_declared]
172
+ end.compact
172
173
 
173
- if !class_def.absolute_path || Config.check_dir_filter(class_def.absolute_path) == :exclude
174
- next if included_mods.empty? && extended_mods.empty? && ivars.empty? && cvars.empty? && iseq_methods.empty? && attr_methods.empty?
175
- end
174
+ cvars = cvars.map do |var, entry|
175
+ next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
176
+ [var, entry.type.screen_name(@scratch), entry.rbs_declared]
177
+ end
176
178
 
177
- output.puts unless first
178
- first = false
179
+ if !class_def.absolute_path || Config.check_dir_filter(class_def.absolute_path) == :exclude
180
+ return nil if consts.empty? && included_mods.empty? && extended_mods.empty? && ivars.empty? && cvars.empty? && iseq_methods.empty? && attr_methods.empty? && inner_classes.empty?
181
+ end
179
182
 
180
- if class_def.superclass
181
- omit = @class_defs[class_def.superclass].klass_obj == Type::Builtin[:obj] || class_def.klass_obj == Type::Builtin[:obj]
182
- superclass = omit ? "" : " < #{ @class_defs[class_def.superclass].name }"
183
- end
183
+ @scratch.namespace = nil
184
184
 
185
- output.puts "#{ class_def.kind } #{ class_def.name }#{ superclass }"
186
- included_mods.sort.each do |ty|
187
- output.puts " include #{ ty }"
188
- end
189
- extended_mods.sort.each do |ty|
190
- output.puts " extend #{ ty }"
191
- end
192
- ivars.each do |var, ty, rbs_declared|
193
- s = rbs_declared ? "# " : " "
194
- output.puts s + "#{ var } : #{ ty }" unless var.start_with?("_")
195
- end
196
- cvars.each do |var, ty, rbs_declared|
197
- s = rbs_declared ? "# " : " "
198
- output.puts s + "#{ var } : #{ ty }"
199
- end
200
- attr_methods.each do |(method_name, hidden), (kind, ty)|
201
- output.puts " attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" } : #{ ty }"
202
- end
203
- explicit_methods.each do |method_name, sigs|
204
- sigs = sigs.sort.join("\n" + "#" + " " * (method_name.size + 6) + "| ")
205
- output.puts "# def #{ method_name } : #{ sigs }"
206
- end
207
- iseq_methods.each do |method_name, sigs|
208
- sigs = sigs.sort.join("\n" + " " * (method_name.size + 7) + "| ")
209
- output.puts " def #{ method_name } : #{ sigs }"
185
+ ClassData.new(
186
+ kind: class_def.kind,
187
+ name: class_def.name,
188
+ superclass: superclass,
189
+ consts: consts,
190
+ included_mods: included_mods,
191
+ extended_mods: extended_mods,
192
+ ivars: ivars,
193
+ cvars: cvars,
194
+ attr_methods: attr_methods,
195
+ explicit_methods: explicit_methods,
196
+ iseq_methods: iseq_methods,
197
+ inner_classes: inner_classes,
198
+ )
199
+ end
200
+
201
+ ClassData = Struct.new(:kind, :name, :superclass, :consts, :included_mods, :extended_mods, :ivars, :cvars, :attr_methods, :explicit_methods, :iseq_methods, :inner_classes, keyword_init: true)
202
+
203
+ def show(stat_eps, output)
204
+ # make the class hierarchy
205
+ root = {}
206
+ @class_defs.each_value do |class_def|
207
+ h = root
208
+ class_def.name.each do |name|
209
+ h = h[name] ||= {}
210
210
  end
211
- output.puts "end"
211
+ h[:class_def] = class_def
212
212
  end
213
213
 
214
+ hierarchy = build_class_hierarchy([], root)
215
+
216
+ output.puts "# Classes" # and Modules
217
+
218
+ show_class_hierarchy(0, hierarchy, output, true)
219
+
214
220
  if ENV["TP_STAT"]
215
221
  output.puts ""
216
222
  output.puts "# TypeProf statistics:"
217
223
  output.puts "# %d execution points" % stat_eps.size
218
224
  end
225
+
219
226
  if ENV["TP_COVERAGE"]
220
227
  coverage = {}
221
228
  stat_eps.each do |ep|
@@ -227,5 +234,75 @@ module TypeProf
227
234
  File.binwrite("typeprof-analysis-coverage.dump", Marshal.dump(coverage))
228
235
  end
229
236
  end
237
+
238
+ def build_class_hierarchy(namespace, hierarchy)
239
+ hierarchy.map do |name, h|
240
+ class_def = h.delete(:class_def)
241
+ class_data = conv_class(namespace, class_def, build_class_hierarchy(namespace + [name], h))
242
+ class_data
243
+ end.compact
244
+ end
245
+
246
+ def show_class_hierarchy(depth, hierarchy, output, first)
247
+ hierarchy.each do |class_data|
248
+ output.puts unless first
249
+ first = false
250
+
251
+ show_class_data(depth, class_data, output)
252
+ end
253
+ end
254
+
255
+ def show_const(namespace, path)
256
+ return path.last.to_s if namespace == path
257
+ i = 0
258
+ i += 1 while namespace[i] && namespace[i] == path[i]
259
+ path[i..].join("::")
260
+ end
261
+
262
+ def show_class_data(depth, class_data, output)
263
+ indent = " " * depth
264
+ name = class_data.name.last
265
+ superclass = " < " + class_data.superclass if class_data.superclass
266
+ output.puts indent + "#{ class_data.kind } #{ name }#{ superclass }"
267
+ first = true
268
+ class_data.consts.each do |name, ty|
269
+ output.puts indent + " #{ name } : #{ ty }"
270
+ first = false
271
+ end
272
+ class_data.included_mods.sort.each do |mod|
273
+ output.puts indent + " include #{ mod }"
274
+ first = false
275
+ end
276
+ class_data.extended_mods.sort.each do |mod|
277
+ output.puts indent + " extend #{ mod }"
278
+ first = false
279
+ end
280
+ class_data.ivars.each do |var, ty, rbs_declared|
281
+ s = rbs_declared ? "# " : " "
282
+ output.puts indent + s + "#{ var } : #{ ty }" unless var.start_with?("_")
283
+ first = false
284
+ end
285
+ class_data.cvars.each do |var, ty, rbs_declared|
286
+ s = rbs_declared ? "# " : " "
287
+ output.puts indent + s + "#{ var } : #{ ty }"
288
+ first = false
289
+ end
290
+ class_data.attr_methods.each do |(method_name, hidden), (kind, ty)|
291
+ output.puts indent + " attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" } : #{ ty }"
292
+ first = false
293
+ end
294
+ class_data.explicit_methods.each do |method_name, sigs|
295
+ sigs = sigs.sort.join("\n" + indent + "#" + " " * (method_name.size + 6) + "| ")
296
+ output.puts indent + "# def #{ method_name } : #{ sigs }"
297
+ first = false
298
+ end
299
+ class_data.iseq_methods.each do |method_name, sigs|
300
+ sigs = sigs.sort.join("\n" + indent + " " * (method_name.size + 7) + "| ")
301
+ output.puts indent + " def #{ method_name } : #{ sigs }"
302
+ first = false
303
+ end
304
+ show_class_hierarchy(depth + 1, class_data.inner_classes, output, first)
305
+ output.puts indent + "end"
306
+ end
230
307
  end
231
308
  end