steep 0.47.0 → 0.49.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +14 -16
  5. data/lib/steep/ast/types/any.rb +2 -0
  6. data/lib/steep/ast/types/boolean.rb +2 -0
  7. data/lib/steep/ast/types/bot.rb +2 -0
  8. data/lib/steep/ast/types/class.rb +2 -0
  9. data/lib/steep/ast/types/factory.rb +162 -138
  10. data/lib/steep/ast/types/helper.rb +8 -0
  11. data/lib/steep/ast/types/instance.rb +2 -0
  12. data/lib/steep/ast/types/intersection.rb +4 -0
  13. data/lib/steep/ast/types/literal.rb +2 -0
  14. data/lib/steep/ast/types/logic.rb +2 -0
  15. data/lib/steep/ast/types/name.rb +6 -0
  16. data/lib/steep/ast/types/nil.rb +2 -0
  17. data/lib/steep/ast/types/proc.rb +9 -0
  18. data/lib/steep/ast/types/record.rb +4 -0
  19. data/lib/steep/ast/types/self.rb +2 -0
  20. data/lib/steep/ast/types/top.rb +2 -0
  21. data/lib/steep/ast/types/tuple.rb +4 -0
  22. data/lib/steep/ast/types/union.rb +4 -0
  23. data/lib/steep/ast/types/var.rb +16 -3
  24. data/lib/steep/ast/types/void.rb +2 -0
  25. data/lib/steep/diagnostic/ruby.rb +23 -11
  26. data/lib/steep/diagnostic/signature.rb +56 -0
  27. data/lib/steep/interface/function.rb +4 -0
  28. data/lib/steep/interface/method_type.rb +14 -26
  29. data/lib/steep/interface/type_param.rb +103 -0
  30. data/lib/steep/server/base_worker.rb +1 -0
  31. data/lib/steep/server/interaction_worker.rb +1 -1
  32. data/lib/steep/server/type_check_worker.rb +2 -2
  33. data/lib/steep/services/signature_service.rb +2 -2
  34. data/lib/steep/services/type_check_service.rb +2 -1
  35. data/lib/steep/signature/validator.rb +221 -49
  36. data/lib/steep/subtyping/cache.rb +30 -0
  37. data/lib/steep/subtyping/check.rb +600 -705
  38. data/lib/steep/subtyping/constraints.rb +66 -30
  39. data/lib/steep/subtyping/relation.rb +60 -0
  40. data/lib/steep/subtyping/result.rb +190 -16
  41. data/lib/steep/type_construction.rb +543 -395
  42. data/lib/steep/type_inference/block_params.rb +31 -3
  43. data/lib/steep/type_inference/context.rb +37 -3
  44. data/lib/steep/version.rb +1 -1
  45. data/lib/steep.rb +4 -4
  46. data/sample/lib/length.rb +35 -0
  47. data/sample/sig/length.rbs +34 -0
  48. data/smoke/diagnostics-rbs/nonregular-type-alias.rbs +3 -0
  49. data/smoke/diagnostics-rbs/recursive-type-alias.rbs +3 -0
  50. data/smoke/diagnostics-rbs/test_expectations.yml +57 -12
  51. data/smoke/tsort/a.rb +1 -1
  52. data/smoke/tsort/test_expectations.yml +1 -63
  53. data/steep.gemspec +1 -1
  54. metadata +13 -10
  55. data/lib/steep/drivers/trace_printer.rb +0 -29
  56. data/lib/steep/interface/method.rb +0 -78
  57. data/lib/steep/subtyping/trace.rb +0 -71
@@ -28,6 +28,8 @@ module Steep
28
28
 
29
29
  include Helper::NoFreeVariables
30
30
 
31
+ include Helper::NoChild
32
+
31
33
  def level
32
34
  [0]
33
35
  end
@@ -90,6 +90,15 @@ module Steep
90
90
  def block_required?
91
91
  block && !block.optional?
92
92
  end
93
+
94
+ def each_child(&block)
95
+ if block_given?
96
+ type.each_child(&block)
97
+ self.block&.type&.each_child(&block)
98
+ else
99
+ enum_for :each_child
100
+ end
101
+ end
93
102
  end
94
103
  end
95
104
  end
@@ -42,6 +42,10 @@ module Steep
42
42
 
43
43
  include Helper::ChildrenLevel
44
44
 
45
+ def each_child(&block)
46
+ elements.each_value(&block)
47
+ end
48
+
45
49
  def level
46
50
  [0] + level_of_children(elements.values)
47
51
  end
@@ -22,6 +22,8 @@ module Steep
22
22
  "self"
23
23
  end
24
24
 
25
+ include Helper::NoChild
26
+
25
27
  def subst(s)
26
28
  s.self_type or raise "Unexpected substitution: #{inspect}"
27
29
  end
@@ -28,6 +28,8 @@ module Steep
28
28
 
29
29
  include Helper::NoFreeVariables
30
30
 
31
+ include Helper::NoChild
32
+
31
33
  def level
32
34
  [2]
33
35
  end
@@ -40,6 +40,10 @@ module Steep
40
40
 
41
41
  include Helper::ChildrenLevel
42
42
 
43
+ def each_child(&block)
44
+ types.each(&block)
45
+ end
46
+
43
47
  def level
44
48
  [0] + level_of_children(types)
45
49
  end
@@ -70,6 +70,10 @@ module Steep
70
70
  end
71
71
  end
72
72
 
73
+ def each_child(&block)
74
+ types.each(&block)
75
+ end
76
+
73
77
  include Helper::ChildrenLevel
74
78
 
75
79
  def level
@@ -21,17 +21,21 @@ module Steep
21
21
 
22
22
  alias eql? ==
23
23
 
24
- def self.fresh(name)
24
+ def self.fresh_name(name)
25
25
  @mutex ||= Mutex.new
26
26
 
27
27
  @mutex.synchronize do
28
28
  @max ||= 0
29
29
  @max += 1
30
30
 
31
- new(name: :"#{name}(#{@max})")
31
+ :"#{name}(#{@max})"
32
32
  end
33
33
  end
34
34
 
35
+ def self.fresh(name, location: nil)
36
+ new(name: fresh_name(name), location: location)
37
+ end
38
+
35
39
  def to_s
36
40
  name.to_s
37
41
  end
@@ -48,12 +52,21 @@ module Steep
48
52
  @fvs ||= Set.new([name])
49
53
  end
50
54
 
55
+ include Helper::NoChild
56
+
51
57
  def level
52
58
  [0]
53
59
  end
54
60
 
61
+ def update(name: self.name, location: self.location)
62
+ self.class.new(
63
+ name: name,
64
+ location: location
65
+ )
66
+ end
67
+
55
68
  def with_location(new_location)
56
- self.class.new(name: name, location: new_location)
69
+ update(location: new_location)
57
70
  end
58
71
  end
59
72
  end
@@ -28,6 +28,8 @@ module Steep
28
28
 
29
29
  include Helper::NoFreeVariables
30
30
 
31
+ include Helper::NoChild
32
+
31
33
  def level
32
34
  [0]
33
35
  end
@@ -26,20 +26,32 @@ module Steep
26
26
  end
27
27
 
28
28
  module ResultPrinter
29
- def print_result_to(io, level: 1)
30
- printer = Drivers::TracePrinter.new(io)
31
- printer.print result.trace, level: level
32
- end
33
-
34
- def trace_lines
35
- StringIO.new.tap do |io|
36
- print_result_to(io)
37
- end.string.chomp
29
+ def relation_message(relation)
30
+ case
31
+ when relation.type?
32
+ relation.to_s
33
+ when relation.method?
34
+ if relation.super_type.is_a?(Interface::MethodType) && relation.sub_type.is_a?(Interface::MethodType)
35
+ relation.to_s
36
+ end
37
+ when relation.interface?
38
+ nil
39
+ when relation.block?
40
+ nil
41
+ when relation.function?
42
+ nil
43
+ when relation.params?
44
+ nil
45
+ end
38
46
  end
39
47
 
40
48
  def detail_lines
41
49
  StringIO.new.tap do |io|
42
- print_result_to(io)
50
+ result.failure_path&.reverse_each.map do |result|
51
+ relation_message(result.relation)
52
+ end.compact.each.with_index(1) do |message, index|
53
+ io.puts "#{" " * (index)}#{message}"
54
+ end
43
55
  end.string.chomp
