steep 0.21.0 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -1
  3. data/Gemfile +7 -0
  4. data/bin/steep-prof +16 -0
  5. data/lib/steep/ast/types.rb +5 -3
  6. data/lib/steep/ast/types/any.rb +1 -3
  7. data/lib/steep/ast/types/boolean.rb +1 -3
  8. data/lib/steep/ast/types/bot.rb +1 -3
  9. data/lib/steep/ast/types/class.rb +2 -2
  10. data/lib/steep/ast/types/factory.rb +58 -16
  11. data/lib/steep/ast/types/helper.rb +6 -0
  12. data/lib/steep/ast/types/instance.rb +2 -2
  13. data/lib/steep/ast/types/intersection.rb +8 -4
  14. data/lib/steep/ast/types/literal.rb +5 -3
  15. data/lib/steep/ast/types/name.rb +13 -9
  16. data/lib/steep/ast/types/nil.rb +1 -3
  17. data/lib/steep/ast/types/proc.rb +5 -2
  18. data/lib/steep/ast/types/record.rb +9 -4
  19. data/lib/steep/ast/types/self.rb +1 -1
  20. data/lib/steep/ast/types/top.rb +1 -3
  21. data/lib/steep/ast/types/tuple.rb +5 -3
  22. data/lib/steep/ast/types/union.rb +11 -3
  23. data/lib/steep/ast/types/var.rb +2 -2
  24. data/lib/steep/ast/types/void.rb +1 -3
  25. data/lib/steep/drivers/check.rb +4 -0
  26. data/lib/steep/interface/method_type.rb +48 -18
  27. data/lib/steep/interface/substitution.rb +32 -2
  28. data/lib/steep/project/target.rb +17 -3
  29. data/lib/steep/server/base_worker.rb +4 -3
  30. data/lib/steep/server/master.rb +3 -1
  31. data/lib/steep/server/signature_worker.rb +3 -0
  32. data/lib/steep/signature/errors.rb +28 -0
  33. data/lib/steep/signature/validator.rb +11 -1
  34. data/lib/steep/source.rb +2 -1
  35. data/lib/steep/subtyping/check.rb +38 -21
  36. data/lib/steep/type_construction.rb +446 -181
  37. data/lib/steep/type_inference/constant_env.rb +1 -1
  38. data/lib/steep/type_inference/context.rb +8 -0
  39. data/lib/steep/type_inference/context_array.rb +4 -3
  40. data/lib/steep/type_inference/logic.rb +31 -0
  41. data/lib/steep/typing.rb +7 -0
  42. data/lib/steep/version.rb +1 -1
  43. data/smoke/hash/d.rb +1 -1
  44. data/steep.gemspec +1 -8
  45. metadata +5 -88
@@ -27,7 +27,7 @@ module Steep
27
27
  end
28
28
 
29
29
  def free_variables
30
- Set.new
30
+ @fvs ||= Set.new([self])
31
31
  end
32
32
 
33
33
  def level
@@ -26,9 +26,7 @@ module Steep
26
26
  "⟙"
27
27
  end
28
28
 
29
- def free_variables
30
- Set.new
31
- end
29
+ include Helper::NoFreeVariables
32
30
 
33
31
  def level
34
32
  [2]
@@ -30,9 +30,11 @@ module Steep
30
30
  "[#{types.join(", ")}]"
31
31
  end
32
32
 
33
- def free_variables
34
- types.each.with_object(Set.new) do |type, set|
35
- set.merge(type.free_variables)
33
+ def free_variables()
34
+ @fvs ||= Set.new.tap do |set|
35
+ types.each do |type|
36
+ set.merge(type.free_variables)
37
+ end
36
38
  end
37
39
  end
38
40
 
@@ -11,6 +11,9 @@ module Steep
11
11
  end
12
12
 
13
13
  def self.build(types:, location: nil)
14
+ return AST::Types::Bot.new if types.empty?
15
+ return types.first if types.size == 1
16
+
14
17
  types.flat_map do |type|
15
18
  if type.is_a?(Union)
16
19
  type.types
@@ -29,7 +32,10 @@ module Steep
29
32
  type
30
33
  end
31
34
  end.compact.uniq.yield_self do |tys|
32
- if tys.length == 1
35
+ case tys.size
36
+ when 0
37
+ AST::Types::Bot.new
38
+ when 1
33
39
  tys.first
34
40
  else
35
41
  new(types: tys.sort_by(&:hash), location: location)
@@ -58,8 +64,10 @@ module Steep
58
64
  end
59
65
 
60
66
  def free_variables
61
- types.each.with_object(Set.new) do |type, set|
62
- set.merge(type.free_variables)
67
+ @fvs ||= Set.new.tap do |set|
68
+ types.each do |type|
69
+ set.merge(type.free_variables)
70
+ end
63
71
  end
64
72
  end
65
73
 
@@ -44,8 +44,8 @@ module Steep
44
44
  end
45
45
  end
46
46
 
47
- def free_variables
48
- Set.new([name])
47
+ def free_variables()
48
+ @fvs ||= Set.new([name])
49
49
  end
50
50
 
51
51
  def level
@@ -26,9 +26,7 @@ module Steep
26
26
  "void"
27
27
  end
28
28
 
29
- def free_variables
30
- Set.new
31
- end
29
+ include Helper::NoFreeVariables
32
30
 
33
31
  def level
34
32
  [0]
@@ -60,6 +60,10 @@ module Steep
60
60
  Steep.log_error source_file.status.error
61
61
  end
62
62
  end
63
+ when Project::Target::SignatureOtherErrorStatus
64
+ Steep.log_error status.error
65
+ else
66
+ Steep.logger.error { "Unexpected status: #{status.class}" }
63
67
  end
64
68
  end
65
69
  end
@@ -211,10 +211,10 @@ module Steep
211
211
  end
212
212
  end
213
213
 
214
- def free_variables
215
- Set.new.tap do |fvs|
214
+ def free_variables()
215
+ @fvs ||= Set.new.tap do |set|
216
216
  each_type do |type|
217
- fvs.merge type.free_variables
217
+ set.merge(type.free_variables)
218
218
  end
219
219
  end
220
220
  end
@@ -224,14 +224,29 @@ module Steep
224
224
  end
225
225
 
226
226
  def subst(s)
227
- self.class.new(
228
- required: required.map {|t| t.subst(s) },
229
- optional: optional.map {|t| t.subst(s) },
230
- rest: rest&.subst(s),
231
- required_keywords: required_keywords.transform_values {|t| t.subst(s) },
232
- optional_keywords: optional_keywords.transform_values {|t| t.subst(s) },
233
- rest_keywords: rest_keywords&.subst(s)
234
- )
227
+ return self if s.empty?
228
+ return self if empty?
229
+ return self if free_variables.disjoint?(s.domain)
230
+
231
+ rs = required.map {|t| t.subst(s) }
232
+ os = optional.map {|t| t.subst(s) }
233
+ r = rest&.subst(s)
234
+ rk = required_keywords.transform_values {|t| t.subst(s) }
235
+ ok = optional_keywords.transform_values {|t| t.subst(s) }
236
+ k = rest_keywords&.subst(s)
237
+
238
+ if rs == required && os == optional && r == rest && rk == required_keywords && ok == optional_keywords && k == rest_keywords
239
+ self
240
+ else
241
+ self.class.new(
242
+ required: required.map {|t| t.subst(s) },
243
+ optional: optional.map {|t| t.subst(s) },
244
+ rest: rest&.subst(s),
245
+ required_keywords: required_keywords.transform_values {|t| t.subst(s) },
246
+ optional_keywords: optional_keywords.transform_values {|t| t.subst(s) },
247
+ rest_keywords: rest_keywords&.subst(s)
248
+ )
249
+ end
235
250
  end
236
251
 
237
252
  def size
@@ -557,14 +572,19 @@ module Steep
557
572
  end
558
573
 
559
574
  def subst(s)
560
- self.class.new(
561
- type: type.subst(s),
562
- optional: optional
563
- )
575
+ ty = type.subst(s)
576
+ if ty == type
577
+ self
578
+ else
579
+ self.class.new(
580
+ type: ty,
581
+ optional: optional
582
+ )
583
+ end
564
584
  end
565
585
 
566
- def free_variables
567
- type.free_variables
586
+ def free_variables()
587
+ @fvs ||= type.free_variables
568
588
  end
569
589
 
570
590
  def to_s
@@ -617,10 +637,20 @@ module Steep
617
637
  end
618
638
 
619
639
  def free_variables
620
- (params.free_variables + (block&.free_variables || Set.new) + return_type.free_variables) - Set.new(type_params)
640
+ @fvs ||= Set.new.tap do |set|
641
+ set.merge(params.free_variables)
642
+ if block
643
+ set.merge(block.free_variables)
644
+ end
645
+ set.merge(return_type.free_variables)
646
+ set.subtract(type_params)
647
+ end
621
648
  end
622
649
 
623
650
  def subst(s)
651
+ return self if s.empty?
652
+ return self if free_variables.disjoint?(s.domain)
653
+
624
654
  s_ = s.except(type_params)
625
655
 
626
656
  self.class.new(
@@ -26,7 +26,32 @@ module Steep
26
26
  end
27
27
 
28
28
  def self.empty
29
- new(dictionary: {}, instance_type: AST::Types::Instance.new, module_type: AST::Types::Class.new, self_type: AST::Types::Self.new)
29
+ new(dictionary: {},
30
+ instance_type: INSTANCE_TYPE,
31
+ module_type: CLASS_TYPE,
32
+ self_type: SELF_TYPE)
33
+ end
34
+
35
+ def empty?
36
+ dictionary.empty? &&
37
+ instance_type.is_a?(AST::Types::Instance) &&
38
+ module_type.is_a?(AST::Types::Class) &&
39
+ self_type.is_a?(AST::Types::Self)
40
+ end
41
+
42
+ INSTANCE_TYPE = AST::Types::Instance.new
43
+ CLASS_TYPE = AST::Types::Class.new
44
+ SELF_TYPE = AST::Types::Self.new
45
+
46
+ def domain
47
+ set = Set.new
48
+
49
+ set.merge(dictionary.keys)
50
+ set << INSTANCE_TYPE unless instance_type.is_a?(AST::Types::Instance)
51
+ set << CLASS_TYPE unless instance_type.is_a?(AST::Types::Class)
52
+ set << SELF_TYPE unless instance_type.is_a?(AST::Types::Self)
53
+
54
+ set
30
55
  end
31
56
 
32
57
  def to_s
@@ -81,6 +106,11 @@ module Steep
81
106
  raise "Duplicated key on merge!: #{key}, #{a}, #{b}"
82
107
  end
83
108
  end
109
+
110
+ @instance_type = instance_type.subst(s)
111
+ @module_type = module_type.subst(s)
112
+ @self_type = self_type.subst(s)
113
+
84
114
  self
85
115
  end
86
116
 
@@ -92,7 +122,7 @@ module Steep
92
122
  end
93
123
 
94
124
  def add!(v, ty)
95
- merge!(Substitution.new(dictionary: { v => ty }, instance_type: nil, module_type: nil, self_type: nil))
125
+ merge!(Substitution.new(dictionary: { v => ty }, instance_type: instance_type, module_type: module_type, self_type: self_type))
96
126
  end
97
127
  end
98
128
  end
@@ -79,6 +79,7 @@ module Steep
79
79
  def self.test_pattern(patterns, path, ext:)
80
80
  patterns.any? do |pattern|
81
81
  p = pattern.end_with?(File::Separator) ? pattern : pattern + File::Separator
82
+ p.delete_prefix!('./')
82
83
  (path.to_s.start_with?(p) && path.extname == ext) || File.fnmatch(pattern, path.to_s)
83
84
  end
84
85
  end
@@ -160,7 +161,18 @@ module Steep
160
161
  else
161
162
  yield env, check, Time.now
162
163
  end
164
+ rescue RBS::DuplicatedDeclarationError => exn
165
+ @status = SignatureValidationErrorStatus.new(
166
+ errors: [
167
+ Signature::Errors::DuplicatedDefinitionError.new(
168
+ name: exn.name,
169
+ location: exn.decls[0].location
170
+ )
171
+ ],
172
+ timestamp: Time.now
173
+ )
163
174
  rescue => exn
175
+ Steep.log_error exn
164
176
  @status = SignatureOtherErrorStatus.new(error: exn, timestamp: Time.now)
165
177
  end
166
178
  end
@@ -183,8 +195,10 @@ module Steep
183
195
  type_check_sources = []
184
196
 
185
197
  target_sources.each do |file|
186
- if file.type_check(check, timestamp)
187
- type_check_sources << file
198
+ Steep.logger.tagged("path=#{file.path}") do
199
+ if file.type_check(check, timestamp)
200
+ type_check_sources << file
201
+ end
188
202
  end
189
203
  end
190
204
 
@@ -205,7 +219,7 @@ module Steep
205
219
  def errors
206
220
  case status
207
221
  when TypeCheckStatus
208
- source_files.each_value.flat_map(&:errors)
222
+ source_files.each_value.flat_map(&:errors).select { |error | options.error_to_report?(error) }
209
223
  else
210
224
  []
211
225
  end
@@ -12,6 +12,7 @@ module Steep
12
12
  @project = project
13
13
  @reader = reader
14
14
  @writer = writer
15
+ @shutdown = false
15
16
  end
16
17
 
17
18
  def handle_request(request)
@@ -28,7 +29,7 @@ module Steep
28
29
  Steep.logger.formatter.push_tags(*tags)
29
30
  Steep.logger.tagged "background" do
30
31
  while job = queue.pop
31
- handle_job(job)
32
+ handle_job(job) unless @shutdown
32
33
  end
33
34
  end
34
35
  end
@@ -38,11 +39,11 @@ module Steep
38
39
  reader.read do |request|
39
40
  case request[:method]
40
41
  when "shutdown"
41
- # nop
42
+ @shutdown = true
42
43
  when "exit"
43
44
  break
44
45
  else
45
- handle_request(request)
46
+ handle_request(request) unless @shutdown
46
47
  end
47
48
  end
48
49
  ensure
@@ -23,6 +23,7 @@ module Steep
23
23
  @signature_worker = signature_worker
24
24
  @code_workers = code_workers
25
25
  @worker_to_paths = {}
26
+ @shutdown = false
26
27
  end
27
28
 
28
29
  def start
@@ -59,7 +60,7 @@ module Steep
59
60
  end
60
61
 
61
62
  while job = queue.pop
62
- writer.write(job)
63
+ writer.write(job) unless @shutdown
63
64
  end
64
65
 
65
66
  writer.io.close
@@ -154,6 +155,7 @@ module Steep
154
155
 
155
156
  when "shutdown"
156
157
  queue << { id: id, result: nil }
158
+ @shutdown = true
157
159
 
158
160
  when "exit"
159
161
  queue << nil
@@ -112,6 +112,9 @@ module Steep
112
112
  target.signature_files.each_key.with_object({}) do |path, hash|
113
113
  hash[path] = []
114
114
  end
115
+ when Project::Target::SignatureOtherErrorStatus
116
+ Steep.log_error status.error
117
+ {}
115
118
  else
116
119
  Steep.logger.info "Unexpected target status: #{status.class}"
117
120
  {}
@@ -19,6 +19,19 @@ module Steep
19
19
  end
20
20
  end
21
21
 
22
+ class DuplicatedDefinitionError < Base
23
+ attr_reader :name
24
+
25
+ def initialize(name:, location:)
26
+ @name = name
27
+ @location = location
28
+ end
29
+
30
+ def puts(io)
31
+ io.puts "#{loc_to_s}\sDuplicatedDefinitionError: name=#{name}"
32
+ end
33
+ end
34
+
22
35
  class UnknownTypeNameError < Base
23
36
  attr_reader :name
24
37
 
@@ -48,6 +61,21 @@ module Steep
48
61
  io.puts "#{loc_to_s}\tInvalidTypeApplicationError: name=#{name}, expected=[#{params.join(", ")}], actual=[#{args.join(", ")}]"
49
62
  end
50
63
  end
64
+
65
+ class InvalidMethodOverloadError < Base
66
+ attr_reader :class_name
67
+ attr_reader :method_name
68
+
69
+ def initialize(class_name:, method_name:, location:)
70
+ @class_name = class_name
71
+ @method_name = method_name
72
+ @location = location
73
+ end
74
+
75
+ def puts(io)
76
+ io.puts "#{loc_to_s}\tInvalidMethodOverloadError: class_name=#{class_name}, method_name=#{method_name}"
77
+ end
78
+ end
51
79
  end
52
80
  end
53
81
  end
@@ -53,6 +53,7 @@ module Steep
53
53
  validate_decl
54
54
  validate_const
55
55
  validate_global
56
+ validate_alias
56
57
  end
57
58
 
58
59
  def validate_type(type)
@@ -99,6 +100,7 @@ module Steep
99
100
  env.constant_decls.each do |name, entry|
100
101
  rescue_validation_errors do
101
102
  Steep.logger.debug "Validating constant `#{name}`..."
103
+ builder.ensure_namespace!(name.namespace, location: entry.decl.location)
102
104
  validate_type entry.decl.type
103
105
  end
104
106
  end
@@ -117,7 +119,9 @@ module Steep
117
119
  env.alias_decls.each do |name, entry|
118
120
  rescue_validation_errors do
119
121
  Steep.logger.debug "Validating alias `#{name}`..."
120
- validate_type(entry.decl.type)
122
+ builder.expand_alias(name).tap do |type|
123
+ validate_type(type)
124
+ end
121
125
  end
122
126
  end
123
127
  end
@@ -136,6 +140,12 @@ module Steep
136
140
  name: factory.type_name(exn.type_name),
137
141
  location: exn.location
138
142
  )
143
+ rescue RBS::InvalidOverloadMethodError => exn
144
+ @errors << Errors::InvalidMethodOverloadError.new(
145
+ class_name: factory.type_name(exn.type_name),
146
+ method_name: exn.method_name,
147
+ location: exn.members[0].location
148
+ )
139
149
  end
140
150
  end
141
151
  end