typeprof 0.31.1 → 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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/doc/report_guide.md +88 -0
  4. data/lib/typeprof/cli/cli.rb +9 -3
  5. data/lib/typeprof/code_range.rb +7 -5
  6. data/lib/typeprof/core/ast/base.rb +31 -6
  7. data/lib/typeprof/core/ast/call.rb +120 -39
  8. data/lib/typeprof/core/ast/const.rb +22 -11
  9. data/lib/typeprof/core/ast/control.rb +62 -32
  10. data/lib/typeprof/core/ast/meta.rb +279 -14
  11. data/lib/typeprof/core/ast/method.rb +82 -24
  12. data/lib/typeprof/core/ast/misc.rb +41 -12
  13. data/lib/typeprof/core/ast/module.rb +38 -4
  14. data/lib/typeprof/core/ast/pattern.rb +51 -23
  15. data/lib/typeprof/core/ast/sig_decl.rb +141 -27
  16. data/lib/typeprof/core/ast/sig_type.rb +113 -32
  17. data/lib/typeprof/core/ast/value.rb +14 -6
  18. data/lib/typeprof/core/ast/variable.rb +17 -4
  19. data/lib/typeprof/core/ast.rb +98 -14
  20. data/lib/typeprof/core/builtin.rb +184 -12
  21. data/lib/typeprof/core/env/method.rb +343 -6
  22. data/lib/typeprof/core/env/method_entity.rb +18 -15
  23. data/lib/typeprof/core/env/module_entity.rb +116 -18
  24. data/lib/typeprof/core/env/static_read.rb +4 -4
  25. data/lib/typeprof/core/env/type_alias_entity.rb +1 -1
  26. data/lib/typeprof/core/env/value_entity.rb +25 -3
  27. data/lib/typeprof/core/env.rb +113 -23
  28. data/lib/typeprof/core/graph/box.rb +508 -63
  29. data/lib/typeprof/core/graph/change_set.rb +64 -46
  30. data/lib/typeprof/core/graph/filter.rb +8 -5
  31. data/lib/typeprof/core/graph/vertex.rb +20 -19
  32. data/lib/typeprof/core/service.rb +340 -24
  33. data/lib/typeprof/core/type.rb +41 -7
  34. data/lib/typeprof/core/util.rb +6 -0
  35. data/lib/typeprof/lsp/messages.rb +5 -0
  36. data/lib/typeprof/lsp/server.rb +35 -4
  37. data/lib/typeprof/version.rb +1 -1
  38. metadata +3 -2
@@ -41,6 +41,40 @@ module TypeProf::Core
41
41
  ActualArguments.new(positionals, splat_flags, keywords, block)
42
42
  end
43
43
 
44
+ def with_keywords_as_last_positional_hash
45
+ return self unless @keywords
46
+
47
+ ActualArguments.new(
48
+ @positionals + [@keywords],
49
+ @splat_flags + [false],
50
+ nil,
51
+ @block,
52
+ )
53
+ end
54
+
55
+ def prepend_positionals(positionals, splat_flags)
56
+ return self if positionals.empty?
57
+
58
+ ActualArguments.new(positionals + @positionals, splat_flags + @splat_flags, @keywords, @block)
59
+ end
60
+
61
+ def with_keywords(keywords)
62
+ ActualArguments.new(@positionals, @splat_flags, keywords, @block)
63
+ end
64
+
65
+ def with_block(block, omittable: false)
66
+ ActualArguments.new(@positionals, @splat_flags, @keywords, block)
67
+ end
68
+
69
+ def add_box_edges(genv, box)
70
+ @keywords.add_edge(genv, box) if @keywords
71
+ @block.add_edge(genv, box) if @block
72
+ end
73
+
74
+ def normalize_for_method_call(_genv)
75
+ self
76
+ end
77
+
44
78
  def get_rest_args(genv, changes, start_rest, end_rest)
45
79
  vtxs = []
46
80
 
@@ -69,6 +103,9 @@ module TypeProf::Core
69
103
  case ty
70
104
  when Type::Hash
71
105
  changes.add_edge(genv, ty.get_value(name), vtx)