44
56
  end
45
57
  end
@@ -440,7 +452,7 @@ module Steep
440
452
  super(node: node)
441
453
  @interface_method = interface_method
442
454
  @annotation_method = annotation_method
443
- @result = result
455
+ @result = relation
444
456
  end
445
457
  end
446
458
 
@@ -101,6 +101,23 @@ module Steep
101
101
  end
102
102
  end
103
103
 
104
+ class UnsatisfiableTypeApplication < Base
105
+ attr_reader :type_name
106
+ attr_reader :type_arg
107
+ attr_reader :type_param
108
+
109
+ def initialize(type_name:, type_arg:, type_param:, location:)
110
+ super(location: location)
111
+ @type_name = type_name
112
+ @type_arg = type_arg
113
+ @type_param = type_param
114
+ end
115
+
116
+ def header_line
117
+ "Type application of `#{type_name}` doesn't satisfy the constraints: #{type_arg} <: #{type_param.upper_bound}"
118
+ end
119
+ end
120
+
104
121
  class InvalidMethodOverload < Base
105
122
  attr_reader :class_name
106
123
  attr_reader :method_name
@@ -307,6 +324,34 @@ module Steep
307
324
  end
308
325
  end
309
326
 
327
+ class RecursiveTypeAlias < Base
328
+ attr_reader :alias_names
329
+
330
+ def initialize(alias_names:, location:)
331
+ @alias_names = alias_names
332
+ super(location: location)
333
+ end
334
+
335
+ def header_line
336
+ "Type aliases cannot be *directly recursive*: #{alias_names.join(", ")}"
337
+ end
338
+ end
339
+
340
+ class NonregularTypeAlias < Base
341
+ attr_reader :type_name
342
+ attr_reader :nonregular_type
343
+
344
+ def initialize(type_name:, nonregular_type:, location:)
345
+ @type_name = type_name
346
+ @nonregular_type = nonregular_type
347
+ @location = location
348
+ end
349
+
350
+ def header_line
351
+ "Type alias #{type_name} is defined *non-regular*: #{nonregular_type}"
352
+ end
353
+ end
354
+
310
355
  def self.from_rbs_error(error, factory:)
311
356
  case error
312
357
  when RBS::ParsingError
@@ -388,6 +433,17 @@ module Steep
388
433
  type_name: error.type_name,
389
434
  member: error.member,
390
435
  )
436
+ when RBS::RecursiveTypeAliasError
437
+ Diagnostic::Signature::RecursiveTypeAlias.new(
438
+ alias_names: error.alias_names,
439
+ location: error.location
440
+ )
441
+ when RBS::NonregularTypeAliasError
442
+ Diagnostic::Signature::NonregularTypeAlias.new(
443
+ type_name: error.diagnostic.type_name,
444
+ nonregular_type: factory.type(error.diagnostic.nonregular_type),
445
+ location: error.location
446
+ )
391
447
  else
392
448
  raise error
393
449
  end
@@ -952,6 +952,10 @@ module Steep
952
952
  )
953
953
  end
954
954
 
955
+ def each_child(&block)
956
+ each_type(&block)
957
+ end
958
+
955
959
  def each_type(&block)
956
960
  if block_given?
957
961
  params.each_type(&block)
@@ -77,7 +77,7 @@ module Steep
77
77
  end
78
78
 
79
79
  def to_s
80
- type_params = !self.type_params.empty? ? "[#{self.type_params.map{|x| "#{x}" }.join(", ")}] " : ""
80
+ type_params = !self.type_params.empty? ? "[#{self.type_params.join(", ")}] " : ""
81
81
  params = type.params.to_s
82
82
  return_type = type.return_type
83
83
  block = self.block ? " #{self.block}" : ""
@@ -95,11 +95,9 @@ module Steep
95
95
  # Returns a new method type which can be used for the method implementation type of both `self` and `other`.
96
96
  #
97
97
  def unify_overload(other)
