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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 937e0870f11951c34a3c7903f0c6e02b2ba0b557d327999a8ce807e195409088
4
- data.tar.gz: ab8eca2e1cbb24729bbc0274bce7ed3ddd59917accd81285069ebc0086fe0208
3
+ metadata.gz: 93757a88e1532a3e0d9faf5b3eb8690b7a61ced6027e28d49dfce43234557015
4
+ data.tar.gz: 8e41f82794582ea68445e0972a6cab30982fcac483a50c5032811e3a2fcb9a60
5
5
  SHA512:
6
- metadata.gz: 40b2629987ea3feed461c74c279cabb25ad462c0e0118d3d451c12387de4cdbab39b1dd1301ee86ceb6f6f5bbb25b4c5425a72749a11eabe2485a35ea463e5a3
7
- data.tar.gz: c8704511e6507f9e50df4b626fb9b481e945954688df4d6c26ecd233b530eb4f1e2e038589c2538f209e9f6cec15c26401d854f48d8ea346325507f29246a475
6
+ metadata.gz: f1020867309edd82548dd3bcfc84d22007324af9c6acd0e6c82bb002678dfef3bb7acc695f6cc19d243d24084871a3a7a0695b1d7f069122ab28a221fca06469
7
+ data.tar.gz: 17cf614ed4edd5e75c772750fe844db59d65dcf7dc129b22269e4a840dbf4a45c1136f7ed74ac780d37d183f08de24e2ca447cd1804ea39bc39e7876f393ede8
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
 
@@ -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
@@ -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
@@ -55,12 +55,14 @@ module TypeProf
55
55
  raise unless first
56
56
  end
57
57
 
58
- def self.from_node(node, encoding = Encoding::UTF_16LE)
58
+ def self.from_node(node, file_context)
59
59
  node = node.location if node.respond_to?(:location)
60
- if node.is_a?(Prism::Location)
61
- pos1 = CodePosition.new(node.start_line, node.start_code_units_column(encoding))
62
- pos2 = CodePosition.new(node.end_line, node.end_code_units_column(encoding))
63
- elsif node.is_a?(RBS::Location)
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
- TypeProf::CodeRange.from_node(@raw_node)
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|
@@ -129,11 +130,12 @@ module TypeProf::Core
129
130
  end
130
131
 
131
132
  def add_diagnostic(diag)
133
+ @diagnostics = Set.empty if @diagnostics.equal?(Set::EMPTY)
132
134
  @diagnostics << diag
133
135
  end
134
136
 
135
137
  def remove_diagnostic(diag)
136
- @diagnostics.delete(diag)
138
+ @diagnostics.delete(diag) unless @diagnostics.equal?(Set::EMPTY)
137
139
  end
138
140
 
139
141
  def diff(prev_node)
@@ -209,20 +211,30 @@ module TypeProf::Core
209
211
  end
210
212
 
211
213
  class ProgramNode < Node
212
- def initialize(raw_node, lenv)
214
+ def initialize(raw_node, lenv, ignore_ranges: [])
213
215
  super(raw_node, lenv)
214
216
 
215
217
  @tbl = raw_node.locals
218
+ @ignore_ranges = ignore_ranges
216
219
  raw_body = raw_node.statements
217
220
 
218
221
  @body = AST.create_node(raw_body, lenv, false)
219
222
  end
220
223
 
221
- attr_reader :tbl, :body
224
+ attr_reader :tbl, :ignore_ranges, :body
222
225
 
223
226
  def subnodes = { body: }
224
227
  def attrs = { tbl: }
225
228
 
229
+ def each_diagnostic(genv, &blk)
230
+ return super if @ignore_ranges.empty?
231
+ super(genv) do |diag|
232
+ line = diag.code_range&.first&.lineno
233
+ next if line && @ignore_ranges.any? { |r| r.cover?(line) }
234
+ blk.call(diag)
235
+ end
236
+ end
237
+
226
238
  def install0(genv)
227
239
  @tbl.each {|var| @lenv.locals[var] = Source.new(genv.nil_type) }
228
240
  @lenv.locals[:"*self"] = lenv.cref.get_self(genv)
@@ -270,7 +282,7 @@ module TypeProf::Core
270
282
  @ret = ret
