typeprof 0.30.0 → 0.31.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -4
  3. data/doc/doc.ja.md +1 -1
  4. data/lib/typeprof/cli/cli.rb +28 -10
  5. data/lib/typeprof/cli.rb +1 -0
  6. data/lib/typeprof/code_range.rb +9 -7
  7. data/lib/typeprof/core/ast/base.rb +27 -10
  8. data/lib/typeprof/core/ast/call.rb +90 -21
  9. data/lib/typeprof/core/ast/const.rb +7 -12
  10. data/lib/typeprof/core/ast/control.rb +349 -74
  11. data/lib/typeprof/core/ast/meta.rb +5 -5
  12. data/lib/typeprof/core/ast/method.rb +19 -5
  13. data/lib/typeprof/core/ast/misc.rb +21 -2
  14. data/lib/typeprof/core/ast/module.rb +10 -7
  15. data/lib/typeprof/core/ast/pattern.rb +9 -1
  16. data/lib/typeprof/core/ast/sig_decl.rb +163 -42
  17. data/lib/typeprof/core/ast/sig_type.rb +394 -24
  18. data/lib/typeprof/core/ast/value.rb +11 -3
  19. data/lib/typeprof/core/ast/variable.rb +32 -2
  20. data/lib/typeprof/core/ast.rb +15 -4
  21. data/lib/typeprof/core/builtin.rb +15 -9
  22. data/lib/typeprof/core/env/method.rb +21 -16
  23. data/lib/typeprof/core/env/method_entity.rb +11 -2
  24. data/lib/typeprof/core/env/module_entity.rb +57 -0
  25. data/lib/typeprof/core/env/narrowing.rb +131 -0
  26. data/lib/typeprof/core/env/static_read.rb +9 -8
  27. data/lib/typeprof/core/env.rb +43 -12
  28. data/lib/typeprof/core/graph/box.rb +218 -101
  29. data/lib/typeprof/core/graph/change_set.rb +44 -37
  30. data/lib/typeprof/core/graph/vertex.rb +7 -21
  31. data/lib/typeprof/core/service.rb +61 -40
  32. data/lib/typeprof/core/type.rb +52 -123
  33. data/lib/typeprof/core.rb +1 -1
  34. data/lib/typeprof/diagnostic.rb +5 -6
  35. data/lib/typeprof/lsp/messages.rb +27 -36
  36. data/lib/typeprof/lsp/server.rb +144 -25
  37. data/lib/typeprof/lsp/text.rb +1 -0
  38. data/lib/typeprof/lsp/util.rb +0 -10
  39. data/lib/typeprof/version.rb +1 -1
  40. data/typeprof.conf.jsonc +22 -0
  41. data/typeprof.gemspec +1 -0
  42. metadata +19 -5
  43. data/lib/typeprof/core/graph.rb +0 -3
@@ -3,6 +3,7 @@ module TypeProf::Core
3
3
  def initialize(node, target)
4
4
  @node = node
5
5
  @target = target
6
+ @new_vertexes = {}
6
7
  @covariant_types = {}
7
8
  @contravariant_types = {}
8
9
  @edges = []
@@ -21,30 +22,35 @@ module TypeProf::Core
21
22
  @new_depended_superclasses = []
22
23
  end
23
24
 
24
- attr_reader :node, :covariant_types, :edges, :boxes, :diagnostics
25
+ attr_reader :node, :target, :covariant_types, :contravariant_types, :edges, :boxes, :diagnostics
25
26
 
26
27
  def reuse(new_node)
27
28
  @node = new_node
28
29
  @boxes.each_value do |box|
29
30
  box.reuse(new_node)
30
31
  end
31
- @diagnostics.each do |diag|
32
- diag.reuse(new_node)
33
- end
34
32
  end
35
33
 
36
34
  def copy_from(other)
37
35
  @covariant_types = other.covariant_types.dup
36
+ @contravariant_types = other.contravariant_types.dup
38
37
  @edges = other.edges.dup
39
38
  @boxes = other.boxes.dup
40
39
  @diagnostics = other.diagnostics.dup
41
40
 
42
41
  other.covariant_types.clear
42
+ other.contravariant_types.clear
43
43
  other.edges.clear
44
44
  other.boxes.clear
45
45
  other.diagnostics.clear
46
46
  end
47
47
 
48
+ def new_vertex(genv, origin, base_vtx)
49
+ new_vtx = @new_vertexes[base_vtx] ||= Vertex.new(origin)
50
+ add_edge(genv, base_vtx, new_vtx)
51
+ new_vtx
52
+ end
53
+
48
54
  def new_covariant_vertex(genv, sig_type_node)
49
55
  # This is used to avoid duplicated vertex generation for the same sig node
50
56
  @covariant_types[sig_type_node] ||= Vertex.new(sig_type_node)
@@ -56,8 +62,6 @@ module TypeProf::Core
56
62
  end
57
63
 
58
64
  def add_edge(genv, src, dst)
59
- raise src.class.to_s unless src.is_a?(BasicVertex)
60
- src.add_edge(genv, dst) if !@edges.include?([src, dst]) && !@new_edges.include?([src, dst])
61
65
  @new_edges << [src, dst]
62
66
  end
63
67
 
@@ -65,84 +69,76 @@ module TypeProf::Core
65
69
 
66
70
  def add_method_call_box(genv, recv, mid, a_args, subclasses)
67
71
  key = [:mcall, recv, mid, a_args, subclasses]
68
- return if @new_boxes[key]
69
- @new_boxes[key] = MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses)
72
+ @new_boxes[key] ||= MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses)
70
73
  end
71
74
 
72
- def add_escape_box(genv, a_ret, f_ret)
75
+ def add_escape_box(genv, a_ret)
73
76
  key = [:return, a_ret]
74
- return if @new_boxes[key]
75
- @new_boxes[key] = EscapeBox.new(@node, genv, a_ret, f_ret)
77
+ @new_boxes[key] ||= EscapeBox.new(@node, genv, a_ret)
76
78
  end
77
79
 
78
- def add_splat_box(genv, arg)
79
- key = [:splat, arg]
80
- return if @new_boxes[key]
81
- @new_boxes[key] = SplatBox.new(@node, genv, arg)
80
+ def add_splat_box(genv, arg, idx = nil)
81
+ key = [:splat, arg, idx]
82
+ @new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx)
82
83
  end
83
84
 
84
85
  def add_hash_splat_box(genv, arg, unified_key, unified_val)
85
86
  key = [:hash_splat, arg, unified_key, unified_val]
86
- return if @new_boxes[key]
87
- @new_boxes[key] = HashSplatBox.new(@node, genv, arg, unified_key, unified_val)
87
+ @new_boxes[key] ||= HashSplatBox.new(@node, genv, arg, unified_key, unified_val)
88
88
  end
89
89
 
90
90
  def add_masgn_box(genv, value, lefts, rest_elem, rights)
91
91
  key = [:masgn, value, lefts, rest_elem, rights]
92
- return if @new_boxes[key]
93
- @new_boxes[key] = MAsgnBox.new(@node, genv, value, lefts, rest_elem, rights)
92
+ @new_boxes[key] ||= MAsgnBox.new(@node, genv, value, lefts, rest_elem, rights)
94
93
  end
95
94
 
96
95
  def add_method_def_box(genv, cpath, singleton, mid, f_args, ret_boxes)
97
96
  key = [:mdef, cpath, singleton, mid, f_args, ret_boxes]
98
- return if @new_boxes[key]
99
- @new_boxes[key] = MethodDefBox.new(@node, genv, cpath, singleton, mid, f_args, ret_boxes)
97
+ @new_boxes[key] ||= MethodDefBox.new(@node, genv, cpath, singleton, mid, f_args, ret_boxes)
100
98
  end
101
99
 
102
100
  def add_method_decl_box(genv, cpath, singleton, mid, method_types, overloading)
103
101
  key = [:mdecl, cpath, singleton, mid, method_types, overloading]
104
- return if @new_boxes[key]
105
- @new_boxes[key] = MethodDeclBox.new(@node, genv, cpath, singleton, mid, method_types, overloading)
102
+ @new_boxes[key] ||= MethodDeclBox.new(@node, genv, cpath, singleton, mid, method_types, overloading)
106
103
  end
107
104
 
108
105
  def add_method_alias_box(genv, cpath, singleton, new_mid, old_mid)
109
106
  key = [:mdecl, cpath, singleton, new_mid, old_mid]
110
- return if @new_boxes[key]
111
- @new_boxes[key] = MethodAliasBox.new(@node, genv, cpath, singleton, new_mid, old_mid)
107
+ @new_boxes[key] ||= MethodAliasBox.new(@node, genv, cpath, singleton, new_mid, old_mid)
112
108
  end
113
109
 
114
110
  def add_const_read_box(genv, static_ret)
115
111
  key = [:cread, static_ret]
116
- return if @new_boxes[key]
117
- @new_boxes[key] = ConstReadBox.new(@node, genv, static_ret)
112
+ @new_boxes[key] ||= ConstReadBox.new(@node, genv, static_ret)
118
113
  end
119
114
 
120
115
  def add_gvar_read_box(genv, var)
121
116
  key = [:gvar_read, var]
122
- return if @new_boxes[key]
123
- @new_boxes[key] = GVarReadBox.new(@node, genv, var)
117
+ @new_boxes[key] ||= GVarReadBox.new(@node, genv, var)
124
118
  end
125
119
 
126
120
  def add_ivar_read_box(genv, cpath, singleton, name)
127
121
  key = [:ivar_read, cpath, singleton, name]
128
- return if @new_boxes[key]
129
- @new_boxes[key] = IVarReadBox.new(@node, genv, cpath, singleton, name)
122
+ @new_boxes[key] ||= IVarReadBox.new(@node, genv, cpath, singleton, name)
130
123
  end
131
124
 
132
125
  def add_cvar_read_box(genv, cpath, name)
133
126
  key = [:cvar_read, cpath, name]
134
- return if @new_boxes[key]
135
- @new_boxes[key] = CVarReadBox.new(@node, genv, cpath, name)
127
+ @new_boxes[key] ||= CVarReadBox.new(@node, genv, cpath, name)
136
128
  end
137
129
 
138
130
  def add_type_read_box(genv, type)
139
131
  key = [:type_read, type]
140
- return if @new_boxes[key]
141
- @new_boxes[key] = TypeReadBox.new(@node, genv, type)
132
+ @new_boxes[key] ||= TypeReadBox.new(@node, genv, type)
133
+ end
134
+
135
+ def add_instance_type_box(genv, singleton_ty_vtx)
136
+ key = [:instance_type, singleton_ty_vtx]
137
+ @new_boxes[key] ||= InstanceTypeBox.new(@node, genv, singleton_ty_vtx)
142
138
  end
143
139
 
144
- def add_diagnostic(meth, msg)
145
- @new_diagnostics << TypeProf::Diagnostic.new(@node, meth, msg)
140
+ def add_diagnostic(meth, msg, node = @node)
141
+ @new_diagnostics << TypeProf::Diagnostic.new(node, meth, msg)
146
142
  end
147
143
 
148
144
  def add_depended_value_entity(ve)
@@ -163,6 +159,9 @@ module TypeProf::Core
163
159
 
164
160
  def reinstall(genv)
165
161
  @new_edges.uniq!
162
+ @new_edges.each do |src, dst|
163
+ src.add_edge(genv, dst) unless @edges.include?([src, dst])
164
+ end
166
165
  @edges.each do |src, dst|
167
166
  src.remove_edge(genv, dst) unless @new_edges.include?([src, dst])
168
167
  end
@@ -175,6 +174,14 @@ module TypeProf::Core
175
174
  @boxes, @new_boxes = @new_boxes, @boxes
176
175
  @new_boxes.clear
177
176
 
