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
@@ -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])
@@ -76,7 +80,7 @@ module TypeProf::Core
76
80
  # TODO: prepended modules
77
81
  yield mod, singleton
78
82
  if singleton
79
- # TODO: extended modules
83
+ each_extended_module(mod, &blk)
80
84
  else
81
85
  each_included_module(mod, &blk)
82
86
  end
@@ -91,6 +95,15 @@ module TypeProf::Core
91
95
  end
92
96
  end
93
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
+
94
107
  def get_superclass(singleton, mod)
95
108
  super_mod = mod.superclass
96
109
  if super_mod
@@ -102,6 +115,9 @@ module TypeProf::Core
102
115
  else
103
116
  return nil
104
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]
105
121
  elsif mod == @mod_module && !singleton
106
122
  return nil
107
123
  else
@@ -113,16 +129,20 @@ module TypeProf::Core
113
129
  def get_instance_type(mod, type_args, changes, base_ty_env, base_ty)
114
130
  ty_env = base_ty_env.dup
115
131
  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
132
+ base_ty.mod.type_params.zip(base_ty.args) do |(param, default_ty), arg|
133
+ ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : Source.new)
118
134
  end
119
135
  elsif base_ty.is_a?(Type::Singleton)
120
- base_ty.mod.type_params&.each do |param|
121
- ty_env[param] = Source.new
136
+ base_ty.mod.type_params&.each do |(param, default_ty)|
137
+ ty_env[param] = default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : Source.new
122
138
  end
123
139
  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
140
+ args = mod.type_params.zip(type_args).map do |(param, default_ty), arg|
141
+ if changes
142
+ (arg || default_ty)&.covariant_vertex(self, changes, ty_env) || Source.new
143
+ else
144
+ Source.new
145
+ end
126
146
  end
127
147
  Type::Instance.new(self, mod, args)
128
148
  end
@@ -213,6 +233,18 @@ module TypeProf::Core
213
233
  raise unless cpath # annotation
214
234
  cpath.each do |cname|
215
235
  mod = mod.inner_modules[cname] ||= ModuleEntity.new(mod.cpath + [cname], mod)
236
+ mod = follow_alias(mod)
237
+ end
238
+ mod
239
+ end
240
+
241
+ def follow_alias(mod)
242
+ visited = nil
243
+ while mod.alias_target
244
+ visited ||= Set.empty
245
+ break if visited.include?(mod) # cycle
246
+ visited << mod
247
+ mod = mod.alias_target
216
248
  end
217
249
  mod
218
250
  end
@@ -251,13 +283,30 @@ module TypeProf::Core
251
283
  mod.get_type_alias(name)
252
284
  end
253
285
 
254
- def load_core_rbs(raw_decls)
255
- lenv = LocalEnv.new(nil, CRef::Toplevel, {}, [])
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
+
297
+ def load_core_rbs(raw_decls, position_encoding)
298
+ file_context = FileContext.new(nil, position_encoding)
299
+ lenv = LocalEnv.new(file_context, CRef::Toplevel, {}, [])
256
300
  decls = raw_decls.map do |raw_decl|
257
301
  AST.create_rbs_decl(raw_decl, lenv)
258
302
  end.compact
259
303
 
260
- decls += AST.parse_rbs("typeprof-rbs-shim.rbs", <<-RBS)
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
+
309
+ decls += AST.parse_rbs("typeprof-rbs-shim.rbs", <<-RBS, position_encoding)
261
310
  class Exception
262
311
  include _Exception
263
312
  end
@@ -265,12 +314,15 @@ module TypeProf::Core
265
314
  include _ToS
266
315
  include _ToStr
267
316
  end
268
- class Array[Elem]
269
- include _ToAry[Elem]
270
- include _Each[Elem]
317
+ class Array[#{ ary_elem }]
318
+ include _ToAry[#{ ary_elem }]
319
+ include _Each[#{ ary_elem }]
271
320
  end
272
- class Hash[K, V]
273
- include _Each[[K, V]]
321
+ class Hash[#{ hash_key }, #{ hash_val }]
322
+ include _Each[[#{ hash_key }, #{ hash_val }]]
323
+ end
324
+ class Object
325
+ include Hash::_Key
274
326
  end
275
327
  RBS
276
328
 
@@ -295,9 +347,41 @@ module TypeProf::Core
295
347
  end
296
348
  end
297
349
 
298
- class LocalEnv
299
- def initialize(path, cref, locals, return_boxes)
350
+ class FileContext
351
+ attr_reader :path, :comments, :position_encoding
352
+ def initialize(path, position_encoding = nil, prism_source = nil, comments = nil)
300
353
  @path = path
354
+ @position_encoding = position_encoding || Encoding::UTF_16LE
355
+ @prism_source = prism_source
356
+ @code_units_cache = nil
357
+ @comments = comments
358
+ end
359
+
360
+ # Returns [start_column, end_column] for a Prism::Location, in the session-configured
361
+ # position encoding. UTF-8 uses Prism's native byte columns directly (Prism's UTF-8
362
+ # code_units_cache reports code points, not bytes — see LSP 3.17 spec).
363
+ def column_offsets_for(prism_location)
364
+ if @position_encoding == Encoding::UTF_8
365
+ [prism_location.start_column, prism_location.end_column]
366
+ else
367
+ cache = code_units_cache
368
+ [
369
+ prism_location.cached_start_code_units_column(cache),
370
+ prism_location.cached_end_code_units_column(cache),
371
+ ]
372
+ end
373
+ end
374
+
375
+ private
376
+
377
+ def code_units_cache
378
+ @code_units_cache ||= @prism_source&.code_units_cache(@position_encoding)
379
+ end
380
+ end
381
+
382
+ class LocalEnv
383
+ def initialize(file_context, cref, locals, return_boxes, forward_args = nil)
384
+ @file_context = file_context
301
385
  @cref = cref
302
386
  @locals = locals
303
387
  @return_boxes = return_boxes
@@ -305,9 +389,16 @@ module TypeProf::Core
305
389
  @next_boxes = []
306
390
  @ivar_narrowings = {}
307
391
  @strict_const_scope = false
392
+ @forward_args = forward_args
308
393
  end
309
394
 
310
- attr_reader :path, :cref, :locals, :return_boxes, :break_vtx, :next_boxes, :strict_const_scope
395
+ attr_reader :file_context, :cref, :locals, :return_boxes, :break_vtx, :next_boxes, :strict_const_scope
396
+ attr_accessor :module_function, :forward_args
397
+
398
+ def path = @file_context&.path
399
+ def code_range_from_node(node)
400
+ TypeProf::CodeRange.from_node(node, @file_context)
401
+ end
311
402
 
312
403
  def new_var(name, node)
313
404
  @locals[name] = Vertex.new(node)
@@ -337,7 +428,6 @@ module TypeProf::Core
337
428
  @break_vtx ||= Vertex.new(:break_vtx)
338
429
  end
339
430
 
340
-
341
431
  def push_ivar_narrowing(name, narrowing)
342
432
  raise unless narrowing.is_a?(Narrowing::Constraint)
343
433
  (@ivar_narrowings[name] ||= []) << narrowing
@@ -380,7 +470,7 @@ module TypeProf::Core
380
470
  case @scope_level
381
471
  when :instance
382
472
  mod = genv.resolve_cpath(@cpath || [])
383
- type_params = mod.type_params.map {|ty_param| Source.new() } # TODO: better support
473
+ type_params = mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
384
474
  ty = Type::Instance.new(genv, mod, type_params)
385
475
  Source.new(ty)
386
476
  when :class