271
283
  end
272
284
 
273
- attr_reader :lenv, :prev_node, :ret
285
+ attr_reader :lenv, :prev_node, :code_range, :ret
274
286
 
275
287
  def boxes(_)
276
288
  []
@@ -1,7 +1,7 @@
1
1
  module TypeProf::Core
2
2
  class AST
3
3
  class CallBaseNode < Node
4
- def initialize(raw_node, recv, mid, mid_code_range, raw_args, last_arg, raw_block, lenv)
4
+ def initialize(raw_node, recv, mid, mid_code_range, raw_args, last_arg, raw_block, lenv, forwarding_arguments: false)
5
5
  super(raw_node, lenv)
6
6
 
7
7
  @recv = recv
@@ -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
- # TODO: Support forwarding arguments
34
+ @forwarding_arguments = true
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) : DummyNilNode.new(code_range, 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,20 @@ module TypeProf::Core
55
57
  else
56
58
  @block_pass = nil
57
59
  @block_tbl = raw_block.locals
58
- # TODO: optional args, etc.
60
+ @block_multi_targets = {}
59
61
  @block_f_args = case raw_block.parameters
60
62
  when Prism::BlockParametersNode
61
- raw_block.parameters.parameters.requireds.map {|n| n.is_a?(Prism::MultiTargetNode) ? nil : n.name }
63
+ params = raw_block.parameters.parameters
64
+ req = params.requireds.each_with_index.map do |n, i|
65
+ if n.is_a?(Prism::MultiTargetNode)
66
+ @block_multi_targets[i] = n
67
+ nil
68
+ else
69
+ n.name
70
+ end
71
+ end
72
+ opt = params.optionals.map {|n| n.name }
73
+ req + opt
62
74
  when Prism::NumberedParametersNode
63
75
  1.upto(raw_block.parameters.maximum).map { |n| :"_#{n}" }
64
76
  when Prism::ItParametersNode
@@ -69,7 +81,13 @@ module TypeProf::Core
69
81
  raise "not supported yet: #{ raw_block.parameters.class }"
70
82
  end
71
83
  ncref = CRef.new(lenv.cref.cpath, :instance, @mid, lenv.cref)
72
- nlenv = LocalEnv.new(@lenv.path, ncref, {}, @lenv.return_boxes)
84
+ nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, @lenv.return_boxes)
85
+ @block_opt_positional_defaults = []
86
+ if raw_block.parameters.is_a?(Prism::BlockParametersNode)
87
+ raw_block.parameters.parameters.optionals.each do |n|
88
+ @block_opt_positional_defaults << AST.create_node(n.value, nlenv)
89
+ end
90
+ end
73
91
  @block_body = raw_block.body ? AST.create_node(raw_block.body, nlenv) : DummyNilNode.new(code_range, lenv)
74
92
  end
75
93
  end
@@ -79,11 +97,12 @@ module TypeProf::Core
79
97
 
80
98
  attr_reader :recv, :mid, :mid_code_range, :yield
81
99
  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 :safe_navigation
100
+ attr_reader :block_tbl, :block_f_args, :block_opt_positional_defaults, :block_body, :block_pass, :anonymous_block_forwarding
101
+ attr_reader :block_multi_targets
102
+ attr_reader :safe_navigation, :forwarding_arguments
84
103
 
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: }
104
+ def subnodes = { recv:, positional_args:, keyword_args:, block_opt_positional_defaults:, block_body:, block_pass: }
105
+ def attrs = { mid:, splat_flags:, block_tbl:, block_f_args:, yield:, safe_navigation:, anonymous_block_forwarding:, forwarding_arguments: }
87
106
 
88
107
  def install0(genv)
89
108
  recv = @recv ? @recv.install(genv) : @yield ? @lenv.get_var(:"*given_block") : @lenv.get_var(:"*self")
@@ -93,22 +112,30 @@ module TypeProf::Core
93
112
  recv = NilFilter.new(genv, self, recv, false).next_vtx
94
113
  end
95
114
 