177
+ @diagnostics.each do |diag|
178
+ genv.add_diagnostic_path(diag.node.lenv.path)
179
+ diag.node.remove_diagnostic(diag)
180
+ end
181
+ @new_diagnostics.each do |diag|
182
+ genv.add_diagnostic_path(diag.node.lenv.path)
183
+ diag.node.add_diagnostic(diag)
184
+ end
178
185
  @diagnostics, @new_diagnostics = @new_diagnostics, @diagnostics
179
186
  @new_diagnostics.clear
180
187
 
@@ -20,28 +20,8 @@ module TypeProf::Core
20
20
  end
21
21
  end
22
22
 
23
- def check_match(genv, changes, vtx)
24
- vtx.each_type do |ty|
25
- if ty.is_a?(Type::Var)
26
- changes.add_edge(genv, self, ty.vtx) if self != ty.vtx
27
- return true
28
- end
29
- end
30
-
31
- return true if @types.empty?
32
- return true if vtx.types.empty?
33
-
34
- each_type do |ty|
35
- return true if vtx.types.include?(ty) # fast path
36
- if ty.check_match(genv, changes, vtx)
37
- return true
38
- end
39
- end
40
-
41
- return false
42
- end
43
-
44
23
  def show
24
+ Fiber[:show_rec] ||= Set[]
45
25
  if Fiber[:show_rec].include?(self)
46
26
  "untyped"
47
27
  else
@@ -83,6 +63,10 @@ module TypeProf::Core
83
63
  end
84
64
  end
85
65
  end
66
+
67
+ def new_vertex(genv, origin)
68
+ raise NotImplementedError
69
+ end
86
70
  end
87
71
 
88
72
  class Source < BasicVertex
@@ -117,6 +101,7 @@ module TypeProf::Core
117
101
  end
118
102
 
119
103
  def show
104
+ Fiber[:show_rec] ||= Set[]
120
105
  if Fiber[:show_rec].include?(self)
121
106
  "...(recursive)..."
122
107
  else
@@ -146,6 +131,7 @@ module TypeProf::Core
146
131
  when ValueEntity
147
132
  when ActualArguments
148
133
  when Array
134
+ when Symbol
149
135
  else
150
136
  raise "unknown class: #{ origin.class }"
151
137
  end
@@ -3,7 +3,8 @@ module TypeProf::Core
3
3
  def initialize(options)
4
4
  @options = options
5
5
 
6
- @text_nodes = {}
6
+ @rb_text_nodes = {}
7
+ @rbs_text_nodes = {}
7
8
 
8
9
  @genv = GlobalEnv.new
9
10
  @genv.load_core_rbs(load_rbs_declarations(@options[:rbs_collection]).declarations)
@@ -26,43 +27,42 @@ module TypeProf::Core
26
27
  attr_reader :genv
27
28
 
28
29
  def reset!
29
- @text_nodes.each_value do |node|
30
- if node.is_a?(Array)
31
- node.each {|n| n.undefine(@genv) }
32
- else
33
- node.undefine(@genv)
34
- end
35
- end
30
+ @rb_text_nodes.each_value {|node| node.undefine(@genv) }
31
+ @rbs_text_nodes.each_value {|nodes| nodes.each {|n| n.undefine(@genv) } }
36
32
  @genv.define_all
37
- @text_nodes.each_value do |node|
38
- if node.is_a?(Array)
39
- node.each {|n| n.uninstall(@genv) }
40
- else
41
- node.uninstall(@genv)
42
- end
43
- end
33
+ @rb_text_nodes.each_value {|node| node.uninstall(@genv) }
34
+ @rbs_text_nodes.each_value {|nodes| nodes.each {|n| n.uninstall(@genv) } }
44
35
  @genv.run_all
45
- @text_nodes.clear
36
+ @rb_text_nodes.clear
37
+ @rbs_text_nodes.clear
46
38
  end
47
39
 
48
40
  def add_workspace(rb_folder, rbs_folder)
49
- Dir.glob(File.expand_path(rb_folder + "/**/*.rb")) do |path|
50
- update_rb_file(path, nil)
41
+ Dir.glob(File.expand_path(rb_folder + "/**/*.{rb,rbs}")) do |path|
42
+ update_file(path, nil)
51
43
  end
52
- Dir.glob(File.expand_path(rbs_folder + "/**/*.rbs")) do |path|
53
- update_rbs_file(path, nil)
44
+ Dir.glob(File.expand_path(rbs_folder + "/**/*.{rb,rbs}")) do |path|
45
+ update_file(path, nil)
46
+ end
47
+ end
48
+
49
+ def update_file(path, code)
50
+ if File.extname(path) == ".rbs"
51
+ update_rbs_file(path, code)
52
+ else
53
+ update_rb_file(path, code)
54
54
  end
55
55
  end
56
56
 
57
57
  def update_rb_file(path, code)
58
- prev_node = @text_nodes[path]
58
+ prev_node = @rb_text_nodes[path]
59
59
 
60
60
  code = File.read(path) unless code
61
61
  node = AST.parse_rb(path, code)
62
62
  return false unless node
63
63
 
64
- node.diff(@text_nodes[path]) if prev_node
65
- @text_nodes[path] = node
64
+ node.diff(@rb_text_nodes[path]) if prev_node
65
+ @rb_text_nodes[path] = node
66
66
 
67
67
  node.define(@genv)
68
68
  prev_node.undefine(@genv) if prev_node
@@ -115,7 +115,7 @@ module TypeProf::Core
115
115
  end
116
116
 
117
117
  def update_rbs_file(path, code)
118
- prev_decls = @text_nodes[path]
118
+ prev_decls = @rbs_text_nodes[path]
119
119
 
120
120
  code = File.read(path) unless code
121
121
  begin
@@ -125,7 +125,7 @@ module TypeProf::Core
125
125
  end
126
126
 
127
127
  # TODO: diff
128
- @text_nodes[path] = decls
128
+ @rbs_text_nodes[path] = decls
129
129
 
130
130
  decls.each {|decl| decl.define(@genv) }
131
131
  prev_decls.each {|decl| decl.undefine(@genv) } if prev_decls
@@ -138,14 +138,17 @@ module TypeProf::Core
138
138
  true
139
139
  end
140
140
 
141
+ def process_diagnostic_paths(&blk)
142
+ @genv.process_diagnostic_paths(&blk)
143
+ end
144
+
141
145
  def diagnostics(path, &blk)
142
- node = @text_nodes[path]
143
- node.diagnostics(@genv, &blk) if node
146
+ @rb_text_nodes[path]&.each_diagnostic(@genv, &blk)
144
147
  end
145
148
 
146
149
  def definitions(path, pos)
147
150
  defs = []
148
- @text_nodes[path].retrieve_at(pos) do |node|
151
+ @rb_text_nodes[path]&.retrieve_at(pos) do |node|
149
152
  node.boxes(:cread) do |box|
150
153
  if box.const_read && box.const_read.cdef
151
154
  box.const_read.cdef.defs.each do |cdef_node|
@@ -165,6 +168,18 @@ module TypeProf::Core
165
168
  boxes.each do |box|
166
169
  box.resolve(genv, nil) do |me, _ty, mid, _orig_ty|
167
170
  next unless me
171
+ me.decls.each do |mdecl|
172
+ next unless mdecl.node.lenv.path
173
+
174
+ code_range =
175
+ if mdecl.node.respond_to?(:mname_code_range)
176
+ mdecl.node.mname_code_range(mid)
177
+ else
178
+ mdecl.node.code_range
179
+ end
180
+
181
+ defs << [mdecl.node.lenv.path, code_range]
182
+ end
168
183
  me.defs.each do |mdef|
169
184
  code_range =
170
185
  if mdef.node.respond_to?(:mname_code_range)
@@ -184,7 +199,7 @@ module TypeProf::Core
184
199
  end
185
200
 
186
201
  def type_definitions(path, pos)
187
- @text_nodes[path].retrieve_at(pos) do |node|
202
+ @rb_text_nodes[path]&.retrieve_at(pos) do |node|
188
203
  if node.ret
189
204
  ty_defs = []
