typeprof 0.32.0 → 0.33.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.
@@ -9,6 +9,8 @@ module TypeProf::Core
9
9
  @include_defs = Set.empty
10
10
  @prepend_decls = []
11
11
  @prepend_defs = []
12
+ @extend_decls = Set.empty
13
+ @extend_defs = Set.empty
12
14
 
13
15
  # `class Foo = Bar` / `module Foo = Bar` declarations attached to this entity.
14
16
  # Maps an alias decl to the target ModuleEntity at the time of registration.
@@ -23,6 +25,7 @@ module TypeProf::Core
23
25
  @self_types = {}
24
26
  @included_modules = {}
25
27
  @prepended_modules = {}
28
+ @extended_modules = {}
26
29
  @basic_object = @cpath == [:BasicObject]
27
30
 
28
31
  # child modules (subclasses and all modules that include me)
@@ -57,6 +60,7 @@ module TypeProf::Core
57
60
  attr_reader :self_types
58
61
  attr_reader :included_modules
59
62
  attr_reader :prepended_modules
63
+ attr_reader :extended_modules
60
64
  attr_reader :child_modules
61
65
 
62
66
  attr_reader :superclass_type_args
@@ -219,6 +223,26 @@ module TypeProf::Core
219
223
  genv.add_static_eval_queue(:parent_modules_changed, self)
220
224
  end
221
225
 
226
+ def add_extend_decl(genv, node)
227
+ @extend_decls << node
228
+ genv.add_static_eval_queue(:parent_modules_changed, self)
229
+ end
230
+
231
+ def remove_extend_decl(genv, node)
232
+ @extend_decls.delete(node) || raise
233
+ genv.add_static_eval_queue(:parent_modules_changed, self)
234
+ end
235
+
236
+ def add_extend_def(genv, node)
237
+ @extend_defs << node
238
+ genv.add_static_eval_queue(:parent_modules_changed, self)
239
+ end
240
+
241
+ def remove_extend_def(genv, node)
242
+ @extend_defs.delete(node) || raise
243
+ genv.add_static_eval_queue(:parent_modules_changed, self)
244
+ end
245
+
222
246
  def add_prepend_decl(genv, node)
223
247
  @prepend_decls << node
224
248
  genv.add_static_eval_queue(:parent_modules_changed, self)
@@ -285,7 +309,8 @@ module TypeProf::Core
285
309
  when AST::ModuleNode
286
310
  return nil
287
311
  when AST::StructNewNode
288
- return [] # inherits from Object (Struct < Object)
312
+ base_cpath = mdef.struct_base_cpath
313
+ return base_cpath == @cpath ? [] : base_cpath
289
314
  else
290
315
  raise
291
316
  end
@@ -396,6 +421,30 @@ module TypeProf::Core
396
421
  any_updated = true
397
422
  end
398
423
  end
424
+ @extend_decls.each do |edecl|
425
+ new_parent_cpath = edecl.static_ret.last.cpath
426
+ new_parent, updated = update_parent(genv, edecl, @extended_modules[edecl], new_parent_cpath)
427
+ if updated
428
+ if new_parent
429
+ @extended_modules[edecl] = new_parent
430
+ else
431
+ @extended_modules.delete(edecl) || raise
432
+ end
433
+ any_updated = true
434
+ end
435
+ end
436
+ @extend_defs.each do |edef|
437
+ new_parent_cpath = edef.static_ret ? edef.static_ret.cpath : nil
438
+ new_parent, updated = update_parent(genv, edef, @extended_modules[edef], new_parent_cpath)
439
+ if updated
440
+ if new_parent
441
+ @extended_modules[edef] = new_parent
442
+ else
443
+ @extended_modules.delete(edef) || raise
444
+ end
445
+ any_updated = true
446
+ end
447
+ end
399
448
  @included_modules.delete_if do |origin, old_mod|
400
449
  if @include_decls.include?(origin) || @include_defs.include?(origin)
401
450
  false
@@ -414,6 +463,15 @@ module TypeProf::Core
414
463
  true
415
464
  end