96
- positional_args = @positional_args.map do |arg|
97
- if arg.is_a?(DummyNilNode)
98
- @lenv.get_var(:"*anonymous_rest")
99
- else
100
- arg.install(genv)
115
+ if @forwarding_arguments
116
+ forward_a_args = (@lenv.forward_args || raise).to_actual_arguments(genv, @changes, self)
117
+ positional_args = forward_a_args.positionals
118
+ splat_flags = forward_a_args.splat_flags
119
+ keyword_args = forward_a_args.keywords
120
+ else
121
+ positional_args = @positional_args.map do |arg|
122
+ if arg.nil?
123
+ @lenv.get_var(:"*anonymous_rest")
124
+ else
125
+ arg.install(genv)
126
+ end
101
127
  end
128
+ splat_flags = @splat_flags
129
+ keyword_args = @keyword_args ? @keyword_args.install(genv) : nil
102
130
  end
103
131
 
104
- keyword_args = @keyword_args ? @keyword_args.install(genv) : nil
105
-
106
132
  if @block_body
107
133
  block_body = @block_body # kinda type annotationty
108
134
  block_tbl = @block_tbl || raise
135
+ block_body.lenv.forward_args = @lenv.forward_args
109
136
  @lenv.locals.each {|var, vtx| block_body.lenv.locals[var] = vtx }
110
137
  block_tbl.each {|var| block_body.lenv.locals[var] = Source.new(genv.nil_type) }
111
- @block_body.lenv.locals[:"*self"] = @block_body.lenv.cref.get_self(genv)
138
+ block_body.lenv.locals[:"*self"] = block_body.lenv.cref.get_self(genv)
112
139
 
113
140
  blk_f_args = []
114
141
  if @block_f_args
@@ -117,11 +144,28 @@ module TypeProf::Core
117
144
  end
118
145
  end
119
146
 
147
+ if @block_opt_positional_defaults && !@block_opt_positional_defaults.empty?
148
+ req_count = blk_f_args.size - @block_opt_positional_defaults.size
149
+ @block_opt_positional_defaults.each_with_index do |expr, i|
150
+ @changes.add_edge(genv, expr.install(genv), blk_f_args[req_count + i])
151
+ end
152
+ end
153
+
154
+ if @block_multi_targets
155
+ @block_multi_targets.each do |idx, raw_multi_target|
156
+ param_vtx = blk_f_args[idx]
157
+ lefts = raw_multi_target.lefts.map do |n|
158
+ block_body.lenv.new_var(n.is_a?(Prism::MultiTargetNode) ? nil : n.name, self)
159
+ end
160
+ @changes.add_masgn_box(genv, param_vtx, lefts, nil, nil)
161
+ end
162
+ end
163
+
120
164
  @lenv.locals.each do |var, vtx|
121
165
  block_body.lenv.set_var(var, vtx)
122
166
  end
123
167
  vars = []
124
- @block_body.modified_vars(@lenv.locals.keys - block_tbl, vars)
168
+ block_body.modified_vars(@lenv.locals.keys - block_tbl, vars)
125
169
  vars.uniq!
126
170
  vars.each do |var|
127
171
  vtx = @lenv.get_var(var)
@@ -130,25 +174,31 @@ module TypeProf::Core
130
174
  block_body.lenv.set_var(var, nvtx)
131
175
  end
132
176
 
133
- @block_body.lenv.locals[:"*expected_block_ret"] = Vertex.new(self)
134
- @block_body.install(genv)
135
- @block_body.lenv.add_next_box(@changes.add_escape_box(genv, @block_body.ret))
177
+ block_body.lenv.locals[:"*expected_block_ret"] = Vertex.new(self)
178
+ block_body.install(genv)
179
+ block_body.lenv.add_next_box(@changes.add_escape_box(genv, block_body.ret))
136
180
 
137
181
  vars.each do |var|
138
182
  @changes.add_edge(genv, block_body.lenv.get_var(var), @lenv.get_var(var))
139
183
  end
140
184
 
141
185
  blk_f_ary_arg = Vertex.new(self)
