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
@@ -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,13 +3,18 @@ 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
12
 
13
+ # `class Foo = Bar` / `module Foo = Bar` declarations attached to this entity.
14
+ # Maps an alias decl to the target ModuleEntity at the time of registration.
15
+ @alias_decls = {}
16
+ @alias_target = nil
17
+
13
18
  @inner_modules = {}
14
19
  @outer_module = outer_module
15
20
 
@@ -25,7 +30,7 @@ module TypeProf::Core
25
30
 
26
31
  # class Foo[X, Y, Z] < Bar[A, B, C]
27
32
  @superclass_type_args = nil # A, B, C
28
- @type_params = [] # X, Y, Z
33
+ @type_params = {} # X, Y, Z
29
34
 
30
35
  @consts = {}
31
36
  @methods = { true => {}, false => {} }
@@ -34,14 +39,16 @@ module TypeProf::Core
34
39
  @type_aliases = {}
35
40
 
36
41
  @static_reads = {}
37
- @subclass_checks = Set[]
38
- @ivar_reads = Set[] # should be handled in @ivars ??
39
- @cvar_reads = Set[]
42
+ @subclass_checks = Set.empty
43
+ @ivar_reads = Set.empty # should be handled in @ivars ??
44
+ @cvar_reads = Set.empty
40
45
  end
41
46
 
42
47
  attr_reader :cpath
43
48
  attr_reader :module_decls
44
49
  attr_reader :module_defs
50
+ attr_reader :alias_decls
51
+ attr_reader :alias_target
45
52
 
46
53
  attr_reader :inner_modules
47
54
  attr_reader :outer_module
@@ -79,7 +86,7 @@ module TypeProf::Core
79
86
  end
80
87
 
81
88
  def exist?
82
- !@module_decls.empty? || !@module_defs.empty?
89
+ !@module_decls.empty? || !@module_defs.empty? || !@alias_decls.empty?
83
90
  end
84
91
 
85
92
  def on_inner_modules_changed(genv, changed_cname)
@@ -115,9 +122,10 @@ module TypeProf::Core
115
122
  @module_decls << decl
116
123
 
117
124
  if @type_params
118
- update_type_params if @type_params != decl.params
125
+ update_type_params
119
126
  else
120
- @type_params = decl.params
127
+ @type_params = {}
128
+ decl.params.zip(decl.params_default_types) {|name, default_type| @type_params[name] = default_type }
121
129
  end
122
130
 
123
131
  if decl.is_a?(AST::SigClassNode) && !@superclass_type_args
@@ -133,7 +141,7 @@ module TypeProf::Core
133
141
  @outer_module.get_const(get_cname).remove_decl(decl)
134
142
  @module_decls.delete(decl) || raise
135
143
 
136
- update_type_params if @type_params == decl.params
144
+ update_type_params
137
145
  if decl.is_a?(AST::SigClassNode) && @superclass_type_args == decl.superclass_args
138
146
  @superclass_type_args = nil
139
147
  @module_decls.each do |decl|
@@ -152,13 +160,12 @@ module TypeProf::Core
152
160
  @module_decls.each do |decl|
153
161
  params = decl.params
154
162
  next unless params
155
- if @type_params
156
- @type_params = params if (@type_params <=> params) > 0
157
- else
158
- @type_params = params
163
+ if !@type_params || @type_params.size < params.size
164
+ @type_params = {}
165
+ params.zip(decl.params_default_types) {|name, default_type| @type_params[name] = default_type }
159
166
  end
160
167
  end
161
- @type_params ||= []
168
+ @type_params ||= {}
162
169
  # TODO: report an error if there are multiple inconsistent declarations
163
170
  end
164
171
 
@@ -176,6 +183,22 @@ module TypeProf::Core
176
183
  on_module_removed(genv)
177
184
  end
178
185
 
186
+ def add_alias_decl(genv, decl, target_mod)
187
+ on_module_added(genv)
188
+ @alias_decls[decl] = target_mod
189
+ @alias_target = @alias_decls.values.first
190
+ ce = @outer_module.get_const(get_cname)
191
+ ce.add_decl(decl)
192
+ ce
193
+ end
194
+
195
+ def remove_alias_decl(genv, decl)
196
+ @outer_module.get_const(get_cname).remove_decl(decl)
197
+ @alias_decls.delete(decl) || raise
198
+ @alias_target = @alias_decls.values.first
199
+ on_module_removed(genv)
200
+ end
201
+
179
202
  def add_include_decl(genv, node)
180
203
  @include_decls << node
181
204
  genv.add_static_eval_queue(:parent_modules_changed, self)
@@ -237,7 +260,7 @@ module TypeProf::Core
237
260
  old_parent.child_modules.delete(self) if set.empty?
238
261
  end
239
262
  if new_parent
240
- set = new_parent.child_modules[self] ||= Set[]
263
+ set = new_parent.child_modules[self] ||= Set.empty
241
264
  set << origin
242
265
  end
243
266
  return [new_parent, true]
@@ -261,6 +284,8 @@ module TypeProf::Core
261
284
  next
262
285
  when AST::ModuleNode
263
286
  return nil
287
+ when AST::StructNewNode
288
+ return [] # inherits from Object (Struct < Object)
264
289
  else
265
290
  raise
266
291
  end
@@ -436,6 +461,19 @@ module TypeProf::Core
436
461
  @ivars[singleton][name] ||= ValueEntity.new
437
462
  end
438
463
 
464
+ def add_ivar_decl(genv, singleton, name, decl)
465
+ ive = get_ivar(singleton, name)
466
+ ive.add_decl(decl)
467
+ ive.on_decl_changed(genv)
468
+ ive
469
+ end
470
+
471
+ def remove_ivar_decl(genv, singleton, name, decl)
472
+ ive = get_ivar(singleton, name)
473
+ ive.remove_decl(decl)
474
+ ive.on_decl_changed(genv)
475
+ end
476
+
439
477
  def get_cvar(name)
440
478
  @cvars[name] ||= ValueEntity.new
441
479
  end
@@ -2,8 +2,8 @@ module TypeProf::Core
2
2
  class StaticRead
3
3
  def initialize(name)
4
4
  @name = name
5
- @followers = Set[]
6
- @source_modules = Set[]
5
+ @followers = Set.empty
6
+ @source_modules = Set.empty
7
7
  end
8
8
 
9
9
  attr_reader :name, :followers
@@ -37,11 +37,11 @@ module TypeProf::Core
37
37
  scope = cref.cpath
38
38
  mod = genv.resolve_cpath(scope)
39
39
  genv.each_superclass(mod, false) do |mod, _singleton|
40
- break if mod == genv.mod_object && break_object
40
+ break if mod == genv.mod_object && (break_object || cref.outer)
41
41
 
42
42
  unless @source_modules.include?(mod)
43
43
  @source_modules << mod
44
- (mod.static_reads[@name] ||= Set[]) << self
44
+ (mod.static_reads[@name] ||= Set.empty) << self
45
45
  end
46
46
 
47
47
  return if check_module(genv, mod)
@@ -1,7 +1,7 @@
1
1
  module TypeProf::Core
2
2
  class TypeAliasEntity
3
3
  def initialize
4
- @decls = Set[]
4
+ @decls = Set.empty
5
5
  @type = nil
6
6
  end
7
7
 
@@ -1,9 +1,9 @@
1
1
  module TypeProf::Core
2
2
  class ValueEntity
3
3
  def initialize
4
- @decls = Set[]
5
- @defs = Set[]
6
- @read_boxes = Set[]
4
+ @decls = Set.empty
5
+ @defs = Set.empty
6
+ @read_boxes = Set.empty
7
7
  @vtx = Vertex.new(self)
8
8
  end
9
9
 
@@ -17,6 +17,14 @@ module TypeProf::Core
17
17
  @decls.delete(decl) || raise
18
18
  end
19
19
 
20
+ # Re-run all read boxes that depend on this entity. Used when a
21
+ # declaration is added or removed so that dependents (e.g. an
22
+ # IVarReadBox that previously fell back to the inferred type) can
23
+ # observe the new state.
24
+ def on_decl_changed(genv)
25
+ @read_boxes.each {|box| genv.add_run(box) }
26
+ end
27
+
20
28
  def add_def(def_)
21
29
  @defs << def_
22
30
  end
@@ -28,5 +36,19 @@ module TypeProf::Core
28
36
  def exist?
29
37
  !@decls.empty? || !@defs.empty?
30
38
  end
39
+
40
+ def on_const_added(genv, cpath)
41
+ unless exist?
42
+ parent_mod = genv.resolve_cpath(cpath[0..-2])
43
+ genv.add_static_eval_queue(:inner_modules_changed, [parent_mod, cpath[-1]])
44
+ end
45
+ end
46
+
47
+ def on_const_removed(genv, cpath)
48
+ unless exist?
49
+ parent_mod = genv.resolve_cpath(cpath[0..-2])
50
+ genv.add_static_eval_queue(:inner_modules_changed, [parent_mod, cpath[-1]])
51
+ end
52
+ end
31
53
  end
32
54
  end
@@ -6,9 +6,9 @@ module TypeProf::Core
6
6
  @static_eval_queue = []
7
7
 
8
8
  @run_queue = []
9
- @run_queue_set = Set[]
9
+ @run_queue_set = Set.empty
10
10
 
11
- @pending_diagnostic_paths = Set[]
11
+ @pending_diagnostic_paths = Set.empty
12
12
 
13
13
  @mod_object = ModuleEntity.new([])
14
14
  @mod_object.inner_modules[:Object] = @mod_object
@@ -36,9 +36,12 @@ module TypeProf::Core
36
36
  @complex_type = Type::Instance.new(self, resolve_cpath([:Complex]), [])
37
37
  @proc_type = Type::Instance.new(self, resolve_cpath([:Proc]), [])
38
38
  @symbol_type = Type::Instance.new(self, resolve_cpath([:Symbol]), [])
39
+ @method_type = Type::Instance.new(self, resolve_cpath([:Method]), [])
39
40
  @set_type = Type::Instance.new(self, resolve_cpath([:Set]), [])
40
41
  @regexp_type = Type::Instance.new(self, resolve_cpath([:Regexp]), [])
41
42
 
43
+ @bot_type = Type::Bot.new(self)
44
+
42
45
  @run_count = 0
43
46
  end
44
47
 
@@ -48,7 +51,8 @@ module TypeProf::Core
48
51
  attr_reader :cls_type, :mod_type
49
52
  attr_reader :obj_type, :nil_type, :true_type, :false_type, :str_type
50
53
  attr_reader :int_type, :float_type, :rational_type, :complex_type
51
- attr_reader :proc_type, :symbol_type, :set_type, :regexp_type
54
+ attr_reader :proc_type, :symbol_type, :method_type, :set_type, :regexp_type
55
+ attr_reader :bot_type
52
56
 
53
57
  def gen_ary_type(elem_vtx)
54
58
  Type::Instance.new(self, @mod_ary, [elem_vtx])
@@ -113,16 +117,20 @@ module TypeProf::Core
113
117
  def get_instance_type(mod, type_args, changes, base_ty_env, base_ty)
114
118
  ty_env = base_ty_env.dup
115
119
  if base_ty.is_a?(Type::Instance)
116
- base_ty.mod.type_params.zip(base_ty.args) do |param, arg|
117
- ty_env[param] = arg || Source.new
120
+ base_ty.mod.type_params.zip(base_ty.args) do |(param, default_ty), arg|
121
+ ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : Source.new)
118
122
  end
119
123
  elsif base_ty.is_a?(Type::Singleton)
120
- base_ty.mod.type_params&.each do |param|
121
- ty_env[param] = Source.new
124
+ base_ty.mod.type_params&.each do |(param, default_ty)|
125
+ ty_env[param] = default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : Source.new
122
126
  end
123
127
  end
124
- args = mod.type_params.zip(type_args).map do |param, arg|
125
- arg && changes ? arg.covariant_vertex(self, changes, ty_env) : Source.new
128
+ args = mod.type_params.zip(type_args).map do |(param, default_ty), arg|
129
+ if changes
130
+ (arg || default_ty)&.covariant_vertex(self, changes, ty_env) || Source.new
131
+ else
132
+ Source.new
133
+ end
126
134
  end
127
135
  Type::Instance.new(self, mod, args)
128
136
  end
@@ -213,6 +221,18 @@ module TypeProf::Core
213
221
  raise unless cpath # annotation
214
222
  cpath.each do |cname|
215
223
  mod = mod.inner_modules[cname] ||= ModuleEntity.new(mod.cpath + [cname], mod)
224
+ mod = follow_alias(mod)
225
+ end
226
+ mod
227
+ end
228
+
229
+ def follow_alias(mod)
230
+ visited = nil
231
+ while mod.alias_target
232
+ visited ||= Set.empty
233
+ break if visited.include?(mod) # cycle
234
+ visited << mod
235
+ mod = mod.alias_target
216
236
  end
217
237
  mod
218
238
  end
@@ -251,13 +271,14 @@ module TypeProf::Core
251
271
  mod.get_type_alias(name)
252
272
  end
253
273
 
