steep 0.43.0 → 0.45.0

Sign up to get free protection for your applications and to get access to all the features.
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
data/lib/steep/typing.rb CHANGED
@@ -102,13 +102,20 @@ module Steep
102
102
  end
103
103
 
104
104
  def block_range(node)
105
- send_node, args_node, _ = node.children
106
- begin_pos = if send_node.type != :lambda && args_node.loc.expression
107
- args_node.loc.expression.end_pos
108
- else
109
- node.loc.begin.end_pos
110
- end
111
- end_pos = node.loc.end.begin_pos
105
+ case node.type
106
+ when :block
107
+ send_node, args_node, _ = node.children
108
+ begin_pos = if send_node.type != :lambda && args_node.loc.expression
109
+ args_node.loc.expression.end_pos
110
+ else
111
+ node.loc.begin.end_pos
112
+ end
113
+ end_pos = node.loc.end.begin_pos
114
+ when :numblock
115
+ send_node, _ = node.children
116
+ begin_pos = node.loc.begin.end_pos
117
+ end_pos = node.loc.end.begin_pos
118
+ end
112
119
 
113
120
  begin_pos..end_pos
114
121
  end
@@ -139,21 +146,39 @@ module Steep
139
146
  add_context(begin_pos..end_pos, context: context)
140
147
 
141
148
  when :def, :defs
142
- args_node = case node.type
143
- when :def
144
- node.children[1]
145
- when :defs
146
- node.children[2]
147
- end
148
- body_begin_pos = if args_node.loc.expression
149
- args_node.loc.expression.end_pos
150
- else
151
- node.loc.name.end_pos
152
- end
153
- body_end_pos = node.loc.end.begin_pos
154
- add_context(body_begin_pos..body_end_pos, context: context)
149
+ if node.children.last
150
+ args_node =
151
+ case node.type
152
+ when :def
153
+ node.children[1]
154
+ when :defs
155
+ node.children[2]
156
+ end
157
+
158
+ body_begin_pos =
159
+ case
160
+ when node.loc.assignment
161
+ # endless def
162
+ node.loc.assignment.end_pos
163
+ when args_node.loc.expression
164
+ # with args
165
+ args_node.loc.expression.end_pos
166
+ else
167
+ # without args
168
+ node.loc.name.end_pos
169
+ end
170
+
171
+ body_end_pos =
172
+ if node.loc.end
173
+ node.loc.end.begin_pos
174
+ else
175
+ node.loc.expression.end_pos
176
+ end
177
+
178
+ add_context(body_begin_pos..body_end_pos, context: context)
179
+ end
155
180
 
156
- when :block
181
+ when :block, :numblock
157
182
  range = block_range(node)
158
183
  add_context(range, context: context)
159
184
 
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "0.43.0"
2
+ VERSION = "0.45.0"
3
3
  end
@@ -0,0 +1,42 @@
1
+ module Steep
2
+ type node = untyped
3
+
4
+ module TypeInference
5
+ class SendArgs
6
+ class PositionalArgs
7
+ end
8
+
9
+ class KeywordArgs
10
+ type value = ArgTypePairs | UnexpectedKeyword | MissingKeyword
11
+
12
+ class ArgTypePairs
13
+ end
14
+
15
+ class UnexpectedKeyword
16
+ end
17
+
18
+ class MissingKeyword
19
+ end
20
+
21
+ def consume: () -> [value, self]?
22
+
23
+ def consume_keys: (Array[Symbol], node: node) -> [AST::Types::t | UnexpectedKeyword, self]
24
+ end
25
+
26
+
27
+ type error = Diagnostics::Ruby::IncompatibleArguments
28
+ | Diagnostics::Ruby::UnexpectedBlockGiven
29
+ | Diagnostics::Ruby::RequiredBlockMissing
30
+
31
+ type arg = PositionalArgs::NodeParamPair
32
+ | PositionalArgs::NodeTypePair
33
+ | PositionalArgs::SplatArg
34
+ | PositionalArgs::UnexpectedArg
35
+ | KeywordArgs::ArgTypePairs
36
+ | KeywordArgs::SplatArg
37
+ | KeywordArgs::UnexpectedKeyword
38
+
39
+ def each_arg: (TypeConstruction) { (arg, TypeConstruction) -> TypeConstruction } -> Array[error]
40
+ end
41
+ end
42
+ end
@@ -16,9 +16,9 @@
16
16
  | (::int, ::int, ::Integer) -> ::Integer
17
17
  | (::int, ::int, ::Array[::Integer]) -> ::Array[::Integer]
18
18
  | (::int, ::int, nil) -> nil
19
- | (::Range[::Integer], ::Integer) -> ::Integer
20
- | (::Range[::Integer], ::Array[::Integer]) -> ::Array[::Integer]
21
- | (::Range[::Integer], nil) -> nil
19
+ | (::Range[(::Integer | nil)], ::Integer) -> ::Integer
20
+ | (::Range[(::Integer | nil)], ::Array[::Integer]) -> ::Array[::Integer]
21
+ | (::Range[(::Integer | nil)], nil) -> nil
22
22
  code: Ruby::UnresolvedOverloading
23
23
  - range:
24
24
  start:
data/smoke/block/c.rb CHANGED
@@ -7,4 +7,3 @@ end
7
7
 
8
8
  OptionalBlock.new.optional_block()
9
9
  OptionalBlock.new.optional_block() { :foo }
10
-
@@ -4,20 +4,20 @@
4
4
  - range:
5
5
  start:
6
6
  line: 8
7
- character: 9
7
+ character: 10
8
8
  end:
9
9
  line: 8
10
- character: 12
10
+ character: 11
11
11
  severity: ERROR
12
- message: Method parameters are incompatible with declaration `() -> ::String`
13
- code: Ruby::MethodArityMismatch
12
+ message: The method parameter is incompatible with the declaration `() -> ::String`
13
+ code: Ruby::MethodParameterMismatch
14
14
  - range:
15
15
  start:
16
16
  line: 13
17
- character: 2
17
+ character: 6
18
18
  end:
19
- line: 15
20
- character: 5
19
+ line: 13
20
+ character: 10
21
21
  severity: ERROR
22
22
  message: |-
23
23
  Cannot allow method body have type `::Integer` because declared as type `::String`
@@ -29,10 +29,10 @@
29
29
  - range:
30
30
  start:
31
31
  line: 22
32
- character: 2
32
+ character: 11
33
33
  end:
34
- line: 24
35
- character: 5
34
+ line: 22
35
+ character: 14
36
36
  severity: ERROR
37
37
  message: |-
38
38
  Cannot allow method body have type `::String` because declared as type `::Integer`
@@ -113,8 +113,5 @@
113
113
  line: 10
114
114
  character: 11
115
115
  severity: ERROR
116
- message: |-
117
- Cannot find method `initialize` of type `::IncompatibleChild` with compatible arity
118
- Method types:
119
- def initialize: (name: ::String) -> untyped
120
- code: Ruby::IncompatibleArguments
116
+ message: 'More keyword arguments are required: name'
117
+ code: Ruby::InsufficientKeywordArguments
@@ -26,16 +26,6 @@
26
26
  severity: ERROR
27
27
  message: Cannot detect the type of the expression
28
28
  code: Ruby::FallbackAny
29
- - range:
30
- start:
31
- line: 8
32
- character: 7
33
- end:
34
- line: 8
35
- character: 8
36
- severity: ERROR
37
- message: Cannot detect the type of the expression
38
- code: Ruby::FallbackAny
39
29
  - range:
40
30
  start:
41
31
  line: 14
@@ -0,0 +1,6 @@
1
+ class Foo
2
+ end
3
+
4
+ class Bar
5
+ include Foo
6
+ end
@@ -229,3 +229,15 @@
229
229
  severity: ERROR
230
230
  message: Cannot find type `ZZZ`
231
231
  code: RBS::UnknownTypeName
232
+ - file: mixin-class-error.rbs
233
+ diagnostics:
234
+ - range:
235
+ start:
236
+ line: 5
237
+ character: 2
238
+ end:
239
+ line: 5
240
+ character: 13
241
+ severity: ERROR
242
+ message: Cannot include a class `::Foo` in the definition of `::Bar`
243
+ code: RBS::MixinClassError
@@ -0,0 +1,5 @@
1
+ target :test do
2
+ typing_options :strict
3
+ check "*.rb"
4
+ signature "*.rbs"
5
+ end
@@ -0,0 +1,3 @@
1
+ class UnsatisfiableConstraint
2
+ def foo: [A, B] (A) { (A) -> void } -> B
3
+ end
@@ -0,0 +1,27 @@
1
+ ---
2
+ - file: unsatisfiable_constraint.rb
3
+ diagnostics:
4
+ - range:
5
+ start:
6
+ line: 3
7
+ character: 0
8
+ end:
9
+ line: 6
10
+ character: 3
11
+ severity: ERROR
12
+ message: |-
13
+ Unsatisfiable constraint `::Array[untyped] <: A(2) <: ::String` is generated through (A(2)) { (A(2)) -> void } -> B(3)
14
+ ::Array[untyped] <: ::String
15
+ ::Object <: ::String
16
+ ::BasicObject <: ::String
17
+ code: Ruby::UnsatisfiableConstraint
18
+ - range:
19
+ start:
20
+ line: 5
21
+ character: 4
22
+ end:
23
+ line: 5
24
+ character: 7
25
+ severity: ERROR
26
+ message: Type `::String` does not have method `foo`
27
+ code: Ruby::NoMethod
@@ -1,4 +1,3 @@
1
-
2
1
  test = UnsatisfiableConstraint.new
