typeprof 0.31.1 → 0.32.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 (37) 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 +18 -6
  7. data/lib/typeprof/core/ast/call.rb +96 -32
  8. data/lib/typeprof/core/ast/const.rb +12 -9
  9. data/lib/typeprof/core/ast/control.rb +60 -30
  10. data/lib/typeprof/core/ast/meta.rb +194 -2
  11. data/lib/typeprof/core/ast/method.rb +74 -20
  12. data/lib/typeprof/core/ast/misc.rb +27 -7
  13. data/lib/typeprof/core/ast/module.rb +33 -3
  14. data/lib/typeprof/core/ast/sig_decl.rb +85 -24
  15. data/lib/typeprof/core/ast/sig_type.rb +77 -31
  16. data/lib/typeprof/core/ast/value.rb +14 -6
  17. data/lib/typeprof/core/ast/variable.rb +11 -4
  18. data/lib/typeprof/core/ast.rb +95 -14
  19. data/lib/typeprof/core/builtin.rb +184 -12
  20. data/lib/typeprof/core/env/method.rb +171 -6
  21. data/lib/typeprof/core/env/method_entity.rb +18 -15
  22. data/lib/typeprof/core/env/module_entity.rb +56 -18
  23. data/lib/typeprof/core/env/static_read.rb +4 -4
  24. data/lib/typeprof/core/env/type_alias_entity.rb +1 -1
  25. data/lib/typeprof/core/env/value_entity.rb +25 -3
  26. data/lib/typeprof/core/env.rb +79 -17
  27. data/lib/typeprof/core/graph/box.rb +379 -52
  28. data/lib/typeprof/core/graph/change_set.rb +59 -46
  29. data/lib/typeprof/core/graph/filter.rb +8 -5
  30. data/lib/typeprof/core/graph/vertex.rb +20 -19
  31. data/lib/typeprof/core/service.rb +317 -23
  32. data/lib/typeprof/core/type.rb +41 -7
  33. data/lib/typeprof/core/util.rb +6 -0
  34. data/lib/typeprof/lsp/messages.rb +5 -0
  35. data/lib/typeprof/lsp/server.rb +35 -4
  36. data/lib/typeprof/version.rb +1 -1
  37. metadata +3 -2
@@ -12,13 +12,13 @@ module TypeProf::Core
12
12
 
13
13
  if @static_cpath
14
14
  ncref = CRef.new(@static_cpath, meta ? :metaclass : :class, nil, lenv.cref)
15
- nlenv = LocalEnv.new(@lenv.path, ncref, {}, [])
15
+ nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, [])
16
16
  @body = raw_scope ? AST.create_node(raw_scope, nlenv, use_result) : DummyNilNode.new(code_range, lenv)
17
17
  else
18
18
  @body = nil
19
19
  end
20
20
 
21
- @cname_code_range = meta ? nil : TypeProf::CodeRange.from_node(raw_node.constant_path)
21
+ @cname_code_range = meta ? nil : lenv.code_range_from_node(raw_node.constant_path)
22
22
  @mod_cdef = nil
23
23
  end
24
24
 
@@ -89,7 +89,22 @@ module TypeProf::Core
89
89
  def initialize(raw_node, lenv, use_result)
90
90
  super(raw_node, lenv, raw_node.constant_path, false, raw_node.body, use_result)
91
91
  raw_superclass = raw_node.superclass
92
- @superclass_cpath = raw_superclass ? AST.create_node(raw_superclass, lenv) : nil
92
+ if raw_superclass
93
+ # In Ruby, the superclass expression is evaluated before the class constant
94
+ # is created. When the superclass is a bare constant with the same name as
95
+ # the class being defined (e.g., `class Foo < Foo` inside a module), use the
96
+ # outer scope to avoid resolving to the class itself.
97
+ if @static_cpath && lenv.cref.outer &&
98
+ raw_superclass.type == :constant_read_node &&
99
+ raw_superclass.name == @static_cpath.last
100
+ slenv = LocalEnv.new(lenv.file_context, lenv.cref.outer, {}, [])
101
+ @superclass_cpath = AST.create_node(raw_superclass, slenv)
102
+ else
103
+ @superclass_cpath = AST.create_node(raw_superclass, lenv)
104
+ end
105
+ else
106
+ @superclass_cpath = nil
107
+ end
93
108
  end
