steep 0.23.0 → 0.29.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/bin/smoke_runner.rb +3 -4
  4. data/lib/steep.rb +4 -3
  5. data/lib/steep/annotation_parser.rb +2 -4
  6. data/lib/steep/ast/builtin.rb +11 -21
  7. data/lib/steep/ast/types.rb +5 -3
  8. data/lib/steep/ast/types/any.rb +1 -3
  9. data/lib/steep/ast/types/boolean.rb +1 -3
  10. data/lib/steep/ast/types/bot.rb +1 -3
  11. data/lib/steep/ast/types/class.rb +2 -2
  12. data/lib/steep/ast/types/factory.rb +249 -89
  13. data/lib/steep/ast/types/helper.rb +6 -0
  14. data/lib/steep/ast/types/instance.rb +2 -2
  15. data/lib/steep/ast/types/intersection.rb +20 -13
  16. data/lib/steep/ast/types/literal.rb +1 -3
  17. data/lib/steep/ast/types/logic.rb +63 -0
  18. data/lib/steep/ast/types/name.rb +15 -67
  19. data/lib/steep/ast/types/nil.rb +1 -3
  20. data/lib/steep/ast/types/proc.rb +5 -2
  21. data/lib/steep/ast/types/record.rb +9 -4
  22. data/lib/steep/ast/types/self.rb +1 -1
  23. data/lib/steep/ast/types/top.rb +1 -3
  24. data/lib/steep/ast/types/tuple.rb +5 -3
  25. data/lib/steep/ast/types/union.rb +16 -9
  26. data/lib/steep/ast/types/var.rb +2 -2
  27. data/lib/steep/ast/types/void.rb +1 -3
  28. data/lib/steep/drivers/check.rb +4 -0
  29. data/lib/steep/errors.rb +14 -0
  30. data/lib/steep/interface/interface.rb +5 -62
  31. data/lib/steep/interface/method_type.rb +394 -93
  32. data/lib/steep/interface/substitution.rb +48 -6
  33. data/lib/steep/module_helper.rb +25 -0
  34. data/lib/steep/project/completion_provider.rb +48 -51
  35. data/lib/steep/project/file.rb +3 -3
  36. data/lib/steep/project/hover_content.rb +4 -6
  37. data/lib/steep/project/target.rb +5 -2
  38. data/lib/steep/server/base_worker.rb +5 -3
  39. data/lib/steep/server/code_worker.rb +2 -0
  40. data/lib/steep/server/master.rb +10 -1
  41. data/lib/steep/signature/validator.rb +3 -3
  42. data/lib/steep/source.rb +4 -3
  43. data/lib/steep/subtyping/check.rb +46 -59
  44. data/lib/steep/subtyping/constraints.rb +8 -0
  45. data/lib/steep/type_construction.rb +771 -513
  46. data/lib/steep/type_inference/block_params.rb +5 -0
  47. data/lib/steep/type_inference/constant_env.rb +3 -6
  48. data/lib/steep/type_inference/context.rb +8 -0
  49. data/lib/steep/type_inference/context_array.rb +4 -3
  50. data/lib/steep/type_inference/logic.rb +31 -0
  51. data/lib/steep/type_inference/logic_type_interpreter.rb +219 -0
  52. data/lib/steep/type_inference/type_env.rb +2 -2
  53. data/lib/steep/typing.rb +7 -0
  54. data/lib/steep/version.rb +1 -1
  55. data/smoke/alias/a.rb +1 -1
  56. data/smoke/case/a.rb +1 -1
  57. data/smoke/hash/d.rb +1 -1
  58. data/smoke/if/a.rb +1 -1
  59. data/smoke/module/a.rb +1 -1
  60. data/smoke/rescue/a.rb +4 -13
  61. data/smoke/type_case/a.rb +0 -7
  62. data/steep.gemspec +2 -2
  63. metadata +10 -10
  64. data/lib/steep/ast/method_type.rb +0 -126
  65. data/lib/steep/ast/namespace.rb +0 -80
  66. data/lib/steep/names.rb +0 -86
@@ -56,6 +56,11 @@ module Steep
56
56
 
57
57
  node.children.each do |arg|
58
58
  var = arg.children.first
59
+
60
+ if var.is_a?(::AST::Node)
61
+ return
62
+ end
63
+
59
64
  type = annotations.var_type(lvar: var.name)
60
65
 
61
66
  case arg.type
@@ -6,7 +6,7 @@ module Steep
6
6
  attr_reader :factory
7
7
  attr_reader :table
8
8
 
9
- # ConstantEnv receives an Names::Module as a context, not a Namespace, because this is a simulation of Ruby.
9
+ # ConstantEnv receives an TypeName as a context, not a Namespace, because this is a simulation of Ruby.
10
10
  # Any namespace is a module or class.
11
11
  def initialize(factory:, context:)
12
12
  @cache = {}
@@ -17,16 +17,13 @@ module Steep
17
17
 
18
18
  def lookup(name)
19
19
  cache[name] ||= begin
20
- constant = table.resolve_constant_reference(
21
- factory.type_name_1(name),
22
- context: context.map {|namespace| factory.namespace_1(namespace) }
23
- )
20
+ constant = table.resolve_constant_reference(name, context: context)
24
21
 
25
22
  if constant
26
23
  factory.type(constant.type)
27
24
  end
28
25
  rescue => exn
29
- Steep.logger.error "Looking up a constant failed: name=#{name}, context=[#{context.join(", ")}], error=#{exn.inspect}"
26
+ Steep.logger.debug "Looking up a constant failed: name=#{name}, context=[#{context.join(", ")}], error=#{exn.inspect}"
30
27
  nil
31
28
  end
32
29
  end
@@ -70,6 +70,14 @@ module Steep
70
70
  def const_context
71
71
  const_env.context
72
72
  end
73
+
74
+ def class_variables
75
+ if module_definition
76
+ @class_variables ||= module_definition.class_variables.transform_values do |var_def|
77
+ var_def.type
78
+ end
79
+ end
80
+ end
73
81
  end
74
82
 
75
83
  attr_reader :method_context
@@ -35,12 +35,13 @@ module Steep
35
35
 
36
36
  def insert_context(range, context:, entry: self.root)
37
37
  entry.sub_entries.each do |sub|
38
- next if sub.range.begin < range.begin && range.end <= sub.range.end
39
- next if range.begin < sub.range.begin && sub.range.end <= range.end
38
+ next if sub.range.begin <= range.begin && range.end <= sub.range.end
39
+ next if range.begin <= sub.range.begin && sub.range.end <= range.end
40
40
  next if range.end <= sub.range.begin
41
41
  next if sub.range.end <= range.begin
42
42
 
43
- raise "Range crossing: sub range=#{sub.range}, new range=#{range}"
43
+ Steep.logger.error { "Range crossing: sub range=#{sub.range}, new range=#{range}" }
44
+ raise
44
45
  end
45
46
 
46
47
  sup = entry.sub_entries.find do |sub|
@@ -47,6 +47,37 @@ module Steep
47
47
  f.merge([node])
48
48
  ]
49
49
 
50
+ when :masgn
51
+ lhs, rhs = node.children
52
+
53
+ lt, lf = nodes(node: lhs)
54
+ rt, rf = nodes(node: rhs)
55
+
56
+ [
57
+ (lt + rt).merge([node]),
58
+ (lf + rf).merge([node])
59
+ ]
60
+
61
+ when :mlhs
62
+ nodes = [node]
63
+
64
+ node.children.each do |child|
65
+ case child.type
66
+ when :lvasgn
67
+ nodes << child
68
+ when :splat
69
+ if node.children[0].type == :lvasgn
70
+ nodes << child
71
+ nodes << child.children[0]
72
+ end
73
+ end
74
+ end
75
+
76
+ [
77
+ Result.new(nodes),
78
+ Result.new(nodes)
79
+ ]
80
+
50
81
  when :and
51
82
  lhs, rhs = node.children
52
83
 
@@ -0,0 +1,219 @@
1
+ module Steep
2
+ module TypeInference
3
+ class LogicTypeInterpreter
4
+ attr_reader :subtyping
5
+ attr_reader :typing
6
+
7
+ def initialize(subtyping:, typing:)
8
+ @subtyping = subtyping
9
+ @typing = typing
10
+ end
11
+
12
+ def factory
13
+ subtyping.factory
14
+ end
15
+
16
+ def guess_type_from_method(node)
17
+ if node.type == :send
18
+ method = node.children[1]
19
+ case method
20
+ when :is_a?, :kind_of?, :instance_of?
21
+ AST::Types::Logic::ReceiverIsArg.new
22
+ when :nil?
23
+ AST::Types::Logic::ReceiverIsNil.new
24
+ when :!
25
+ AST::Types::Logic::Not.new
26
+ when :===
27
+ AST::Types::Logic::ArgIsReceiver.new
28
+ end
29
+ end
30
+ end
31
+
32
+ def eval(env:, type:, node:)
33
+ value_node, vars = decompose_value(node)
34
+
35
+ truthy_env = env
36
+ falsy_env = env
37
+
38
+ if type.is_a?(AST::Types::Any)
39
+ type = guess_type_from_method(node) || type
40
+ end
41
+
42
+ if type.is_a?(AST::Types::Logic::Base)
43
+ vars.each do |var_name|
44
+ truthy_env = truthy_env.assign!(var_name, node: node, type: AST::Builtin.true_type)
45
+ falsy_env = truthy_env.assign!(var_name, node: node, type: AST::Builtin.false_type)
46
+ end
47
+
48
+ case type
49
+ when AST::Types::Logic::ReceiverIsNil
50
+ case value_node.type
51
+ when :send
52
+ receiver = value_node.children[0]
53
+
54
+ if receiver
55
+ _, receiver_vars = decompose_value(receiver)
56
+
57
+ receiver_vars.each do |receiver_var|
58
+ var_type = env[receiver_var]
59
+ truthy_type, falsy_type = factory.unwrap_optional(var_type)
60
+
61
+ truthy_env = truthy_env.assign!(receiver_var, node: node, type: falsy_type || AST::Builtin.nil_type)
62
+ falsy_env = falsy_env.assign!(receiver_var, node: node, type: truthy_type)
63
+ end
64
+ end
65
+ end
66
+ when AST::Types::Logic::ReceiverIsArg
67
+ case value_node.type
68
+ when :send
69
+ receiver, _, arg = value_node.children
70
+
71
+ if receiver
72
+ _, receiver_vars = decompose_value(receiver)
73
+ arg_type = typing.type_of(node: arg)
74
+
75
+ if arg_type.is_a?(AST::Types::Name::Singleton)
76
+ receiver_vars.each do |var_name|
77
+ var_type = env[var_name]
78
+ truthy_type, falsy_type = type_case_select(var_type, arg_type.name)
79
+
80
+ truthy_env = truthy_env.assign!(var_name, node: node, type: truthy_type)
81
+ falsy_env = falsy_env.assign!(var_name, node: node, type: falsy_type)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ when AST::Types::Logic::ArgIsReceiver
87
+ case value_node.type
88
+ when :send
89
+ receiver, _, arg = value_node.children
90
+
91
+ if receiver
92
+ _, arg_vars = decompose_value(arg)
93
+ receiver_type = typing.type_of(node: receiver)
94
+
95
+ if receiver_type.is_a?(AST::Types::Name::Singleton)
96
+ arg_vars.each do |var_name|
97
+ var_type = env[var_name]
98
+ truthy_type, falsy_type = type_case_select(var_type, receiver_type.name)
99
+
100
+ truthy_env = truthy_env.assign!(var_name, node: node, type: truthy_type)
101
+ falsy_env = falsy_env.assign!(var_name, node: node, type: falsy_type)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ when AST::Types::Logic::Not
107
+ receiver, * = value_node.children
108
+ receiver_type = typing.type_of(node: receiver)
109
+ falsy_env, truthy_env = eval(env: env, type: receiver_type, node: receiver)
110
+ end
111
+ else
112
+ _, vars = decompose_value(node)
113
+
114
+ vars.each do |var_name|
115
+ var_type = env[var_name]
116
+ truthy_type, falsy_type = factory.unwrap_optional(var_type)
117
+
118
+ if falsy_type
119
+ truthy_env = truthy_env.assign!(var_name, node: node, type: truthy_type)
120
+ falsy_env = falsy_env.assign!(var_name, node: node, type: falsy_type)
121
+ else
122
+ truthy_env = truthy_env.assign!(var_name, node: node, type: truthy_type)
123
+ falsy_env = falsy_env.assign!(var_name, node: node, type: truthy_type)
124
+ end
125
+ end
126
+ end
127
+
128
+ [truthy_env, falsy_env]
129
+ end
130
+
131
+ def decompose_value(node)
132
+ case node.type
133
+ when :lvar
134
+ [node, Set[node.children[0].name]]
135
+ when :masgn
136
+ lhs, rhs = node.children
137
+ lhs_vars = lhs.children.select {|m| m.type == :lvasgn }.map {|m| m.children[0].name }
138
+ val, vars = decompose_value(rhs)
139
+ [val, vars + lhs_vars]
140
+ when :lvasgn
141
+ var, rhs = node.children
142
+ val, vars = decompose_value(rhs)
143
+ [val, vars + [var.name]]
144
+ when :begin
145
+ decompose_value(node.children.last)
146
+ when :and
147
+ left, right = node.children
148
+ _, left_vars = decompose_value(left)
149
+ val, right_vars = decompose_value(right)
150
+ [val, left_vars + right_vars]
151
+ else
152
+ [node, Set[]]
153
+ end
154
+ end
155
+
156
+ def type_case_select(type, klass)
157
+ truth_types, false_types = type_case_select0(type, klass)
158
+
159
+ [
160
+ AST::Types::Union.build(types: truth_types),
161
+ AST::Types::Union.build(types: false_types)
162
+ ]
163
+ end
164
+
165
+ def type_case_select0(type, klass)
166
+ instance_type = factory.instance_type(klass)
167
+
168
+ case type
169
+ when AST::Types::Union
170
+ truthy_types = []
171
+ falsy_types = []
172
+
173
+ type.types.each do |ty|
174
+ truths, falses = type_case_select0(ty, klass)
175
+
176
+ if truths.empty?
177
+ falsy_types.push(ty)
178
+ else
179
+ truthy_types.push(*truths)
180
+ falsy_types.push(*falses)
181
+ end
182
+ end
183
+
184
+ [truthy_types, falsy_types]
185
+
186
+ when AST::Types::Name::Instance
187
+ relation = Subtyping::Relation.new(sub_type: type, super_type: instance_type)
188
+ if subtyping.check(relation, constraints: Subtyping::Constraints.empty, self_type: AST::Types::Self.new).success?
189
+ [
190
+ [type],
191
+ []
192
+ ]
193
+ else
194
+ [
195
+ [],
196
+ [type]
197
+ ]
198
+ end
199
+
200
+ when AST::Types::Name::Alias
201
+ ty = factory.expand_alias(type)
202
+ type_case_select0(ty, klass)
203
+
204
+ when AST::Types::Any, AST::Types::Top
205
+ [
206
+ [instance_type],
207
+ [type]
208
+ ]
209
+
210
+ else
211
+ [
212
+ [],
213
+ [type]
214
+ ]
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
@@ -57,7 +57,7 @@ module Steep
57
57
  end
58
58
  end
59
59
 
60
- # @type method assert: (const: Names::Module) { () -> void } -> AST::Type
60
+ # @type method assert: (const: TypeName) { () -> void } -> AST::Type
61
61
  # | (gvar: Symbol) { () -> void } -> AST::Type
62
62
  # | (ivar: Symbol) { () -> void } -> AST::Type
63
63
  def get(const: nil, gvar: nil, ivar: nil)
@@ -98,7 +98,7 @@ module Steep
98
98
  end
99
99
  end
100
100
 
101
- # @type method assign: (const: Names::Module, type: AST::Type) { (Subtyping::Result::Failure | nil) -> void } -> AST::Type
101
+ # @type method assign: (const: TypeName, type: AST::Type) { (Subtyping::Result::Failure | nil) -> void } -> AST::Type
102
102
  # | (gvar: Symbol, type: AST::Type) { (Subtyping::Result::Failure | nil) -> void } -> AST::Type
103
103
  # | (ivar: Symbol, type: AST::Type) { (Subtyping::Result::Failure | nil) -> void } -> AST::Type
104
104
  def assign(const: nil, gvar: nil, ivar: nil, type:, self_type:, &block)
@@ -126,6 +126,13 @@ module Steep
126
126
  end_pos = node.loc.end.begin_pos
127
127
  add_context(begin_pos..end_pos, context: context)
128
128
 
129
+ when :for
130
+ _, collection, _ = node.children
131
+
132
+ begin_pos = collection.loc.expression.end_pos
133
+ end_pos = node.loc.end.begin_pos
134
+
135
+ add_context(begin_pos..end_pos, context: context)
129
136
  else
130
137
  raise "Unexpected node for insert_context: #{node.type}"
131
138
  end
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "0.23.0"
2
+ VERSION = "0.29.0"
3
3
  end
@@ -1,7 +1,7 @@
1
1
  # @type var x: foo
2
2
  x = ""
3
3
 
4
- # !expects ArgumentTypeMismatch: receiver=(::Integer | ::String), expected=::string, actual=::Integer
4
+ # !expects* UnresolvedOverloading: receiver=(::String | ::Integer), method_name=+,
5
5
  x + 123
6
6
 
7
7
  # @type var y: bar
@@ -1,6 +1,6 @@
1
1
  # @type var a: Integer
2
2
 
3
- # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Array[::String] | ::Integer | ::String | nil)
3
+ # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Integer | ::Array[::String] | nil | ::String)
4
4
  a = case 1
5
5
  when 2
6
6
  1
@@ -2,5 +2,5 @@
2
2
 
3
3
  params = { id: 30, name: "Matz" }
4
4
 
5
- # !expects IncompatibleAssignment: lhs_type={ :name => ::String, :id => ::Integer }, rhs_type={ :id => ::String, :name => ::String, :email => ::String }
5
+ # !expects IncompatibleAssignment: lhs_type={ :id => ::Integer, :name => ::String }, rhs_type={ :email => ::String, :id => ::String, :name => ::String }
6
6
  params = { id: "30", name: "foo", email: "matsumoto@soutaro.com" }
@@ -13,7 +13,7 @@ else
13
13
  "baz"
14
14
  end
15
15
 
16
- # !expects IncompatibleAssignment: lhs_type=::String, rhs_type=(::Integer | ::String)
16
+ # !expects IncompatibleAssignment: lhs_type=::String, rhs_type=(::String | ::Integer)
17
17
  a = if z
18
18
  "foofoo"
19
19
  else
@@ -13,7 +13,7 @@ module A
13
13
  # !expects IncompatibleAssignment: lhs_type=::String, rhs_type=::Integer
14
14
  s = n
15
15
 
16
- # !expects NoMethodError: type=(::A & ::Object & ::_Each2[::Integer, ::A]), method=foo
16
+ # !expects NoMethodError: type=(::Object & ::_Each2[::Integer, ::A] & ::A), method=foo
17
17
  foo()
18
18
 
19
19
  n
@@ -1,6 +1,6 @@
1
1
  # @type var a: Integer
2
2
 
3
- # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Integer | ::String)
3
+ # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::String | ::Integer)
4
4
  a = begin
5
5
  'foo'
6
6
  rescue
@@ -9,12 +9,12 @@ a = begin
9
9
 
10
10
  # @type var b: Integer
11
11
 
12
- # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Integer | ::String)
12
+ # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::String | ::Integer)
13
13
  b = 'foo' rescue 1
14
14
 
15
15
  # @type var c: Integer
16
16
 
17
- # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Integer | ::String | ::Symbol)
17
+ # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::String | ::Symbol | ::Integer)
18
18
  c = begin
19
19
  'foo'
20
20
  rescue RuntimeError
@@ -23,18 +23,9 @@ c = begin
23
23
  1
24
24
  end
25
25
 
26
- # @type var d: Integer
27
-
28
- # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=::String
29
- d = begin
30
- 1
31
- else
32
- 'foo'
33
- end
34
-
35
26
  # @type var e: Integer
36
27
 
37
- # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Array[::Integer] | ::Integer | ::Symbol)
28
+ # !expects IncompatibleAssignment: lhs_type=::Integer, rhs_type=(::Array[::Integer] | ::Symbol | ::Integer)
38
29
  e = begin
39
30
  'foo'
40
31
  rescue RuntimeError