3
2
 
4
3
  test.foo([]) do |x|
@@ -13,10 +13,6 @@ end
13
13
  class UnknownConstantAssigned
14
14
  end
15
15
 
16
- class UnsatisfiableConstraint
17
- def foo: [A, B] (A) { (A) -> void } -> B
18
- end
19
-
20
16
  class UnexpectedKeyword
21
17
  def foo: (foo: Integer) -> void
22
18
  end
@@ -0,0 +1,9 @@
1
+ class DifferentMethodParameterKind
2
+ # @type method foo: (Integer, String) -> void
3
+ def foo(a = 0, *b)
4
+ end
5
+
6
+ # @type method bar: (name: String, size: Integer) -> void
7
+ def bar(name: "foo", **rest)
8
+ end
9
+ end
@@ -1,6 +1,6 @@
1
1
  class MethodArityMismatch
2
- # @type method foo: () -> void
3
- def foo(x)
2
+ # @type method foo: (name: String) -> void
3
+ def foo()
4
4
 
5
5
  end
6
6
 
@@ -0,0 +1,10 @@
1
+ class MethodParameterMismatch
2
+ # @type method foo: (?String, *Integer) -> void
3
+ def foo(a, b)
4
+ end
5
+
6
+ # @type method bar: (?name: String) -> void
7
+ def self.bar(name:)
8
+
9
+ end
10
+ end
@@ -63,6 +63,52 @@
63
63
  ::Object <: ::Integer
64
64
  ::BasicObject <: ::Integer
65
65
  code: Ruby::BreakTypeMismatch
66
+ - file: different_method_parameter_kind.rb
67
+ diagnostics:
68
+ - range:
69
+ start:
70
+ line: 3
71
+ character: 10
72
+ end:
73
+ line: 3
74
+ character: 15
75
+ severity: ERROR
76
+ message: The method parameter has different kind from the declaration `(::Integer,
77
+ ::String) -> void`
78
+ code: Ruby::DifferentMethodParameterKind
79
+ - range:
80
+ start:
81
+ line: 3
82
+ character: 17
83
+ end:
84
+ line: 3
85
+ character: 19
86
+ severity: ERROR
87
+ message: The method parameter has different kind from the declaration `(::Integer,
88
+ ::String) -> void`
89
+ code: Ruby::DifferentMethodParameterKind
90
+ - range:
91
+ start:
92
+ line: 7
93
+ character: 10
94
+ end:
95
+ line: 7
96
+ character: 21
97
+ severity: ERROR
98
+ message: 'The method parameter has different kind from the declaration `(size:
99
+ ::Integer, name: ::String) -> void`'
100
+ code: Ruby::DifferentMethodParameterKind
101
+ - range:
102
+ start:
103
+ line: 7
104
+ character: 23
105
+ end:
106
+ line: 7
107
+ character: 29
108
+ severity: ERROR
109
+ message: 'The method parameter has different kind from the declaration `(size:
110
+ ::Integer, name: ::String) -> void`'
111
+ code: Ruby::DifferentMethodParameterKind
66
112
  - file: else_on_exhaustive_case.rb
67
113
  diagnostics:
68
114
  - range:
@@ -96,16 +142,23 @@
96
142
  - range:
97
143
  start:
98
144
  line: 1
99
- character: 6
145
+ character: 11
100
146
  end:
101
147
  line: 1
102
- character: 8
148
+ character: 12
103
149
  severity: ERROR
104
- message: |-
105
- Cannot find method `at` of type `::Array[::Integer]` with compatible arity
106
- Method types:
107
- def at: (::int) -> (::Integer | nil)
108
- code: Ruby::IncompatibleArguments
150
+ message: Unexpected positional argument
151
+ code: Ruby::UnexpectedPositionalArgument
152
+ - range:
153
+ start:
154
+ line: 1
155
+ character: 13
156
+ end:
157
+ line: 1
158
+ character: 14
159
+ severity: ERROR
160
+ message: Unexpected positional argument
161
+ code: Ruby::UnexpectedPositionalArgument
109
162
  - file: incompatible_assignment.rb
110
163
  diagnostics:
111
164
  - range:
@@ -160,9 +213,10 @@
160
213
  character: 9
161
214
  end:
162
215
  line: 3
163
- character: 12
216
+ character: 11
164
217
  severity: ERROR
165
- message: Method parameters are incompatible with declaration `() -> void`
218
+ message: 'Method parameters are incompatible with declaration `(name: ::String)
219
+ -> void`'
166
220
  code: Ruby::MethodArityMismatch
167
221
  - range:
168
222
  start:
@@ -179,10 +233,10 @@
179
233
  - range:
180
234
  start:
181
235
  line: 3
182
- character: 2
236
+ character: 6
183
237
  end:
184
- line: 5
185
- character: 5
238
+ line: 3
239
+ character: 9
186
240
  severity: ERROR
187
241
  message: |-
188
242
  Cannot allow method body have type `::String` because declared as type `::Integer`
@@ -202,6 +256,41 @@
202
256
  severity: ERROR
203
257
  message: Cannot find implementation of method `::MethodDefinitionMissing#foo`
204
258
  code: Ruby::MethodDefinitionMissing
259
+ - file: method_parameter_mismatch.rb
260
+ diagnostics:
261
+ - range:
262
+ start:
263
+ line: 3
264
+ character: 10
265
+ end:
266
+ line: 3
267
+ character: 11
268
+ severity: ERROR
269
+ message: The method parameter is incompatible with the declaration `(?::String,
270
+ *::Integer) -> void`
271
+ code: Ruby::MethodParameterMismatch
272
+ - range:
273
+ start:
274
+ line: 3
275
+ character: 13
276
+ end:
277
+ line: 3
278
+ character: 14
279
+ severity: ERROR
280
+ message: The method parameter is incompatible with the declaration `(?::String,
281
+ *::Integer) -> void`
282
+ code: Ruby::MethodParameterMismatch
283
+ - range:
284
+ start:
285
+ line: 7
286
+ character: 15
287
+ end:
288
+ line: 7
289
+ character: 20
290
+ severity: ERROR
291
+ message: 'The method parameter is incompatible with the declaration `(?name: ::String)
292
+ -> void`'
293
+ code: Ruby::MethodParameterMismatch
205
294
  - file: method_return_type_annotation_mismatch.rb
206
295
  diagnostics:
207
296
  - range:
@@ -224,13 +313,13 @@
224
313
  - range:
225
314
  start:
226
315
  line: 1
227
- character: 23
316
+ character: 19
228
317
  end:
229
318
  line: 1
230
319
  character: 32
231
320
  severity: ERROR
232
- message: 'Cannot omit required keywords: `foo`'
233
- code: Ruby::MissingKeyword
321
+ message: 'More keyword arguments are required: foo'
322
+ code: Ruby::InsufficientKeywordArguments
234
323
  - file: no_method.rb
235
324
  diagnostics:
236
325
  - range:
@@ -335,25 +424,13 @@
335
424
  - range:
336
425
  start:
337
426
  line: 1
338
- character: 26
339
- end:
340
- line: 1
341
- character: 45
342
- severity: ERROR
343
- message: 'Cannot specify unexpected keyword arguments: `bar`'
344
- code: Ruby::UnexpectedKeyword
345
- - file: unexpected_splat.rb
346
- diagnostics:
347
- - range:
348
- start:
349
- line: 1
350
- character: 13
427
+ character: 36
351
428
  end:
352
429
  line: 1
353
- character: 18
430
+ character: 39
354
431
  severity: ERROR
355
- message: Hash splat is given with object other than `Hash[X, Y]`
356
- code: Ruby::UnexpectedSplat
432
+ message: Unexpected keyword argument
433
+ code: Ruby::UnexpectedKeywordArgument
357
434
  - file: unexpected_yield.rb
358
435
  diagnostics:
359
436
  - range:
@@ -436,32 +513,6 @@
436
513
  | (::Rational) -> ::Rational
437
514
  | (::Complex) -> ::Complex
438
515
  code: Ruby::UnresolvedOverloading
439
- - file: unsatisfiable_constraint.rb
440
- diagnostics:
441
- - range:
442
- start:
443
- line: 4
444
- character: 0
445
- end:
446
- line: 7
447
- character: 3
448
- severity: ERROR
449
- message: |-
450
- Unsatisfiable constraint `::Array[untyped] <: A(1) <: ::String` is generated through (A(1)) { (A(1)) -> void } -> B(2)
451
- ::Array[untyped] <: ::String
452
- ::Object <: ::String
453
- ::BasicObject <: ::String
454
- code: Ruby::UnsatisfiableConstraint
455
- - range:
456
- start:
457
- line: 6
458
- character: 4
459
- end:
460
- line: 6
461
- character: 7
462
- severity: ERROR
463
- message: Type `::String` does not have method `foo`
464
- code: Ruby::NoMethod
465
516
  - file: unsupported_syntax.rb
466
517
  diagnostics:
467
518
  - range: