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
@@ -3,30 +3,33 @@ module TypeProf::Core
3
3
  def initialize(node, target)
4
4
  @node = node
5
5
  @target = target
6
- @new_vertexes = {}
7
- @covariant_types = {}
8
- @contravariant_types = {}
9
- @edges = []
10
- @new_edges = []
11
- @boxes = {}
12
- @new_boxes = {}
13
6
  @diagnostics = []
14
7
  @new_diagnostics = []
15
- @depended_value_entities = []
16
- @new_depended_value_entities = []
17
- @depended_method_entities = []
18
- @new_depended_method_entities = []
19
- @depended_static_reads = []
20
- @new_depended_static_reads = []
21
- @depended_superclasses = []
22
- @new_depended_superclasses = []
8
+ if target
9
+ @depended_value_entities = []
10
+ @new_depended_value_entities = []
11
+ @depended_method_entities = []
12
+ @new_depended_method_entities = []
13
+ @depended_static_reads = []
14
+ @new_depended_static_reads = []
15
+ @depended_superclasses = []
16
+ @new_depended_superclasses = []
17
+ end
23
18
  end
24
19
 
25
- attr_reader :node, :target, :covariant_types, :contravariant_types, :edges, :boxes, :diagnostics
20
+ attr_reader :node, :target, :diagnostics
21
+
22
+ def covariant_types = @covariant_types ||= {}
23
+ def contravariant_types = @contravariant_types ||= {}
24
+ def edges = @edges ||= {}
25
+ def boxes = @boxes ||= {}
26
+
27
+ private def new_edges = @new_edges ||= {}
28
+ private def new_boxes = @new_boxes ||= {}
26
29
 
27
30
  def reuse(new_node)
28
31
  @node = new_node
29
- @boxes.each_value do |box|
32
+ boxes.each_value do |box|
30
33
  box.reuse(new_node)
31
34
  end
32
35
  end
@@ -46,6 +49,7 @@ module TypeProf::Core
46
49
  end
47
50
 
48
51
  def new_vertex(genv, origin, base_vtx)
52
+ @new_vertexes ||= {}
49
53
  new_vtx = @new_vertexes[base_vtx] ||= Vertex.new(origin)
50
54
  add_edge(genv, base_vtx, new_vtx)
51
55
  new_vtx
@@ -53,88 +57,88 @@ module TypeProf::Core
53
57
 
54
58
  def new_covariant_vertex(genv, sig_type_node)
55
59
  # This is used to avoid duplicated vertex generation for the same sig node
56
- @covariant_types[sig_type_node] ||= Vertex.new(sig_type_node)
60
+ covariant_types[sig_type_node] ||= Vertex.new(sig_type_node)
57
61
  end
58
62
 
59
63
  def new_contravariant_vertex(genv, sig_type_node)
60
64
  # This is used to avoid duplicated vertex generation for the same sig node
61
- @contravariant_types[sig_type_node] ||= Vertex.new(sig_type_node)
65
+ contravariant_types[sig_type_node] ||= Vertex.new(sig_type_node)
62
66
  end
63
67
 
64
68
  def add_edge(genv, src, dst)
65
- @new_edges << [src, dst]
69
+ (new_edges[src] ||= {})[dst] = true
66
70
  end
67
71
 
68
72
  # TODO: if an edge is removed during one analysis, we may need to remove sub-boxes?
69
73
 
70
- def add_method_call_box(genv, recv, mid, a_args, subclasses)
71
- key = [:mcall, recv, mid, a_args, subclasses]
72
- @new_boxes[key] ||= MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses)
74
+ def add_method_call_box(genv, recv, mid, a_args, subclasses, suppress_errors: false)
75
+ key = [:mcall, recv, mid, a_args, subclasses, suppress_errors]
76
+ new_boxes[key] ||= MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses, suppress_errors: suppress_errors)
73
77
  end
74
78
 
75
79
  def add_escape_box(genv, a_ret)
76
80
  key = [:return, a_ret]
77
- @new_boxes[key] ||= EscapeBox.new(@node, genv, a_ret)
81
+ new_boxes[key] ||= EscapeBox.new(@node, genv, a_ret)
78
82
  end
79
83
 
80
- def add_splat_box(genv, arg, idx = nil)
81
- key = [:splat, arg, idx]
82
- @new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx)
84
+ def add_splat_box(genv, arg, idx = nil, orig = nil)
85
+ key = [:splat, arg, idx, orig]
86
+ new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx, orig)
83
87
  end
