type-guessr 0.0.2 → 0.0.4

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/ruby_lsp/type_guessr/addon.rb +4 -5
  4. data/lib/ruby_lsp/type_guessr/code_index_adapter.rb +18 -1
  5. data/lib/ruby_lsp/type_guessr/{graph_builder.rb → debug_graph_builder.rb} +3 -3
  6. data/lib/ruby_lsp/type_guessr/debug_server.rb +2 -2
  7. data/lib/ruby_lsp/type_guessr/dsl/activerecord_adapter.rb +404 -0
  8. data/lib/ruby_lsp/type_guessr/dsl/ar_schema_watcher.rb +96 -0
  9. data/lib/ruby_lsp/type_guessr/dsl/ar_type_mapper.rb +51 -0
  10. data/lib/ruby_lsp/type_guessr/dsl.rb +3 -0
  11. data/lib/ruby_lsp/type_guessr/dsl_type_registrar.rb +60 -0
  12. data/lib/ruby_lsp/type_guessr/hover.rb +46 -40
  13. data/lib/ruby_lsp/type_guessr/rails_server_addon.rb +83 -0
  14. data/lib/ruby_lsp/type_guessr/runtime_adapter.rb +90 -16
  15. data/lib/type-guessr.rb +2 -13
  16. data/lib/type_guessr/core/cache/gem_signature_cache.rb +3 -2
  17. data/lib/type_guessr/core/cache.rb +5 -0
  18. data/lib/{ruby_lsp/type_guessr → type_guessr/core}/config.rb +2 -2
  19. data/lib/type_guessr/core/converter/call_converter.rb +161 -0
  20. data/lib/type_guessr/core/converter/container_mutation_converter.rb +241 -0
  21. data/lib/type_guessr/core/converter/context.rb +144 -0
  22. data/lib/type_guessr/core/converter/control_flow_converter.rb +425 -0
  23. data/lib/type_guessr/core/converter/definition_converter.rb +312 -0
  24. data/lib/type_guessr/core/converter/literal_converter.rb +217 -0
  25. data/lib/type_guessr/core/converter/prism_converter.rb +9 -1682
  26. data/lib/type_guessr/core/converter/rbs_converter.rb +15 -1
  27. data/lib/type_guessr/core/converter/registration.rb +100 -0
  28. data/lib/type_guessr/core/converter/variable_converter.rb +225 -0
  29. data/lib/type_guessr/core/converter.rb +4 -0
  30. data/lib/type_guessr/core/index.rb +3 -0
  31. data/lib/type_guessr/core/inference/resolver.rb +206 -208
  32. data/lib/type_guessr/core/inference.rb +4 -0
  33. data/lib/type_guessr/core/ir.rb +3 -0
  34. data/lib/type_guessr/core/logger.rb +3 -5
  35. data/lib/type_guessr/core/registry/method_registry.rb +9 -0
  36. data/lib/type_guessr/core/registry/signature_registry.rb +73 -16
  37. data/lib/type_guessr/core/registry.rb +6 -0
  38. data/lib/type_guessr/core/type_serializer.rb +18 -14
  39. data/lib/type_guessr/core/type_simplifier.rb +5 -5
  40. data/lib/type_guessr/core/types.rb +64 -22
  41. data/lib/type_guessr/core.rb +29 -0
  42. data/lib/type_guessr/mcp/server.rb +55 -46
  43. data/lib/type_guessr/mcp/standalone_runtime.rb +70 -110
  44. data/lib/type_guessr/version.rb +1 -1
  45. metadata +24 -4
  46. data/.mcp.json +0 -9