94
109
 
95
110
  attr_reader :superclass_cpath
@@ -113,6 +128,21 @@ module TypeProf::Core
113
128
 
114
129
  def install0(genv)
115
130
  @superclass_cpath.install(genv) if @superclass_cpath
131
+ if @static_cpath && @superclass_cpath
132
+ const_read = @superclass_cpath.static_ret
133
+ if const_read && const_read.cpath
134
+ super_mod = genv.resolve_cpath(const_read.cpath)
135
+ self_mod = genv.resolve_cpath(@static_cpath)
136
+ mod = super_mod
137
+ while mod
138
+ if mod == self_mod
139
+ @changes.add_diagnostic(:code_range, "circular inheritance", @superclass_cpath)
140
+ break
141
+ end
142
+ mod = mod.superclass
143
+ end
144
+ end
145
+ end
116
146
  super(genv)
117
147
  end
118
148
  end
@@ -15,20 +15,27 @@ module TypeProf::Core
15
15
  # TODO: decl.type_params
16
16
  # TODO: decl.super_class.args
17
17
  ncref = CRef.new(@cpath, :class, nil, lenv.cref)
18
- nlenv = LocalEnv.new(@lenv.path, ncref, {}, [])
18
+ nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, [])
19
19
  @members = raw_decl.members.map do |member|
20
20
  AST.create_rbs_member(member, nlenv)
21
21
  end.compact
22
22
  # TODO?: param.variance, param.unchecked, param.upper_bound
23
23
  @params = raw_decl.type_params.map {|param| param.name }
24
+ @params_default_types = raw_decl.type_params.map do |param|
25
+ ty = param.default_type
26
+ ty ? AST.create_rbs_type(ty, lenv) : nil
27
+ end
24
28
  end
25
29
 
26
- attr_reader :cpath, :members, :params
30
+ attr_reader :cpath, :members, :params, :params_default_types
27
31
 
28
- def subnodes = { members: }
32
+ def subnodes = { members:, params_default_types: }
29
33
  def attrs = { cpath:, params: }
30
34
 
31
35
  def define0(genv)
36
+ @params_default_types.each do |ty|
37
+ ty&.define(genv)
38
+ end
32
39
  @members.each do |member|
33
40
  member.define(genv)
34
41
  end
@@ -46,6 +53,9 @@ module TypeProf::Core
46
53
  def undefine0(genv)
47
54
  mod = genv.resolve_cpath(@cpath)
48
55
  mod.remove_module_decl(genv, self)
56
+ @params_default_types.each do |ty|
57
+ ty&.undefine(genv)
58
+ end
49
59
  @members.each do |member|
50
60
  member.undefine(genv)
51
61
  end
@@ -180,19 +190,19 @@ module TypeProf::Core
180
190
  def initialize(raw_decl, lenv)
181
191
  super(raw_decl, lenv)
182
192
  @mid = raw_decl.name
183
- @mid_code_range = TypeProf::CodeRange.from_node(raw_decl.location[:name])
193
+ @mid_code_range = lenv.code_range_from_node(raw_decl.location[:name])
184
194
  @singleton = raw_decl.singleton?
185
195
  @instance = raw_decl.instance?
186
- @method_types = raw_decl.overloads.map do |overload|
196
+ @method_types = OverloadSet.new(raw_decl.overloads.map do |overload|
187
197
  method_type = overload.method_type
188
198
  AST.create_rbs_func_type(method_type, method_type.type_params, method_type.block, lenv)
189
- end
199
+ end)
190
200
  @overloading = raw_decl.overloading
191
201
  end
192
202
 
193
203
  attr_reader :mid, :singleton, :instance, :method_types, :overloading, :mid_code_range
194
204
 
195
- def subnodes = { method_types: }
205
+ def subnodes = { method_types: @method_types.to_a }
196
206
  def attrs = { mid:, mid_code_range:, singleton:, instance:, overloading: }
197
207
 
198
208
  def mname_code_range(_name) = @mid_code_range
@@ -222,10 +232,10 @@ module TypeProf::Core
222
232
  def define0(genv)
223
233
  @args.each {|arg| arg.define(genv) }
224
234
  const_reads = []
225
- const_read = BaseConstRead.new(genv, @cpath.first, @toplevel ? CRef::Toplevel : @lenv.cref, false)
235
+ const_read = BaseConstRead.new(genv, @cpath.first, @toplevel ? CRef::Toplevel : @lenv.cref, true)
226
236
  const_reads << const_read
227
237
  @cpath[1..].each do |cname|
228
- const_read = ScopedConstRead.new(cname, const_read, false)
238
+ const_read = ScopedConstRead.new(cname, const_read, true)
229
239
  const_reads << const_read
230
240
  end
231
241
  mod = genv.resolve_cpath(@lenv.cref.cpath)
@@ -271,10 +281,10 @@ module TypeProf::Core
271
281
  def define0(genv)
272
282
  @args.each {|arg| arg.define(genv) }
273
283
  const_reads = []
274
- const_read = BaseConstRead.new(genv, @cpath.first, @toplevel ? CRef::Toplevel : @lenv.cref, false)
284
+ const_read = BaseConstRead.new(genv, @cpath.first, @toplevel ? CRef::Toplevel : @lenv.cref, true)
275
285
  const_reads << const_read
276
286
  @cpath[1..].each do |cname|
277
- const_read = ScopedConstRead.new(cname, const_read, false)
287
+ const_read = ScopedConstRead.new(cname, const_read, true)
278
288
  const_reads << const_read
279
289
  end
280
290
  mod = genv.resolve_cpath(@lenv.cref.cpath)
@@ -336,6 +346,7 @@ module TypeProf::Core
336
346
  location: raw_decl.type.location
337
347
  )
338
348
  @method_type = AST.create_rbs_func_type(rbs_method_type, [], nil, lenv)
349
+ @method_types = OverloadSet.new([@method_type])
339
350
  end
340
351
 
341
352
  attr_reader :mid, :method_type
@@ -344,7 +355,7 @@ module TypeProf::Core
344
355
  def attrs = { mid: }
345
356
 
346
357
  def install0(genv)
347
- @changes.add_method_decl_box(genv, @lenv.cref.cpath, false, @mid, [@method_type], false)
358
+ @changes.add_method_decl_box(genv, @lenv.cref.cpath, false, @mid, @method_types, false)
348
359
  Source.new
349
360
  end
350
361
  end
@@ -371,6 +382,7 @@ module TypeProf::Core
371
382
  location: raw_decl.type.location
372
383
  )
373
384
  @method_type = AST.create_rbs_func_type(rbs_method_type, [], nil, lenv)
385
+ @method_types = OverloadSet.new([@method_type])
374
386
  end
375
387
 
376
388
  attr_reader :mid, :method_type
@@ -379,7 +391,7 @@ module TypeProf::Core
379
391
  def attrs = { mid: }
380
392
 
381
393
  def install0(genv)
382
- @changes.add_method_decl_box(genv, @lenv.cref.cpath, false, @mid, [@method_type], false)
394
+ @changes.add_method_decl_box(genv, @lenv.cref.cpath, false, @mid, @method_types, false)
383
395
  Source.new
384
396
  end
385
397
  end
@@ -402,6 +414,52 @@ module TypeProf::Core
402
414
  end
403
415
  end
404
416
 
417
+ class SigModuleAliasBaseNode < Node
418
+ def initialize(raw_decl, lenv)
419
+ super(raw_decl, lenv)
420
+ @cpath = AST.resolve_rbs_name(raw_decl.new_name, lenv)
421
+ @old_cpath = AST.resolve_rbs_name(raw_decl.old_name, lenv)
422
+ end
423
+
424
+ attr_reader :cpath, :old_cpath
425
+ def attrs = { cpath:, old_cpath: }
426
+
427
+ def define0(genv)
428
+ outer = genv.resolve_cpath(@cpath[0..-2])
429
+ cname = @cpath.last
430
+ alias_mod = outer.inner_modules[cname] ||= ModuleEntity.new(outer.cpath + [cname], outer)
431
+ target_mod = genv.resolve_cpath(@old_cpath)
432
+ alias_mod.add_alias_decl(genv, self, target_mod)
433
+ end
434
+
435
+ def define_copy(genv)
436
+ outer = genv.resolve_cpath(@cpath[0..-2])
437
+ alias_mod = outer.inner_modules[@cpath.last]
438
+ target_mod = genv.resolve_cpath(@old_cpath)
439
+ alias_mod.add_alias_decl(genv, self, target_mod)
440
+ alias_mod.remove_alias_decl(genv, @prev_node)
441
+ super(genv)
442
+ end
443
+
444
+ def undefine0(genv)
445
+ outer = genv.resolve_cpath(@cpath[0..-2])
446
+ alias_mod = outer.inner_modules[@cpath.last]
447
+ alias_mod.remove_alias_decl(genv, self)
448
+ end
449
+
450
+ def install0(genv)
451
+ mod_val = Source.new(Type::Singleton.new(genv, genv.resolve_cpath(@cpath)))
452
+ @changes.add_edge(genv, mod_val, @static_ret.vtx)
453
+ Source.new
454
+ end
455
+ end
456
+
457
+ class SigClassAliasNode < SigModuleAliasBaseNode
458
+ end
459
+
460
+ class SigModuleAliasNode < SigModuleAliasBaseNode
461
+ end
462
+
405
463
  class SigConstNode < Node