84
88
 
85
89
  def add_hash_splat_box(genv, arg, unified_key, unified_val)
86
90
  key = [:hash_splat, arg, unified_key, unified_val]
87
- @new_boxes[key] ||= HashSplatBox.new(@node, genv, arg, unified_key, unified_val)
91
+ new_boxes[key] ||= HashSplatBox.new(@node, genv, arg, unified_key, unified_val)
88
92
  end
89
93
 
90
94
  def add_masgn_box(genv, value, lefts, rest_elem, rights)
91
95
  key = [:masgn, value, lefts, rest_elem, rights]
92
- @new_boxes[key] ||= MAsgnBox.new(@node, genv, value, lefts, rest_elem, rights)
96
+ new_boxes[key] ||= MAsgnBox.new(@node, genv, value, lefts, rest_elem, rights)
93
97
  end
94
98
 
95
99
  def add_method_def_box(genv, cpath, singleton, mid, f_args, ret_boxes)
96
100
  key = [:mdef, cpath, singleton, mid, f_args, ret_boxes]
97
- @new_boxes[key] ||= MethodDefBox.new(@node, genv, cpath, singleton, mid, f_args, ret_boxes)
101
+ new_boxes[key] ||= MethodDefBox.new(@node, genv, cpath, singleton, mid, f_args, ret_boxes)
98
102
  end
99
103
 
100
104
  def add_method_decl_box(genv, cpath, singleton, mid, method_types, overloading)
101
105
  key = [:mdecl, cpath, singleton, mid, method_types, overloading]
102
- @new_boxes[key] ||= MethodDeclBox.new(@node, genv, cpath, singleton, mid, method_types, overloading)
106
+ new_boxes[key] ||= MethodDeclBox.new(@node, genv, cpath, singleton, mid, method_types, overloading)
103
107
  end
104
108
 
105
109
  def add_method_alias_box(genv, cpath, singleton, new_mid, old_mid)
106
110
  key = [:mdecl, cpath, singleton, new_mid, old_mid]
107
- @new_boxes[key] ||= MethodAliasBox.new(@node, genv, cpath, singleton, new_mid, old_mid)
111
+ new_boxes[key] ||= MethodAliasBox.new(@node, genv, cpath, singleton, new_mid, old_mid)
108
112
  end
109
113
 
110
114
  def add_const_read_box(genv, static_ret)
111
115
  key = [:cread, static_ret]
112
- @new_boxes[key] ||= ConstReadBox.new(@node, genv, static_ret)
116
+ new_boxes[key] ||= ConstReadBox.new(@node, genv, static_ret)
113
117
  end
114
118
 
115
119
  def add_gvar_read_box(genv, var)
116
120
  key = [:gvar_read, var]
117
- @new_boxes[key] ||= GVarReadBox.new(@node, genv, var)
121
+ new_boxes[key] ||= GVarReadBox.new(@node, genv, var)
118
122
  end
119
123
 
120
124
  def add_ivar_read_box(genv, cpath, singleton, name)
121
125
  key = [:ivar_read, cpath, singleton, name]
122
- @new_boxes[key] ||= IVarReadBox.new(@node, genv, cpath, singleton, name)
126
+ new_boxes[key] ||= IVarReadBox.new(@node, genv, cpath, singleton, name)
123
127
  end
124
128
 
125
129
  def add_cvar_read_box(genv, cpath, name)
126
130
  key = [:cvar_read, cpath, name]
127
- @new_boxes[key] ||= CVarReadBox.new(@node, genv, cpath, name)
131
+ new_boxes[key] ||= CVarReadBox.new(@node, genv, cpath, name)
128
132
  end
129
133
 
130
134
  def add_type_read_box(genv, type)
131
135
  key = [:type_read, type]
132
- @new_boxes[key] ||= TypeReadBox.new(@node, genv, type)
136
+ new_boxes[key] ||= TypeReadBox.new(@node, genv, type)
133
137
  end
134
138
 
135
139
  def add_instance_type_box(genv, singleton_ty_vtx)
136
140
  key = [:instance_type, singleton_ty_vtx]
137
- @new_boxes[key] ||= InstanceTypeBox.new(@node, genv, singleton_ty_vtx)
141
+ new_boxes[key] ||= InstanceTypeBox.new(@node, genv, singleton_ty_vtx)
138
142
  end