190
205
  node.ret.types.map do |ty, _source|
@@ -206,7 +221,7 @@ module TypeProf::Core
206
221
  #: (String, TypeProf::CodePosition) -> Array[[String?, TypeProf::CodeRange]]?
207
222
  def references(path, pos)
208
223
  refs = []
209
- @text_nodes[path].retrieve_at(pos) do |node|
224
+ @rb_text_nodes[path]&.retrieve_at(pos) do |node|
210
225
  case node
211
226
  when AST::DefNode
212
227
  if node.mid_code_range.include?(pos)
@@ -246,7 +261,7 @@ module TypeProf::Core
246
261
  def rename(path, pos)
247
262
  mdefs = []
248
263
  cdefs = []
249
- @text_nodes[path].retrieve_at(pos) do |node|
264
+ @rb_text_nodes[path]&.retrieve_at(pos) do |node|
250
265
  node.boxes(:mcall) do |box|
251
266
  box.resolve(genv, nil) do |me, _ty, _mid, _orig_ty|
252
267
  next unless me
@@ -262,6 +277,13 @@ module TypeProf::Core
262
277
  end
263
278
  end
264
279
  end
280
+ if node.is_a?(AST::ConstantWriteNode)
281
+ if node.cname_code_range.include?(pos) && node.static_cpath
282
+ genv.resolve_const(node.static_cpath).defs.each do |cdef|
283
+ cdefs << cdef
284
+ end
285
+ end
286
+ end
265
287
  if node.is_a?(AST::DefNode) && node.mid_code_range.include?(pos)
266
288
  node.boxes(:mdef) do |mdef|
267
289
  mdefs << mdef
@@ -298,7 +320,7 @@ module TypeProf::Core
298
320
  end
299
321
 
300
322
  def hover(path, pos)
301
- @text_nodes[path].retrieve_at(pos) do |node|
323
+ @rb_text_nodes[path]&.retrieve_at(pos) do |node|
302
324
  node.boxes(:mcall) do |box|
303
325
  boxes = []
304
326
  box.changes.boxes.each do |key, box|
@@ -332,7 +354,7 @@ module TypeProf::Core
332
354
 
333
355
  def code_lens(path)
334
356
  cpaths = []
335
- @text_nodes[path].traverse do |event, node|
357
+ @rb_text_nodes[path]&.traverse do |event, node|
336
358
  if node.is_a?(AST::ModuleBaseNode)
337
359
  if node.static_cpath
338
360
  if event == :enter
@@ -356,7 +378,7 @@ module TypeProf::Core
356
378
  end
357
379
 
358
380
  def completion(path, trigger, pos)
359
- @text_nodes[path].retrieve_at(pos) do |node|
381
+ @rb_text_nodes[path]&.retrieve_at(pos) do |node|
360
382
  if node.code_range.last == pos.right
361
383
  node.ret.types.map do |ty, _source|
362
384
  base_ty = ty.base_type(genv)
@@ -386,7 +408,7 @@ module TypeProf::Core
386
408
  def dump_declarations(path)
387
409
  stack = []
388
410
  out = []
389
- @text_nodes[path].traverse do |event, node|
411
+ @rb_text_nodes[path]&.traverse do |event, node|
390
412
  case node
391
413
  when AST::ModuleNode
392
414
  if node.static_cpath
@@ -481,11 +503,7 @@ module TypeProf::Core
481
503
  i += 1
482
504
  end
483
505
 
484
- if File.extname(file) == ".rbs"
485
- res = update_rbs_file(file, File.read(file))
486
- else
487
- res = update_rb_file(file, File.read(file))
488
- end
506
+ res = update_file(file, File.read(file))
489
507
 
490
508
  if res
491
509
  true
@@ -493,6 +511,9 @@ module TypeProf::Core
493
511
  output.puts "# failed to analyze: #{ file }"
494
512
  false
495
513
  end
514
+ rescue => e
515
+ output.puts "# error: #{ file }"
516
+ raise e
496
517
  end
497
518
  if @options[:display_indicator]
498
519
  $stderr << "\r\e[K"