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
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: