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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8dc7b580d84d82c2a49d4a2d24ddf32ddcafc938d4f9511fc4b6d4543f0b8e19
|
|
4
|
+
data.tar.gz: eed64be210440c2ea2627a7b21c95c7eedee374b760e9e6aa479dfa4c27df54f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b314eccaecb06cac94df1af7ab96084b06d1d70710c9ece6934c98ca5379ca98e82ab93838322081357d13c0200681beb24d6108f699aac4a8904952333a7d83
|
|
7
|
+
data.tar.gz: 7ebcd55f8c8d91b1eac583c7d585723de31a83da5fa85c7485cda77e520e24d09f1652b1dde7657db62c8151166cbfdc71d3563647ccd60871bb31e74fc6055a
|
data/README.md
CHANGED
|
@@ -37,7 +37,8 @@ $ bundle exec rake test
|
|
|
37
37
|
|
|
38
38
|
## More details
|
|
39
39
|
|
|
40
|
-
https://speakerdeck.com/mame/good-first-issues-of-typeprof
|
|
40
|
+
- [Good first issues of TypeProf](https://speakerdeck.com/mame/good-first-issues-of-typeprof) (slides)
|
|
41
|
+
- [How to report bugs / propose features](doc/report_guide.md)
|
|
41
42
|
|
|
42
43
|
## LICENSE
|
|
43
44
|
|
data/doc/report_guide.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# How to Report Bugs and Propose Features
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in improving TypeProf! When reporting a bug or proposing a feature, including a **scenario file** is very helpful (but not mandatory).
|
|
4
|
+
|
|
5
|
+
## What is a Scenario File?
|
|
6
|
+
|
|
7
|
+
A scenario file describes TypeProf's behavior — the input code and the expected type inference result. You can find many examples in the [`scenario/`](../scenario/) directory.
|
|
8
|
+
|
|
9
|
+
## Writing a Scenario File
|
|
10
|
+
|
|
11
|
+
### Basic pattern
|
|
12
|
+
|
|
13
|
+
A minimal scenario file has two sections: `## update` (input code) and `## assert` (expected type signatures in [RBS](https://github.com/ruby/rbs) syntax).
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
## update
|
|
17
|
+
def foo(n)
|
|
18
|
+
n.to_s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
foo(42)
|
|
22
|
+
|
|
23
|
+
## assert
|
|
24
|
+
class Object
|
|
25
|
+
def foo: (Integer) -> String
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Multiple updates
|
|
30
|
+
|
|
31
|
+
You can include multiple `## update` / `## assert` pairs to test how TypeProf handles code changes:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
## update
|
|
35
|
+
def foo = "symbol#{ 42 }"
|
|
36
|
+
|
|
37
|
+
## assert
|
|
38
|
+
class Object
|
|
39
|
+
def foo: -> String
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
## update
|
|
43
|
+
def foo = :"symbol#{ 42 }"
|
|
44
|
+
|
|
45
|
+
## assert
|
|
46
|
+
class Object
|
|
47
|
+
def foo: -> Symbol
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Diagnostics
|
|
52
|
+
|
|
53
|
+
Use `## diagnostics` to verify that TypeProf reports type errors at the expected locations:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
## update
|
|
57
|
+
def foo(x)
|
|
58
|
+
x
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
foo(1, 2)
|
|
62
|
+
|
|
63
|
+
## diagnostics
|
|
64
|
+
(5,0)-(5,3): wrong number of arguments (2 for 1)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Running a Scenario File
|
|
68
|
+
|
|
69
|
+
Run a single scenario file:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
$ ruby tool/scenario_runner.rb path/to/your_scenario.rb
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Run all tests:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
$ bundle exec rake test
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Reporting a Bug
|
|
82
|
+
|
|
83
|
+
1. Create a scenario file that reproduces the issue.
|
|
84
|
+
2. Run it with `ruby tool/scenario_runner.rb your_scenario.rb` to confirm the problem.
|
|
85
|
+
3. Open an issue and include:
|
|
86
|
+
- Your TypeProf version (`typeprof --version`)
|
|
87
|
+
- The scenario file content
|
|
88
|
+
- What you expected vs. what actually happened
|
data/lib/typeprof/cli/cli.rb
CHANGED
|
@@ -12,6 +12,7 @@ module TypeProf::CLI
|
|
|
12
12
|
output = nil
|
|
13
13
|
rbs_collection_path = nil
|
|
14
14
|
initialize_config_file = false
|
|
15
|
+
exclude_patterns = []
|
|
15
16
|
|
|
16
17
|
opt.separator ""
|
|
17
18
|
opt.separator "Options:"
|
|
@@ -25,6 +26,7 @@ module TypeProf::CLI
|
|
|
25
26
|
opt.on("--version", "Display typeprof version") { cli_options[:display_version] = true }
|
|
26
27
|
opt.on("--collection PATH", "File path of collection configuration") { |v| rbs_collection_path = v }
|
|
27
28
|
opt.on("--no-collection", "Ignore collection configuration") { rbs_collection_path = :no }
|
|
29
|
+
opt.on("--exclude PATTERN", "Exclude files matching glob PATTERN (can be specified multiple times)") { |v| exclude_patterns << v }
|
|
28
30
|
opt.on("--lsp", "LSP server mode") do |v|
|
|
29
31
|
core_options[:display_indicator] = false
|
|
30
32
|
cli_options[:lsp] = true
|
|
@@ -36,6 +38,7 @@ module TypeProf::CLI
|
|
|
36
38
|
opt.on("--[no-]show-errors", "Display possible errors found during the analysis") {|v| core_options[:output_diagnostics] = v }
|
|
37
39
|
opt.on("--[no-]show-parameter-names", "Display parameter names for methods") {|v| core_options[:output_parameter_names] = v }
|
|
38
40
|
opt.on("--[no-]show-source-locations", "Display definition source locations for methods") {|v| core_options[:output_source_locations] = v }
|
|
41
|
+
opt.on("--[no-]show-stats", "Display type inference statistics after analysis (for debugging purpose)") {|v| core_options[:output_stats] = v }
|
|
39
42
|
|
|
40
43
|
opt.separator ""
|
|
41
44
|
opt.separator "Advanced options:"
|
|
@@ -65,6 +68,8 @@ module TypeProf::CLI
|
|
|
65
68
|
output_errors: false,
|
|
66
69
|
output_parameter_names: false,
|
|
67
70
|
output_source_locations: false,
|
|
71
|
+
output_stats: false,
|
|
72
|
+
exclude_patterns: exclude_patterns,
|
|
68
73
|
}.merge(core_options)
|
|
69
74
|
|
|
70
75
|
@lsp_options = {
|
|
@@ -120,7 +125,7 @@ module TypeProf::CLI
|
|
|
120
125
|
if @lsp_options[:stdio]
|
|
121
126
|
TypeProf::LSP::Server.start_stdio(@core_options)
|
|
122
127
|
else
|
|
123
|
-
TypeProf::LSP::Server.start_socket(@core_options)
|
|
128
|
+
TypeProf::LSP::Server.start_socket(@core_options, @lsp_options[:port])
|
|
124
129
|
end
|
|
125
130
|
rescue Exception
|
|
126
131
|
puts $!.detailed_message(highlight: false).gsub(/^/, "---")
|
|
@@ -151,7 +156,7 @@ module TypeProf::CLI
|
|
|
151
156
|
files = []
|
|
152
157
|
@cli_options[:argv].each do |path|
|
|
153
158
|
if File.directory?(path)
|
|
154
|
-
files.concat(Dir.glob("#{ path }/**/*.{rb,rbs}"))
|
|
159
|
+
files.concat(Dir.glob("#{ path }/**/*.{rb,rbs}").select {|f| File.file?(f) })
|
|
155
160
|
elsif File.file?(path)
|
|
156
161
|
files << path
|
|
157
162
|
else
|
|
@@ -189,7 +194,8 @@ module TypeProf::CLI
|
|
|
189
194
|
{
|
|
190
195
|
"typeprof_version": "experimental",
|
|
191
196
|
"rbs_dir": "sig/",
|
|
192
|
-
"analysis_unit_dirs": #{exist_dirs.inspect}
|
|
197
|
+
"analysis_unit_dirs": #{exist_dirs.inspect},
|
|
198
|
+
// "exclude": ["**/templates/**/*.rb"],
|
|
193
199
|
// "diagnostic_severity": "warning"
|
|
194
200
|
}
|
|
195
201
|
JSONC
|
data/lib/typeprof/code_range.rb
CHANGED
|
@@ -55,12 +55,14 @@ module TypeProf
|
|
|
55
55
|
raise unless first
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def self.from_node(node,
|
|
58
|
+
def self.from_node(node, file_context)
|
|
59
59
|
node = node.location if node.respond_to?(:location)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
case node
|
|
61
|
+
when Prism::Location
|
|
62
|
+
start_col, end_col = file_context.column_offsets_for(node)
|
|
63
|
+
pos1 = CodePosition.new(node.start_line, start_col)
|
|
64
|
+
pos2 = CodePosition.new(node.end_line, end_col)
|
|
65
|
+
when RBS::Location
|
|
64
66
|
pos1 = CodePosition.new(*node.start_loc)
|
|
65
67
|
pos2 = CodePosition.new(*node.end_loc)
|
|
66
68
|
else
|
|
@@ -9,7 +9,7 @@ module TypeProf::Core
|
|
|
9
9
|
@ret = nil
|
|
10
10
|
|
|
11
11
|
@changes = ChangeSet.new(self, nil)
|
|
12
|
-
@diagnostics = Set
|
|
12
|
+
@diagnostics = Set::EMPTY
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
attr_reader :lenv
|
|
@@ -53,7 +53,7 @@ module TypeProf::Core
|
|
|
53
53
|
|
|
54
54
|
def code_range
|
|
55
55
|
if @raw_node
|
|
56
|
-
|
|
56
|
+
@lenv.code_range_from_node(@raw_node)
|
|
57
57
|
else
|
|
58
58
|
pp self
|
|
59
59
|
raise
|
|
@@ -104,6 +104,7 @@ module TypeProf::Core
|
|
|
104
104
|
@changes.reuse(self)
|
|
105
105
|
(@prev_node || raise).diagnostics.each do |diag|
|
|
106
106
|
diag.reuse(self)
|
|
107
|
+
@diagnostics = Set.empty if @diagnostics.equal?(Set::EMPTY)
|
|
107
108
|
@diagnostics << diag
|
|
108
109
|
end
|
|
109
110
|
each_subnode do |subnode|
|
|
@@ -117,6 +118,19 @@ module TypeProf::Core
|
|
|
117
118
|
raise "should override"
|
|
118
119
|
end
|
|
119
120
|
|
|
121
|
+
# Counterpart of install/install0 for pattern position; reuses the install
|
|
122
|
+
# machinery so @changes is reconciled the same way during incremental analysis.
|
|
123
|
+
def install_pattern(genv, subject)
|
|
124
|
+
@ret = install_pattern0(genv, subject)
|
|
125
|
+
@changes.reinstall(genv)
|
|
126
|
+
@ret
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# By default a pattern behaves as a plain expression, ignoring the matched value.
|
|
130
|
+
def install_pattern0(genv, subject)
|
|
131
|
+
install0(genv)
|
|
132
|
+
end
|
|
133
|
+
|
|
120
134
|
def uninstall(genv)
|
|
121
135
|
@changes.reinstall(genv)
|
|
122
136
|
each_subnode do |subnode|
|
|
@@ -129,11 +143,12 @@ module TypeProf::Core
|
|
|
129
143
|
end
|
|
130
144
|
|
|
131
145
|
def add_diagnostic(diag)
|
|
146
|
+
@diagnostics = Set.empty if @diagnostics.equal?(Set::EMPTY)
|
|
132
147
|
@diagnostics << diag
|
|
133
148
|
end
|
|
134
149
|
|
|
135
150
|
def remove_diagnostic(diag)
|
|
136
|
-
@diagnostics.delete(diag)
|
|
151
|
+
@diagnostics.delete(diag) unless @diagnostics.equal?(Set::EMPTY)
|
|
137
152
|
end
|
|
138
153
|
|
|
139
154
|
def diff(prev_node)
|
|
@@ -209,20 +224,30 @@ module TypeProf::Core
|
|
|
209
224
|
end
|
|
210
225
|
|
|
211
226
|
class ProgramNode < Node
|
|
212
|
-
def initialize(raw_node, lenv)
|
|
227
|
+
def initialize(raw_node, lenv, ignore_ranges: [])
|
|
213
228
|
super(raw_node, lenv)
|
|
214
229
|
|
|
215
230
|
@tbl = raw_node.locals
|
|
231
|
+
@ignore_ranges = ignore_ranges
|
|
216
232
|
raw_body = raw_node.statements
|
|
217
233
|
|
|
218
234
|
@body = AST.create_node(raw_body, lenv, false)
|
|
219
235
|
end
|
|
220
236
|
|
|
221
|
-
attr_reader :tbl, :body
|
|
237
|
+
attr_reader :tbl, :ignore_ranges, :body
|
|
222
238
|
|
|
223
239
|
def subnodes = { body: }
|
|
224
240
|
def attrs = { tbl: }
|
|
225
241
|
|
|
242
|
+
def each_diagnostic(genv, &blk)
|
|
243
|
+
return super if @ignore_ranges.empty?
|
|
244
|
+
super(genv) do |diag|
|
|
245
|
+
line = diag.code_range&.first&.lineno
|
|
246
|
+
next if line && @ignore_ranges.any? { |r| r.cover?(line) }
|
|
247
|
+
blk.call(diag)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
226
251
|
def install0(genv)
|
|
227
252
|
@tbl.each {|var| @lenv.locals[var] = Source.new(genv.nil_type) }
|
|
228
253
|
@lenv.locals[:"*self"] = lenv.cref.get_self(genv)
|
|
@@ -270,7 +295,7 @@ module TypeProf::Core
|
|
|
270
295
|
@ret = ret
|
|
271
296
|
end
|
|
272
297
|
|
|
273
|
-
attr_reader :lenv, :prev_node, :ret
|
|
298
|
+
attr_reader :lenv, :prev_node, :code_range, :ret
|
|
274
299
|
|
|
275
300
|
def boxes(_)
|
|
276
301
|
[]
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module TypeProf::Core
|
|
2
2
|
class AST
|
|
3
3
|
class CallBaseNode < Node
|
|
4
|
-
def initialize(raw_node, recv, mid,
|
|
4
|
+
def initialize(raw_node, recv, mid, mid_code_range_loc, raw_args, last_arg, raw_block, lenv, forwarding_arguments: false)
|
|
5
5
|
super(raw_node, lenv)
|
|
6
6
|
|
|
7
7
|
@recv = recv
|
|
8
8
|
@mid = mid
|
|
9
|
-
@
|
|
9
|
+
@mid_code_range_loc = mid_code_range_loc
|
|
10
10
|
|
|
11
11
|
# args
|
|
12
12
|
@positional_args = []
|
|
@@ -16,9 +16,11 @@ module TypeProf::Core
|
|
|
16
16
|
@block_pass = nil
|
|
17
17
|
@block_tbl = nil
|
|
18
18
|
@block_f_args = nil
|
|
19
|
+
@block_opt_positional_defaults = nil
|
|
19
20
|
@block_body = nil
|
|
20
21
|
@safe_navigation = raw_node.respond_to?(:safe_navigation?) && raw_node.safe_navigation?
|
|
21
22
|
@anonymous_block_forwarding = false
|
|
23
|
+
@forwarding_arguments = forwarding_arguments
|
|
22
24
|
|
|
23
25
|
if raw_args
|
|
24
26
|
args = []
|
|
@@ -29,13 +31,13 @@ module TypeProf::Core
|
|
|
29
31
|
args << raw_arg.expression
|
|
30
32
|
@splat_flags << true
|
|
31
33
|
when Prism::ForwardingArgumentsNode
|
|
32
|
-
|
|
34
|
+
@forwarding_arguments = :rest
|
|
33
35
|
else
|
|
34
36
|
args << raw_arg
|
|
35
37
|
@splat_flags << false
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
|
-
@positional_args = args.map {|arg| arg ? AST.create_node(arg, lenv) :
|
|
40
|
+
@positional_args = args.map {|arg| arg ? AST.create_node(arg, lenv) : nil }
|
|
39
41
|
|
|
40
42
|
kw = @positional_args.last
|
|
41
43
|
if kw.is_a?(TypeProf::Core::AST::HashNode) && kw.keywords
|
|
@@ -55,10 +57,27 @@ module TypeProf::Core
|
|
|
55
57
|
else
|
|
56
58
|
@block_pass = nil
|
|
57
59
|
@block_tbl = raw_block.locals
|
|
58
|
-
|
|
60
|
+
@block_multi_targets = {}
|
|
59
61
|
@block_f_args = case raw_block.parameters
|
|
60
62
|
when Prism::BlockParametersNode
|
|
61
|
-
|
|
63
|
+
# `{ || ... }` (empty pipes) and `{ |; x| ... }`
|
|
64
|
+
# (block-local-only) yield BlockParametersNode
|
|
65
|
+
# whose inner `parameters` is nil.
|
|
66
|
+
params = raw_block.parameters.parameters
|
|
67
|
+
if params
|
|
68
|
+
req = params.requireds.each_with_index.map do |n, i|
|
|
69
|
+
if n.is_a?(Prism::MultiTargetNode)
|
|
70
|
+
@block_multi_targets[i] = n
|
|
71
|
+
nil
|
|
72
|
+
else
|
|
73
|
+
n.name
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
opt = params.optionals.map {|n| n.name }
|
|
77
|
+
req + opt
|
|
78
|
+
else
|
|
79
|
+
[]
|
|
80
|
+
end
|
|
62
81
|
when Prism::NumberedParametersNode
|
|
63
82
|
1.upto(raw_block.parameters.maximum).map { |n| :"_#{n}" }
|
|
64
83
|
when Prism::ItParametersNode
|
|
@@ -69,7 +88,13 @@ module TypeProf::Core
|
|
|
69
88
|
raise "not supported yet: #{ raw_block.parameters.class }"
|
|
70
89
|
end
|
|
71
90
|
ncref = CRef.new(lenv.cref.cpath, :instance, @mid, lenv.cref)
|
|
72
|
-
nlenv = LocalEnv.new(@lenv.
|
|
91
|
+
nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, @lenv.return_boxes)
|
|
92
|
+
@block_opt_positional_defaults = []
|
|
93
|
+
if raw_block.parameters.is_a?(Prism::BlockParametersNode) && raw_block.parameters.parameters
|
|
94
|
+
raw_block.parameters.parameters.optionals.each do |n|
|
|
95
|
+
@block_opt_positional_defaults << AST.create_node(n.value, nlenv)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
73
98
|
@block_body = raw_block.body ? AST.create_node(raw_block.body, nlenv) : DummyNilNode.new(code_range, lenv)
|
|
74
99
|
end
|
|
75
100
|
end
|
|
@@ -77,13 +102,18 @@ module TypeProf::Core
|
|
|
77
102
|
@yield = raw_node.type == :yield_node
|
|
78
103
|
end
|
|
79
104
|
|
|
80
|
-
attr_reader :recv, :mid, :
|
|
105
|
+
attr_reader :recv, :mid, :yield
|
|
106
|
+
|
|
107
|
+
def mid_code_range
|
|
108
|
+
@mid_code_range ||= @lenv.code_range_from_node(@mid_code_range_loc) if @mid_code_range_loc
|
|
109
|
+
end
|
|
81
110
|
attr_reader :positional_args, :splat_flags, :keyword_args
|
|
82
|
-
attr_reader :block_tbl, :block_f_args, :block_body, :block_pass, :anonymous_block_forwarding
|
|
83
|
-
attr_reader :
|
|
111
|
+
attr_reader :block_tbl, :block_f_args, :block_opt_positional_defaults, :block_body, :block_pass, :anonymous_block_forwarding
|
|
112
|
+
attr_reader :block_multi_targets
|
|
113
|
+
attr_reader :safe_navigation, :forwarding_arguments
|
|
84
114
|
|
|
85
|
-
def subnodes = { recv:, positional_args:, keyword_args:, block_body:, block_pass: }
|
|
86
|
-
def attrs = { mid:, splat_flags:, block_tbl:, block_f_args:, yield:, safe_navigation:, anonymous_block_forwarding: }
|
|
115
|
+
def subnodes = { recv:, positional_args:, keyword_args:, block_opt_positional_defaults:, block_body:, block_pass: }
|
|
116
|
+
def attrs = { mid:, splat_flags:, block_tbl:, block_f_args:, yield:, safe_navigation:, anonymous_block_forwarding:, forwarding_arguments: }
|
|
87
117
|
|
|
88
118
|
def install0(genv)
|
|
89
119
|
recv = @recv ? @recv.install(genv) : @yield ? @lenv.get_var(:"*given_block") : @lenv.get_var(:"*self")
|
|
@@ -93,22 +123,36 @@ module TypeProf::Core
|
|
|
93
123
|
recv = NilFilter.new(genv, self, recv, false).next_vtx
|
|
94
124
|
end
|
|
95
125
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
126
|
+
if @forwarding_arguments
|
|
127
|
+
forward_a_args = (@lenv.forward_args || raise).to_actual_arguments(
|
|
128
|
+
genv,
|
|
129
|
+
@changes,
|
|
130
|
+
self,
|
|
131
|
+
include_leading_positionals: @forwarding_arguments != :rest,
|
|
132
|
+
activation_required: @forwarding_arguments == :rest,
|
|
133
|
+
)
|
|
134
|
+
# An anonymous rest cannot appear here: `bar(*, ...)` is a syntax error
|
|
135
|
+
leading_args = @positional_args.map {|arg| arg.install(genv) }
|
|
136
|
+
a_args = forward_a_args.prepend_positionals(leading_args, @splat_flags)
|
|
137
|
+
a_args = a_args.with_keywords(@keyword_args.install(genv)) if @keyword_args
|
|
138
|
+
else
|
|
139
|
+
positional_args = @positional_args.map do |arg|
|
|
140
|
+
if arg.nil?
|
|
141
|
+
@lenv.get_var(:"*anonymous_rest")
|
|
142
|
+
else
|
|
143
|
+
arg.install(genv)
|
|
144
|
+
end
|
|
101
145
|
end
|
|
146
|
+
a_args = ActualArguments.new(positional_args, @splat_flags, @keyword_args ? @keyword_args.install(genv) : nil, nil)
|
|
102
147
|
end
|
|
103
148
|
|
|
104
|
-
keyword_args = @keyword_args ? @keyword_args.install(genv) : nil
|
|
105
|
-
|
|
106
149
|
if @block_body
|
|
107
150
|
block_body = @block_body # kinda type annotationty
|
|
108
151
|
block_tbl = @block_tbl || raise
|
|
152
|
+
block_body.lenv.forward_args = @lenv.forward_args
|
|
109
153
|
@lenv.locals.each {|var, vtx| block_body.lenv.locals[var] = vtx }
|
|
110
154
|
block_tbl.each {|var| block_body.lenv.locals[var] = Source.new(genv.nil_type) }
|
|
111
|
-
|
|
155
|
+
block_body.lenv.locals[:"*self"] = block_body.lenv.cref.get_self(genv)
|
|
112
156
|
|
|
113
157
|
blk_f_args = []
|
|
114
158
|
if @block_f_args
|
|
@@ -117,11 +161,28 @@ module TypeProf::Core
|
|
|
117
161
|
end
|
|
118
162
|
end
|
|
119
163
|
|
|
164
|
+
if @block_opt_positional_defaults && !@block_opt_positional_defaults.empty?
|
|
165
|
+
req_count = blk_f_args.size - @block_opt_positional_defaults.size
|
|
166
|
+
@block_opt_positional_defaults.each_with_index do |expr, i|
|
|
167
|
+
@changes.add_edge(genv, expr.install(genv), blk_f_args[req_count + i])
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
if @block_multi_targets
|
|
172
|
+
@block_multi_targets.each do |idx, raw_multi_target|
|
|
173
|
+
param_vtx = blk_f_args[idx]
|
|
174
|
+
lefts = raw_multi_target.lefts.map do |n|
|
|
175
|
+
block_body.lenv.new_var(n.is_a?(Prism::MultiTargetNode) ? nil : n.name, self)
|
|
176
|
+
end
|
|
177
|
+
@changes.add_masgn_box(genv, param_vtx, lefts, nil, nil)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
120
181
|
@lenv.locals.each do |var, vtx|
|
|
121
182
|
block_body.lenv.set_var(var, vtx)
|
|
122
183
|
end
|
|
123
184
|
vars = []
|
|
124
|
-
|
|
185
|
+
block_body.modified_vars(@lenv.locals.keys - block_tbl, vars)
|
|
125
186
|
vars.uniq!
|
|
126
187
|
vars.each do |var|
|
|
127
188
|
vtx = @lenv.get_var(var)
|
|
@@ -130,25 +191,35 @@ module TypeProf::Core
|
|
|
130
191
|
block_body.lenv.set_var(var, nvtx)
|
|
131
192
|
end
|
|
132
193
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
194
|
+
block_body.lenv.locals[:"*expected_block_ret"] = Vertex.new(self)
|
|
195
|
+
block_body.install(genv)
|
|
196
|
+
block_body.lenv.add_next_box(@changes.add_escape_box(genv, block_body.ret))
|
|
136
197
|
|
|
137
198
|
vars.each do |var|
|
|
138
199
|
@changes.add_edge(genv, block_body.lenv.get_var(var), @lenv.get_var(var))
|
|
139
200
|
end
|
|
140
201
|
|
|
141
202
|
blk_f_ary_arg = Vertex.new(self)
|
|
142
|
-
|
|
143
|
-
|
|
203
|
+
# TODO: support splat "do |a, *b, c|"
|
|
204
|
+
blk_f_args.each_with_index do |f_arg, i|
|
|
205
|
+
elem_vtx = @changes.add_splat_box(genv, blk_f_ary_arg, i).ret
|
|
206
|
+
@changes.add_edge(genv, elem_vtx, f_arg)
|
|
207
|
+
end
|
|
208
|
+
block = Block.new(self, blk_f_ary_arg, blk_f_args, block_body.lenv.next_boxes)
|
|
144
209
|
blk_ty = Source.new(Type::Proc.new(genv, block))
|
|
145
210
|
elsif @block_pass
|
|
146
211
|
blk_ty = @block_pass.install(genv)
|
|
147
212
|
elsif @anonymous_block_forwarding
|
|
148
213
|
blk_ty = @lenv.get_var(:"*anonymous_block")
|
|
214
|
+
elsif @forwarding_arguments
|
|
215
|
+
blk_ty = forward_a_args.block
|
|
149
216
|
end
|
|
150
217
|
|
|
151
|
-
|
|
218
|
+
if @forwarding_arguments
|
|
219
|
+
a_args = a_args.with_block(blk_ty, omittable: !@block_body && !@block_pass && !@anonymous_block_forwarding)
|
|
220
|
+
else
|
|
221
|
+
a_args = a_args.with_block(blk_ty)
|
|
222
|
+
end
|
|
152
223
|
box = @changes.add_method_call_box(genv, recv, @mid, a_args, !@recv)
|
|
153
224
|
|
|
154
225
|
block_body = @block_body
|
|
@@ -180,7 +251,7 @@ module TypeProf::Core
|
|
|
180
251
|
end
|
|
181
252
|
|
|
182
253
|
def retrieve_at(pos, &blk)
|
|
183
|
-
yield self if
|
|
254
|
+
yield self if mid_code_range&.include?(pos)
|
|
184
255
|
each_subnode do |subnode|
|
|
185
256
|
next unless subnode
|
|
186
257
|
subnode.retrieve_at(pos, &blk)
|
|
@@ -197,7 +268,7 @@ module TypeProf::Core
|
|
|
197
268
|
subnode.modified_vars(tbl, vars)
|
|
198
269
|
end
|
|
199
270
|
else
|
|
200
|
-
subnode.each {|n| n
|
|
271
|
+
subnode.each {|n| n&.modified_vars(tbl, vars) }
|
|
201
272
|
end
|
|
202
273
|
end
|
|
203
274
|
end
|
|
@@ -207,10 +278,9 @@ module TypeProf::Core
|
|
|
207
278
|
def initialize(raw_node, lenv)
|
|
208
279
|
recv = raw_node.receiver ? AST.create_node(raw_node.receiver, lenv) : nil
|
|
209
280
|
mid = raw_node.name
|
|
210
|
-
mid_code_range = TypeProf::CodeRange.from_node(raw_node.message_loc) if raw_node.message_loc
|
|
211
281
|
raw_args = raw_node.arguments
|
|
212
282
|
raw_block = raw_node.block
|
|
213
|
-
super(raw_node, recv, mid,
|
|
283
|
+
super(raw_node, recv, mid, raw_node.message_loc, raw_args, nil, raw_block, lenv)
|
|
214
284
|
end
|
|
215
285
|
|
|
216
286
|
def narrowings
|
|
@@ -231,6 +301,20 @@ module TypeProf::Core
|
|
|
231
301
|
else
|
|
232
302
|
super
|
|
233
303
|
end
|
|
304
|
+
when :nil?
|
|
305
|
+
if @recv.is_a?(LocalVariableReadNode)
|
|
306
|
+
[
|
|
307
|
+
Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(true) }),
|
|
308
|
+
Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(false) })
|
|
309
|
+
]
|
|
310
|
+
elsif @recv.is_a?(InstanceVariableReadNode)
|
|
311
|
+
[
|
|
312
|
+
Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(true) }),
|
|
313
|
+
Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(false) })
|
|
314
|
+
]
|
|
315
|
+
else
|
|
316
|
+
super
|
|
317
|
+
end
|
|
234
318
|
when :!
|
|
235
319
|
then_narrowing, else_narrowing = @recv.narrowings
|
|
236
320
|
[else_narrowing, then_narrowing]
|
|
@@ -251,9 +335,9 @@ module TypeProf::Core
|
|
|
251
335
|
|
|
252
336
|
class ForwardingSuperNode < CallBaseNode
|
|
253
337
|
def initialize(raw_node, lenv)
|
|
254
|
-
raw_args = nil
|
|
338
|
+
raw_args = nil
|
|
255
339
|
raw_block = raw_node.block
|
|
256
|
-
super(raw_node, nil, :"*super", nil, raw_args, nil, raw_block, lenv)
|
|
340
|
+
super(raw_node, nil, :"*super", nil, raw_args, nil, raw_block, lenv, forwarding_arguments: :all)
|
|
257
341
|
end
|
|
258
342
|
end
|
|
259
343
|
|
|
@@ -267,9 +351,8 @@ module TypeProf::Core
|
|
|
267
351
|
class OperatorNode < CallBaseNode
|
|
268
352
|
def initialize(raw_node, recv, lenv)
|
|
269
353
|
mid = raw_node.binary_operator
|
|
270
|
-
mid_code_range = TypeProf::CodeRange.from_node(raw_node.binary_operator_loc)
|
|
271
354
|
last_arg = AST.create_node(raw_node.value, lenv)
|
|
272
|
-
super(raw_node, recv, mid,
|
|
355
|
+
super(raw_node, recv, mid, raw_node.binary_operator_loc, nil, last_arg, nil, lenv)
|
|
273
356
|
end
|
|
274
357
|
end
|
|
275
358
|
|
|
@@ -300,8 +383,7 @@ module TypeProf::Core
|
|
|
300
383
|
def initialize(raw_node, lenv)
|
|
301
384
|
recv = AST.create_node(raw_node.receiver, lenv)
|
|
302
385
|
mid = raw_node.read_name
|
|
303
|
-
|
|
304
|
-
super(raw_node, recv, mid, mid_code_range, nil, nil, nil, lenv)
|
|
386
|
+
super(raw_node, recv, mid, raw_node.message_loc, nil, nil, nil, lenv)
|
|
305
387
|
end
|
|
306
388
|
end
|
|
307
389
|
|
|
@@ -309,9 +391,8 @@ module TypeProf::Core
|
|
|
309
391
|
def initialize(raw_node, rhs, lenv)
|
|
310
392
|
recv = AST.create_node(raw_node.receiver, lenv)
|
|
311
393
|
mid = raw_node.is_a?(Prism::CallTargetNode) ? raw_node.name : raw_node.write_name
|
|
312
|
-
mid_code_range = TypeProf::CodeRange.from_node(raw_node.message_loc)
|
|
313
394
|
@rhs = rhs
|
|
314
|
-
super(raw_node, recv, mid,
|
|
395
|
+
super(raw_node, recv, mid, raw_node.message_loc, nil, rhs, nil, lenv)
|
|
315
396
|
end
|
|
316
397
|
|
|
317
398
|
attr_reader :rhs
|