98
- type_params = []
99
- s1 = Substitution.build(self.type_params)
100
- type_params.push(*s1.dictionary.values.map(&:name))
101
- s2 = Substitution.build(other.type_params)
102
- type_params.push(*s2.dictionary.values.map(&:name))
98
+ type_params_1, s1 = TypeParam.rename(self.type_params)
99
+ type_params_2, s2 = TypeParam.rename(other.type_params)
100
+ type_params = type_params_1 + type_params_2
103
101
 
104
102
  block = case
105
103
  when self.block && other.block
@@ -134,17 +132,12 @@ module Steep
134
132
  # Returns nil if self and other are incompatible.
135
133
  #
136
134
  def |(other)
137
- self_type_params = Set.new(self.type_params)
138
- other_type_params = Set.new(other.type_params)
139
-
140
- unless (common_type_params = (self_type_params & other_type_params).to_a).empty?
141
- fresh_types = common_type_params.map {|name| AST::Types::Var.fresh(name) }
142
- fresh_names = fresh_types.map(&:name)
143
- subst = Substitution.build(common_type_params, fresh_types)
144
- other = other.instantiate(subst)
145
- type_params = (self_type_params + (other_type_params - common_type_params + Set.new(fresh_names))).to_a
135
+ if other.type_params.empty?
136
+ type_params = self.type_params
146
137
  else
147
- type_params = (self_type_params + other_type_params).to_a
138
+ other_params, s2 = TypeParam.rename(other.type_params)
139
+ other = other.instantiate(s2)
140
+ type_params = self.type_params + other_params
148
141
  end
149
142
 
150
143
  params = self.type.params & other.type.params or return
@@ -182,17 +175,12 @@ module Steep
182
175
  # Returns nil if self and other are incompatible.
183
176
  #
184
177
  def &(other)
185
- self_type_params = Set.new(self.type_params)
186
- other_type_params = Set.new(other.type_params)
187
-
188
- unless (common_type_params = (self_type_params & other_type_params).to_a).empty?
189
- fresh_types = common_type_params.map {|name| AST::Types::Var.fresh(name) }
190
- fresh_names = fresh_types.map(&:name)
191
- subst = Substitution.build(common_type_params, fresh_types)
192
- other = other.subst(subst)
193
- type_params = (self_type_params + (other_type_params - common_type_params + Set.new(fresh_names))).to_a
178
+ if other.type_params.empty?
179
+ type_params = self.type_params
194
180
  else
195
- type_params = (self_type_params + other_type_params).to_a
181
+ other_params, s2 = TypeParam.rename(other.type_params)
182
+ other = other.instantiate(s2)
183
+ type_params = self.type_params + other_params
196
184
  end
197
185
 
198
186
  params = self.type.params | other.type.params
@@ -0,0 +1,103 @@
1
+ module Steep
2
+ module Interface
3
+ class TypeParam
4
+ attr_reader :name
5
+ attr_reader :upper_bound
6
+ attr_reader :variance
7
+ attr_reader :unchecked
8
+ attr_reader :location
9
+
10
+ def initialize(name:, upper_bound:, variance:, unchecked:, location: nil)
11
+ @name = name
12
+ @upper_bound = upper_bound
13
+ @variance = variance
14
+ @unchecked = unchecked
15
+ @location = location
16
+ end
17
+
18
+ def ==(other)
19
+ other.is_a?(TypeParam) &&
20
+ other.name == name &&
21
+ other.upper_bound == upper_bound &&
22
+ other.variance == variance &&
23
+ other.unchecked == unchecked
24
+ end
25
+
26
+ alias eql? ==
27
+
28
+ def hash
29
+ name.hash ^ upper_bound.hash ^ variance.hash ^ unchecked.hash
30
+ end
31
+
32
+ def self.rename(params, conflicting_names = params.map(&:name))
33
+ unless conflicting_names.empty?
34
+ new_names = conflicting_names.map {|n| AST::Types::Var.fresh_name(n) }
35
+ hash = conflicting_names.zip(new_names).to_h
36
+ new_types = new_names.map {|n| AST::Types::Var.new(name: n) }
37
+
38
+ subst = Substitution.build(conflicting_names, new_types)
39
+
40
+ [
41
+ params.map do |param|
42
+ if hash.key?(param.name) || param.upper_bound
43
+ TypeParam.new(
44
+ name: hash[param.name] || param.name,
45
+ upper_bound: param.upper_bound&.subst(subst),
46
+ variance: param.variance,
47
+ unchecked: param.unchecked,
48
+ location: param.location
49
+ )
50
+ else
51
+ param
52
+ end
53
+ end,
54
+ subst
55
+ ]
56
+ else
57
+ [params, Substitution.empty]
58
+ end
59
+ end
60
+
61
+ def to_s
62
+ buf = ""
63
+
64
+ if unchecked
65
+ buf << "unchecked "
66
+ end
67
+
68
+ case variance
69
+ when :covariant
70
+ buf << "out "
71
+ when :contravariant
72
+ buf << "in "
73
+ end
74
+
75
+ buf << name.to_s
76
+
77
+ if upper_bound
78
+ buf << " < #{upper_bound}"
79
+ end
80
+
81
+ buf
82
+ end
83
+
84
+ def update(name: self.name, upper_bound: self.upper_bound, variance: self.variance, unchecked: self.unchecked, location: self.location)
85
+ TypeParam.new(
86
+ name: name,
87
+ upper_bound: upper_bound,
88
+ variance: variance,
89
+ unchecked: unchecked,
90
+ location: location
91
+ )
92
+ end
93
+
94
+ def subst(s)
95
+ if u = upper_bound
96
+ update(upper_bound: u.subst(s))
97
+ else
98
+ self
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -14,6 +14,7 @@ module Steep
14
14
  @writer = writer
15
15
  @skip_job = false
16
16
  @shutdown = false
17
+ @skip_jobs_after_shutdown = nil
17
18
  end
18
19
 
19
20
  def skip_jobs_after_shutdown!(flag = true)
@@ -338,7 +338,7 @@ HOVER
338
338
  else
339
339
  ps = params.each.map do |param|
340
340
  s = ""
341
- if param.skip_validation
341
+ if param.unchecked?
342
342
  s << "unchecked "
343
343
  end
344
344
  case param.variance
@@ -144,7 +144,7 @@ module Steep
144
144
  if job.guid == current_type_check_guid
145
145
  Steep.logger.info { "Processing ValidateAppSignature for guid=#{job.guid}, path=#{job.path}" }
146
146
  service.validate_signature(path: project.relative_path(job.path)) do |path, diagnostics|
147
- formatter = Diagnostic::LSPFormatter.new({})
147
+ formatter = Diagnostic::LSPFormatter.new({}, **{})
148
148
 
149
149
  writer.write(
150
150
  method: :"textDocument/publishDiagnostics",
@@ -162,7 +162,7 @@ module Steep
162
162
  if job.guid == current_type_check_guid
163
163
  Steep.logger.info { "Processing ValidateLibrarySignature for guid=#{job.guid}, path=#{job.path}" }
164
164
  service.validate_signature(path: job.path) do |path, diagnostics|
165
- formatter = Diagnostic::LSPFormatter.new({})
165
+ formatter = Diagnostic::LSPFormatter.new({}, **{})
166
166
 
167
167
  writer.write(
168
168
  method: :"textDocument/publishDiagnostics",
@@ -224,7 +224,7 @@ module Steep
224
224
  Steep.measure "Loading new decls" do
225
225
  updated_files.each_value do |content|
226
226
  case decls = content.decls
227
- when RBS::ErrorBase
227
+ when RBS::BaseError
228
228
  errors << content.decls
229
229
  else
230
230
  begin
@@ -283,7 +283,7 @@ module Steep
283
283
  def rescue_rbs_error(errors)
284
284
  begin
285
285
  yield
286
- rescue RBS::ErrorBase => exn
286
+ rescue RBS::BaseError => exn
287
287
  errors << exn
288
288
  end
289
289
  end
@@ -366,7 +366,8 @@ module Steep
366
366
  self_type: AST::Builtin::Object.instance_type,
367
367
  type_env: type_env,
368
368
  lvar_env: lvar_env,
369
- call_context: TypeInference::MethodCall::TopLevelContext.new
369
+ call_context: TypeInference::MethodCall::TopLevelContext.new,
370
+ variable_context: TypeInference::Context::TypeVariableContext.empty
370
371
  )
371
372
 
372
373
  typing = Typing.new(source: source, root_context: context)