416
465
  end
466
+ @extended_modules.delete_if do |origin, old_mod|
467
+ if @extend_decls.include?(origin) || @extend_defs.include?(origin)
468
+ false
469
+ else
470
+ _new_parent, updated = update_parent(genv, origin, old_mod, nil)
471
+ any_updated ||= updated
472
+ true
473
+ end
474
+ end
417
475
 
418
476
  if any_updated
419
477
  @subclass_checks.each do |mcall_box|
@@ -424,6 +482,8 @@ module TypeProf::Core
424
482
  end
425
483
 
426
484
  def on_ancestors_updated(genv, base_mod)
485
+ # `include` and `extend` can form a cycle in valid Ruby
486
+ return if base_mod == self
427
487
  @child_modules.each_key {|child_mod| child_mod.on_ancestors_updated(genv, base_mod || self) }
428
488
  @static_reads.each_value do |static_reads|
429
489
  static_reads.each do |static_read|
@@ -80,7 +80,7 @@ module TypeProf::Core
80
80
  # TODO: prepended modules
81
81
  yield mod, singleton
82
82
  if singleton
83
- # TODO: extended modules
83
+ each_extended_module(mod, &blk)
84
84
  else
85
85
  each_included_module(mod, &blk)
86
86
  end
@@ -95,6 +95,15 @@ module TypeProf::Core
95
95
  end
96
96
  end
97
97
 
98
+ def each_extended_module(mod, &blk)
99
+ # An extended module contributes its instance methods (singleton = false)
100
+ # as the receiver's singleton methods.
101
+ mod.extended_modules.each do |_ext_decl, ext_mod|
102
+ yield ext_mod, false
103
+ each_included_module(ext_mod, &blk)
104
+ end
105
+ end
106
+
98
107
  def get_superclass(singleton, mod)
99
108
  super_mod = mod.superclass
100
109
  if super_mod
@@ -106,6 +115,9 @@ module TypeProf::Core
106
115
  else
107
116
  return nil
108
117
  end
118
+ elsif mod == @mod_object
119
+ # Unresolved while loading the core RBS; the Module fallback below would cycle
120
+ return [singleton, @mod_basic_object]
109
121
  elsif mod == @mod_module && !singleton
110
122
  return nil
111
123
  else
@@ -271,6 +283,17 @@ module TypeProf::Core
271
283
  mod.get_type_alias(name)
272
284
  end
273
285
 
286
+ # Returns the type parameter names of the first generic declaration of cpath
287
+ def find_type_params(decls, cpath)
288
+ decls.each do |decl|
289
+ next unless decl.cpath == cpath
290
+ next unless decl.respond_to?(:params)
291
+ params = decl.params
292
+ return params if params && !params.empty?
293
+ end
294
+ nil
295
+ end
296
+
274
297
  def load_core_rbs(raw_decls, position_encoding)
275
298
  file_context = FileContext.new(nil, position_encoding)
276
299
  lenv = LocalEnv.new(file_context, CRef::Toplevel, {}, [])
@@ -278,6 +301,11 @@ module TypeProf::Core
278
301
  AST.create_rbs_decl(raw_decl, lenv)
279
302
  end.compact
280
303
 
304
+ # A module entity has one set of parameter names shared by all its declarations,
305
+ # so the shim must reuse the core's ones (Array's was `Elem`, and is `E` since RBS 4.1)
306
+ ary_elem, = find_type_params(decls, [:Array]) || [:Elem]
307
+ hash_key, hash_val = find_type_params(decls, [:Hash]) || [:K, :V]
308
+
281
309
  decls += AST.parse_rbs("typeprof-rbs-shim.rbs", <<-RBS, position_encoding)
282
310
  class Exception
283
311
  include _Exception
@@ -286,12 +314,12 @@ module TypeProf::Core
286
314
  include _ToS
287
315
  include _ToStr
288
316
  end