142
- @changes.add_masgn_box(genv, blk_f_ary_arg, blk_f_args, nil, nil) # TODO: support splat "do |a, *b, c|"
143
- block = Block.new(self, blk_f_ary_arg, blk_f_args, @block_body.lenv.next_boxes)
186
+ # TODO: support splat "do |a, *b, c|"
187
+ blk_f_args.each_with_index do |f_arg, i|
188
+ elem_vtx = @changes.add_splat_box(genv, blk_f_ary_arg, i).ret
189
+ @changes.add_edge(genv, elem_vtx, f_arg)
190
+ end
191
+ block = Block.new(self, blk_f_ary_arg, blk_f_args, block_body.lenv.next_boxes)
144
192
  blk_ty = Source.new(Type::Proc.new(genv, block))
145
193
  elsif @block_pass
146
194
  blk_ty = @block_pass.install(genv)
147
195
  elsif @anonymous_block_forwarding
148
196
  blk_ty = @lenv.get_var(:"*anonymous_block")
197
+ elsif @forwarding_arguments
198
+ blk_ty = forward_a_args.block
149
199
  end
150
200
 
151
- a_args = ActualArguments.new(positional_args, @splat_flags, keyword_args, blk_ty)
201
+ a_args = ActualArguments.new(positional_args, splat_flags, keyword_args, blk_ty)
152
202
  box = @changes.add_method_call_box(genv, recv, @mid, a_args, !@recv)
153
203
 
154
204
  block_body = @block_body
@@ -197,7 +247,7 @@ module TypeProf::Core
197
247
  subnode.modified_vars(tbl, vars)
198
248
  end
199
249
  else
200
- subnode.each {|n| n.modified_vars(tbl, vars) }
250
+ subnode.each {|n| n&.modified_vars(tbl, vars) }
201
251
  end
202
252
  end
203
253
  end
@@ -207,7 +257,7 @@ module TypeProf::Core
207
257
  def initialize(raw_node, lenv)
208
258
  recv = raw_node.receiver ? AST.create_node(raw_node.receiver, lenv) : nil
209
259
  mid = raw_node.name
210
- mid_code_range = TypeProf::CodeRange.from_node(raw_node.message_loc) if raw_node.message_loc
260
+ mid_code_range = lenv.code_range_from_node(raw_node.message_loc) if raw_node.message_loc
211
261
  raw_args = raw_node.arguments
212
262
  raw_block = raw_node.block
213
263
  super(raw_node, recv, mid, mid_code_range, raw_args, nil, raw_block, lenv)
@@ -231,6 +281,20 @@ module TypeProf::Core
231
281
  else
232
282
  super
233
283
  end
284
+ when :nil?
285
+ if @recv.is_a?(LocalVariableReadNode)
286
+ [
287
+ Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(true) }),
288
+ Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(false) })
289
+ ]
290
+ elsif @recv.is_a?(InstanceVariableReadNode)
291
+ [
292
+ Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(true) }),
293
+ Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(false) })
294
+ ]
295
+ else
296
+ super
297
+ end
234
298
  when :!
235
299
  then_narrowing, else_narrowing = @recv.narrowings
236
300
  [else_narrowing, then_narrowing]
@@ -251,9 +315,9 @@ module TypeProf::Core
251
315
 
252
316
  class ForwardingSuperNode < CallBaseNode
253
317
  def initialize(raw_node, lenv)
254
- raw_args = nil # TODO: forward args properly
318
+ raw_args = nil
255
319
  raw_block = raw_node.block
256
- super(raw_node, nil, :"*super", nil, raw_args, nil, raw_block, lenv)
320
+ super(raw_node, nil, :"*super", nil, raw_args, nil, raw_block, lenv, forwarding_arguments: true)
257
321
  end
258
322
  end
259
323
 
@@ -267,7 +331,7 @@ module TypeProf::Core
267
331
  class OperatorNode < CallBaseNode
268
332
  def initialize(raw_node, recv, lenv)
269
333
  mid = raw_node.binary_operator
270
- mid_code_range = TypeProf::CodeRange.from_node(raw_node.binary_operator_loc)
334
+ mid_code_range = lenv.code_range_from_node(raw_node.binary_operator_loc)
271
335
  last_arg = AST.create_node(raw_node.value, lenv)
272
336
  super(raw_node, recv, mid, mid_code_range, nil, last_arg, nil, lenv)
273
337
  end
@@ -300,7 +364,7 @@ module TypeProf::Core
300
364
  def initialize(raw_node, lenv)
301
365
  recv = AST.create_node(raw_node.receiver, lenv)