139
143
 
140
144
  def add_diagnostic(meth, msg, node = @node)
@@ -158,21 +162,28 @@ module TypeProf::Core
158
162
  end
159
163
 
160
164
  def reinstall(genv)
161
- @new_edges.uniq!
162
- @new_edges.each do |src, dst|
163
- src.add_edge(genv, dst) unless @edges.include?([src, dst])
165
+ # Edges stored as nested hashes: {src => {dst => true}}
166
+ new_edges.each do |src, new_dsts|
167
+ old_dsts = edges[src]
168
+ new_dsts.each_key do |dst|
169
+ src.add_edge(genv, dst) unless old_dsts&.key?(dst)
170
+ end
164
171
  end
165
- @edges.each do |src, dst|
166
- src.remove_edge(genv, dst) unless @new_edges.include?([src, dst])
172
+ edges.each do |src, old_dsts|
173
+ new_dsts = new_edges[src]
174
+ old_dsts.each_key do |dst|
175
+ src.remove_edge(genv, dst) unless new_dsts&.key?(dst)
176
+ end
167
177
  end
168
178
  @edges, @new_edges = @new_edges, @edges
169
- @new_edges.clear
179
+ new_edges.each_value(&:clear)
180
+ new_edges.clear
170
181
 
171
- @boxes.each do |key, box|
182
+ boxes.each do |key, box|
172
183
  box.destroy(genv)
173
184
  end
174
185
  @boxes, @new_boxes = @new_boxes, @boxes
175
- @new_boxes.clear
186
+ new_boxes.clear
176
187
 
177
188
  @diagnostics.each do |diag|
178
189
  genv.add_diagnostic_path(diag.node.lenv.path)
@@ -185,6 +196,8 @@ module TypeProf::Core
185
196
  @diagnostics, @new_diagnostics = @new_diagnostics, @diagnostics
186
197
  @new_diagnostics.clear
187
198
 
199
+ return unless @target
200
+
188
201
  @depended_value_entities.each do |ve|
189
202
  ve.read_boxes.delete(@target) || raise
190
203
  end
@@ -39,7 +39,7 @@ module TypeProf::Core
39
39
  class IsAFilter < Filter
40
40
  def initialize(genv, node, prev_vtx, neg, const_read)
41
41
  @node = node
42
- @types = Set[]
42
+ @types = Set.empty
43
43
  @const_read = const_read
44
44
  @const_read.followers << self
45
45
  @next_vtx = Vertex.new(node)
@@ -112,14 +112,17 @@ module TypeProf::Core
112
112
 
113
113
  def on_type_added(genv, src_var, added_types)
114
114
  if src_var == @base_vtx
115
- if @base_vtx.types.size == 1 && @base_vtx.types.include?(Type::Bot.new(genv))
115
+ if @base_vtx.types.size == 1 && @base_vtx.types.include?(genv.bot_type)
116
116
  @next_vtx.on_type_removed(genv, self, @types.keys & @next_vtx.types.keys) # XXX: smoke/control/bot2.rb
117
+ else
118
+ # base_vtx is no longer bot-only; restore any previously suppressed types
119
+ @next_vtx.on_type_added(genv, self, @types.keys - @next_vtx.types.keys)
117
120
  end
118
121
  else
119
122
  added_types.each do |ty|
120
123
  @types[ty] = true
121
124
  end
122
- if @base_vtx.types.size == 1 && @base_vtx.types.include?(Type::Bot.new(genv))
125
+ if @base_vtx.types.size == 1 && @base_vtx.types.include?(genv.bot_type)
123
126
  # ignore
124
127
  else
125
128
  @next_vtx.on_type_added(genv, self, added_types - @next_vtx.types.keys) # XXX: smoke/control/bot4.rb
@@ -129,7 +132,7 @@ module TypeProf::Core
129
132
 
130
133
  def on_type_removed(genv, src_var, removed_types)
131
134
  if src_var == @base_vtx
132
- if @base_vtx.types.size == 1 && @base_vtx.types.include?(Type::Bot.new(genv))
135
+ if @base_vtx.types.size == 1 && @base_vtx.types.include?(genv.bot_type)
133
136
  # ignore
134
137
  else
135
138
  @next_vtx.on_type_added(genv, self, @types.keys - @next_vtx.types.keys) # XXX: smoke/control/bot4.rb
@@ -138,7 +141,7 @@ module TypeProf::Core
138
141
  removed_types.each do |ty|
139
142
  @types.delete(ty) || raise
140
143
  end
141
- if @base_vtx.types.size == 1 && @base_vtx.types.include?(Type::Bot.new(genv))
144
+ if @base_vtx.types.size == 1 && @base_vtx.types.include?(genv.bot_type)
142
145
  # ignore
143
146
  else
144
147
  @next_vtx.on_type_removed(genv, self, removed_types & @next_vtx.types.keys) # XXX: smoke/control/bot2.rb
@@ -2,7 +2,6 @@ module TypeProf::Core
2
2
  class BasicVertex
3
3
  def initialize(types)
4
4
  @types = types
5
- @types_to_be_added = {}
6
5
  end
7
6
 
8
7
  attr_reader :types
@@ -10,18 +9,20 @@ module TypeProf::Core
10
9
  def each_type(&blk)
11
10
  @types.each_key(&blk)
12
11
 
13
- until @types_to_be_added.empty?
14
- h = @types_to_be_added.dup
15
- h.each do |ty, source|
16
- @types[ty] = source
12
+ if @types_to_be_added
13
+ until @types_to_be_added.empty?
14
+ h = @types_to_be_added.dup
15
+ h.each do |ty, source|
16
+ @types[ty] = source
17
+ end
18
+ @types_to_be_added.clear
19
+ h.each_key(&blk)
17
20
  end
18
- @types_to_be_added.clear
19
- h.each_key(&blk)
20
21
  end
21
22
  end
22
23
 
23
24
  def show
24
- Fiber[:show_rec] ||= Set[]
25
+ Fiber[:show_rec] ||= Set.empty
25
26
  if Fiber[:show_rec].include?(self)
26
27
  "untyped"
27
28
  else
@@ -101,7 +102,7 @@ module TypeProf::Core
101
102
  end
102
103
 
103
104
  def show
104
- Fiber[:show_rec] ||= Set[]
105
+ Fiber[:show_rec] ||= Set.empty
105
106
  if Fiber[:show_rec].include?(self)
106
107
  "...(recursive)..."
107
108
  else
@@ -135,29 +136,29 @@ module TypeProf::Core
135
136
  else
136
137
  raise "unknown class: #{ origin.class }"
137
138
  end
138
- @next_vtxs = Set[]
139
+ @next_vtxs = Set.empty
139
140
  super({})
140
141
  end
141
142
 
142
143
  attr_reader :next_vtxs, :types
143
144
 
144
145
  def on_type_added(genv, src_var, added_types)
145
- new_added_types = []
146
+ new_added_types = nil
146
147
  added_types.each do |ty|
147
148
  if @types[ty]
148
149
  @types[ty] << src_var
149
150
  else
150
- set = Set[]
151
+ set = Set.empty
151
152
  begin
152
153
  @types[ty] = set
153
154
  rescue
154
- @types_to_be_added[ty] = set
155
+ (@types_to_be_added ||= {})[ty] = set
155
156
  end
156
157
  set << src_var
157
- new_added_types << ty
158
+ (new_added_types ||= []) << ty
158
159
  end
159
160
  end
160
- unless new_added_types.empty?
161
+ if new_added_types
161
162
  @next_vtxs.each do |nvtx|
162
163
  nvtx.on_type_added(genv, self, new_added_types)
163
164
  end
@@ -165,16 +166,16 @@ module TypeProf::Core
165
166
  end
166
167
 
167
168
  def on_type_removed(genv, src_var, removed_types)
168
- new_removed_types = []
169
+ new_removed_types = nil
169
170
  removed_types.each do |ty|
170
- raise "!!! not implemented" if @types_to_be_added[ty]
171
+ raise "!!! not implemented" if @types_to_be_added&.[](ty)
171
172
  @types[ty].delete(src_var) || raise
172
173
  if @types[ty].empty?
173
174
  @types.delete(ty) || raise
174
- new_removed_types << ty
175
+ (new_removed_types ||= []) << ty
175
176
  end
176
177
  end
177
- unless new_removed_types.empty?
178
+ if new_removed_types
178
179
  @next_vtxs.each do |nvtx|
179
180
  nvtx.on_type_removed(genv, self, new_removed_types)
180
181
  end