289
- class Array[Elem]
290
- include _ToAry[Elem]
291
- include _Each[Elem]
317
+ class Array[#{ ary_elem }]
318
+ include _ToAry[#{ ary_elem }]
319
+ include _Each[#{ ary_elem }]
292
320
  end
293
- class Hash[K, V]
294
- include _Each[[K, V]]
321
+ class Hash[#{ hash_key }, #{ hash_val }]
322
+ include _Each[[#{ hash_key }, #{ hash_val }]]
295
323
  end
296
324
  class Object
297
325
  include Hash::_Key
@@ -17,6 +17,7 @@ module TypeProf::Core
17
17
  $box_counts[self.class] -= 1
18
18
  $box_counts[Box] -= 1
19
19
  @destroyed = true
20
+ destroy_symbol_proc_call_boxes(genv)
20
21
  @changes.reinstall(genv) # rollback all changes
21
22
  end
22
23
 
@@ -43,6 +44,24 @@ module TypeProf::Core
43
44
  raise NotImplementedError
44
45
  end
45
46
 
47
+ def add_symbol_proc_call_box(_changes, genv, sym, caller_positionals, caller_keywords = nil)
48
+ return if caller_positionals.empty?
49
+
50
+ recv = caller_positionals.first
51
+ positionals = caller_positionals[1..]
52
+ @symbol_proc_call_boxes ||= {}
53
+ @symbol_proc_call_boxes[[recv, sym, *positionals, caller_keywords]] ||= begin
54
+ a_args = ActualArguments.new(positionals, ::Array.new(positionals.size, false), caller_keywords, nil)
55
+ MethodCallBox.new(@node, genv, recv, sym, a_args, false)
56
+ end
57
+ end
58
+
59
+ def destroy_symbol_proc_call_boxes(genv)
60
+ return unless @symbol_proc_call_boxes
61
+ @symbol_proc_call_boxes.each_value { |box| box.destroy(genv) }
62
+ @symbol_proc_call_boxes = nil
63
+ end
64
+
46
65
  def to_s
47
66
  "#{ self.class.to_s.split("::").last }#{ @id ||= $new_id += 1 }"
48
67
  end
@@ -127,6 +146,12 @@ module TypeProf::Core
127
146
  }
128
147
  end
129
148
 
149
+ # lazy cache: overloads split into fixed-arity ones and rest-positional ones
150
+ def partition_by_rest_positionals
151
+ @partition_by_rest_positionals ||=
152
+ @method_types.partition {|method_type| !method_type.rest_positionals }
153
+ end
154
+
130
155
  private
131
156
 
132
157
  # Check if two method types have structurally identical positional
@@ -258,6 +283,7 @@ module TypeProf::Core
258
283
  me = genv.resolve_method(@cpath, @singleton, @mid)
259
284
  me.remove_decl(self)
260
285
  me.add_run_all_method_call_boxes(genv)
286
+ destroy_symbol_proc_call_boxes(genv)
261
287
  end
262
288
 
263
289
  def match_arguments?(genv, changes, param_map, a_args, method_type)
@@ -382,6 +408,8 @@ module TypeProf::Core
382
408
  end
383
409
  end
384
410
  end
411
+ when Type::Symbol
412
+ resolve_symbol_proc(changes, genv, ty.sym, blk_a_args, rbs_blk, param_map0)
385
413
  end
386
414
  end
387
415
  end
@@ -396,6 +424,13 @@ module TypeProf::Core
396
424
  end
397
425
  end
398
426
 
427
+ def resolve_symbol_proc(changes, genv, sym, blk_a_args, rbs_blk, param_map)
428
+ box = add_symbol_proc_call_box(changes, genv, sym, blk_a_args)
429
+ return unless box
430
+
431
+ rbs_blk.return_type.typecheck(genv, changes, box.ret, param_map)
432
+ end
433
+
399
434
  def resolve_overloads(changes, genv, node, param_map, a_args, ret, &blk)
400
435
  if @method_types.size == 1
401
436
  method_type = @method_types.first
@@ -451,11 +486,19 @@ module TypeProf::Core
451
486
  return
452
487
  end
453
488
 
489
+ # A splatted call can match only a rest-positional overload; otherwise prefer
490
+ # the fixed-arity ones, as a rest-positional one is usually a catch-all
491
+ overload_groups =
492
+ a_args.splat_flags.any? ? [@method_types] : @method_types.partition_by_rest_positionals
493
+
454
494
  match_any_overload = false
455
- @method_types.each do |method_type|
456
- if resolve_overload(changes, genv, method_type, node, param_map, a_args, ret, false, &blk)
457
- match_any_overload = true
495
+ overload_groups.each do |method_types|
496
+ method_types.each do |method_type|
497
+ if resolve_overload(changes, genv, method_type, node, param_map, a_args, ret, false, &blk)
498
+ match_any_overload = true
499
+ end
458
500
  end
501
+ break if match_any_overload
459
502
  end
460
503
  unless match_any_overload
461
504
  meth = node.mid_code_range ? :mid_code_range : :code_range
@@ -600,13 +643,13 @@ module TypeProf::Core
600
643
  end
601
644
 
602
645
  class SplatBox < Box
603
- def initialize(node, genv, ary, idx, orig = nil)
646
+ def initialize(node, genv, ary, idx, unresolved_recv = nil)
604
647
  super(node)
605
648
  @ary = ary
606
649
  @idx = idx
607
- @orig = orig
650
+ @unresolved_recv = unresolved_recv
608
651
  @ary.add_edge(genv, self)
609
- @orig.add_edge(genv, self) if @orig
652
+ @unresolved_recv.add_edge(genv, self) if @unresolved_recv
610
653
  @ret = Vertex.new(node)
611
654
  end
612
655
 
@@ -632,14 +675,41 @@ module TypeProf::Core
632
675
  end
633
676
  end
634
677
  # For types where to_a is not defined, [*x] wraps x as [x]
635
- if @orig && @ary.types.empty?
636
- @orig.each_type do |ty|
678
+ if @unresolved_recv
679
+ @unresolved_recv.each_type do |ty|
637
680
  changes.add_edge(genv, Source.new(ty), @ret)
638
681
  end
639
682
  end
640
683
  end
641
684
  end
642
685
 
686
+ # Merges the keywords being forwarded into the `**rest` hash while keeping the
687
+ # rest's own fields, so that the callee can still tell the two apart.
688
+ class KeywordMergeBox < Box
689
+ def initialize(node, genv, rest, literal_pairs, fallback)
690
+ super(node)
691
+ @rest = rest
692
+ @literal_pairs = literal_pairs
693
+ @fallback = fallback
694
+ @rest.add_edge(genv, self)
695
+ @ret = Vertex.new(node)
696
+ end
697
+
698
+ attr_reader :ret
699
+
700
+ def run0(genv, changes)
701
+ merged = false
702
+ @rest.each_type do |ty|
703
+ if ty.is_a?(Type::Record)
704
+ fields = ty.fields.merge(@literal_pairs)
705
+ changes.add_edge(genv, Source.new(Type::Record.new(genv, fields, ty.base_type(genv))), @ret)
706
+ merged = true
707
+ end
708
+ end
709
+ changes.add_edge(genv, @fallback, @ret) unless merged
710
+ end
711
+ end
712
+
643
713
  class HashSplatBox < Box
644
714
  def initialize(node, genv, hsh, unified_key, unified_val)
645
715
  super(node)
@@ -1004,29 +1074,42 @@ module TypeProf::Core
1004
1074
  end
1005
1075
 
1006
1076
  class MethodCallBox < Box
1007
- def initialize(node, genv, recv, mid, a_args, subclasses, suppress_errors: false)
1077
+ # `unresolved_recv`, when given, collects the receiver types for which no
1078
+ # method entity was found. Without it those types only become "undefined
1079
+ # method" diagnostics; with it the caller can handle them itself, as `[*x]`
1080
+ # does to wrap a receiver that has no `to_a`.
1081
+ def initialize(node, genv, recv, mid, a_args, subclasses, suppress_errors: false, unresolved_recv: nil)
1008
1082
  raise mid.to_s unless mid
1009
1083
  super(node)
1010
1084
  @recv = recv.new_vertex(genv, node)
1011
1085
  @recv.add_edge(genv, self)
1012
1086
  @mid = mid
1013
1087
  @a_args = a_args.new_vertexes(genv, node)
1014
- @a_args.keywords.add_edge(genv, self) if @a_args.keywords
1015
- @a_args.block.add_edge(genv, self) if @a_args.block
1088
+ @a_args.add_box_edges(genv, self)
1016
1089
  @ret = Vertex.new(node)
1017
1090
  @subclasses = subclasses
1018
1091
  @suppress_errors = suppress_errors
1092
+ @unresolved_recv = unresolved_recv
1019
1093
  @generics = {}
1020
1094
  end
1021
1095
 
1022
1096
  attr_reader :recv, :mid, :ret
1023
1097
 
1024
1098
  def run0(genv, changes)
1099
+ a_args = @a_args.normalize_for_method_call(genv)
1100
+ return unless a_args
1101
+
1025
1102
  edges = Set.empty
1026
1103
  called_mdefs = Set.empty
1027
1104
  error_count = 0
1028
1105
  resolve(genv, changes) do |me, ty, mid, orig_ty|
1029
- if !me
1106
+ if @node.is_a?(AST::YieldNode) && mid == :call && orig_ty.is_a?(Type::Symbol)
1107
+ box = add_symbol_proc_call_box(changes, genv, orig_ty.sym, a_args.positionals, a_args.keywords)
1108
+ changes.add_edge(genv, box.ret, @ret) if box
1109
+ elsif !me
1110
+ if @unresolved_recv
1111
+ changes.add_edge(genv, Source.new(orig_ty), @unresolved_recv)
1112
+ end
1030
1113
  unless @suppress_errors
1031
1114
  if error_count < 3
1032
1115
  meth = @node.mid_code_range ? :mid_code_range : :code_range
@@ -1034,7 +1117,7 @@ module TypeProf::Core
1034
1117
  end
1035
1118
  end
1036
1119
  error_count += 1
1037
- elsif me.builtin && me.builtin[changes, @node, orig_ty, @a_args, @ret]
1120
+ elsif me.builtin && me.builtin[changes, @node, orig_ty, a_args, @ret]
1038
1121
  # do nothing
1039
1122
  elsif !me.decls.empty?
1040
1123
  # TODO: support "| ..."
@@ -1047,7 +1130,7 @@ module TypeProf::Core
1047
1130
  ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(genv, changes, ty_env) : Source.new)
1048
1131
  end
1049
1132
  end
1050
- mdecl.resolve_overloads(changes, genv, @node, ty_env, @a_args, @ret) do |method_type|
1133
+ mdecl.resolve_overloads(changes, genv, @node, ty_env, a_args, @ret) do |method_type|
1051
1134
  @generics[method_type] ||= method_type.type_params.map { Vertex.new(@node) }
1052
1135
  end
1053
1136
  end
@@ -1055,7 +1138,7 @@ module TypeProf::Core
1055
1138
  me.defs.each do |mdef|
1056
1139
  next if called_mdefs.include?(mdef)
1057
1140
  called_mdefs << mdef
1058
- mdef.call(changes, genv, @a_args, @ret)
1141
+ mdef.call(changes, genv, a_args, @ret)
1059
1142
  end
1060
1143
  else
1061
1144
  pp me
@@ -1068,7 +1151,7 @@ module TypeProf::Core
1068
1151
  me.defs.each do |mdef|
1069
1152
  next if called_mdefs.include?(mdef)
1070
1153
  called_mdefs << mdef
1071
- mdef.call(changes, genv, @a_args, @ret)
1154
+ mdef.call(changes, genv, a_args, @ret)
1072
1155
  end
1073
1156
  end
1074
1157
  end
@@ -1126,7 +1209,10 @@ module TypeProf::Core
1126
1209
  skip = false
1127
1210
 
1128
1211
  if ty.is_a?(Type::Singleton)
1129
- # TODO: extended modules
1212
+ # Check extended modules (their instance methods are singleton methods here)
1213
+ break if resolve_extended_modules(genv, changes, base_ty_env, ty, mid) do |me, ty, mid|
1214
+ yield me, ty, mid, orig_ty
1215
+ end
1130
1216
  else
1131
1217
  # Finally check included modules
1132
1218
  break if resolve_included_modules(genv, changes, base_ty_env, ty, mid) do |me, ty, mid|
@@ -1226,6 +1312,38 @@ module TypeProf::Core
1226
1312
  found
1227
1313
  end
1228
1314
 
1315
+ def resolve_extended_modules(genv, changes, base_ty_env, ty, mid, &blk)
1316
+ found = false
1317
+
1318
+ alias_limit = 0
1319
+ # An extended module's instance methods are resolved as the receiver's
1320
+ # singleton methods, so look them up with singleton = false.
1321
+ ty.mod.extended_modules.each do |ext_decl, ext_mod|
1322
+ if ext_decl.is_a?(AST::SigExtendNode) && ext_mod.type_params
1323
+ ext_ty = genv.get_instance_type(ext_mod, ext_decl.args, changes, base_ty_env, ty)
1324
+ else
1325
+ type_params = ext_mod.type_params.map { Source.new() } # TODO: better support
1326
+ ext_ty = Type::Instance.new(genv, ext_mod, type_params)
1327
+ end
1328
+
1329
+ me = ext_ty.mod.get_method(false, mid)
1330
+ changes.add_depended_method_entity(me) if changes
1331
+ if !me.aliases.empty?
1332
+ mid = me.aliases.values.first
1333
+ alias_limit += 1
1334
+ redo if alias_limit < 5
1335
+ end
1336
+ if me.exist?
1337
+ found = true
1338
+ yield me, ext_ty, mid
1339
+ else
1340
+ # The extended module may itself include other modules.
1341
+ found ||= resolve_included_modules(genv, changes, base_ty_env, ext_ty, mid, &blk)
1342
+ end
1343
+ end
1344
+ found
1345
+ end
1346
+
1229
1347
  def resolve_subclasses(genv, changes)
1230
1348
  # TODO: This does not follow new subclasses
1231
1349
  @recv.each_type do |ty|
@@ -71,9 +71,9 @@ module TypeProf::Core
71
71
 
72
72
  # TODO: if an edge is removed during one analysis, we may need to remove sub-boxes?
73
73
 
74
- def add_method_call_box(genv, recv, mid, a_args, subclasses, suppress_errors: false)
75
- key = [:mcall, recv, mid, a_args, subclasses, suppress_errors]
76
- new_boxes[key] ||= MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses, suppress_errors: suppress_errors)
74
+ def add_method_call_box(genv, recv, mid, a_args, subclasses, suppress_errors: false, unresolved_recv: nil)
75
+ key = [:mcall, recv, mid, a_args, subclasses, suppress_errors, unresolved_recv]
76
+ new_boxes[key] ||= MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses, suppress_errors: suppress_errors, unresolved_recv: unresolved_recv)
77
77
  end
78
78
 
79
79
  def add_escape_box(genv, a_ret)
@@ -81,9 +81,14 @@ module TypeProf::Core
81
81
  new_boxes[key] ||= EscapeBox.new(@node, genv, a_ret)
82
82
  end
83
83
 
84
- def add_splat_box(genv, arg, idx = nil, orig = nil)
85
- key = [:splat, arg, idx, orig]
86
- new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx, orig)
84
+ def add_splat_box(genv, arg, idx = nil, unresolved_recv = nil)
85
+ key = [:splat, arg, idx, unresolved_recv]
86
+ new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx, unresolved_recv)
87
+ end
88
+
89
+ def add_keyword_merge_box(genv, rest, literal_pairs, fallback)
90
+ key = [:kw_merge, rest, literal_pairs, fallback]
91
+ new_boxes[key] ||= KeywordMergeBox.new(@node, genv, rest, literal_pairs, fallback)
87
92
  end
