steep 0.43.0 → 0.45.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +8 -0
  3. data/.github/workflows/ruby.yml +3 -2
  4. data/.gitignore +0 -1
  5. data/CHANGELOG.md +30 -0
  6. data/Gemfile +0 -1
  7. data/Gemfile.lock +77 -0
  8. data/bin/output_test.rb +8 -2
  9. data/lib/steep.rb +4 -1
  10. data/lib/steep/ast/builtin.rb +7 -1
  11. data/lib/steep/ast/types/factory.rb +19 -25
  12. data/lib/steep/diagnostic/ruby.rb +137 -60
  13. data/lib/steep/diagnostic/signature.rb +34 -0
  14. data/lib/steep/equatable.rb +21 -0
  15. data/lib/steep/index/source_index.rb +55 -5
  16. data/lib/steep/interface/block.rb +4 -0
  17. data/lib/steep/interface/function.rb +798 -579
  18. data/lib/steep/server/interaction_worker.rb +239 -20
  19. data/lib/steep/server/master.rb +40 -19
  20. data/lib/steep/server/type_check_worker.rb +68 -0
  21. data/lib/steep/services/file_loader.rb +26 -19
  22. data/lib/steep/services/goto_service.rb +322 -0
  23. data/lib/steep/services/hover_content.rb +131 -79
  24. data/lib/steep/services/type_check_service.rb +25 -0
  25. data/lib/steep/source.rb +7 -10
  26. data/lib/steep/type_construction.rb +496 -518
  27. data/lib/steep/type_inference/block_params.rb +2 -5
  28. data/lib/steep/type_inference/method_params.rb +483 -0
  29. data/lib/steep/type_inference/send_args.rb +610 -128
  30. data/lib/steep/typing.rb +46 -21
  31. data/lib/steep/version.rb +1 -1
  32. data/sig/steep/type_inference/send_args.rbs +42 -0
  33. data/smoke/array/test_expectations.yml +3 -3
  34. data/smoke/block/c.rb +0 -1
  35. data/smoke/class/test_expectations.yml +12 -15
  36. data/smoke/const/test_expectations.yml +0 -10
  37. data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
  38. data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
  39. data/smoke/diagnostics-ruby-unsat/Steepfile +5 -0
  40. data/smoke/diagnostics-ruby-unsat/a.rbs +3 -0
  41. data/smoke/diagnostics-ruby-unsat/test_expectations.yml +27 -0
  42. data/smoke/{diagnostics → diagnostics-ruby-unsat}/unsatisfiable_constraint.rb +0 -1
  43. data/smoke/diagnostics/a.rbs +0 -4
  44. data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
  45. data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
  46. data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
  47. data/smoke/diagnostics/test_expectations.yml +108 -57
  48. data/smoke/ensure/test_expectations.yml +3 -3
  49. data/smoke/enumerator/test_expectations.yml +1 -1
  50. data/smoke/literal/test_expectations.yml +2 -2
  51. data/smoke/method/test_expectations.yml +11 -10
  52. data/smoke/regression/issue_372.rb +8 -0
  53. data/smoke/regression/issue_372.rbs +4 -0
  54. data/smoke/regression/test_expectations.yml +0 -12
  55. data/smoke/rescue/test_expectations.yml +3 -3
  56. data/smoke/toplevel/test_expectations.yml +3 -3
  57. data/smoke/tsort/test_expectations.yml +2 -2
  58. data/steep.gemspec +2 -2
  59. metadata +24 -10
@@ -134,13 +134,10 @@ module Steep
134
134
  rest = rest_param&.yield_self {|param| param.type.args[0] }
135
135
  end
136
136
 