106
+ when Type::Record
107
+ field_vtx = ty.get_value(name)
108
+ changes.add_edge(genv, field_vtx, vtx) if field_vtx
72
109
  when Type::Instance
73
110
  if ty.mod == genv.mod_hash
74
111
  changes.add_edge(genv, ty.args[1], vtx)
@@ -81,6 +118,311 @@ module TypeProf::Core
81
118
  end
82
119
  end
83
120
 
121
+ class ForwardingActualArguments < ActualArguments
122
+ def initialize(positionals, splat_flags, keywords, block, positionals_omittable, keywords_omittable, block_omittable, activation, activation_required)
123
+ super(positionals, splat_flags, keywords, block)
124
+ @positionals_omittable = positionals_omittable
125
+ @keywords_omittable = keywords_omittable
126
+ @block_omittable = block_omittable
127
+ @activation = activation
128
+ @activation_required = activation_required
129
+ end
130
+
131
+ attr_reader :positionals_omittable, :keywords_omittable, :block_omittable
132
+
133
+ def new_vertexes(genv, node)
134
+ positionals = @positionals.map {|arg| arg.new_vertex(genv, node) }
135
+ splat_flags = @splat_flags
136
+ keywords = @keywords ? @keywords.new_vertex(genv, node) : nil
137
+ block = @block ? @block.new_vertex(genv, node) : nil
138
+ activation = @activation.new_vertex(genv, node)
139
+ ForwardingActualArguments.new(positionals, splat_flags, keywords, block, @positionals_omittable, @keywords_omittable, @block_omittable, activation, @activation_required)
140
+ end
141
+
142
+ def with_keywords_as_last_positional_hash
143
+ return self unless @keywords
144
+
145
+ ForwardingActualArguments.new(
146
+ @positionals + [@keywords],
147
+ @splat_flags + [false],
148
+ nil,
149
+ @block,
150
+ @positionals_omittable + [false],
151
+ false,
152
+ @block_omittable,
153
+ @activation,
154
+ @activation_required,
155
+ )
156
+ end
157
+
158
+ def prepend_positionals(positionals, splat_flags)
159
+ return self if positionals.empty?
160
+
161
+ ForwardingActualArguments.new(
162
+ positionals + @positionals,
163
+ splat_flags + @splat_flags,
164
+ @keywords,
165
+ @block,
166
+ ::Array.new(positionals.size, false) + @positionals_omittable,
167
+ @keywords_omittable,
168
+ @block_omittable,
169
+ @activation,
170
+ @activation_required,
171
+ )
172
+ end
173
+
174
+ def with_keywords(keywords, keywords_omittable: false)
175
+ ForwardingActualArguments.new(
176
+ @positionals,
177
+ @splat_flags,
178
+ keywords,
179
+ @block,
180
+ @positionals_omittable,
181
+ keywords_omittable,
182
+ @block_omittable,
183
+ @activation,
184
+ @activation_required,
185
+ )
186
+ end
187
+
188
+ def with_block(block, omittable: false)
189
+ ForwardingActualArguments.new(
190
+ @positionals,
191
+ @splat_flags,
192
+ @keywords,
193
+ block,
194
+ @positionals_omittable,
195
+ @keywords_omittable,
196
+ omittable,
197
+ @activation,
198
+ @activation_required,
199
+ )
200
+ end
201
+
202
+ def add_box_edges(genv, box)
203
+ @activation.add_edge(genv, box)
204
+ super
205
+ end
206
+
207
+ def normalize_for_method_call(genv)
208
+ if @activation.types.empty?
209
+ return nil if @activation_required
210
+ return self
211
+ end
212
+
213
+ positionals = []
214
+ splat_flags = []
215
+
216
+ @positionals.each_with_index do |arg, i|
217
+ unless @positionals_omittable[i] && @splat_flags[i] && empty_omittable_splat_argument?(genv, arg)
218
+ positionals << arg
219
+ splat_flags << @splat_flags[i]
220
+ end
221
+ end
222
+
223
+ keywords = @keywords
224
+ keywords = nil if @keywords_omittable && keywords && keywords.types.empty?
225
+
226
+ block = @block
227
+ block = nil if @block_omittable && block && block.types.empty?
228
+
229
+ ActualArguments.new(positionals, splat_flags, keywords, block)
230
+ end
231
+
232
+ private
233
+
234
+ def empty_omittable_splat_argument?(genv, arg)
235
+ empty = true
236
+ arg.each_type do |ty|
237
+ ty = ty.base_type(genv)
238
+ unless ty.is_a?(Type::Instance) && ty.mod == genv.mod_ary && ty.args[0] && ty.args[0].types.empty?
239
+ empty = false
240
+ break
241
+ end
242
+ end
243
+ empty
244
+ end
245
+ end
246
+
247
+ class ForwardingArguments
248
+ def initialize(req_positionals, opt_positionals, rest_positionals, post_positionals, req_keyword_pairs, opt_keyword_pairs, rest_keywords, block, activation)
249
+ @req_positionals = req_positionals
250
+ @opt_positionals = opt_positionals
251
+ @rest_positionals = rest_positionals
252
+ @post_positionals = post_positionals
253
+ @req_keyword_pairs = req_keyword_pairs
254
+ @opt_keyword_pairs = opt_keyword_pairs
255
+ @rest_keywords = rest_keywords
256
+ @block = block
257
+ @activation = activation
258
+ end
259
+
260
+ def to_actual_arguments(genv, changes, node, include_leading_positionals: true, activation_required: false)
261
+ positionals = include_leading_positionals ? @req_positionals.dup : []
262
+ splat_flags = ::Array.new(positionals.size, false)
263
+ positionals_omittable = ::Array.new(positionals.size, false)
264
+
265
+ @opt_positionals.each do |elem_vtx|
266
+ positionals << Source.new(genv.gen_ary_type(elem_vtx))
267
+ splat_flags << true
268
+ positionals_omittable << true
269
+ end
270
+
271
+ if @rest_positionals
272
+ positionals << Source.new(genv.gen_ary_type(@rest_positionals))
273
+ splat_flags << true
274
+ positionals_omittable << true
275
+ end
276
+
277
+ @post_positionals.each do |arg|
278
+ positionals << arg
279
+ splat_flags << false
280
+ positionals_omittable << false
281
+ end
282
+
283
+ keywords, keywords_omittable = build_keyword_args(genv, changes, node)
284
+ ForwardingActualArguments.new(positionals, splat_flags, keywords, @block, positionals_omittable, keywords_omittable, true, @activation, activation_required)
285
+ end
286
+
287
+ def accept_actual_arguments(genv, changes, a_args)
288
+ changes.add_edge(genv, Source.new(genv.true_type), @activation)
289
+
290
+ if a_args.splat_flags.any?
291
+ start_rest = [a_args.splat_flags.index(true), @req_positionals.size + @opt_positionals.size].min
292
+ end_rest = [a_args.splat_flags.rindex(true) + 1, a_args.positionals.size - @post_positionals.size].max
293
+ rest_vtxs = a_args.get_rest_args(genv, changes, start_rest, end_rest)
294
+
295
+ @req_positionals.each_with_index do |f_vtx, i|
296
+ if i < start_rest
297
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
298
+ else
299
+ rest_vtxs.each do |vtx|
300
+ changes.add_edge(genv, vtx, f_vtx)
301
+ end
302
+ end
303
+ end
304
+
305
+ @opt_positionals.each_with_index do |elem_vtx, i|
306
+ i += @req_positionals.size
307
+ if i < start_rest
308
+ changes.add_edge(genv, a_args.positionals[i], elem_vtx)
309
+ else
310
+ rest_vtxs.each do |vtx|
311
+ changes.add_edge(genv, vtx, elem_vtx)
312
+ end
313
+ end
314
+ end
315
+
316
+ @post_positionals.each_with_index do |f_vtx, i|
317
+ i += a_args.positionals.size - @post_positionals.size
318
+ if end_rest <= i
319
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
320
+ else
321
+ rest_vtxs.each do |vtx|
322
+ changes.add_edge(genv, vtx, f_vtx)
323
+ end
324
+ end
325
+ end
326
+
327
+ if @rest_positionals
328
+ rest_vtxs.each do |vtx|
329
+ changes.add_edge(genv, vtx, @rest_positionals)
330
+ end
331
+ end
332
+
333
+ else
334
+ @req_positionals.each_with_index do |f_vtx, i|
335
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
336
+ end
337
+
338
+ @post_positionals.each_with_index do |f_vtx, i|
339
+ i -= @post_positionals.size
340
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
341
+ end
342
+
343
+ start_rest = @req_positionals.size
344
+ end_rest = a_args.positionals.size - @post_positionals.size
345
+ i = 0
346
+ while i < @opt_positionals.size && start_rest < end_rest
347
+ changes.add_edge(genv, a_args.positionals[start_rest], @opt_positionals[i])
348
+ i += 1
349
+ start_rest += 1
350
+ end
351
+
352
+ if @rest_positionals
353
+ start_rest.upto(end_rest - 1) do |i|
354
+ changes.add_edge(genv, a_args.positionals[i], @rest_positionals)
355
+ end
356
+ end
357
+ end
358
+
359
+ changes.add_edge(genv, a_args.block, @block) if @block && a_args.block
360
+
361
+ return unless a_args.keywords
362
+
363
+ @req_keyword_pairs.each do |name, f_vtx|
364
+ changes.add_edge(genv, a_args.get_keyword_arg(genv, changes, name), f_vtx)
365
+ end
366
+
367
+ @opt_keyword_pairs.each do |name, f_vtx|
368
+ changes.add_edge(genv, a_args.get_keyword_arg(genv, changes, name), f_vtx)
369
+ end
370
+
371
+ if @rest_keywords
372
+ named_keys = @req_keyword_pairs.map(&:first) + @opt_keyword_pairs.map(&:first)
373
+ a_args.keywords.each_type do |kw_ty|
374
+ case kw_ty
375
+ when Type::Record
376
+ rest_fields = kw_ty.fields.reject {|key, _| named_keys.include?(key) }
377
+ base = kw_ty.base_type(genv)
378
+ rest_record = Type::Record.new(genv, rest_fields, base)
379
+ changes.add_edge(genv, Source.new(rest_record), @rest_keywords)
380
+ when Type::Hash, Type::Instance
381
+ changes.add_edge(genv, Source.new(kw_ty), @rest_keywords)
382
+ end
383
+ end
384
+ end
385
+ end
386
+
387
+ private
388
+
389
+ def build_keyword_args(genv, changes, node)
390
+ opt_keyword_pairs = @opt_keyword_pairs
391
+
392
+ if @req_keyword_pairs.empty? && opt_keyword_pairs.empty?
393
+ return @rest_keywords, !!@rest_keywords
394
+ end
395
+
396
+ unified_key = Vertex.new(node)
397
+ unified_val = Vertex.new(node)
398
+ literal_pairs = {}
399
+
400
+ @req_keyword_pairs.each do |name, vtx|
401
+ changes.add_edge(genv, Source.new(Type::Symbol.new(genv, name)), unified_key)
402
+ changes.add_edge(genv, vtx, unified_val)
403
+ literal_pairs[name] = vtx
404
+ end
405
+
406
+ opt_keyword_pairs.each do |name, vtx|
407
+ changes.add_edge(genv, Source.new(Type::Symbol.new(genv, name)), unified_key)
408
+ changes.add_edge(genv, vtx, unified_val)
409
+ literal_pairs[name] = vtx
410
+ end
411
+
412
+ base_hash_type = genv.gen_hash_type(unified_key, unified_val)
413
+ changes.add_hash_splat_box(genv, @rest_keywords, unified_key, unified_val) if @rest_keywords
414
+
415
+ if literal_pairs.empty?
416
+ [Source.new(base_hash_type), false]
417
+ elsif @rest_keywords
418
+ fallback = Source.new(Type::Record.new(genv, literal_pairs, base_hash_type))
419
+ [changes.add_keyword_merge_box(genv, @rest_keywords, literal_pairs, fallback).ret, false]
420
+ else
421
+ [Source.new(Type::Record.new(genv, literal_pairs, base_hash_type)), false]
422
+ end
423
+ end
424
+ end
425
+
84
426
  class Block
85
427
  #: (AST::CallBaseNode, Vertex, Array[Vertex], Array[EscapeBox]) -> void
86
428
  def initialize(node, f_ary_arg, f_args, next_boxes)
@@ -94,12 +436,7 @@ module TypeProf::Core
94
436
 
95
437
  def accept_args(genv, changes, caller_positionals)
96
438
  if caller_positionals.size == 1 && @f_args.size >= 2
97
- single_arg = caller_positionals[0]
98
-
99
- @f_args.each_with_index do |f_arg, i|
100
- elem_vtx = changes.add_splat_box(genv, single_arg, i).ret
101
- changes.add_edge(genv, elem_vtx, f_arg)
102
- end
439
+ changes.add_edge(genv, caller_positionals[0], @f_ary_arg)
103
440
  else
104
441
  caller_positionals.zip(@f_args) do |a_arg, f_arg|
105
442
  changes.add_edge(genv, a_arg, f_arg) if f_arg
@@ -2,60 +2,63 @@ module TypeProf::Core
2
2
  class MethodEntity
3
3
  def initialize
4
4
  @builtin = nil
5
- @decls = Set[]
6
- @overloading_decls = Set[]
7
- @defs = Set[]
8
- @aliases = {}
9
- @method_call_boxes = Set[]
10
5
  end
11
6
 
12
- attr_reader :decls, :defs, :aliases, :method_call_boxes
13
7
  attr_accessor :builtin
14
8
 
9
+ def decls = @decls ||= Set.empty
10
+ def defs = @defs ||= Set.empty
11
+ def aliases = @aliases ||= {}
12
+ def method_call_boxes = @method_call_boxes ||= Set.empty
13
+
14
+ private def overloading_decls = @overloading_decls ||= Set.empty
15
+
15
16
  def add_decl(decl)
16
17
  if decl.overloading
17
- @overloading_decls << decl
18
+ overloading_decls << decl
18
19
  else
19
- @decls << decl
20
+ decls << decl
20
21
  end
21
22
  end
22
23
 
23
24
  def remove_decl(decl)
24
25
  if decl.overloading
25
- @overloading_decls.delete(decl) || raise
26
+ overloading_decls.delete(decl) || raise
26
27
  else
27
- @decls.delete(decl) || raise
28
+ decls.delete(decl) || raise
28
29
  end
29
30
  end
30
31
 
31
32
  def add_def(mdef)
32
- @defs << mdef
33
+ defs << mdef
33
34
  self
34
35
  end
35
36
 
36
37
  def remove_def(mdef)
37
- @defs.delete(mdef) || raise
38
+ defs.delete(mdef) || raise
38
39
  end