88
93
 
89
94
  def add_hash_splat_box(genv, arg, unified_key, unified_val)
@@ -405,7 +405,9 @@ module TypeProf::Core
405
405
  break
406
406
  end
407
407
  end
408
- yield mid, "#{ mod.cpath.join("::" )}#{ singleton ? "." : "#" }#{ mid } : #{ sig }" if sig
408
+ # The base class has no name to show
409
+ owner = mod.cpath.last == AST::StructNewNode::BASE_CNAME ? base_ty.mod : mod
410
+ yield mid, "#{ owner.cpath.join("::" )}#{ singleton ? "." : "#" }#{ mid } : #{ sig }" if sig
409
411
  end
410
412
  end
411
413
  end
@@ -456,6 +458,7 @@ module TypeProf::Core
456
458
  def dump_declarations(path)
457
459
  stack = []
458
460
  out = []
461
+ seen_consts = Set.empty
459
462
  @rb_text_nodes[path]&.traverse do |event, node|
460
463
  case node
461
464
  when AST::ModuleNode
@@ -480,6 +483,10 @@ module TypeProf::Core
480
483
  s = "class #{ format_declared_const_path(node.static_cpath, stack) }"
481
484
  mod = @genv.resolve_cpath(node.static_cpath)
482
485
  superclass = mod.superclass
