steep 0.44.1 → 0.47.1
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/.github/dependabot.yml +8 -0
- data/.github/workflows/ruby.yml +3 -2
- data/.gitignore +0 -1
- data/CHANGELOG.md +43 -0
- data/Gemfile +1 -4
- data/Gemfile.lock +73 -0
- data/README.md +2 -1
- data/lib/steep/annotation_parser.rb +1 -1
- data/lib/steep/ast/builtin.rb +7 -1
- data/lib/steep/ast/types/factory.rb +19 -25
- data/lib/steep/cli.rb +7 -1
- data/lib/steep/diagnostic/lsp_formatter.rb +59 -6
- data/lib/steep/diagnostic/ruby.rb +188 -60
- data/lib/steep/diagnostic/signature.rb +38 -15
- data/lib/steep/drivers/check.rb +3 -0
- data/lib/steep/drivers/init.rb +10 -3
- data/lib/steep/drivers/utils/driver_helper.rb +15 -0
- data/lib/steep/drivers/validate.rb +1 -1
- data/lib/steep/drivers/watch.rb +3 -0
- data/lib/steep/equatable.rb +21 -0
- data/lib/steep/interface/function.rb +798 -579
- data/lib/steep/project/dsl.rb +135 -36
- data/lib/steep/project/options.rb +13 -53
- data/lib/steep/project/target.rb +22 -8
- data/lib/steep/server/interaction_worker.rb +245 -26
- data/lib/steep/server/type_check_worker.rb +6 -9
- data/lib/steep/services/file_loader.rb +26 -19
- data/lib/steep/services/hover_content.rb +135 -80
- data/lib/steep/source.rb +12 -11
- data/lib/steep/type_construction.rb +435 -502
- data/lib/steep/type_inference/block_params.rb +3 -6
- data/lib/steep/type_inference/method_params.rb +483 -0
- data/lib/steep/type_inference/send_args.rb +599 -128
- data/lib/steep/typing.rb +46 -21
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +5 -3
- data/sample/Steepfile +10 -3
- data/smoke/alias/Steepfile +2 -1
- data/smoke/and/Steepfile +2 -1
- data/smoke/array/Steepfile +2 -1
- data/smoke/array/test_expectations.yml +3 -3
- data/smoke/block/Steepfile +2 -2
- data/smoke/block/c.rb +0 -1
- data/smoke/case/Steepfile +2 -1
- data/smoke/class/Steepfile +2 -1
- data/smoke/class/test_expectations.yml +12 -15
- data/smoke/const/Steepfile +2 -1
- data/smoke/diagnostics/Steepfile +2 -1
- data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
- data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
- data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
- data/smoke/diagnostics/test_expectations.yml +108 -31
- data/smoke/diagnostics-rbs/Steepfile +1 -1
- data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
- data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
- data/smoke/diagnostics-rbs-duplicated/Steepfile +2 -1
- data/smoke/diagnostics-ruby-unsat/Steepfile +2 -1
- data/smoke/dstr/Steepfile +2 -1
- data/smoke/ensure/Steepfile +2 -1
- data/smoke/ensure/test_expectations.yml +3 -3
- data/smoke/enumerator/Steepfile +2 -1
- data/smoke/enumerator/test_expectations.yml +1 -1
- data/smoke/extension/Steepfile +2 -1
- data/smoke/extension/e.rbs +1 -1
- data/smoke/hash/Steepfile +2 -1
- data/smoke/hello/Steepfile +2 -1
- data/smoke/if/Steepfile +2 -1
- data/smoke/implements/Steepfile +2 -1
- data/smoke/initialize/Steepfile +2 -1
- data/smoke/integer/Steepfile +2 -1
- data/smoke/interface/Steepfile +2 -1
- data/smoke/kwbegin/Steepfile +2 -1
- data/smoke/lambda/Steepfile +2 -1
- data/smoke/literal/Steepfile +2 -1
- data/smoke/literal/test_expectations.yml +2 -2
- data/smoke/map/Steepfile +2 -1
- data/smoke/method/Steepfile +2 -1
- data/smoke/method/test_expectations.yml +11 -10
- data/smoke/module/Steepfile +2 -1
- data/smoke/regexp/Steepfile +2 -1
- data/smoke/regression/Steepfile +2 -1
- data/smoke/rescue/Steepfile +2 -1
- data/smoke/rescue/test_expectations.yml +3 -3
- data/smoke/self/Steepfile +2 -1
- data/smoke/skip/Steepfile +2 -1
- data/smoke/stdout/Steepfile +2 -1
- data/smoke/super/Steepfile +2 -1
- data/smoke/toplevel/Steepfile +2 -1
- data/smoke/toplevel/test_expectations.yml +3 -3
- data/smoke/tsort/Steepfile +4 -5
- data/smoke/tsort/test_expectations.yml +2 -2
- data/smoke/type_case/Steepfile +2 -1
- data/smoke/unexpected/Steepfile +2 -1
- data/smoke/yield/Steepfile +2 -1
- data/steep.gemspec +2 -2
- metadata +16 -10
- data/sig/project.rbi +0 -109
@@ -1,194 +1,665 @@
|
|
1
1
|
module Steep
|
2
2
|
module TypeInference
|
3
3
|
class SendArgs
|
4
|
-
|
5
|
-
|
4
|
+
class PositionalArgs
|
5
|
+
class NodeParamPair
|
6
|
+
attr_reader :node
|
7
|
+
attr_reader :param
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def initialize(node:, param:)
|
10
|
+
@node = node
|
11
|
+
@param = param
|
12
|
+
end
|
13
|
+
|
14
|
+
include Equatable
|
11
15
|
|
12
|
-
|
13
|
-
|
16
|
+
def to_ary
|
17
|
+
[node, param]
|
18
|
+
end
|
19
|
+
end
|
14
20
|
|
15
|
-
|
16
|
-
|
21
|
+
class NodeTypePair
|
22
|
+
attr_reader :node
|
23
|
+
attr_reader :type
|
17
24
|
|
18
|
-
|
19
|
-
|
25
|
+
def initialize(node:, type:)
|
26
|
+
@node = node
|
27
|
+
@type = type
|
28
|
+
end
|
29
|
+
|
30
|
+
include Equatable
|
31
|
+
|
32
|
+
def node_type
|
33
|
+
case node.type
|
34
|
+
when :splat
|
35
|
+
AST::Builtin::Array.instance_type(type)
|
36
|
+
else
|
37
|
+
type
|
38
|
+
end
|
39
|
+
end
|
20
40
|
end
|
21
41
|
|
22
|
-
|
23
|
-
|
42
|
+
class SplatArg
|
43
|
+
attr_reader :node
|
44
|
+
attr_accessor :type
|
45
|
+
|
46
|
+
def initialize(node:)
|
47
|
+
@node = node
|
48
|
+
@type = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
include Equatable
|
24
52
|
end
|
25
53
|
|
26
|
-
|
27
|
-
|
54
|
+
class UnexpectedArg
|
55
|
+
attr_reader :node
|
28
56
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
57
|
+
def initialize(node:)
|
58
|
+
@node = node
|
59
|
+
end
|
33
60
|
|
34
|
-
|
35
|
-
|
36
|
-
self.class.new(args: args.take(args.size - 1), block_pass_arg: block_pass_arg)
|
37
|
-
end
|
61
|
+
include Equatable
|
62
|
+
end
|
38
63
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
yield node
|
45
|
-
end
|
46
|
-
end
|
64
|
+
class MissingArg
|
65
|
+
attr_reader :params
|
66
|
+
|
67
|
+
def initialize(params:)
|
68
|
+
@params = params
|
47
69
|
end
|
48
|
-
|
49
|
-
|
70
|
+
|
71
|
+
include Equatable
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_reader :args
|
75
|
+
attr_reader :index
|
76
|
+
attr_reader :positional_params
|
77
|
+
attr_reader :uniform
|
78
|
+
|
79
|
+
def initialize(args:, index:, positional_params:, uniform: false)
|
80
|
+
@args = args
|
81
|
+
@index = index
|
82
|
+
@positional_params = positional_params
|
83
|
+
@uniform = uniform
|
84
|
+
end
|
85
|
+
|
86
|
+
def node
|
87
|
+
args[index]
|
50
88
|
end
|
51
|
-
end
|
52
89
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
90
|
+
def following_args
|
91
|
+
args[index..]
|
92
|
+
end
|
93
|
+
|
94
|
+
def param
|
95
|
+
positional_params&.head
|
96
|
+
end
|
97
|
+
|
98
|
+
def update(index: self.index, positional_params: self.positional_params, uniform: self.uniform)
|
99
|
+
PositionalArgs.new(args: args, index: index, positional_params: positional_params, uniform: uniform)
|
100
|
+
end
|
101
|
+
|
102
|
+
def next()
|
103
|
+
case
|
104
|
+
when !node && param.is_a?(Interface::Function::Params::PositionalParams::Required)
|
105
|
+
[
|
106
|
+
MissingArg.new(params: positional_params),
|
107
|
+
update(index: index, positional_params: nil)
|
108
|
+
]
|
109
|
+
when !node && param.is_a?(Interface::Function::Params::PositionalParams::Optional)
|
110
|
+
nil
|
111
|
+
when !node && param.is_a?(Interface::Function::Params::PositionalParams::Rest)
|
112
|
+
nil
|
113
|
+
when !node && !param
|
114
|
+
nil
|
115
|
+
when node && node.type != :splat && param.is_a?(Interface::Function::Params::PositionalParams::Required)
|
116
|
+
[
|
117
|
+
NodeParamPair.new(node: node, param: param),
|
118
|
+
update(index: index+1, positional_params: positional_params.tail)
|
119
|
+
]
|
120
|
+
when node && node.type != :splat && param.is_a?(Interface::Function::Params::PositionalParams::Optional)
|
121
|
+
[
|
122
|
+
NodeParamPair.new(node: node, param: param),
|
123
|
+
update(index: index+1, positional_params: positional_params.tail)
|
124
|
+
]
|
125
|
+
when node && node.type != :splat && param.is_a?(Interface::Function::Params::PositionalParams::Rest)
|
126
|
+
[
|
127
|
+
NodeParamPair.new(node: node, param: param),
|
128
|
+
update(index: index+1)
|
129
|
+
]
|
130
|
+
when node && node.type != :splat && !param
|
131
|
+
[
|
132
|
+
UnexpectedArg.new(node: node),
|
133
|
+
update(index: index + 1)
|
134
|
+
]
|
135
|
+
when node && node.type == :splat
|
136
|
+
[
|
137
|
+
SplatArg.new(node: node),
|
138
|
+
self
|
139
|
+
]
|
57
140
|
end
|
58
|
-
else
|
59
|
-
[]
|
60
141
|
end
|
61
|
-
end
|
62
142
|
|
63
|
-
|
64
|
-
|
65
|
-
|
143
|
+
def uniform_type
|
144
|
+
return nil unless positional_params
|
145
|
+
if positional_params.each.any? {|param| param.is_a?(Interface::Function::Params::PositionalParams::Rest) }
|
146
|
+
AST::Types::Intersection.build(types: positional_params.each.map(&:type))
|
147
|
+
end
|
66
148
|
end
|
67
|
-
end
|
68
149
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
150
|
+
def consume(n, node:)
|
151
|
+
ps = []
|
152
|
+
params = consume0(n, node: node, params: positional_params, ps: ps)
|
153
|
+
case params
|
154
|
+
when UnexpectedArg
|
155
|
+
[
|
156
|
+
params,
|
157
|
+
update(index: index+1, positional_params: nil)
|
158
|
+
]
|
76
159
|
else
|
77
|
-
|
160
|
+
[ps, update(index: index+1, positional_params: params)]
|
78
161
|
end
|
79
162
|
end
|
80
163
|
|
81
|
-
|
82
|
-
case
|
83
|
-
when
|
84
|
-
|
85
|
-
[node, AST::Types::Intersection.build(types: types_)]
|
164
|
+
def consume0(n, node:, params:, ps:)
|
165
|
+
case n
|
166
|
+
when 0
|
167
|
+
params
|
86
168
|
else
|
87
|
-
|
169
|
+
head = params&.head
|
170
|
+
case head
|
171
|
+
when nil
|
172
|
+
UnexpectedArg.new(node: node)
|
173
|
+
when Interface::Function::Params::PositionalParams::Required, Interface::Function::Params::PositionalParams::Optional
|
174
|
+
ps << head
|
175
|
+
consume0(n-1, node: node, params: params.tail, ps: ps)
|
176
|
+
when Interface::Function::Params::PositionalParams::Rest
|
177
|
+
ps << head
|
178
|
+
consume0(n-1, node: node, params: params, ps: ps)
|
179
|
+
end
|
88
180
|
end
|
89
181
|
end
|
90
182
|
end
|
91
183
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
184
|
+
class KeywordArgs
|
185
|
+
class ArgTypePairs
|
186
|
+
attr_reader :pairs
|
187
|
+
|
188
|
+
def initialize(pairs:)
|
189
|
+
@pairs = pairs
|
190
|
+
end
|
191
|
+
|
192
|
+
include Equatable
|
193
|
+
|
194
|
+
def [](index)
|
195
|
+
pairs[index]
|
196
|
+
end
|
197
|
+
|
198
|
+
def size
|
199
|
+
pairs.size
|
98
200
|
end
|
99
201
|
end
|
100
|
-
end
|
101
202
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
[[]]
|
203
|
+
class SplatArg
|
204
|
+
attr_reader :node
|
205
|
+
attr_accessor :type
|
106
206
|
|
107
|
-
|
108
|
-
|
109
|
-
|
207
|
+
def initialize(node:)
|
208
|
+
@node = node
|
209
|
+
@type = nil
|
210
|
+
end
|
110
211
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
212
|
+
include Equatable
|
213
|
+
end
|
214
|
+
|
215
|
+
class UnexpectedKeyword
|
216
|
+
attr_reader :keyword
|
217
|
+
attr_reader :node
|
218
|
+
|
219
|
+
include Equatable
|
220
|
+
|
221
|
+
def initialize(keyword:, node:)
|
222
|
+
@keyword = keyword
|
223
|
+
@node = node
|
224
|
+
end
|
117
225
|
|
118
|
-
|
226
|
+
def key_node
|
227
|
+
if node.type == :pair
|
228
|
+
node.children[0]
|
119
229
|
end
|
120
|
-
else
|
121
|
-
[]
|
122
230
|
end
|
123
231
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
232
|
+
def value_node
|
233
|
+
if node.type == :pair
|
234
|
+
node.children[1]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
128
238
|
|
129
|
-
|
239
|
+
class MissingKeyword
|
240
|
+
attr_reader :keywords
|
130
241
|
|
131
|
-
|
132
|
-
|
242
|
+
include Equatable
|
243
|
+
|
244
|
+
def initialize(keywords:)
|
245
|
+
@keywords = keywords
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
attr_reader :kwarg_nodes
|
250
|
+
attr_reader :keyword_params
|
251
|
+
attr_reader :index
|
252
|
+
attr_reader :consumed_keywords
|
253
|
+
|
254
|
+
def initialize(kwarg_nodes:, keyword_params:, index: 0, consumed_keywords: Set[])
|
255
|
+
@kwarg_nodes = kwarg_nodes
|
256
|
+
@keyword_params = keyword_params
|
257
|
+
@index = index
|
258
|
+
@consumed_keywords = consumed_keywords
|
259
|
+
end
|
260
|
+
|
261
|
+
def update(index: self.index, consumed_keywords: self.consumed_keywords)
|
262
|
+
KeywordArgs.new(
|
263
|
+
kwarg_nodes: kwarg_nodes,
|
264
|
+
keyword_params: keyword_params,
|
265
|
+
index: index,
|
266
|
+
consumed_keywords: consumed_keywords
|
267
|
+
)
|
268
|
+
end
|
269
|
+
|
270
|
+
def keyword_pair
|
271
|
+
kwarg_nodes[index]
|
272
|
+
end
|
273
|
+
|
274
|
+
def required_keywords
|
275
|
+
keyword_params.requireds
|
276
|
+
end
|
277
|
+
|
278
|
+
def optional_keywords
|
279
|
+
keyword_params.optionals
|
280
|
+
end
|
281
|
+
|
282
|
+
def rest_type
|
283
|
+
keyword_params.rest
|
284
|
+
end
|
285
|
+
|
286
|
+
def keyword_type(key)
|
287
|
+
required_keywords[key] || optional_keywords[key]
|
288
|
+
end
|
289
|
+
|
290
|
+
def all_keys
|
291
|
+
keys = Set.new
|
292
|
+
keys.merge(required_keywords.each_key)
|
293
|
+
keys.merge(optional_keywords.each_key)
|
294
|
+
keys.sort_by(&:to_s).to_a
|
295
|
+
end
|
296
|
+
|
297
|
+
def all_values
|
298
|
+
keys = Set.new
|
299
|
+
keys.merge(required_keywords.each_value)
|
300
|
+
keys.merge(optional_keywords.each_value)
|
301
|
+
keys.sort_by(&:to_s).to_a
|
302
|
+
end
|
303
|
+
|
304
|
+
def possible_key_type
|
305
|
+
key_types = all_keys.map {|key| AST::Types::Literal.new(value: key) }
|
306
|
+
key_types << AST::Builtin::Symbol.instance_type if rest_type
|
307
|
+
|
308
|
+
AST::Types::Union.build(types: key_types)
|
309
|
+
end
|
310
|
+
|
311
|
+
def possible_value_type
|
312
|
+
value_types = all_values
|
313
|
+
value_types << rest_type if rest_type
|
314
|
+
|
315
|
+
AST::Types::Intersection.build(types: value_types)
|
316
|
+
end
|
317
|
+
|
318
|
+
def next()
|
319
|
+
node = keyword_pair
|
320
|
+
|
321
|
+
if node
|
322
|
+
case node.type
|
323
|
+
when :pair
|
324
|
+
key_node, value_node = node.children
|
325
|
+
|
326
|
+
if key_node.type == :sym
|
327
|
+
key = key_node.children[0]
|
328
|
+
|
329
|
+
case
|
330
|
+
when value_type = keyword_type(key)
|
331
|
+
[
|
332
|
+
ArgTypePairs.new(
|
333
|
+
pairs: [
|
334
|
+
[key_node, AST::Types::Literal.new(value: key)],
|
335
|
+
[value_node, value_type]
|
336
|
+
]
|
337
|
+
),
|
338
|
+
update(
|
339
|
+
index: index+1,
|
340
|
+
consumed_keywords: consumed_keywords + [key]
|
341
|
+
)
|
342
|
+
]
|
343
|
+
when value_type = rest_type
|
344
|
+
[
|
345
|
+
ArgTypePairs.new(
|
346
|
+
pairs: [
|
347
|
+
[key_node, AST::Builtin::Symbol.instance_type],
|
348
|
+
[value_node, value_type]
|
349
|
+
]
|
350
|
+
),
|
351
|
+
update(
|
352
|
+
index: index+1,
|
353
|
+
consumed_keywords: consumed_keywords + [key]
|
354
|
+
)
|
355
|
+
]
|
356
|
+
else
|
357
|
+
[
|
358
|
+
UnexpectedKeyword.new(keyword: key, node: node),
|
359
|
+
update(index: index+1)
|
360
|
+
]
|
361
|
+
end
|
362
|
+
else
|
363
|
+
if !all_keys.empty? || rest_type
|
364
|
+
[
|
365
|
+
ArgTypePairs.new(
|
366
|
+
pairs: [
|
367
|
+
[key_node, possible_key_type],
|
368
|
+
[value_node, possible_value_type]
|
369
|
+
]
|
370
|
+
),
|
371
|
+
update(index: index+1)
|
372
|
+
]
|
373
|
+
else
|
374
|
+
[
|
375
|
+
UnexpectedKeyword.new(keyword: nil, node: node),
|
376
|
+
update(index: index+1)
|
377
|
+
]
|
378
|
+
end
|
379
|
+
end
|
380
|
+
when :kwsplat
|
381
|
+
[
|
382
|
+
SplatArg.new(node: node),
|
383
|
+
self
|
384
|
+
]
|
133
385
|
end
|
134
386
|
else
|
135
|
-
|
387
|
+
left = Set.new(required_keywords.keys) - consumed_keywords
|
388
|
+
unless left.empty?
|
389
|
+
[
|
390
|
+
MissingKeyword.new(keywords: left),
|
391
|
+
update(consumed_keywords: consumed_keywords + left)
|
392
|
+
]
|
393
|
+
end
|
136
394
|
end
|
395
|
+
end
|
137
396
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
last_arg = args.last
|
397
|
+
def consume_keys(keys, node:)
|
398
|
+
consumed_keys = []
|
399
|
+
types = []
|
142
400
|
|
143
|
-
|
401
|
+
unexpected_keyword = nil
|
144
402
|
|
145
|
-
|
146
|
-
|
403
|
+
keys.each do |key|
|
404
|
+
case
|
405
|
+
when type = keyword_type(key)
|
406
|
+
consumed_keys << key
|
407
|
+
types << type
|
408
|
+
when rest_type
|
409
|
+
types << rest_type
|
147
410
|
else
|
148
|
-
|
149
|
-
ps + [p]
|
150
|
-
end + no_keyword
|
411
|
+
unexpected_keyword = key
|
151
412
|
end
|
413
|
+
end
|
414
|
+
|
415
|
+
[
|
416
|
+
if unexpected_keyword
|
417
|
+
UnexpectedKeyword.new(keyword: unexpected_keyword, node: node)
|
418
|
+
else
|
419
|
+
types
|
420
|
+
end,
|
421
|
+
update(index: index + 1, consumed_keywords: consumed_keywords + consumed_keys)
|
422
|
+
]
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
class BlockPassArg
|
427
|
+
attr_reader :node
|
428
|
+
attr_reader :block
|
429
|
+
|
430
|
+
def initialize(node:, block:)
|
431
|
+
@node = node
|
432
|
+
@block = block
|
433
|
+
end
|
434
|
+
|
435
|
+
include Equatable
|
436
|
+
|
437
|
+
def no_block?
|
438
|
+
!node && !block
|
439
|
+
end
|
440
|
+
|
441
|
+
def compatible?
|
442
|
+
if node
|
443
|
+
block ? true : false
|
152
444
|
else
|
153
|
-
|
445
|
+
!block || block.optional?
|
154
446
|
end
|
447
|
+
end
|
155
448
|
|
156
|
-
|
157
|
-
|
158
|
-
|
449
|
+
def block_missing?
|
450
|
+
!node && block&.required?
|
451
|
+
end
|
159
452
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
453
|
+
def unexpected_block?
|
454
|
+
node && !block
|
455
|
+
end
|
456
|
+
|
457
|
+
def pair
|
458
|
+
raise unless compatible?
|
459
|
+
|
460
|
+
if node && block
|
461
|
+
[
|
462
|
+
node,
|
463
|
+
block.type
|
464
|
+
]
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
def node_type
|
469
|
+
type = AST::Types::Proc.new(type: block.type, block: nil)
|
470
|
+
|
471
|
+
if block.optional?
|
472
|
+
type = AST::Types::Union.build(types: [type, AST::Builtin.nil_type])
|
473
|
+
end
|
474
|
+
|
475
|
+
type
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
attr_reader :node
|
480
|
+
attr_reader :arguments
|
481
|
+
attr_reader :method_type
|
482
|
+
attr_reader :method_name
|
483
|
+
|
484
|
+
def initialize(node:, arguments:, method_name:, method_type:)
|
485
|
+
@node = node
|
486
|
+
@arguments = arguments
|
487
|
+
@method_type = method_type
|
488
|
+
@method_name = method_name
|
489
|
+
end
|
490
|
+
|
491
|
+
def positional_params
|
492
|
+
method_type.type.params.positional_params
|
493
|
+
end
|
494
|
+
|
495
|
+
def keyword_params
|
496
|
+
method_type.type.params.keyword_params
|
497
|
+
end
|
498
|
+
|
499
|
+
def kwargs_node
|
500
|
+
unless keyword_params.empty?
|
501
|
+
arguments.find {|node| node.type == :kwargs }
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
def positional_arg
|
506
|
+
args = if keyword_params.empty?
|
507
|
+
arguments.take_while {|node| node.type != :block_pass }
|
508
|
+
else
|
509
|
+
arguments.take_while {|node| node.type != :kwargs && node.type != :block_pass }
|
510
|
+
end
|
511
|
+
PositionalArgs.new(args: args, index: 0, positional_params: positional_params)
|
512
|
+
end
|
513
|
+
|
514
|
+
def keyword_args
|
515
|
+
KeywordArgs.new(
|
516
|
+
kwarg_nodes: kwargs_node&.children || [],
|
517
|
+
keyword_params: keyword_params
|
518
|
+
)
|
519
|
+
end
|
520
|
+
|
521
|
+
def block_pass_arg
|
522
|
+
node = arguments.find {|node| node.type == :block_pass }
|
523
|
+
block = method_type.block
|
524
|
+
|
525
|
+
BlockPassArg.new(node: node, block: block)
|
526
|
+
end
|
527
|
+
|
528
|
+
def each
|
529
|
+
if block_given?
|
530
|
+
errors = []
|
531
|
+
positional_count = 0
|
532
|
+
|
533
|
+
positional_arg.tap do |args|
|
534
|
+
while (value, args = args.next())
|
535
|
+
yield value
|
536
|
+
|
537
|
+
case value
|
538
|
+
when PositionalArgs::SplatArg
|
539
|
+
type = value.type
|
540
|
+
|
541
|
+
case type
|
542
|
+
when nil
|
543
|
+
raise
|
544
|
+
when AST::Types::Tuple
|
545
|
+
ts, args = args.consume(type.types.size, node: value.node)
|
546
|
+
|
547
|
+
case ts
|
548
|
+
when Array
|
549
|
+
ty = AST::Types::Tuple.new(types: ts.map(&:type))
|
550
|
+
yield PositionalArgs::NodeTypePair.new(node: value.node, type: ty)
|
551
|
+
when PositionalArgs::UnexpectedArg
|
552
|
+
errors << ts
|
553
|
+
yield ts
|
554
|
+
end
|
555
|
+
else
|
556
|
+
if t = args.uniform_type
|
557
|
+
args.following_args.each do |node|
|
558
|
+
yield PositionalArgs::NodeTypePair.new(node: node, type: t)
|
559
|
+
end
|
560
|
+
else
|
561
|
+
args.following_args.each do |node|
|
562
|
+
arg = PositionalArgs::UnexpectedArg.new(node: node)
|
563
|
+
yield arg
|
564
|
+
errors << arg
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
break
|
569
|
+
end
|
570
|
+
when PositionalArgs::UnexpectedArg, PositionalArgs::MissingArg
|
571
|
+
errors << value
|
572
|
+
end
|
167
573
|
end
|
574
|
+
end
|
168
575
|
|
169
|
-
|
170
|
-
|
171
|
-
|
576
|
+
keyword_args.tap do |args|
|
577
|
+
while (a, args = args.next)
|
578
|
+
case a
|
579
|
+
when KeywordArgs::MissingKeyword
|
580
|
+
errors << a
|
581
|
+
when KeywordArgs::UnexpectedKeyword
|
582
|
+
errors << a
|
583
|
+
end
|
584
|
+
|
585
|
+
yield a
|
586
|
+
|
587
|
+
case a
|
588
|
+
when KeywordArgs::SplatArg
|
589
|
+
case type = a.type
|
590
|
+
when nil
|
591
|
+
raise
|
592
|
+
when AST::Types::Record
|
593
|
+
keys = type.elements.keys
|
594
|
+
ts, args = args.consume_keys(keys, node: a.node)
|
595
|
+
|
596
|
+
case ts
|
597
|
+
when KeywordArgs::UnexpectedKeyword
|
598
|
+
yield ts
|
599
|
+
errors << ts
|
600
|
+
when Array
|
601
|
+
record = AST::Types::Record.new(elements: Hash[keys.zip(ts)])
|
602
|
+
yield KeywordArgs::ArgTypePairs.new(pairs: [[a.node, record]])
|
603
|
+
end
|
604
|
+
else
|
605
|
+
args = args.update(index: args.index + 1)
|
606
|
+
|
607
|
+
if args.rest_type
|
608
|
+
type = AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type, args.possible_value_type)
|
609
|
+
yield KeywordArgs::ArgTypePairs.new(pairs: [[a.node, type]])
|
610
|
+
else
|
611
|
+
yield KeywordArgs::UnexpectedKeyword.new(keyword: nil, node: a.node)
|
612
|
+
end
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
172
616
|
end
|
173
617
|
|
174
|
-
|
175
|
-
if
|
176
|
-
|
177
|
-
|
618
|
+
pass = block_pass_arg
|
619
|
+
# if pass.node
|
620
|
+
# yield pass
|
621
|
+
# end
|
178
622
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
623
|
+
diagnostics = []
|
624
|
+
|
625
|
+
missing_keywords = []
|
626
|
+
errors.each do |error|
|
627
|
+
case error
|
628
|
+
when KeywordArgs::UnexpectedKeyword
|
629
|
+
diagnostics << Diagnostic::Ruby::UnexpectedKeywordArgument.new(
|
630
|
+
node: error.node,
|
631
|
+
method_type: method_type,
|
632
|
+
method_name: method_name
|
633
|
+
)
|
634
|
+
when KeywordArgs::MissingKeyword
|
635
|
+
missing_keywords.push(*error.keywords)
|
636
|
+
when PositionalArgs::UnexpectedArg
|
637
|
+
diagnostics << Diagnostic::Ruby::UnexpectedPositionalArgument.new(
|
638
|
+
node: error.node,
|
639
|
+
method_type: method_type,
|
640
|
+
method_name: method_name
|
641
|
+
)
|
642
|
+
when PositionalArgs::MissingArg
|
643
|
+
diagnostics << Diagnostic::Ruby::InsufficientPositionalArguments.new(
|
644
|
+
node: node,
|
645
|
+
method_name: method_name,
|
646
|
+
method_type: method_type
|
647
|
+
)
|
184
648
|
end
|
649
|
+
end
|
185
650
|
|
186
|
-
|
187
|
-
|
188
|
-
|
651
|
+
unless missing_keywords.empty?
|
652
|
+
diagnostics << Diagnostic::Ruby::InsufficientKeywordArguments.new(
|
653
|
+
node: node,
|
654
|
+
method_name: method_name,
|
655
|
+
method_type: method_type,
|
656
|
+
missing_keywords: missing_keywords
|
657
|
+
)
|
189
658
|
end
|
659
|
+
|
660
|
+
diagnostics
|
190
661
|
else
|
191
|
-
|
662
|
+
enum_for :each
|
192
663
|
end
|
193
664
|
end
|
194
665
|
end
|