39
40
 
40
41
  def add_alias(node, old_mid)
41
- @aliases[node] = old_mid
42
+ aliases[node] = old_mid
42
43
  end
43
44
 
44
45
  def remove_alias(node)
45
- @aliases.delete(node) || raise
46
+ aliases.delete(node) || raise
46
47
  end
47
48
 
48
49
  def exist?
49
- @builtin || !@decls.empty? || !@defs.empty?
50
+ @builtin || (@decls && !@decls.empty?) || (@defs && !@defs.empty?)
50
51
  end
51
52
 
52
53
  def add_run_all_mdefs(genv)
54
+ return unless @defs
53
55
  @defs.each do |mdef|
54
56
  genv.add_run(mdef)
55
57
  end
56
58
  end
57
59
 
58
60
  def add_run_all_method_call_boxes(genv)
61
+ return unless @method_call_boxes
59
62
  @method_call_boxes.each do |box|
60
63
  genv.add_run(box)
61
64
  end
@@ -3,12 +3,19 @@ module TypeProf::Core
3
3
  def initialize(cpath, outer_module = self)
4
4
  @cpath = cpath
5
5
 
6
- @module_decls = Set[]
7
- @module_defs = Set[]
8
- @include_decls = Set[]
9
- @include_defs = Set[]
6
+ @module_decls = Set.empty
7
+ @module_defs = Set.empty
8
+ @include_decls = Set.empty
9
+ @include_defs = Set.empty
10
10
  @prepend_decls = []