486
+ # The base class is not shown; its members are printed inline
487
+ while superclass && superclass.cpath.last == AST::StructNewNode::BASE_CNAME
488
+ superclass = superclass.superclass
489
+ end
483
490
  if superclass == nil
484
491
  s << " # failed to identify its superclass"
485
492
  elsif superclass.cpath != []
@@ -496,8 +503,16 @@ module TypeProf::Core
496
503
  out << " " * stack.size + "include #{ inc_mod.show_cpath }"
497
504
  end
498
505
  end
506
+ mod.extended_modules.each do |ext_def, ext_mod|
507
+ if ext_def.is_a?(AST::ConstantReadNode) && ext_def.lenv.path == path
508
+ out << " " * stack.size + "extend #{ ext_mod.show_cpath }"
509
+ end
510
+ end
499
511
  # Output method definitions from meta nodes (StructNewNode etc.)
500
512
  node.boxes(:mdef) do |mdef|
513
+ # A user-written method overrides the generated one
514
+ next if node.is_a?(AST::StructNewNode) &&
515
+ !@genv.resolve_method(node.static_cpath, mdef.singleton, mdef.mid).defs.empty?
501
516
  out << " " * stack.size + "def #{ mdef.singleton ? "self." : "" }#{ mdef.mid }: " + mdef.show(@options[:output_parameter_names])