406
464
  def initialize(raw_decl, lenv)
407
465
  super(raw_decl, lenv)
@@ -415,9 +473,10 @@ module TypeProf::Core
415
473
 
416
474
  def define0(genv)
417
475
  @type.define(genv)
418
- mod = genv.resolve_const(@cpath)
419
- mod.add_decl(self)
420
- mod
476
+ cdef = genv.resolve_const(@cpath)
477
+ cdef.on_const_added(genv, @cpath)
478
+ cdef.add_decl(self)
479
+ cdef
421
480
  end
422
481
 
423
482
  def define_copy(genv)
@@ -428,7 +487,9 @@ module TypeProf::Core
428
487
  end
429
488
 
430
489
  def undefine0(genv)
431
- genv.resolve_const(@cpath).remove_decl(self)
490
+ cdef = genv.resolve_const(@cpath)
491
+ cdef.remove_decl(self)
492
+ cdef.on_const_removed(genv, @cpath)
432
493
  @type.undefine(genv)
433
494
  end
434
495
 
@@ -490,20 +551,20 @@ module TypeProf::Core
490
551
 
491
552
  def define0(genv)
492
553
  @type.define(genv)
493
- mod = genv.resolve_ivar(cpath, @class_scope, @var)
494
- mod.add_decl(self)
495
- mod
554
+ mod = genv.resolve_cpath(cpath)
555
+ mod.add_ivar_decl(genv, @class_scope, @var, self)
496
556
  end
497
557
 
498
558
  def define_copy(genv)
499
- mod = genv.resolve_ivar(cpath, @class_scope, @var)
500
- mod.add_decl(self)
501
- mod.remove_decl(@prev_node)
559
+ mod = genv.resolve_cpath(cpath)
560
+ mod.add_ivar_decl(genv, @class_scope, @var, self)
561
+ mod.remove_ivar_decl(genv, @class_scope, @var, @prev_node)
502
562
  super(genv)
503
563
  end
504
564
 
505
565
  def undefine0(genv)
506
- genv.resolve_ivar(cpath, @class_scope, @var).remove_decl(self)
566
+ mod = genv.resolve_cpath(cpath)
567
+ mod.remove_ivar_decl(genv, @class_scope, @var, self)
507
568
  @type.undefine(genv)
508
569
  end
509
570
 
@@ -2,7 +2,10 @@ module TypeProf::Core
2
2
  class AST
3
3
  def self.typecheck_for_module(genv, changes, f_mod, f_args, a_vtx, subst)
4
4
  changes.add_edge(genv, a_vtx, changes.target)
5
+ found_any = false
5
6
  a_vtx.each_type do |ty|
7
+ next if ty.is_a?(Type::Bot)
8
+ found_any = true
6
9
  ty = ty.base_type(genv)
7
10
  while ty
8
11
  if ty.mod == f_mod && ty.is_a?(Type::Instance)
@@ -25,7 +28,7 @@ module TypeProf::Core
25
28
  ty = genv.get_superclass_type(ty, changes, {})
26
29
  end
27
30
  end
28
- return false
31
+ return !found_any
29
32
  end
30
33
 
31
34
  def self.typecheck_for_prepended_modules(genv, changes, a_ty, f_mod, f_args, subst)
@@ -33,7 +36,7 @@ module TypeProf::Core
33
36
  if prep_decl.is_a?(AST::SigPrependNode) && prep_mod.type_params
34
37
  prep_ty = genv.get_instance_type(prep_mod, prep_decl.args, changes, {}, a_ty)
35
38
  else
36
- type_params = prep_mod.type_params.map {|ty_param| Source.new() } # TODO: better support
39
+ type_params = prep_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
37
40
  prep_ty = Type::Instance.new(genv, prep_mod, type_params)
38
41
  end
39
42
  if prep_ty.mod == f_mod
@@ -58,7 +61,7 @@ module TypeProf::Core
58
61
  if inc_decl.is_a?(AST::SigIncludeNode) && inc_mod.type_params
59
62
  inc_ty = genv.get_instance_type(inc_mod, inc_decl.args, changes, {}, a_ty)
60
63
  else
61
- type_params = inc_mod.type_params.map {|ty_param| Source.new() } # TODO: better support
64
+ type_params = inc_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
62
65
  inc_ty = Type::Instance.new(genv, inc_mod, type_params)
63
66
  end
64
67
  if inc_ty.mod == f_mod
@@ -108,13 +111,19 @@ module TypeProf::Core
108
111
  param = raw_decl.type.rest_positionals
109
112
  @rest_positionals = param ? AST.create_rbs_type(param.type, lenv) : nil
110
113
 
111
- @req_keywords = raw_decl.type.required_keywords.to_h do |key, ty|
114
+ @req_keyword_keys = []
115
+ @req_keyword_values = []
116
+ raw_decl.type.required_keywords.each do |key, ty|
112
117
  raise "unsupported argument type: #{ ty.class }" if !ty.is_a?(RBS::Types::Function::Param)
113
- [key, AST.create_rbs_type(ty.type, lenv)]
118
+ @req_keyword_keys << key
119
+ @req_keyword_values << AST.create_rbs_type(ty.type, lenv)
114
120
  end
115
- @opt_keywords = raw_decl.type.optional_keywords.to_h do |key, ty|
121
+ @opt_keyword_keys = []
122
+ @opt_keyword_values = []
123
+ raw_decl.type.optional_keywords.each do |key, ty|
116
124
  raise "unsupported argument type: #{ ty.class }" if !ty.is_a?(RBS::Types::Function::Param)
117
- [key, AST.create_rbs_type(ty.type, lenv)]
125
+ @opt_keyword_keys << key
126
+ @opt_keyword_values << AST.create_rbs_type(ty.type, lenv)
118
127
  end
119
128
  param = raw_decl.type.rest_keywords
120
129
  @rest_keywords = param ? AST.create_rbs_type(param.type, lenv) : nil
@@ -124,8 +133,10 @@ module TypeProf::Core
124
133
  @post_positionals = []
125
134
  @opt_positionals = []
126
135
  @rest_positionals = SigTyBaseAnyNode.new(raw_decl, lenv)
127
- @req_keywords = {}
128
- @opt_keywords = {}
136
+ @req_keyword_keys = []
137
+ @req_keyword_values = []
138
+ @opt_keyword_keys = []
139
+ @opt_keyword_values = []
129
140
  @rest_keywords = nil
130
141
  end
131
142
 
@@ -137,8 +148,10 @@ module TypeProf::Core
137
148
  attr_reader :post_positionals
138
149
  attr_reader :opt_positionals
139
150
  attr_reader :rest_positionals
140
- attr_reader :req_keywords
141
- attr_reader :opt_keywords
151
+ attr_reader :req_keyword_keys
152
+ attr_reader :req_keyword_values
153
+ attr_reader :opt_keyword_keys
154
+ attr_reader :opt_keyword_values
142
155
  attr_reader :rest_keywords
143
156
  attr_reader :return_type
144
157
 
@@ -148,12 +161,17 @@ module TypeProf::Core
148
161
  post_positionals:,
149
162
  opt_positionals:,
150
163
  rest_positionals:,
151
- req_keywords:,
152
- opt_keywords:,
164
+ req_keyword_values:,
165
+ opt_keyword_values:,
153
166
  rest_keywords:,
154
167
  return_type:,
155
168
  }
156
- def attrs = { type_params:, block_required: }
169
+ def attrs = {
170
+ type_params:,
171
+ req_keyword_keys:,
172
+ opt_keyword_keys:,
173
+ block_required:,
174
+ }
157
175
  end