11
11
  @prepend_defs = []
12
+ @extend_decls = Set.empty
13
+ @extend_defs = Set.empty
14
+
15
+ # `class Foo = Bar` / `module Foo = Bar` declarations attached to this entity.
16
+ # Maps an alias decl to the target ModuleEntity at the time of registration.
17
+ @alias_decls = {}
18
+ @alias_target = nil
12
19
 
13
20
  @inner_modules = {}
14
21
  @outer_module = outer_module
@@ -18,6 +25,7 @@ module TypeProf::Core
18
25
  @self_types = {}
19
26
  @included_modules = {}
20
27
  @prepended_modules = {}
28
+ @extended_modules = {}
21
29
  @basic_object = @cpath == [:BasicObject]
22
30
 
23
31
  # child modules (subclasses and all modules that include me)
@@ -25,7 +33,7 @@ module TypeProf::Core
25
33
 
26
34
  # class Foo[X, Y, Z] < Bar[A, B, C]
27
35
  @superclass_type_args = nil # A, B, C
28
- @type_params = [] # X, Y, Z
36
+ @type_params = {} # X, Y, Z
29
37
 
30
38
  @consts = {}
31
39
  @methods = { true => {}, false => {} }
@@ -34,14 +42,16 @@ module TypeProf::Core
34
42
  @type_aliases = {}
35
43
 
36
44
  @static_reads = {}
37
- @subclass_checks = Set[]
38
- @ivar_reads = Set[] # should be handled in @ivars ??
39
- @cvar_reads = Set[]
45
+ @subclass_checks = Set.empty
46
+ @ivar_reads = Set.empty # should be handled in @ivars ??
47
+ @cvar_reads = Set.empty
40
48
  end
41
49
 
42
50
  attr_reader :cpath
43
51
  attr_reader :module_decls
44
52
  attr_reader :module_defs
53
+ attr_reader :alias_decls
54
+ attr_reader :alias_target
45
55
 
46
56
  attr_reader :inner_modules
47
57
  attr_reader :outer_module
@@ -50,6 +60,7 @@ module TypeProf::Core
50
60
  attr_reader :self_types
51
61
  attr_reader :included_modules
52
62
  attr_reader :prepended_modules
63
+ attr_reader :extended_modules
53
64
  attr_reader :child_modules
54
65
 
55
66
  attr_reader :superclass_type_args
@@ -79,7 +90,7 @@ module TypeProf::Core
79
90
  end
80
91
 
81
92
  def exist?
82
- !@module_decls.empty? || !@module_defs.empty?
93
+ !@module_decls.empty? || !@module_defs.empty? || !@alias_decls.empty?
83
94
  end
84
95
 
85
96
  def on_inner_modules_changed(genv, changed_cname)
@@ -115,9 +126,10 @@ module TypeProf::Core
115
126
  @module_decls << decl
116
127
 
117
128
  if @type_params
118
- update_type_params if @type_params != decl.params
129
+ update_type_params
119
130
  else
120
- @type_params = decl.params
131
+ @type_params = {}
132
+ decl.params.zip(decl.params_default_types) {|name, default_type| @type_params[name] = default_type }
121
133
  end
122
134
 
123
135
  if decl.is_a?(AST::SigClassNode) && !@superclass_type_args
@@ -133,7 +145,7 @@ module TypeProf::Core
133
145
  @outer_module.get_const(get_cname).remove_decl(decl)
134
146
  @module_decls.delete(decl) || raise
135
147
 
136
- update_type_params if @type_params == decl.params
148
+ update_type_params
137
149
  if decl.is_a?(AST::SigClassNode) && @superclass_type_args == decl.superclass_args
138
150
  @superclass_type_args = nil
139
151
  @module_decls.each do |decl|
@@ -152,13 +164,12 @@ module TypeProf::Core
152
164
  @module_decls.each do |decl|
153
165
  params = decl.params
154
166
  next unless params