254
- def load_core_rbs(raw_decls)
255
- lenv = LocalEnv.new(nil, CRef::Toplevel, {}, [])
274
+ def load_core_rbs(raw_decls, position_encoding)
275
+ file_context = FileContext.new(nil, position_encoding)
276
+ lenv = LocalEnv.new(file_context, CRef::Toplevel, {}, [])
256
277
  decls = raw_decls.map do |raw_decl|
257
278
  AST.create_rbs_decl(raw_decl, lenv)
258
279
  end.compact
259
280
 
260
- decls += AST.parse_rbs("typeprof-rbs-shim.rbs", <<-RBS)
281
+ decls += AST.parse_rbs("typeprof-rbs-shim.rbs", <<-RBS, position_encoding)
261
282
  class Exception
262
283
  include _Exception
263
284
  end
@@ -272,6 +293,9 @@ module TypeProf::Core
272
293
  class Hash[K, V]
273
294
  include _Each[[K, V]]
274
295
  end
296
+ class Object
297
+ include Hash::_Key
298
+ end
275
299
  RBS
276
300
 
277
301
  # Loading frequently used modules first will reduces constant resolution
@@ -295,9 +319,41 @@ module TypeProf::Core
295
319
  end
296
320
  end
297
321
 
298
- class LocalEnv
299
- def initialize(path, cref, locals, return_boxes)
322
+ class FileContext
323
+ attr_reader :path, :comments, :position_encoding
324
+ def initialize(path, position_encoding = nil, prism_source = nil, comments = nil)
300
325
  @path = path
326
+ @position_encoding = position_encoding || Encoding::UTF_16LE
327
+ @prism_source = prism_source
328
+ @code_units_cache = nil
329
+ @comments = comments
330
+ end
331
+
332
+ # Returns [start_column, end_column] for a Prism::Location, in the session-configured
333
+ # position encoding. UTF-8 uses Prism's native byte columns directly (Prism's UTF-8
334
+ # code_units_cache reports code points, not bytes — see LSP 3.17 spec).
335
+ def column_offsets_for(prism_location)
336
+ if @position_encoding == Encoding::UTF_8
337
+ [prism_location.start_column, prism_location.end_column]
338
+ else
339
+ cache = code_units_cache
340
+ [
341
+ prism_location.cached_start_code_units_column(cache),
342
+ prism_location.cached_end_code_units_column(cache),
343
+ ]
344
+ end
345
+ end
346
+
347
+ private
348
+
349
+ def code_units_cache
350
+ @code_units_cache ||= @prism_source&.code_units_cache(@position_encoding)
351
+ end
352
+ end
353
+
354
+ class LocalEnv
355
+ def initialize(file_context, cref, locals, return_boxes, forward_args = nil)
356
+ @file_context = file_context
301
357
  @cref = cref
302
358
  @locals = locals
303
359
  @return_boxes = return_boxes
@@ -305,9 +361,16 @@ module TypeProf::Core
305
361
  @next_boxes = []
306
362
  @ivar_narrowings = {}
307
363
  @strict_const_scope = false
364
+ @forward_args = forward_args
308
365
  end
309
366
 
310
- attr_reader :path, :cref, :locals, :return_boxes, :break_vtx, :next_boxes, :strict_const_scope
367
+ attr_reader :file_context, :cref, :locals, :return_boxes, :break_vtx, :next_boxes, :strict_const_scope
368
+ attr_accessor :module_function, :forward_args
369
+
370
+ def path = @file_context&.path
371
+ def code_range_from_node(node)
372
+ TypeProf::CodeRange.from_node(node, @file_context)
373
+ end
311
374
 
312
375
  def new_var(name, node)
313
376
  @locals[name] = Vertex.new(node)
@@ -337,7 +400,6 @@ module TypeProf::Core
337
400
  @break_vtx ||= Vertex.new(:break_vtx)
338
401
  end
339
402
 
340
-
341
403
  def push_ivar_narrowing(name, narrowing)
342
404
  raise unless narrowing.is_a?(Narrowing::Constraint)
343
405
  (@ivar_narrowings[name] ||= []) << narrowing
@@ -380,7 +442,7 @@ module TypeProf::Core
380
442
  case @scope_level
381
443
  when :instance
382
444
  mod = genv.resolve_cpath(@cpath || [])
383
- type_params = mod.type_params.map {|ty_param| Source.new() } # TODO: better support
445
+ type_params = mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
384
446
  ty = Type::Instance.new(genv, mod, type_params)
385
447
  Source.new(ty)
386
448
  when :class