158
176
 
159
177
  class SigTyNode < Node
@@ -182,6 +200,7 @@ module TypeProf::Core
182
200
  def typecheck(genv, changes, vtx, subst)
183
201
  changes.add_edge(genv, vtx, changes.target)
184
202
  vtx.each_type do |ty|
203
+ next if ty.is_a?(Type::Bot)
185
204
  return false unless ty == genv.true_type || ty == genv.false_type
186
205
  end
187
206
  true
@@ -204,6 +223,7 @@ module TypeProf::Core
204
223
  def typecheck(genv, changes, vtx, subst)
205
224
  changes.add_edge(genv, vtx, changes.target)
206
225
  vtx.each_type do |ty|
226
+ next if ty.is_a?(Type::Bot)
207
227
  return false unless ty == genv.nil_type
208
228
  end
209
229
  true
@@ -287,11 +307,11 @@ module TypeProf::Core
287
307
 
288
308
  class SigTyBaseBottomNode < SigTyNode
289
309
  def covariant_vertex0(genv, changes, vtx, subst)
290
- changes.add_edge(genv, Source.new(Type::Bot.new(genv)), vtx)
310
+ changes.add_edge(genv, Source.new(genv.bot_type), vtx)
291
311
  end
292
312
 
293
313
  def contravariant_vertex0(genv, changes, vtx, subst)
294
- changes.add_edge(genv, Source.new(Type::Bot.new(genv)), vtx)
314
+ changes.add_edge(genv, Source.new(genv.bot_type), vtx)
295
315
  end
296
316
 
297
317
  def typecheck(genv, changes, vtx, subst)
@@ -530,6 +550,8 @@ module TypeProf::Core
530
550
  name = raw_decl.name
531
551
  @cpath = name.namespace.path + [name.name]
532
552
  @toplevel = name.namespace.absolute?
553
+ # RBS::Types::ClassSingleton#args was added in RBS 4.0
554
+ @args = raw_decl.respond_to?(:args) ? raw_decl.args.map {|arg| AST.create_rbs_type(arg, lenv) } : []
533
555
  end
534
556
 
535
557
  attr_reader :cpath, :toplevel
@@ -578,7 +600,10 @@ module TypeProf::Core
578
600
  return unless cpath
579
601
  f_mod = genv.resolve_cpath(cpath)
580
602
  changes.add_edge(genv, vtx, changes.target)
603
+ found_any = false
581
604
  vtx.each_type do |ty|
605
+ next if ty.is_a?(Type::Bot)
606
+ found_any = true
582
607
  case ty
583
608
  when Type::Singleton
584
609
  if f_mod.module?
@@ -593,7 +618,7 @@ module TypeProf::Core
593
618
  end
594
619
  end
595
620
  end
596
- false
621
+ !found_any
597
622
  end
598
623
 
599
624
  def show
@@ -714,7 +739,10 @@ module TypeProf::Core
714
739
 
715
740
  def typecheck(genv, changes, vtx, subst)
716
741
  changes.add_edge(genv, vtx, changes.target)
742
+ found_any = false
717
743
  vtx.each_type do |ty|
744
+ next if ty.is_a?(Type::Bot)
745
+ found_any = true
718
746
  case ty
719
747
  when Type::Array
720
748
  next if ty.elems.size != @types.size
@@ -729,7 +757,7 @@ module TypeProf::Core
729
757
  return true
730
758
  end
731
759
  end
732
- false
760
+ !found_any
733
761
  end
734
762
 
735
763
  def show
@@ -740,16 +768,18 @@ module TypeProf::Core
740
768
  class SigTyRecordNode < SigTyNode
741
769
  def initialize(raw_decl, lenv)
742
770
  super(raw_decl, lenv)
743
- @fields = raw_decl.fields.transform_values { |val| AST.create_rbs_type(val, lenv) }
771
+ @keys = raw_decl.fields.keys
772
+ @vals = raw_decl.fields.values.map { |val| AST.create_rbs_type(val, lenv) }
744
773
  end
745
774
 
746
- attr_reader :fields
747
- def subnodes = { fields: }
775
+ attr_reader :keys, :vals
776
+ def subnodes = { vals: }
777
+ def attrs = { keys: }
748
778
 
749
779
  def covariant_vertex0(genv, changes, vtx, subst)
750
780
  field_vertices = {}
751
- @fields.each do |key, field_node|
752
- field_vertices[key] = field_node.covariant_vertex(genv, changes, subst)
781
+ @keys.zip(@vals) do |key, val_node|
782
+ field_vertices[key] = val_node.covariant_vertex(genv, changes, subst)
753
783
  end
754
784
 
755
785
  # Create base Hash type for Record
@@ -766,8 +796,8 @@ module TypeProf::Core
766
796
 
767
797
  def contravariant_vertex0(genv, changes, vtx, subst)
768
798
  field_vertices = {}
769
- @fields.each do |key, field_node|
770
- field_vertices[key] = field_node.contravariant_vertex(genv, changes, subst)
799
+ @keys.zip(@vals) do |key, val_node|
800
+ field_vertices[key] = val_node.contravariant_vertex(genv, changes, subst)
771
801
  end
772
802
 
773
803
  # Create base Hash type for Record
@@ -784,22 +814,25 @@ module TypeProf::Core
784
814
 
785
815
  def typecheck(genv, changes, vtx, subst)
786
816
  changes.add_edge(genv, vtx, changes.target)
817
+ found_any = false
787
818
  vtx.each_type do |ty|
819
+ next if ty.is_a?(Type::Bot)
820
+ found_any = true
788
821
  case ty
789
822
  when Type::Hash
790
- @fields.each do |key, field_node|
823
+ @keys.zip(@vals) do |key, val_node|
791
824
  val_vtx = ty.get_value(key)
792
- return false unless field_node.typecheck(genv, changes, val_vtx, subst)
825
+ return false unless val_node.typecheck(genv, changes, val_vtx, subst)
793
826
  end
794
827
  return true
795
828
  end
796
829
  end
797
- false
830
+ !found_any
798
831
  end
799
832
 
800
833
  def show
801
- field_strs = @fields.map do |key, field_node|
802
- "#{ key }: #{ field_node.show }"
834
+ field_strs = @keys.zip(@vals).map do |key, val_node|
835
+ "#{ key }: #{ val_node.show }"
803
836
  end
804
837
  "{ #{ field_strs.join(", ") } }"
805
838
  end
@@ -855,6 +888,16 @@ module TypeProf::Core
855
888
  end
856
889
 
857
890
  def typecheck(genv, changes, vtx, subst)
891
+ # For optional type T?, check if all non-nil types match T.
892
+ # nil is always acceptable.
893
+ changes.add_edge(genv, vtx, changes.target)
894
+ has_non_nil = false
895
+ vtx.each_type do |ty|
896
+ next if ty.is_a?(Type::Bot)
897
+ next if ty == genv.nil_type
898
+ has_non_nil = true
899
+ end
900
+ return true unless has_non_nil
858
901
  @type.typecheck(genv, changes, vtx, subst)
859
902
  end
860
903
 
@@ -900,13 +943,16 @@ module TypeProf::Core
900
943
  def typecheck(genv, changes, vtx, subst)
901
944
  if @lit.is_a?(::Symbol)
902
945
  changes.add_edge(genv, vtx, changes.target)
946
+ found_any = false
903
947
  vtx.each_type do |ty|
948
+ next if ty.is_a?(Type::Bot)
949
+ found_any = true
904
950
  case ty
905
951
  when Type::Symbol
906
952
  return true if ty.sym == @lit
907
953
  end
908
954
  end
909
- return false
955
+ return !found_any
910
956
  end
911
957
  f_mod = get_type(genv).mod
912
958
  AST.typecheck_for_module(genv, changes, f_mod, [], vtx, subst)
@@ -5,7 +5,7 @@ module TypeProf::Core
5
5
  when :string_node
6
6
  AST.create_node(raw_part, lenv)
7
7
  when :embedded_statements_node
8
- raw_part.statements ? AST.create_node(raw_part.statements, lenv) : DummyNilNode.new(TypeProf::CodeRange.from_node(raw_part), lenv)
8
+ raw_part.statements ? AST.create_node(raw_part.statements, lenv) : DummyNilNode.new(lenv.code_range_from_node(raw_part), lenv)
9
9
  when :embedded_variable_node
10
10
  AST.create_node(raw_part.variable, lenv)
11
11
  else
@@ -275,7 +275,7 @@ module TypeProf::Core
275
275
  if raw_elem.value
276
276
  @vals << AST.create_node(raw_elem.value, lenv)
277
277
  else
278
- @vals << DummyNilNode.new(code_range, lenv)
278
+ @vals << nil
279
279
  end
280
280
  @splat = true
281
281
  else
@@ -293,15 +293,20 @@ module TypeProf::Core
293
293
  unified_key = Vertex.new(self)
294
294
  unified_val = Vertex.new(self)
295
295
  literal_pairs = {}
296
+ all_symbol_keys = true
296
297
  @keys.zip(@vals) do |key, val|
297
298
  if key
298
299
  k = key.install(genv).new_vertex(genv, self)
299
300
  v = val.install(genv).new_vertex(genv, self)
300
301
  @changes.add_edge(genv, k, unified_key)
301
302
  @changes.add_edge(genv, v, unified_val)
302
- literal_pairs[key.lit] = v if key.is_a?(SymbolNode)
303
+ if key.is_a?(SymbolNode)
304
+ literal_pairs[key.lit] = v
305
+ else
306
+ all_symbol_keys = false
307
+ end
303
308
  else
304
- if val.is_a?(DummyNilNode)
309
+ if val.nil?
305
310
  h = @lenv.get_var(:"**anonymous_keyword")
306
311
  else
307
312
  h = val.install(genv)
@@ -310,10 +315,13 @@ module TypeProf::Core
310
315
  @changes.add_hash_splat_box(genv, h, unified_key, unified_val)
311
316
  end
312
317
  end
318
+ base_hash_type = genv.gen_hash_type(unified_key, unified_val)
313
319
  if @splat
314
- Source.new(genv.gen_hash_type(unified_key, unified_val))
320
+ Source.new(base_hash_type)
321
+ elsif all_symbol_keys
322
+ Source.new(Type::Record.new(genv, literal_pairs, base_hash_type))
315
323
  else
316
- Source.new(Type::Hash.new(genv, literal_pairs, genv.gen_hash_type(unified_key, unified_val)))
324
+ Source.new(Type::Hash.new(genv, literal_pairs, base_hash_type))
317
325
  end
318
326
  end
319
327
  end
@@ -46,7 +46,7 @@ module TypeProf::Core
46
46
  def initialize(raw_node, rhs, lenv)
47
47
  super(raw_node, lenv)
48
48
  @var = raw_node.name
49
- @var_code_range = TypeProf::CodeRange.from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
49
+ @var_code_range = lenv.code_range_from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
50
50
  @rhs = rhs
51
51
  end
52
52
 
@@ -68,6 +68,13 @@ module TypeProf::Core
68
68
  super(pos, &blk)
69
69
  end
70
70
 
71
+ def narrowings
72
+ @narrowings ||= [
73
+ Narrowing.new({ @var => Narrowing::NilConstraint.new(false) }),
74
+ Narrowing.new({ @var => Narrowing::NilConstraint.new(true) }),
75
+ ]
76
+ end
77
+
71
78
  def modified_vars(tbl, vars)
72
79
  vars << self.var if tbl.include?(self.var)
73
80
  end
@@ -109,7 +116,7 @@ module TypeProf::Core
109
116
  def initialize(raw_node, rhs, lenv)
110
117
  super(raw_node, lenv)
111
118
  @var = raw_node.name
112
- @var_code_range = TypeProf::CodeRange.from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
119
+ @var_code_range = lenv.code_range_from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
113
120
  @rhs = rhs
114
121
  end
115
122
 
@@ -191,7 +198,7 @@ module TypeProf::Core
191
198
  def initialize(raw_node, rhs, lenv)
192
199
  super(raw_node, lenv)
193
200
  @var = raw_node.name
194
- @var_code_range = TypeProf::CodeRange.from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
201
+ @var_code_range = lenv.code_range_from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
195
202
  @rhs = rhs
196
203
  end
197
204
 
@@ -264,7 +271,7 @@ module TypeProf::Core
264
271
  def initialize(raw_node, rhs, lenv)
265
272
  super(raw_node, lenv)
266
273
  @var = raw_node.name
267
- @var_code_range = TypeProf::CodeRange.from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
274
+ @var_code_range = lenv.code_range_from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
268
275
  @rhs = rhs
269
276
  end
270
277