steep 0.44.1 → 0.47.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) 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 +43 -0
  6. data/Gemfile +1 -4
  7. data/Gemfile.lock +73 -0
  8. data/README.md +2 -1
  9. data/lib/steep/annotation_parser.rb +1 -1
  10. data/lib/steep/ast/builtin.rb +7 -1
  11. data/lib/steep/ast/types/factory.rb +19 -25
  12. data/lib/steep/cli.rb +7 -1
  13. data/lib/steep/diagnostic/lsp_formatter.rb +59 -6
  14. data/lib/steep/diagnostic/ruby.rb +188 -60
  15. data/lib/steep/diagnostic/signature.rb +38 -15
  16. data/lib/steep/drivers/check.rb +3 -0
  17. data/lib/steep/drivers/init.rb +10 -3
  18. data/lib/steep/drivers/utils/driver_helper.rb +15 -0
  19. data/lib/steep/drivers/validate.rb +1 -1
  20. data/lib/steep/drivers/watch.rb +3 -0
  21. data/lib/steep/equatable.rb +21 -0
  22. data/lib/steep/interface/function.rb +798 -579
  23. data/lib/steep/project/dsl.rb +135 -36
  24. data/lib/steep/project/options.rb +13 -53
  25. data/lib/steep/project/target.rb +22 -8
  26. data/lib/steep/server/interaction_worker.rb +245 -26
  27. data/lib/steep/server/type_check_worker.rb +6 -9
  28. data/lib/steep/services/file_loader.rb +26 -19
  29. data/lib/steep/services/hover_content.rb +135 -80
  30. data/lib/steep/source.rb +12 -11
  31. data/lib/steep/type_construction.rb +435 -502
  32. data/lib/steep/type_inference/block_params.rb +3 -6
  33. data/lib/steep/type_inference/method_params.rb +483 -0
  34. data/lib/steep/type_inference/send_args.rb +599 -128
  35. data/lib/steep/typing.rb +46 -21
  36. data/lib/steep/version.rb +1 -1
  37. data/lib/steep.rb +5 -3
  38. data/sample/Steepfile +10 -3
  39. data/smoke/alias/Steepfile +2 -1
  40. data/smoke/and/Steepfile +2 -1
  41. data/smoke/array/Steepfile +2 -1
  42. data/smoke/array/test_expectations.yml +3 -3
  43. data/smoke/block/Steepfile +2 -2
  44. data/smoke/block/c.rb +0 -1
  45. data/smoke/case/Steepfile +2 -1
  46. data/smoke/class/Steepfile +2 -1
  47. data/smoke/class/test_expectations.yml +12 -15
  48. data/smoke/const/Steepfile +2 -1
  49. data/smoke/diagnostics/Steepfile +2 -1
  50. data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
  51. data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
  52. data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
  53. data/smoke/diagnostics/test_expectations.yml +108 -31
  54. data/smoke/diagnostics-rbs/Steepfile +1 -1
  55. data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
  56. data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
  57. data/smoke/diagnostics-rbs-duplicated/Steepfile +2 -1
  58. data/smoke/diagnostics-ruby-unsat/Steepfile +2 -1
  59. data/smoke/dstr/Steepfile +2 -1
  60. data/smoke/ensure/Steepfile +2 -1
  61. data/smoke/ensure/test_expectations.yml +3 -3
  62. data/smoke/enumerator/Steepfile +2 -1
  63. data/smoke/enumerator/test_expectations.yml +1 -1
  64. data/smoke/extension/Steepfile +2 -1
  65. data/smoke/extension/e.rbs +1 -1
  66. data/smoke/hash/Steepfile +2 -1
  67. data/smoke/hello/Steepfile +2 -1
  68. data/smoke/if/Steepfile +2 -1
  69. data/smoke/implements/Steepfile +2 -1
  70. data/smoke/initialize/Steepfile +2 -1
  71. data/smoke/integer/Steepfile +2 -1
  72. data/smoke/interface/Steepfile +2 -1
  73. data/smoke/kwbegin/Steepfile +2 -1
  74. data/smoke/lambda/Steepfile +2 -1
  75. data/smoke/literal/Steepfile +2 -1
  76. data/smoke/literal/test_expectations.yml +2 -2
  77. data/smoke/map/Steepfile +2 -1
  78. data/smoke/method/Steepfile +2 -1
  79. data/smoke/method/test_expectations.yml +11 -10
  80. data/smoke/module/Steepfile +2 -1
  81. data/smoke/regexp/Steepfile +2 -1
  82. data/smoke/regression/Steepfile +2 -1
  83. data/smoke/rescue/Steepfile +2 -1
  84. data/smoke/rescue/test_expectations.yml +3 -3
  85. data/smoke/self/Steepfile +2 -1
  86. data/smoke/skip/Steepfile +2 -1
  87. data/smoke/stdout/Steepfile +2 -1
  88. data/smoke/super/Steepfile +2 -1
  89. data/smoke/toplevel/Steepfile +2 -1
  90. data/smoke/toplevel/test_expectations.yml +3 -3
  91. data/smoke/tsort/Steepfile +4 -5
  92. data/smoke/tsort/test_expectations.yml +2 -2
  93. data/smoke/type_case/Steepfile +2 -1
  94. data/smoke/unexpected/Steepfile +2 -1
  95. data/smoke/yield/Steepfile +2 -1
  96. data/steep.gemspec +2 -2
  97. metadata +16 -10
  98. data/sig/project.rbi +0 -109
@@ -131,16 +131,13 @@ module Steep
131
131
  else
132
132
  leadings = leading_params.map {|param| param.type || AST::Types::Any.new }
133
133
  optionals = optional_params.map {|param| param.type || AST::Types::Any.new }
134
- rest = rest_param&.yield_self {|param| param.type.args[0] }
134
+ rest = rest_param&.yield_self {|param| param.type&.args&.[](0) || AST::Types::Any.new }
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