155
- if @type_params
156
- @type_params = params if (@type_params <=> params) > 0
157
- else
158
- @type_params = params
167
+ if !@type_params || @type_params.size < params.size
168
+ @type_params = {}
169
+ params.zip(decl.params_default_types) {|name, default_type| @type_params[name] = default_type }
159
170
  end
160
171
  end
161
- @type_params ||= []
172
+ @type_params ||= {}
162
173
  # TODO: report an error if there are multiple inconsistent declarations
163
174
  end
164
175
 
@@ -176,6 +187,22 @@ module TypeProf::Core
176
187
  on_module_removed(genv)
177
188
  end
178
189
 
190
+ def add_alias_decl(genv, decl, target_mod)
191
+ on_module_added(genv)
192
+ @alias_decls[decl] = target_mod
193
+ @alias_target = @alias_decls.values.first
194
+ ce = @outer_module.get_const(get_cname)
195
+ ce.add_decl(decl)
196
+ ce
197
+ end
198
+
199
+ def remove_alias_decl(genv, decl)
200
+ @outer_module.get_const(get_cname).remove_decl(decl)
201
+ @alias_decls.delete(decl) || raise
202
+ @alias_target = @alias_decls.values.first
203
+ on_module_removed(genv)
204
+ end
205
+
179
206
  def add_include_decl(genv, node)
180
207
  @include_decls << node
181
208
  genv.add_static_eval_queue(:parent_modules_changed, self)
@@ -196,6 +223,26 @@ module TypeProf::Core
196
223
  genv.add_static_eval_queue(:parent_modules_changed, self)
197
224
  end
198
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
+
199
246
  def add_prepend_decl(genv, node)
200
247
  @prepend_decls << node
201
248
  genv.add_static_eval_queue(:parent_modules_changed, self)
@@ -237,7 +284,7 @@ module TypeProf::Core
237
284
  old_parent.child_modules.delete(self) if set.empty?
238
285
  end
239
286
  if new_parent
240
- set = new_parent.child_modules[self] ||= Set[]
287
+ set = new_parent.child_modules[self] ||= Set.empty
241
288
  set << origin
242
289
  end
243
290
  return [new_parent, true]
@@ -261,6 +308,9 @@ module TypeProf::Core
261
308
  next
262
309
  when AST::ModuleNode
263
310
  return nil
311
+ when AST::StructNewNode
312
+ base_cpath = mdef.struct_base_cpath
313
+ return base_cpath == @cpath ? [] : base_cpath
264
314
  else
265
315
  raise
266
316
  end
@@ -371,6 +421,30 @@ module TypeProf::Core
371
421
  any_updated = true
372
422
  end
373
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
374
448
  @included_modules.delete_if do |origin, old_mod|
375
449
  if @include_decls.include?(origin) || @include_defs.include?(origin)
376
450
  false
@@ -389,6 +463,15 @@ module TypeProf::Core
389
463
  true
390
464
  end
391
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
392
475
 
393
476
  if any_updated
394
477
  @subclass_checks.each do |mcall_box|
@@ -399,6 +482,8 @@ module TypeProf::Core
399
482
  end
400
483
 
401
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
402
487
  @child_modules.each_key {|child_mod| child_mod.on_ancestors_updated(genv, base_mod || self) }
403
488
  @static_reads.each_value do |static_reads|
404
489
  static_reads.each do |static_read|
@@ -436,6 +521,19 @@ module TypeProf::Core
436
521
  @ivars[singleton][name] ||= ValueEntity.new
437
522
  end
438
523
 
524
+ def add_ivar_decl(genv, singleton, name, decl)
525
+ ive = get_ivar(singleton, name)
526
+ ive.add_decl(decl)
527
+ ive.on_decl_changed(genv)
528
+ ive
529
+ end
530
+
531
+ def remove_ivar_decl(genv, singleton, name, decl)
532
+ ive = get_ivar(singleton, name)
533
+ ive.remove_decl(decl)
534
+ ive.on_decl_changed(genv)
535
+ end
536
+
439
537
  def get_cvar(name)
440
538
  @cvars[name] ||= ValueEntity.new
441
539
  end