137
- Interface::Function::Params.new(
137
+ Interface::Function::Params.build(
138
138
  required: leadings,
139
139
  optional: optionals,
140
- rest: rest,
141
- required_keywords: {},
142
- optional_keywords: {},
143
- rest_keywords: nil
140
+ rest: rest
144
141
  )
145
142
  end
146
143
 
@@ -0,0 +1,483 @@
1
+ module Steep
2
+ module TypeInference
3
+ class MethodParams
4
+ class BaseParameter
5
+ attr_reader :name
6
+ attr_reader :type
7
+ attr_reader :node
8
+
9
+ def initialize(name:, type:, node:)
10
+ @name = name
11
+ @type = type
12
+ @node = node
13
+ end
14
+
15
+ def optional?
16
+ case node.type
17
+ when :optarg, :kwoptarg
18
+ true
19
+ else
20
+ false
21
+ end
22
+ end
23
+
24
+ def value
25
+ case node.type
26
+ when :optarg, :kwoptarg
27
+ node.children[1]
28
+ end
29
+ end
30
+
31
+ def var_type
32
+ type || AST::Builtin.any_type
33
+ end
34
+
35
+ def untyped?
36
+ !type
37
+ end
38
+
39
+ def ==(other)
40
+ other.class == self.class &&
41
+ other.name == name &&
42
+ other.type == type &&
43
+ other.value == value &&
44
+ other.node == node
45
+ end
46
+
47
+ alias eql? ==
48
+
49
+ def hash
50
+ self.class.hash ^ name.hash ^ type.hash ^ value.hash ^ node.hash
51
+ end
52
+ end
53
+
54
+ class PositionalParameter < BaseParameter; end
55
+ class KeywordParameter < BaseParameter; end
56
+
57
+ class BaseRestParameter
58
+ attr_reader :name
59
+ attr_reader :type
60
+ attr_reader :node
61
+
62
+ def initialize(name:, type:, node:)
63
+ @name = name
64
+ @type = type
65
+ @node = node
66
+ end
67
+
68
+ def ==(other)
69
+ other.class == self.class &&
70
+ other.name == name &&
71
+ other.type == type &&
72
+ other.node == node
73
+ end
74
+
75
+ alias eql? ==
76
+
77
+ def hash
78
+ self.class.hash ^ name.hash ^ type.hash ^ node.hash
79
+ end
80
+ end
81
+
82
+ class PositionalRestParameter < BaseRestParameter
83
+ def var_type
84
+ AST::Builtin::Array.instance_type(type || AST::Builtin.any_type)
85
+ end
86
+ end
87
+
88
+ class KeywordRestParameter < BaseRestParameter
89
+ def var_type
90
+ AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type, type || AST::Builtin.any_type)
91
+ end
92
+ end
93
+
94
+ class BlockParameter
95
+ attr_reader :name
96
+ attr_reader :type
97
+ attr_reader :node
98
+
99
+ def initialize(name:, type:, node:, optional:)
100
+ @name = name
101
+ @type = type
102
+ @node = node
103
+ @optional = optional
104
+ end
105
+
106
+ def optional?
107
+ @optional ? true : false
108
+ end
109
+
110
+ def var_type
111
+ if type
112
+ proc_type = AST::Types::Proc.new(type: type, block: nil)
113
+
114
+ if optional?
115
+ AST::Types::Union.build(types: [proc_type, AST::Builtin.nil_type], location: proc_type.location)
116
+ else
117
+ proc_type
118
+ end
119
+ else
120
+ AST::Builtin.nil_type
121
+ end
122
+ end
123
+
124
+ def ==(other)
125
+ other.class == self.class &&
126
+ other.name == name &&
127
+ other.type == type &&
128
+ other.node == node &&
129
+ other.optional? == optional?
130
+ end
131
+
132
+ alias eql? ==
133
+
134
+ def hash
135
+ self.class.hash ^ name.hash ^ type.hash ^ node.hash ^ optional?.hash
136
+ end
137
+ end
138
+
139
+ attr_reader :args
140
+ attr_reader :method_type
141
+ attr_reader :params
142
+ attr_reader :errors
143
+
144
+ def initialize(args:, method_type:)
145
+ @args = args
146
+ @method_type = method_type
147
+ @params = {}
148
+ @errors = []
149
+ end
150
+
151
+ def [](name)
152
+ params[name] or raise "Unknown variable name: #{name}"
153
+ end
154
+
155
+ def size
156
+ params.size
157
+ end
158
+
159
+ def each_param(&block)
160
+ params.each_value(&block)
161
+ end
162
+
163
+ def each
164
+ if block_given?
165
+ each_param do |param|
166
+ yield param.name, param.var_type
167
+ end
168
+ else
169
+ enum_for :each
170
+ end
171
+ end
172
+
173
+ def self.empty(node:)
174
+ args_node =
175
+ case node.type
176
+ when :def
177
+ node.children[1]
178
+ when :defs
179
+ node.children[2]
180
+ else
181
+ raise
182
+ end
183
+
184
+ params = new(args: args_node.children, method_type: nil)
185
+
186
+ args_node.children.each do |arg|
187
+ case arg.type
188
+ when :arg, :optarg
189
+ name = arg.children[0]
190
+ params.params[name] = PositionalParameter.new(name: name, type: nil, node: arg)
191
+ when :kwarg, :kwoptarg
192
+ name = arg.children[0]
193
+ params.params[name] = KeywordParameter.new(name: name, type: nil, node: arg)
194
+ when :restarg
195
+ name = arg.children[0]
196
+ params.params[name] = PositionalRestParameter.new(name: name, type: nil, node: arg)
197
+ when :kwrestarg
198
+ name = arg.children[0]
199
+ params.params[name] = KeywordRestParameter.new(name: name, type: nil, node: arg)
200
+ when :blockarg
201
+ name = arg.children[0]
202
+ params.params[name] = BlockParameter.new(name: name, type: nil, optional: nil, node: arg)
203
+ end
204
+ end
205
+
206
+ params
207
+ end
208
+
209
+ def self.build(node:, method_type:)
210
+ args_node =
211
+ case node.type
212
+ when :def
213
+ node.children[1]
214
+ when :defs
215
+ node.children[2]
216
+ else
217
+ raise
218
+ end
219
+ original = args_node.children
220
+ args = original.dup
221
+
222
+ instance = new(args: original, method_type: method_type)
223
+
224
+ positional_params = method_type.type.params.positional_params
225
+
226
+ loop do
227
+ arg = args.first
228
+
229
+ case arg&.type
230
+ when :arg
231
+ name = arg.children[0]
232
+ param = positional_params&.head
233
+
234
+ case param
235
+ when Interface::Function::Params::PositionalParams::Required
236
+ instance.params[name] = PositionalParameter.new(name: name, type: param.type, node: arg)
237
+ when Interface::Function::Params::PositionalParams::Optional
238
+ method_param = PositionalParameter.new(name: name, type: param.type, node: arg)
239
+ instance.params[name] = method_param
240
+ instance.errors << Diagnostic::Ruby::MethodParameterMismatch.new(
241
+ method_param: method_param,
242
+ method_type: method_type
243
+ )
244
+ when Interface::Function::Params::PositionalParams::Rest
245
+ method_param = PositionalParameter.new(name: name, type: param.type, node: arg)
246
+ instance.params[name] = method_param
247
+ instance.errors << Diagnostic::Ruby::MethodParameterMismatch.new(
248
+ method_param: method_param,
249
+ method_type: method_type
250
+ )
251
+ when nil
252
+ method_param = PositionalParameter.new(name: name, type: nil, node: arg)
253
+ instance.params[name] = method_param
254
+ instance.errors << Diagnostic::Ruby::MethodParameterMismatch.new(
255
+ method_param: method_param,
256
+ method_type: method_type
257
+ )
258
+ end
259
+
260
+ positional_params = positional_params&.tail
261
+
262
+ when :optarg
263
+ name = arg.children[0]
264
+ param = positional_params&.head
265
+
266
+ case param
267
+ when Interface::Function::Params::PositionalParams::Required
268
+ method_param = PositionalParameter.new(name: name, type: param.type, node: arg)
269
+ instance.params[name] = method_param
270
+ instance.errors << Diagnostic::Ruby::DifferentMethodParameterKind.new(
271
+ method_param: method_param,
272
+ method_type: method_type
273
+ )
274
+ when Interface::Function::Params::PositionalParams::Optional
275
+ instance.params[name] = PositionalParameter.new(name: name, type: param.type, node: arg)
276
+ when Interface::Function::Params::PositionalParams::Rest
277
+ method_param = PositionalParameter.new(name: name, type: param.type, node: arg)
278
+ instance.params[name] = method_param
279
+ instance.errors << Diagnostic::Ruby::DifferentMethodParameterKind.new(
280
+ method_param: method_param,
281
+ method_type: method_type
282
+ )
283
+ when nil
284
+ method_param = PositionalParameter.new(name: name, type: nil, node: arg)
285
+ instance.params[name] = method_param
286
+ instance.errors << Diagnostic::Ruby::DifferentMethodParameterKind.new(
287
+ method_param: method_param,
288
+ method_type: method_type
289
+ )
290
+ end
291
+
292
+ positional_params = positional_params&.tail
293
+ else
294
+ break
295
+ end
296
+
297
+ args.shift
298
+ end
299
+
300
+ if (arg = args.first)&.type == :restarg
301
+ name = arg.children[0]
302
+ rest_types = []
303
+ has_error = false
304
+
305
+ loop do
306
+ param = positional_params&.head
307
+
308
+ case param
309
+ when Interface::Function::Params::PositionalParams::Required
310
+ rest_types << param.type
311
+ has_error = true
312
+ when Interface::Function::Params::PositionalParams::Optional
313
+ rest_types << param.type
314
+ has_error = true
315
+ when Interface::Function::Params::PositionalParams::Rest
316
+ rest_types << param.type
317
+ positional_params = nil
318
+ break
319
+ when nil
320
+ has_error = true
321
+ break
322
+ end
323
+
324
+ positional_params = positional_params.tail
325
+ end
326
+
327
+ type = rest_types.empty? ? nil : AST::Types::Union.build(types: rest_types)
328
+
329
+ method_param = PositionalRestParameter.new(name: name, type: type, node: arg)
330
+ instance.params[name] = method_param
331
+ if has_error
332
+ instance.errors << Diagnostic::Ruby::DifferentMethodParameterKind.new(
333
+ method_param: method_param,
334
+ method_type: method_type
335
+ )
336
+ end
337
+ end
338
+
339
+ if positional_params
340
+ instance.errors << Diagnostic::Ruby::MethodArityMismatch.new(node: node, method_type: method_type)
341
+ end
342
+
343
+ keyword_params = method_type.type.params.keyword_params
344
+ keywords = keyword_params.keywords
345
+
346
+ loop do
347
+ arg = args.first
348
+
349
+ case arg&.type
350
+ when :kwarg
351
+ name = arg.children[0]
352
+
353
+ case
354
+ when type = keyword_params.requireds[name]
355
+ instance.params[name] = KeywordParameter.new(name: name, type: type, node: arg)
356
+ keywords.delete(name)
357
+ when type = keyword_params.optionals[name]
358
+ method_param = KeywordParameter.new(name: name, type: type, node: arg)
359
+ instance.params[name] = method_param
360
+ instance.errors << Diagnostic::Ruby::MethodParameterMismatch.new(
361
+ method_param: method_param,
362
+ method_type: method_type
363
+ )
364
+ keywords.delete(name)
365
+ when type = keyword_params.rest
366
+ method_param = KeywordParameter.new(name: name, type: type, node: arg)
367
+ instance.params[name] = method_param
368
+ instance.errors << Diagnostic::Ruby::MethodParameterMismatch.new(
369
+ method_param: method_param,
370
+ method_type: method_type
371
+ )
372
+ keywords.delete(name)
373
+ else
374
+ method_param = KeywordParameter.new(name: name, type: nil, node: arg)
375
+ instance.params[name] = method_param
376
+ instance.errors << Diagnostic::Ruby::MethodParameterMismatch.new(
377
+ method_param: method_param,
378
+ method_type: method_type
379
+ )
380
+ end
381
+ when :kwoptarg
382
+ name = arg.children[0]
383
+
384
+ case
385
+ when type = keyword_params.requireds[name]
386
+ method_param = KeywordParameter.new(name: name, type: type, node: arg)
387
+ instance.params[name] = method_param
388
+ instance.errors << Diagnostic::Ruby::DifferentMethodParameterKind.new(
389
+ method_param: method_param,
390
+ method_type: method_type
391
+ )
392
+ keywords.delete(name)
393
+ when type = keyword_params.optionals[name]
394
+ method_param = KeywordParameter.new(name: name, type: type, node: arg)
395
+ instance.params[name] = method_param
396
+ keywords.delete(name)
397
+ when type = keyword_params.rest
398
+ method_param = KeywordParameter.new(name: name, type: type, node: arg)
399
+ instance.params[name] = method_param
400
+ instance.errors << Diagnostic::Ruby::DifferentMethodParameterKind.new(
401
+ method_param: method_param,
402
+ method_type: method_type
403
+ )
404
+ keywords.delete(name)
405
+ else
406
+ method_param = KeywordParameter.new(name: name, type: nil, node: arg)
407
+ instance.params[name] = method_param
408
+ instance.errors << Diagnostic::Ruby::DifferentMethodParameterKind.new(
409
+ method_param: method_param,
410
+ method_type: method_type
411
+ )
412
+ end
413
+ else
414
+ break
415
+ end
416
+
417
+ args.shift
418
+ end
419
+
420
+ if (arg = args.first)&.type == :kwrestarg
421
+ name = arg.children[0]
422
+ rest_types = []
423
+ has_error = false
424
+
425
+ keywords.each do |keyword|
426
+ rest_types << keyword_params.requireds[keyword] || keyword_params.optionals[keyword]
427
+ has_error = true
428
+ end
429
+ keywords.clear
430
+
431
+ if keyword_params.rest
432
+ rest_types << keyword_params.rest
433
+ else
434
+ has_error = true
435
+ end
436
+
437
+ type = rest_types.empty? ? nil : AST::Types::Union.build(types: rest_types)
438
+
439
+ method_param = KeywordRestParameter.new(name: name, type: type, node: arg)
440
+ instance.params[name] = method_param
441
+
442
+ if has_error
443
+ instance.errors << Diagnostic::Ruby::DifferentMethodParameterKind.new(
444
+ method_param: method_param,
445
+ method_type: method_type
446
+ )
447
+ end
448
+
449
+ args.shift
450
+ else
451
+ if !keywords.empty? || keyword_params.rest
452
+ instance.errors << Diagnostic::Ruby::MethodArityMismatch.new(
453
+ node: node,
454
+ method_type: method_type
455
+ )
456
+ end
457
+ end
458
+
459
+ if (arg = args.first)&.type == :blockarg
460
+ name = arg.children[0]
461
+
462
+ if method_type.block
463
+ instance.params[name] = BlockParameter.new(
464
+ name: name,
465
+ type: method_type.block.type,
466
+ optional: method_type.block.optional?,
467
+ node: arg
468
+ )
469
+ else
470
+ instance.params[name] = BlockParameter.new(
471
+ name: name,
472
+ type: nil,
473
+ optional: nil,
474
+ node: arg
475
+ )
476
+ end
477
+ end
478
+
479
+ instance
480
+ end
481
+ end
482
+ end
483
+ end