502
517
  end
503
518
  else
@@ -508,7 +523,14 @@ module TypeProf::Core
508
523
  when AST::ConstantWriteNode
509
524
  if node.static_cpath
510
525
  if event == :enter
511
- out << " " * stack.size + "#{ format_declared_const_path(node.static_cpath, stack) }: #{ node.ret.show }"
526
+ unless seen_consts.include?(node.static_cpath)
527
+ seen_consts << node.static_cpath
528
+ cdef = @genv.resolve_const(node.static_cpath)
529
+ # Use the assigned value for a single write so a same-named class
530
+ # or module (e.g. declared in RBS) is not mixed in; union the types otherwise.
531
+ ret = cdef.defs.size > 1 ? cdef.vtx : node.ret
532
+ out << " " * stack.size + "#{ format_declared_const_path(node.static_cpath, stack) }: #{ ret.show }"
533
+ end
512
534
  end
513
535
  end
514
536
  else
@@ -1,3 +1,3 @@
1
1
  module TypeProf
2
- VERSION = "0.32.0"
2
+ VERSION = "0.33.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typeprof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.0
4
+ version: 0.33.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yusuke Endoh
@@ -118,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
118
  - !ruby/object:Gem::Version
119
119
  version: '0'
120
120
  requirements: []
121
- rubygems_version: 4.0.10
121
+ rubygems_version: 4.0.16
122
122
  specification_version: 4
123
123
  summary: TypeProf is a type analysis tool for Ruby code based on abstract interpretation
124
124
  test_files: []