@@ -0,0 +1,312 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeGuessr
4
+ module Core
5
+ module Converter
6
+ # Method/class/module/constant definition methods for PrismConverter
7
+ class PrismConverter
8
+ private def convert_def(prism_node, context, module_function: false)
9
+ def_context = context.fork(:method)
10
+ def_context.current_method = prism_node.name.to_s
11
+ def_context.in_singleton_method = prism_node.receiver.is_a?(Prism::SelfNode)
12
+
13
+ # Convert parameters
14
+ params = []
15
+ if prism_node.parameters
16
+ parameters_node = prism_node.parameters
17
+
18
+ # Required parameters
19
+ parameters_node.requireds&.each do |param|
20
+ extract_param_nodes(param, :required, def_context).each do |param_node|
21
+ params << param_node
22
+ end
23
+ end
24
+
25
+ # Optional parameters
26
+ parameters_node.optionals&.each do |param|
27
+ default_node = convert(param.value, def_context)
28
+ param_node = IR::ParamNode.new(param.name, :optional, default_node, [], convert_loc(param.location))
29
+ params << param_node
30
+ def_context.register_variable(param.name, param_node)
31
+ end
32
+
33
+ # Rest parameter (*args)
34
+ if parameters_node.rest.is_a?(Prism::RestParameterNode)
35
+ rest = parameters_node.rest
36
+ param_node = IR::ParamNode.new(rest.name || :*, :rest, nil, [], convert_loc(rest.location))
37
+ params << param_node
38
+ def_context.register_variable(rest.name, param_node) if rest.name
39
+ end
40
+
41
+ # Required keyword parameters (name:)
42
+ parameters_node.keywords&.each do |kw|
43
+ case kw
44
+ when Prism::RequiredKeywordParameterNode
45
+ param_node = IR::ParamNode.new(kw.name, :keyword_required, nil, [], convert_loc(kw.location))
46
+ params << param_node
47
+ def_context.register_variable(kw.name, param_node)
48
+ when Prism::OptionalKeywordParameterNode
49
+ default_node = convert(kw.value, def_context)
50
+ param_node = IR::ParamNode.new(kw.name, :keyword_optional, default_node, [], convert_loc(kw.location))
51
+ params << param_node
52
+ def_context.register_variable(kw.name, param_node)
53
+ end
54
+ end
55
+
56
+ # Keyword rest parameter (**kwargs)
57
+ if parameters_node.keyword_rest.is_a?(Prism::KeywordRestParameterNode)
58
+ kwrest = parameters_node.keyword_rest
59
+ param_node = IR::ParamNode.new(kwrest.name || :**, :keyword_rest, nil, [], convert_loc(kwrest.location))
60
+ params << param_node
61
+ def_context.register_variable(kwrest.name, param_node) if kwrest.name
62
+ elsif parameters_node.keyword_rest.is_a?(Prism::ForwardingParameterNode)
63
+ # Forwarding parameter (...)
64
+ fwd = parameters_node.keyword_rest
65
+ param_node = IR::ParamNode.new(:"...", :forwarding, nil, [], convert_loc(fwd.location))
66
+ params << param_node
67
+ end
68
+
69
+ # Block parameter (&block)
70
+ if parameters_node.block
71
+ block = parameters_node.block
72
+ param_node = IR::ParamNode.new(block.name || :&, :block, nil, [], convert_loc(block.location))
73
+ params << param_node
74
+ def_context.register_variable(block.name, param_node) if block.name
75
+ end
76
+ end
77
+
78
+ # Convert method body - collect all body nodes
79
+ body_nodes = []
80
+
81
+ if prism_node.body.is_a?(Prism::StatementsNode)
82
+ prism_node.body.body.each do |stmt|
83
+ node = convert(stmt, def_context)
84
+ body_nodes << node if node
85
+ end
86
+ elsif prism_node.body.is_a?(Prism::BeginNode)
87
+ # Method with rescue/ensure block
88
+ begin_node = prism_node.body
89
+ body_nodes = extract_begin_body_nodes(begin_node, def_context)
90
+ elsif prism_node.body
91
+ node = convert(prism_node.body, def_context)
92
+ body_nodes << node if node
93
+ end
94
+
95
+ # Collect all return points: explicit returns + implicit last expression
96
+ return_node = compute_return_node(body_nodes, prism_node.name_loc)
97
+
98
+ IR::DefNode.new(
99
+ prism_node.name,
100
+ def_context.current_class_name,
101
+ params,
102
+ return_node,
103
+ body_nodes,
104
+ [],
105
+ convert_loc(prism_node.name_loc),
106
+ prism_node.receiver.is_a?(Prism::SelfNode),
107
+ module_function: module_function
108
+ )
109
+ end
110
+
111
+ # Compute the return node for a method by collecting all return points
112
+ # @param body_nodes [Array<IR::Node>] All nodes in the method body
113
+ # @param loc [Prism::Location] Location for the MergeNode if needed
114
+ # @return [IR::Node, nil] The return node (MergeNode if multiple returns)
115
+ private def compute_return_node(body_nodes, loc)
116
+ return nil if body_nodes.empty?
117
+
118
+ # Collect all explicit returns from the body
119
+ explicit_returns = collect_returns(body_nodes)
120
+
121
+ # The implicit return is the last non-ReturnNode in body
122
+ implicit_return = body_nodes.grep_v(IR::ReturnNode).last
123
+
124
+ # Determine all return points
125
+ return_points = explicit_returns.dup
126
+ return_points << implicit_return if implicit_return && !last_node_returns?(body_nodes)
127
+
128
+ case return_points.size
129
+ when 0
130
+ nil
131
+ when 1
132
+ return_points.first
133
+ else
134
+ IR::MergeNode.new(return_points, [], convert_loc(loc))
135
+ end
136
+ end
137
+
138
+ # Collect all ReturnNode instances from body nodes (recursive)
139
+ # Searches inside MergeNode branches to find nested returns from if/case
140
+ # @param nodes [Array<IR::Node>] Nodes to search
141
+ # @return [Array<IR::ReturnNode>] All explicit return nodes
142
+ private def collect_returns(nodes)
143
+ returns = []
144
+ nodes.each do |node|
145
+ case node
146
+ when IR::ReturnNode
147
+ returns << node
148
+ when IR::MergeNode
149
+ returns.concat(collect_returns(node.branches))
150
+ when IR::OrNode
151
+ returns.concat(collect_returns([node.lhs, node.rhs]))
152
+ end
153
+ end
154
+ returns
155
+ end
156
+
157
+ # Check if the last node in body is a ReturnNode
158
+ # @param body_nodes [Array<IR::Node>] Body nodes
159
+ # @return [Boolean]
160
+ private def last_node_returns?(body_nodes)
161
+ body_nodes.last.is_a?(IR::ReturnNode)
162
+ end
163
+
164
+ private def convert_constant_read(prism_node, context)
165
+ name = case prism_node
166
+ when Prism::ConstantReadNode
167
+ prism_node.name.to_s
168
+ when Prism::ConstantPathNode
169
+ prism_node.slice
170
+ else
171
+ prism_node.to_s
172
+ end
173
+
174
+ IR::ConstantNode.new(name, context.lookup_constant(name), [], convert_loc(prism_node.location))
175
+ end
176
+
177
+ private def convert_constant_write(prism_node, context)
178
+ value_node = convert(prism_node.value, context)
179
+ context.register_constant(prism_node.name.to_s, value_node)
180
+ IR::ConstantNode.new(prism_node.name.to_s, value_node, [], convert_loc(prism_node.location))
181
+ end
182
+
183
+ private def convert_class_or_module(prism_node, context)
184
+ # Get class/module name first
185
+ name = case prism_node.constant_path
186
+ when Prism::ConstantReadNode
187
+ prism_node.constant_path.name.to_s
188
+ when Prism::ConstantPathNode
189
+ prism_node.constant_path.slice
190
+ else
191
+ "Anonymous"
192
+ end
193
+
194
+ # Create a new context for class/module scope with the full class path
195
+ class_context = context.fork(:class)
196
+ parent_path = context.current_class_name
197
+ full_name = parent_path ? "#{parent_path}::#{name}" : name
198
+ class_context.current_class = full_name
199
+
200
+ # Collect all method definitions and nested classes from the body
201
+ methods = []
202
+ nested_classes = []
203
+ if prism_node.body.is_a?(Prism::StatementsNode)
204
+ prism_node.body.body.each do |stmt|
205
+ if attr_accessor_call?(stmt)
206
+ # attr_reader/attr_accessor/attr_writer DSL → 合成DefNodeを生成
207
+ methods.concat(synthesize_attr_defs(stmt, class_context))
208
+ next
209
+ end
210
+
211
+ node = convert(stmt, class_context)
212
+ if node.is_a?(IR::DefNode)
213
+ methods << node
214
+ elsif node.is_a?(IR::ClassModuleNode)
215
+ # Store nested class/module for separate indexing with proper scope
216
+ nested_classes << node
217
+ end
218
+ end
219
+ end
220
+ # Store nested classes in methods array (RuntimeAdapter handles both types)
221
+ methods.concat(nested_classes)
222
+
223
+ IR::ClassModuleNode.new(name, methods, [], convert_loc(prism_node.constant_path&.location || prism_node.location))
224
+ end
225
+
226
+ ATTR_DSL_NAMES = %i[attr_reader attr_writer attr_accessor].freeze
227
+ private_constant :ATTR_DSL_NAMES
228
+
229
+ private def attr_accessor_call?(prism_node)
230
+ return false unless prism_node.is_a?(Prism::CallNode)
231
+ return false unless prism_node.receiver.nil?
232
+
233
+ ATTR_DSL_NAMES.include?(prism_node.name)
234
+ end
235
+
236
+ # attr_reader/attr_writer/attr_accessor呼び出しから合成DefNodeを生成する
237
+ # @return [Array<IR::DefNode>]
238
+ private def synthesize_attr_defs(prism_node, context)
239
+ dsl_name = prism_node.name
240
+ args = prism_node.arguments&.arguments || []
241
+
242
+ args.flat_map do |arg|
243
+ attr_name = extract_attr_name(arg)
244
+ next [] unless attr_name
245
+
246
+ loc = convert_loc(arg.location)
247
+ case dsl_name
248
+ when :attr_reader
249
+ [build_reader_def(attr_name, context.current_class_name, loc)]
250
+ when :attr_writer
251
+ [build_writer_def(attr_name, context.current_class_name, loc)]
252
+ when :attr_accessor
253
+ [
254
+ build_reader_def(attr_name, context.current_class_name, loc),
255
+ build_writer_def(attr_name, context.current_class_name, loc),
256
+ ]
257
+ end
258
+ end
259
+ end
260
+
261
+ # 記号/文字列リテラル引数から属性名を取り出す
262
+ # @return [Symbol, nil] シンボルで返す。リテラルでない場合はnil
263
+ private def extract_attr_name(arg)
264
+ case arg
265
+ when Prism::SymbolNode
266
+ arg.value&.to_sym
267
+ when Prism::StringNode
268
+ arg.unescaped.to_sym
269
+ end
270
+ end
271
+
272
+ # attr_readerに対応する合成DefNode (引数なし、@ivarを返す)
273
+ private def build_reader_def(attr_name, class_name, loc)
274
+ ivar_name = :"@#{attr_name}"
275
+ ivar_read = IR::InstanceVariableReadNode.new(ivar_name, class_name, nil, [], loc)
276
+ IR::DefNode.new(attr_name, class_name, [], ivar_read, [ivar_read], [], loc, false)
277
+ end
278
+
279
+ # attr_writerに対応する合成DefNode (name= :引数を返す)
280
+ private def build_writer_def(attr_name, class_name, loc)
281
+ setter_name = :"#{attr_name}="
282
+ param = IR::ParamNode.new(:value, :required, nil, [], loc)
283
+ IR::DefNode.new(setter_name, class_name, [param], param, [param], [], loc, false)
284
+ end
285
+
286
+ private def convert_singleton_class(prism_node, context)
287
+ # Create a new context for singleton class scope
288
+ singleton_context = context.fork(:class)
289
+
290
+ # Generate singleton class name in format: Parent::<Class:ParentName>
291
+ # This matches the scope convention used by RuntimeAdapter and RubyIndexer
292
+ parent_path = context.current_class_name || ""
293
+ parent_name = IR.extract_last_name(parent_path) || "Object"
294
+ singleton_suffix = "<Class:#{parent_name}>"
295
+ singleton_name = parent_path.empty? ? singleton_suffix : "#{parent_path}::#{singleton_suffix}"
296
+ singleton_context.current_class = singleton_name
297
+
298
+ # Collect all method definitions from the body
299
+ methods = []
300
+ if prism_node.body.is_a?(Prism::StatementsNode)
301
+ prism_node.body.body.each do |stmt|
302
+ node = convert(stmt, singleton_context)
303
+ methods << node if node.is_a?(IR::DefNode)
304
+ end
305
+ end
306
+
307
+ IR::ClassModuleNode.new(singleton_name, methods, [], convert_loc(prism_node.location))
308
+ end
309
+ end
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeGuessr
4
+ module Core
5
+ module Converter
6
+ # Literal and type inference helpers for PrismConverter
7
+ class PrismConverter
8
+ private def convert_literal(prism_node)
9
+ type = literal_type_for(prism_node)
10
+ literal_value = extract_literal_value(prism_node)
11
+ IR::LiteralNode.new(type, literal_value, nil, [], convert_loc(prism_node.location))
12
+ end
13
+
14
+ # Extract the actual value from a literal node (for Symbol, Integer, String)
15
+ private def extract_literal_value(prism_node)
16
+ case prism_node
17
+ when Prism::SymbolNode
18
+ prism_node.value.to_sym
19
+ when Prism::IntegerNode
20
+ prism_node.value
21
+ when Prism::StringNode
22
+ prism_node.content
23
+ end
24
+ end
25
+
26
+ private def convert_array_literal(prism_node, context)
27
+ type = array_element_type_for(prism_node)
28
+
29
+ # Convert each element to an IR node
30
+ value_nodes = prism_node.elements.filter_map do |elem|
31
+ next if elem.nil?
32
+
33
+ case elem
34
+ when Prism::SplatNode
35
+ # *arr → convert to CallNode for to_a
36
+ splat_expr = convert(elem.expression, context)
37
+ IR::CallNode.new(:to_a, splat_expr, [], [], nil, false, [], convert_loc(elem.location))
38
+ else
39
+ convert(elem, context)
40
+ end
41
+ end
42
+
43
+ IR::LiteralNode.new(type, nil, value_nodes.empty? ? nil : value_nodes, [], convert_loc(prism_node.location))
44
+ end
45
+
46
+ private def convert_hash_literal(prism_node, context)
47
+ type = hash_element_types_for(prism_node)
48
+ build_hash_literal_node(prism_node, type, context)
49
+ end
50
+
51
+ # Convert KeywordHashNode (keyword arguments in method calls like `foo(a: 1, b: x)`)
52
+ private def convert_keyword_hash(prism_node, context)
53
+ type = infer_keyword_hash_type(prism_node)
54
+ build_hash_literal_node(prism_node, type, context)
55
+ end
56
+
57
+ # Shared helper for hash-like nodes (HashNode, KeywordHashNode)
58
+ private def build_hash_literal_node(prism_node, type, context)
59
+ value_nodes = prism_node.elements.filter_map do |elem|
60
+ case elem
61
+ when Prism::AssocNode
62
+ convert(elem.value, context)
63
+ when Prism::AssocSplatNode
64
+ convert(elem.value, context)
65
+ end
66
+ end
67
+
68
+ IR::LiteralNode.new(type, nil, value_nodes.empty? ? nil : value_nodes, [], convert_loc(prism_node.location))
69
+ end
70
+
71
+ # Infer type for KeywordHashNode (always has symbol keys)
72
+ private def infer_keyword_hash_type(keyword_hash_node)
73
+ return Types::HashShape.new({}) if keyword_hash_node.elements.empty?
74
+
75
+ fields = keyword_hash_node.elements.each_with_object({}) do |elem, hash|
76
+ next unless elem.is_a?(Prism::AssocNode) && elem.key.is_a?(Prism::SymbolNode)
77
+
78
+ hash[elem.key.value.to_sym] = literal_type_for(elem.value)
79
+ end
80
+ Types::HashShape.new(fields)
81
+ end
82
+
83
+ private def literal_type_for(prism_node)
84
+ case prism_node
85
+ when Prism::IntegerNode
86
+ Types::ClassInstance.for("Integer")
87
+ when Prism::FloatNode
88
+ Types::ClassInstance.for("Float")
89
+ when Prism::StringNode, Prism::InterpolatedStringNode
90
+ Types::ClassInstance.for("String")
91
+ when Prism::SymbolNode
92
+ Types::ClassInstance.for("Symbol")
93
+ when Prism::TrueNode
94
+ Types::ClassInstance.for("TrueClass")
95
+ when Prism::FalseNode
96
+ Types::ClassInstance.for("FalseClass")
97
+ when Prism::NilNode
98
+ Types::ClassInstance.for("NilClass")
99
+ when Prism::ArrayNode
100
+ # Infer element type from array contents
101
+ array_element_type_for(prism_node)
102
+ when Prism::HashNode
103
+ hash_element_types_for(prism_node)
104
+ when Prism::RangeNode
105
+ range_element_type_for(prism_node)
106
+ when Prism::RegularExpressionNode, Prism::InterpolatedRegularExpressionNode
107
+ Types::ClassInstance.for("Regexp")
108
+ when Prism::ImaginaryNode
109
+ Types::ClassInstance.for("Complex")
110
+ when Prism::RationalNode
111
+ Types::ClassInstance.for("Rational")
112
+ when Prism::XStringNode, Prism::InterpolatedXStringNode
113
+ Types::ClassInstance.for("String")
114
+ else
115
+ Types::Unknown.instance
116
+ end
117
+ end
118
+
119
+ private def range_element_type_for(range_node)
120
+ left_type = range_node.left ? literal_type_for(range_node.left) : nil
121
+ right_type = range_node.right ? literal_type_for(range_node.right) : nil
122
+
123
+ types = [left_type, right_type].compact
124
+
125
+ # No bounds at all (shouldn't happen in valid Ruby, but handle gracefully)
126
+ return Types::RangeType.new if types.empty?
127
+
128
+ unique_types = types.uniq
129
+
130
+ element_type = if unique_types.size == 1
131
+ unique_types.first
132
+ else
133
+ Types::Union.new(unique_types)
134
+ end
135
+
136
+ Types::RangeType.new(element_type)
137
+ end
138
+
139
+ private def array_element_type_for(array_node)
140
+ return Types::TupleType.new([]) if array_node.elements.empty?
141
+
142
+ element_types = array_node.elements.filter_map do |elem|
143
+ literal_type_for(elem) unless elem.nil?
144
+ end
145
+
146
+ return Types::ArrayType.new if element_types.empty?
147
+
148
+ if element_types.any?(Types::Unknown)
149
+ # Splat or unknown elements → widen to ArrayType(Union)
150
+ unique_types = element_types.uniq
151
+ Types::ArrayType.new(Types::Union.new(unique_types))
152
+ else
153
+ Types::TupleType.new(element_types)
154
+ end
155
+ end
156
+
157
+ private def hash_element_types_for(hash_node)
158
+ return Types::HashShape.new({}) if hash_node.elements.empty?
159
+
160
+ # Check if all keys are symbols for HashShape
161
+ all_symbol_keys = hash_node.elements.all? do |elem|
162
+ elem.is_a?(Prism::AssocNode) && elem.key.is_a?(Prism::SymbolNode)
163
+ end
164
+
165
+ if all_symbol_keys
166
+ # Build HashShape with field types
167
+ fields = {}
168
+ hash_node.elements.each do |elem|
169
+ next unless elem.is_a?(Prism::AssocNode) && elem.key.is_a?(Prism::SymbolNode)
170
+
171
+ field_name = elem.key.value.to_sym
172
+ field_type = literal_type_for(elem.value)
173
+ fields[field_name] = field_type
174
+ end
175
+ Types::HashShape.new(fields)
176
+ else
177
+ # Non-symbol keys or mixed keys - return HashType
178
+ key_types = []
179
+ value_types = []
180
+
181
+ hash_node.elements.each do |elem|
182
+ case elem
183
+ when Prism::AssocNode
184
+ key_types << literal_type_for(elem.key) if elem.key
185
+ value_types << literal_type_for(elem.value) if elem.value
186
+ end
187
+ end
188
+
189
+ return Types::HashType.new if key_types.empty? && value_types.empty?
190
+
191
+ # Deduplicate types
192
+ unique_key_types = key_types.uniq
193
+ unique_value_types = value_types.uniq
194
+
195
+ key_type = if unique_key_types.size == 1
196
+ unique_key_types.first
197
+ elsif unique_key_types.empty?
198
+ Types::Unknown.instance
199
+ else
200
+ Types::Union.new(unique_key_types)
201
+ end
202
+
203
+ value_type = if unique_value_types.size == 1
204
+ unique_value_types.first
205
+ elsif unique_value_types.empty?
206
+ Types::Unknown.instance
207
+ else
208
+ Types::Union.new(unique_value_types)
209
+ end
210
+
211
+ Types::HashType.new(key_type, value_type)
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end