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.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/doc/report_guide.md +88 -0
- data/lib/typeprof/cli/cli.rb +9 -3
- data/lib/typeprof/code_range.rb +7 -5
- data/lib/typeprof/core/ast/base.rb +31 -6
- data/lib/typeprof/core/ast/call.rb +120 -39
- data/lib/typeprof/core/ast/const.rb +22 -11
- data/lib/typeprof/core/ast/control.rb +62 -32
- data/lib/typeprof/core/ast/meta.rb +279 -14
- data/lib/typeprof/core/ast/method.rb +82 -24
- data/lib/typeprof/core/ast/misc.rb +41 -12
- data/lib/typeprof/core/ast/module.rb +38 -4
- data/lib/typeprof/core/ast/pattern.rb +51 -23
- data/lib/typeprof/core/ast/sig_decl.rb +141 -27
- data/lib/typeprof/core/ast/sig_type.rb +113 -32
- data/lib/typeprof/core/ast/value.rb +14 -6
- data/lib/typeprof/core/ast/variable.rb +17 -4
- data/lib/typeprof/core/ast.rb +98 -14
- data/lib/typeprof/core/builtin.rb +184 -12
- data/lib/typeprof/core/env/method.rb +343 -6
- data/lib/typeprof/core/env/method_entity.rb +18 -15
- data/lib/typeprof/core/env/module_entity.rb +116 -18
- data/lib/typeprof/core/env/static_read.rb +4 -4
- data/lib/typeprof/core/env/type_alias_entity.rb +1 -1
- data/lib/typeprof/core/env/value_entity.rb +25 -3
- data/lib/typeprof/core/env.rb +113 -23
- data/lib/typeprof/core/graph/box.rb +508 -63
- data/lib/typeprof/core/graph/change_set.rb +64 -46
- data/lib/typeprof/core/graph/filter.rb +8 -5
- data/lib/typeprof/core/graph/vertex.rb +20 -19
- data/lib/typeprof/core/service.rb +340 -24
- data/lib/typeprof/core/type.rb +41 -7
- data/lib/typeprof/core/util.rb +6 -0
- data/lib/typeprof/lsp/messages.rb +5 -0
- data/lib/typeprof/lsp/server.rb +35 -4
- data/lib/typeprof/version.rb +1 -1
- 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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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, :
|
|
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
|
-
|
|
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,93 @@ 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
+
def add_method_call_box(genv, recv, mid, a_args, subclasses, suppress_errors: false, unresolved_recv: nil)
|
|
75
|
+
key = [:mcall, recv, mid, a_args, subclasses, suppress_errors, unresolved_recv]
|
|
76
|
+
new_boxes[key] ||= MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses, suppress_errors: suppress_errors, unresolved_recv: unresolved_recv)
|
|
73
77
|
end
|
|
74
78
|
|
|
75
79
|
def add_escape_box(genv, a_ret)
|
|
76
80
|
key = [:return, a_ret]
|
|
77
|
-
|
|
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
|
-
|
|
84
|
+
def add_splat_box(genv, arg, idx = nil, unresolved_recv = nil)
|
|
85
|
+
key = [:splat, arg, idx, unresolved_recv]
|
|
86
|
+
new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx, unresolved_recv)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def add_keyword_merge_box(genv, rest, literal_pairs, fallback)
|
|
90
|
+
key = [:kw_merge, rest, literal_pairs, fallback]
|
|
91
|
+
new_boxes[key] ||= KeywordMergeBox.new(@node, genv, rest, literal_pairs, fallback)
|
|
83
92
|
end
|
|
84
93
|
|
|
85
94
|
def add_hash_splat_box(genv, arg, unified_key, unified_val)
|
|
86
95
|
key = [:hash_splat, arg, unified_key, unified_val]
|
|
87
|
-
|
|
96
|
+
new_boxes[key] ||= HashSplatBox.new(@node, genv, arg, unified_key, unified_val)
|
|
88
97
|
end
|
|
89
98
|
|
|
90
99
|
def add_masgn_box(genv, value, lefts, rest_elem, rights)
|
|
91
100
|
key = [:masgn, value, lefts, rest_elem, rights]
|
|
92
|
-
|
|
101
|
+
new_boxes[key] ||= MAsgnBox.new(@node, genv, value, lefts, rest_elem, rights)
|
|
93
102
|
end
|
|
94
103
|
|
|
95
104
|
def add_method_def_box(genv, cpath, singleton, mid, f_args, ret_boxes)
|
|
96
105
|
key = [:mdef, cpath, singleton, mid, f_args, ret_boxes]
|
|
97
|
-
|
|
106
|
+
new_boxes[key] ||= MethodDefBox.new(@node, genv, cpath, singleton, mid, f_args, ret_boxes)
|
|
98
107
|
end
|
|
99
108
|
|
|
100
109
|
def add_method_decl_box(genv, cpath, singleton, mid, method_types, overloading)
|
|
101
110
|
key = [:mdecl, cpath, singleton, mid, method_types, overloading]
|
|
102
|
-
|
|
111
|
+
new_boxes[key] ||= MethodDeclBox.new(@node, genv, cpath, singleton, mid, method_types, overloading)
|
|
103
112
|
end
|
|
104
113
|
|
|
105
114
|
def add_method_alias_box(genv, cpath, singleton, new_mid, old_mid)
|
|
106
115
|
key = [:mdecl, cpath, singleton, new_mid, old_mid]
|
|
107
|
-
|
|
116
|
+
new_boxes[key] ||= MethodAliasBox.new(@node, genv, cpath, singleton, new_mid, old_mid)
|
|
108
117
|
end
|
|
109
118
|
|
|
110
119
|
def add_const_read_box(genv, static_ret)
|
|
111
120
|
key = [:cread, static_ret]
|
|
112
|
-
|
|
121
|
+
new_boxes[key] ||= ConstReadBox.new(@node, genv, static_ret)
|
|
113
122
|
end
|
|
114
123
|
|
|
115
124
|
def add_gvar_read_box(genv, var)
|
|
116
125
|
key = [:gvar_read, var]
|
|
117
|
-
|
|
126
|
+
new_boxes[key] ||= GVarReadBox.new(@node, genv, var)
|
|
118
127
|
end
|
|
119
128
|
|
|
120
129
|
def add_ivar_read_box(genv, cpath, singleton, name)
|
|
121
130
|
key = [:ivar_read, cpath, singleton, name]
|
|
122
|
-
|
|
131
|
+
new_boxes[key] ||= IVarReadBox.new(@node, genv, cpath, singleton, name)
|
|
123
132
|
end
|
|
124
133
|
|
|
125
134
|
def add_cvar_read_box(genv, cpath, name)
|
|
126
135
|
key = [:cvar_read, cpath, name]
|
|
127
|
-
|
|
136
|
+
new_boxes[key] ||= CVarReadBox.new(@node, genv, cpath, name)
|
|
128
137
|
end
|
|
129
138
|
|
|
130
139
|
def add_type_read_box(genv, type)
|
|
131
140
|
key = [:type_read, type]
|
|
132
|
-
|
|
141
|
+
new_boxes[key] ||= TypeReadBox.new(@node, genv, type)
|
|
133
142
|
end
|
|
134
143
|
|
|
135
144
|
def add_instance_type_box(genv, singleton_ty_vtx)
|
|
136
145
|
key = [:instance_type, singleton_ty_vtx]
|
|
137
|
-
|
|
146
|
+
new_boxes[key] ||= InstanceTypeBox.new(@node, genv, singleton_ty_vtx)
|
|
138
147
|
end
|
|
139
148
|
|
|
140
149
|
def add_diagnostic(meth, msg, node = @node)
|
|
@@ -158,21 +167,28 @@ module TypeProf::Core
|
|
|
158
167
|
end
|
|
159
168
|
|
|
160
169
|
def reinstall(genv)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
170
|
+
# Edges stored as nested hashes: {src => {dst => true}}
|
|
171
|
+
new_edges.each do |src, new_dsts|
|
|
172
|
+
old_dsts = edges[src]
|
|
173
|
+
new_dsts.each_key do |dst|
|
|
174
|
+
src.add_edge(genv, dst) unless old_dsts&.key?(dst)
|
|
175
|
+
end
|
|
164
176
|
end
|
|
165
|
-
|
|
166
|
-
|
|
177
|
+
edges.each do |src, old_dsts|
|
|
178
|
+
new_dsts = new_edges[src]
|
|
179
|
+
old_dsts.each_key do |dst|
|
|
180
|
+
src.remove_edge(genv, dst) unless new_dsts&.key?(dst)
|
|
181
|
+
end
|
|
167
182
|
end
|
|
168
183
|
@edges, @new_edges = @new_edges, @edges
|
|
169
|
-
|
|
184
|
+
new_edges.each_value(&:clear)
|
|
185
|
+
new_edges.clear
|
|
170
186
|
|
|
171
|
-
|
|
187
|
+
boxes.each do |key, box|
|
|
172
188
|
box.destroy(genv)
|
|
173
189
|
end
|
|
174
190
|
@boxes, @new_boxes = @new_boxes, @boxes
|
|
175
|
-
|
|
191
|
+
new_boxes.clear
|
|
176
192
|
|
|
177
193
|
@diagnostics.each do |diag|
|
|
178
194
|
genv.add_diagnostic_path(diag.node.lenv.path)
|
|
@@ -185,6 +201,8 @@ module TypeProf::Core
|
|
|
185
201
|
@diagnostics, @new_diagnostics = @new_diagnostics, @diagnostics
|
|
186
202
|
@new_diagnostics.clear
|
|
187
203
|
|
|
204
|
+
return unless @target
|
|
205
|
+
|
|
188
206
|
@depended_value_entities.each do |ve|
|
|
189
207
|
ve.read_boxes.delete(@target) || raise
|
|
190
208
|
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?(
|
|
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?(
|
|
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?(
|
|
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?(
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|