302
366
  mid = raw_node.read_name
303
- mid_code_range = TypeProf::CodeRange.from_node(raw_node.message_loc)
367
+ mid_code_range = lenv.code_range_from_node(raw_node.message_loc)
304
368
  super(raw_node, recv, mid, mid_code_range, nil, nil, nil, lenv)
305
369
  end
306
370
  end
@@ -309,7 +373,7 @@ module TypeProf::Core
309
373
  def initialize(raw_node, rhs, lenv)
310
374
  recv = AST.create_node(raw_node.receiver, lenv)
311
375
  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)
376
+ mid_code_range = lenv.code_range_from_node(raw_node.message_loc)
313
377
  @rhs = rhs
314
378
  super(raw_node, recv, mid, mid_code_range, nil, rhs, nil, lenv)
315
379
  end
@@ -8,7 +8,7 @@ module TypeProf::Core
8
8
  @cbase = nil
9
9
  @toplevel = false
10
10
  @cname = raw_node.name
11
- @cname_code_range = TypeProf::CodeRange.from_node(raw_node.location)
11
+ @cname_code_range = lenv.code_range_from_node(raw_node.location)
12
12
  when :constant_path_node, :constant_path_target_node
13
13
  if raw_node.parent
14
14
  @cbase = AST.create_node(raw_node.parent, lenv)
@@ -18,7 +18,7 @@ module TypeProf::Core
18
18
  @toplevel = true
19
19
  end
20
20
  @cname = raw_node.name
21
- @cname_code_range = TypeProf::CodeRange.from_node(raw_node.name_loc)
21
+ @cname_code_range = lenv.code_range_from_node(raw_node.name_loc)
22
22
  else
23
23
  raise raw_node.type.to_s
24
24
  end
@@ -57,17 +57,17 @@ module TypeProf::Core
57
57
  # C = expr
58
58
  @cpath = nil
59
59
  @static_cpath = lenv.cref.cpath + [raw_node.name]
60
- @cname_code_range = TypeProf::CodeRange.from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
60
+ @cname_code_range = lenv.code_range_from_node(raw_node.respond_to?(:name_loc) ? raw_node.name_loc : raw_node)
61
61
  when :constant_path_write_node, :constant_path_operator_write_node, :constant_path_or_write_node, :constant_path_and_write_node
62
62
  # expr::C = expr
63
63
  @cpath = AST.create_node(raw_node.target, lenv)
64
64
  @static_cpath = AST.parse_cpath(raw_node.target, lenv.cref)
65
- @cname_code_range = TypeProf::CodeRange.from_node(raw_node.target)
65
+ @cname_code_range = lenv.code_range_from_node(raw_node.target)
66
66
  when :constant_path_target_node
67
67
  # expr::C, * = ary
68
68
  @cpath = ConstantReadNode.new(raw_node, lenv)
69
69
  @static_cpath = AST.parse_cpath(raw_node, lenv.cref)
70
- @cname_code_range = TypeProf::CodeRange.from_node(raw_node)
70
+ @cname_code_range = lenv.code_range_from_node(raw_node)
71
71
  else
72
72
  raise
73
73
  end
@@ -83,9 +83,10 @@ module TypeProf::Core
83
83
  @cpath.define(genv) if @cpath
84
84
  @rhs.define(genv) if @rhs
85
85
  if @static_cpath
86
- mod = genv.resolve_const(@static_cpath)
87
- mod.add_def(self)
88
- mod
86
+ cdef = genv.resolve_const(@static_cpath)
87
+ cdef.on_const_added(genv, @static_cpath)
88
+ cdef.add_def(self)
89
+ cdef
89
90
  else
90
91
  nil
91
92
  end
@@ -102,7 +103,9 @@ module TypeProf::Core
102
103
 
103
104
  def undefine0(genv)
104
105
  if @static_cpath
105
- genv.resolve_const(@static_cpath).remove_def(self)
106
+ cdef = genv.resolve_const(@static_cpath)
107
+ cdef.remove_def(self)
108
+ cdef.on_const_removed(genv, @static_cpath)
106
109
  end
107
110
  @rhs.undefine(genv) if @rhs
108
111
  @cpath.undefine(genv) if @cpath