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
@@ -383,6 +383,31 @@ module Steep
383
383
 
384
384
  typing
385
385
  end
386
+
387
+ def source_file?(path)
388
+ if source_files.key?(path)
389
+ project.target_for_source_path(path)
390
+ end
391
+ end
392
+
393
+ def signature_file?(path)
394
+ relative_path = project.relative_path(path)
395
+ targets = signature_services.select {|_, sig| sig.files.key?(relative_path) || sig.env_rbs_paths.include?(path) }
396
+ unless targets.empty?
397
+ targets.keys
398
+ end
399
+ end
400
+
401
+ def app_signature_file?(path)
402
+ target_names = signature_services.select {|_, sig| sig.files.key?(path) }.keys
403
+ unless target_names.empty?
404
+ target_names
405
+ end
406
+ end
407
+
408
+ def lib_signature_file?(path)
409
+ signature_services.each_value.any? {|sig| sig.env_rbs_paths.include?(path) }
410
+ end
386
411
  end
387
412
  end
388
413
  end
data/lib/steep/source.rb CHANGED
@@ -35,28 +35,25 @@ module Steep
35
35
 
36
36
  self.emit_lambda = true
37
37
  self.emit_procarg0 = true
38
+ self.emit_kwargs = true
38
39
  end
39
40
 
40
- def self.parser
41
- ::Parser::Ruby27.new(Builder.new).tap do |parser|
41
+ def self.new_parser
42
+ ::Parser::Ruby30.new(Builder.new).tap do |parser|
42
43
  parser.diagnostics.all_errors_are_fatal = true
43
44
  parser.diagnostics.ignore_warnings = true
44
45
  end
45
46
  end
46
47
 
47
48
  def self.parse(source_code, path:, factory:)
48
- buffer = ::Parser::Source::Buffer.new(path.to_s, 1)
49
- buffer.source = source_code
50
- node = parser.parse(buffer)
49
+ buffer = ::Parser::Source::Buffer.new(path.to_s, 1, source: source_code)
50
+ node = new_parser().parse(buffer)
51
51
 
52
52
  annotations = []
53
53
 
54
54
  _, comments, _ = yield_self do
55
- buffer = ::Parser::Source::Buffer.new(path.to_s)
56
- buffer.source = source_code
57
- parser = ::Parser::Ruby27.new
58
-
59
- parser.tokenize(buffer)
55
+ buffer = ::Parser::Source::Buffer.new(path.to_s, 1, source: source_code)
56
+ new_parser().tokenize(buffer)
60
57
  end
61
58
 
62
59
  buffer = RBS::Buffer.new(name: path, content: source_code)
@@ -10,10 +10,7 @@ module Steep
10
10
  end
11
11
 
12
12
  def with(type: self.type, constr: self.constr)
13
- self.class.new(
14
- type: type,
15
- constr: constr
16
- )
13
+ self.class.new(type: type, constr: constr)
17
14
  end
18
15
 
19
16
  def +(other)
@@ -162,23 +159,6 @@ module Steep
162
159
 
163
160
  # constructor_method = method&.attributes&.include?(:constructor)
164
161
 
165
- if method_type
166
- var_types = TypeConstruction.parameter_types(args, method_type.type)
167
- unless TypeConstruction.valid_parameter_env?(var_types, args.reject {|arg| arg.type == :blockarg}, method_type.type.params)
168
- typing.add_error Diagnostic::Ruby::MethodArityMismatch.new(node: node, method_type: method_type)
169
- end
170
- end
171
-
172
- if (block_arg = args.find {|arg| arg.type == :blockarg})
173
- if method_type&.block
174
- block_type = AST::Types::Proc.new(type: method_type.block.type, block: nil)
175
- if method_type.block.optional?
176
- block_type = AST::Types::Union.build(types: [block_type, AST::Builtin.nil_type])
177
- end
178
- var_types[block_arg.children[0]] = block_type
179
- end
180
- end
181
-
182
162
  super_method = if definition
183
163
  if (this_method = definition.methods[method_name])
184
164
  if module_context&.class_name == this_method.defined_in
@@ -219,13 +199,21 @@ module Steep
219
199
  class_type: module_context.module_type
220
200
  )
221
201
 
222
- if var_types
223
- lvar_env = lvar_env.update(
224
- assigned_types: var_types.each.with_object({}) {|(var, type), hash|
225
- arg_node = args.find {|arg| arg.children[0] == var }
226
- hash[var] = TypeInference::LocalVariableTypeEnv::Entry.new(type: type, nodes: [arg_node].compact)
227
- }
228
- )
202
+ method_params =
203
+ if method_type
204
+ TypeInference::MethodParams.build(node: node, method_type: method_type)
205
+ else
206
+ TypeInference::MethodParams.empty(node: node)
207
+ end
208
+
209
+ method_params.each_param do |param|
210
+ lvar_env = lvar_env.assign(param.name, type: param.var_type, node: param.node) {
211
+ raise "Unexpected assignment error: #{param.name}"
212
+ }
213
+ end
214
+
215
+ method_params.errors.each do |error|
216
+ typing.add_error error
229
217
  end
230
218
 
231
219
  lvar_env = lvar_env.annotate(annots)
@@ -692,7 +680,7 @@ module Steep
692
680
  end
693
681
 
694
682
  def synthesize(node, hint: nil, condition: false)
695
- Steep.logger.tagged "synthesize:(#{node.location.expression.to_s.split(/:/, 2).last})" do
683
+ Steep.logger.tagged "synthesize:(#{node.location&.yield_self {|loc| loc.expression.to_s.split(/:/, 2).last } || "-"})" do
696
684
  Steep.logger.debug node.type
697
685
  case node.type
698
686
  when :begin, :kwbegin
@@ -732,8 +720,17 @@ module Steep
732
720
  when :__skip__
733
721
  add_typing(node, type: AST::Builtin.any_type)
734
722
  else
735
- if !hint || hint.is_a?(AST::Types::Void)
736
- hint = context.lvar_env.declared_types[name]&.type
723
+ if declared_type = context.lvar_env.declared_types[name]&.type
724
+ case hint
725
+ when nil
726
+ hint = declared_type
727
+ else
728
+ if check_relation(sub_type: declared_type, super_type: hint).success?
729
+ # declared_type is compatible with hint and more specific to hint.
730
+ # This typically happens when hint is untyped, top, or void.
731
+ hint = declared_type
732
+ end
733
+ end
737
734
  end
738
735
 
739
736
  rhs_result = synthesize(rhs, hint: hint)
@@ -751,7 +748,7 @@ module Steep
751
748
  end
752
749
  end
753
750
 
754
- add_typing(node, type: rhs_result.type, constr: constr)
751
+ constr.add_typing(node, type: rhs_result.type)
755
752
  end
756
753
  end
757
754
 
@@ -888,13 +885,12 @@ module Steep
888
885
  checker.factory.method_type(method_type, self_type: self_type, method_decls: Set[decl])
889
886
  }
890
887
  )
891
- args = TypeInference::SendArgs.from_nodes(node.children.dup)
892
888
 
893
889
  call, constr = type_method_call(node,
894
890
  receiver_type: self_type,
895
891
  method_name: method_context.name,
896
892
  method: super_method,
897
- args: args,
893
+ arguments: node.children,
898
894
  block_params: nil,
899
895
  block_body: nil,
900
896
  topdown_hint: true)
@@ -940,18 +936,46 @@ module Steep
940
936
  end
941
937
  end
942
938
 
939
+ when :numblock
940
+ yield_self do
941
+ send_node, max_num, body = node.children
942
+
943
+ if max_num == 1
944
+ arg_nodes = [Parser::AST::Node.new(:procarg0, [:_1])]
945
+ else
946
+ arg_nodes = max_num.times.map {|i| Parser::AST::Node.new(:arg, [:"_#{i+1}"]) }
947
+ end
948
+
949
+ params = Parser::AST::Node.new(:args, arg_nodes)
950
+
951
+ if send_node.type == :lambda
952
+ type_lambda(node, block_params: params, block_body: body, type_hint: hint)
953
+ else
954
+ type_send(node, send_node: send_node, block_params: params, block_body: body, unwrap: send_node.type == :csend)
955
+ end
956
+ end
957
+
943
958
  when :def
944
959
  yield_self do
945
960
  name, args_node, body_node = node.children
946
961
 
947
- new = for_new_method(name,
948
- node,
949
- args: args_node.children,
950
- self_type: module_context&.instance_type,
951
- definition: module_context&.instance_definition)
962
+ new = for_new_method(
963
+ name,
964
+ node,
965
+ args: args_node.children,
966
+ self_type: module_context&.instance_type,
967
+ definition: module_context&.instance_definition
968
+ )
952
969
  new.typing.add_context_for_node(node, context: new.context)
953
970
  new.typing.add_context_for_body(node, context: new.context)
954
971
 
972
+ new.method_context.tap do |method_context|
973
+ if method_context.method
974
+ method_name = InstanceMethodName.new(type_name: method_context.method.implemented_in, method_name: name)
975
+ new.typing.source_index.add_definition(method: method_name, definition: node)
976
+ end
977
+ end
978
+
955
979
  new = new.synthesize_children(args_node)
956
980
 
957
981
  body_pair = if body_node
@@ -990,10 +1014,13 @@ module Steep
990
1014
  end
991
1015
 
992
1016
  if body_node
993
- begin_pos = body_node.loc.expression.end_pos
994
- end_pos = node.loc.end.begin_pos
995
-
996
- typing.add_context(begin_pos..end_pos, context: body_pair.context)
1017
+ # Add context to ranges from the end of the method body to the beginning of the `end` keyword
1018
+ if node.loc.end
1019
+ # Skip end-less def
1020
+ begin_pos = body_node.loc.expression.end_pos
1021
+ end_pos = node.loc.end.begin_pos
1022
+ typing.add_context(begin_pos..end_pos, context: body_pair.context)
1023
+ end
997
1024
  end
998
1025
 
999
1026
  if module_context
@@ -1006,24 +1033,43 @@ module Steep
1006
1033
  when :defs
1007
1034
  synthesize(node.children[0]).type.tap do |self_type|
1008
1035
  self_type = expand_self(self_type)
1009
- definition = case self_type
1010
- when AST::Types::Name::Instance
1011
- name = self_type.name
1012
- checker.factory.definition_builder.build_singleton(name)
1013
- when AST::Types::Name::Singleton
1014
- name = self_type.name
1015
- checker.factory.definition_builder.build_singleton(name)
1016
- end
1036
+ definition =
1037
+ case self_type
1038
+ when AST::Types::Name::Instance
1039
+ name = self_type.name
1040
+ checker.factory.definition_builder.build_instance(name)
1041
+ when AST::Types::Name::Singleton
1042
+ name = self_type.name
1043
+ checker.factory.definition_builder.build_singleton(name)
1044
+ end
1017
1045
 
1018
1046
  args_node = node.children[2]
1019
- new = for_new_method(node.children[1],
1020
- node,
1021
- args: args_node.children,
1022
- self_type: self_type,
1023
- definition: definition)
1047
+ new = for_new_method(
1048
+ node.children[1],
1049
+ node,
1050
+ args: args_node.children,
1051
+ self_type: self_type,
1052
+ definition: definition
1053
+ )
1024
1054
  new.typing.add_context_for_node(node, context: new.context)
1025
1055
  new.typing.add_context_for_body(node, context: new.context)
1026
1056
 
1057
+ new.method_context.tap do |method_context|
1058
+ if method_context.method
1059
+ name_ = node.children[1]
1060
+
1061
+ method_name =
1062
+ case self_type
1063
+ when AST::Types::Name::Instance
1064
+ InstanceMethodName.new(type_name: method_context.method.implemented_in, method_name: name_)
1065
+ when AST::Types::Name::Singleton
1066
+ SingletonMethodName.new(type_name: method_context.method.implemented_in, method_name: name_)
1067
+ end
1068
+
1069
+ new.typing.source_index.add_definition(method: method_name, definition: node)
1070
+ end
1071
+ end
1072
+
1027
1073
  new = new.synthesize_children(args_node)
1028
1074
 
1029
1075
  each_child_node(node.children[2]) do |arg|
@@ -1115,15 +1161,16 @@ module Steep
1115
1161
  )
1116
1162
  end
1117
1163
  else
1118
- check_relation(sub_type: AST::Builtin.nil_type, super_type: break_type).else do |result|
1119
- typing.add_error(
1120
- Diagnostic::Ruby::BreakTypeMismatch.new(
1121
- node: node,
1122
- expected: break_type,
1123
- actual: AST::Builtin.nil_type,
1124
- result: result
1164
+ unless break_type.is_a?(AST::Types::Bot)
1165
+ check_relation(sub_type: AST::Builtin.nil_type, super_type: break_type).else do |result|
1166
+ typing.add_error(
1167
+ Diagnostic::Ruby::ImplicitBreakValueMismatch.new(
1168
+ node: node,
1169
+ jump_type: break_type,
1170
+ result: result
1171
+ )
1125
1172
  )
1126
- )
1173
+ end
1127
1174
  end
1128
1175
  end
1129
1176
  else
@@ -1335,52 +1382,20 @@ module Steep
1335
1382
  add_typing(node, type: AST::Types::Boolean.new)
1336
1383
  end
1337
1384
 
1338
- when :hash
1339
- yield_self do
1340
- ty = try_hash_type(node, hint) and return ty
1341
-
1342
- if AST::Builtin::Hash.instance_type?(hint)
1343
- key_hint = hint.args[0]
1344
- value_hint = hint.args[1]
1345
- end
1346
-
1347
- key_types = []
1348
- value_types = []
1349
-
1350
- each_child_node(node) do |child|
1351
- case child.type
1352
- when :pair
1353
- key, value = child.children
1354
- key_types << synthesize(key, hint: key_hint).type.yield_self do |type|
1355
- select_super_type(type, key_hint)
1356
- end
1357
- value_types << synthesize(value, hint: value_hint).type.yield_self do |type|
1358
- select_super_type(type, value_hint)
1359
- end
1360
- when :kwsplat
1361
- expand_alias(synthesize(child.children[0]).type) do |splat_type, original_type|
1362
- if AST::Builtin::Hash.instance_type?(splat_type)
1363
- key_types << splat_type.args[0]
1364
- value_types << splat_type.args[1]
1365
- else
1366
- typing.add_error Diagnostic::Ruby::UnexpectedSplat.new(node: child, type: original_type)
1367
- key_types << AST::Builtin.any_type
1368
- value_types << AST::Builtin.any_type
1369
- end
1370
- end
1385
+ when :hash, :kwargs
1386
+ # :kwargs happens for method calls with keyword argument, but the method doesn't have keyword params.
1387
+ # Conversion from kwargs to hash happens, and this when-clause is to support it.
1388
+ type_hash(node, hint: hint).tap do |pair|
1389
+ if pair.type == AST::Builtin::Hash.instance_type(fill_untyped: true)
1390
+ case hint
1391
+ when AST::Types::Any, AST::Types::Top, AST::Types::Void
1392
+ # ok
1393
+ when hint == pair.type
1394
+ # ok
1371
1395
  else
1372
- raise "Unexpected non pair: #{child.inspect}" unless child.type == :pair
1396
+ pair.constr.typing.add_error Diagnostic::Ruby::FallbackAny.new(node: node)
1373
1397
  end
1374
1398
  end
1375
-
1376
- key_type = key_types.empty? ? AST::Builtin.any_type : AST::Types::Union.build(types: key_types)
1377
- value_type = value_types.empty? ? AST::Builtin.any_type : AST::Types::Union.build(types: value_types)
1378
-
1379
- if key_types.empty? && value_types.empty? && !hint
1380
- typing.add_error Diagnostic::Ruby::FallbackAny.new(node: node)
1381
- end
1382
-
1383
- add_typing(node, type: AST::Builtin::Hash.instance_type(key_type, value_type))
1384
1399
  end
1385
1400
 
1386
1401
  when :dstr, :xstr
@@ -1443,9 +1458,22 @@ module Steep
1443
1458
  constr = self
1444
1459
 
1445
1460
  name, _ = node.children
1446
- _, constr = constr.synthesize(name)
1461
+ if name.type == :const
1462
+ # skip the last constant reference
1463
+ if const_parent = name.children[0]
1464
+ _, constr = constr.synthesize(const_parent)
1465
+ end
1466
+ else
1467
+ _, constr = constr.synthesize(name)
1468
+ end
1447
1469
 
1448
1470
  for_module(node).yield_self do |constructor|
1471
+ if module_type = constructor.module_context&.module_type
1472
+ _, constructor = constructor.add_typing(name, type: module_type)
1473
+ else
1474
+ _, constructor = constructor.fallback_to_any(name)
1475
+ end
1476
+
1449
1477
  constructor.typing.source_index.add_definition(
1450
1478
  constant: constructor.module_context.class_name,
1451
1479
  definition: node
@@ -1642,8 +1670,7 @@ module Steep
1642
1670
  tuples.each do |tuple|
1643
1671
  typing.new_child(node_range) do |child_typing|
1644
1672
  if pair = with_new_typing(child_typing).try_tuple_type(node, tuple)
1645
- child_typing.save!
1646
- return pair.with(constr: pair.constr.with_new_typing(typing))
1673
+ return pair.with(constr: pair.constr.save_typing)
1647
1674
  end
1648
1675
  end
1649
1676
  end
@@ -1654,8 +1681,7 @@ module Steep
1654
1681
  typing.new_child(node_range) do |child_typing|
1655
1682
  pair = with_new_typing(child_typing).try_array_type(node, array)
1656
1683
  if pair.constr.check_relation(sub_type: pair.type, super_type: hint).success?
1657
- child_typing.save!
1658
- return pair.with(constr: pair.constr.with_new_typing(typing))
1684
+ return pair.with(constr: pair.constr.save_typing)
1659
1685
  end
1660
1686
  end
1661
1687
  end
@@ -2250,7 +2276,9 @@ module Steep
2250
2276
  unless return_types.empty?
2251
2277
  type = AST::Types::Proc.new(
2252
2278
  type: Interface::Function.new(
2253
- params: Interface::Function::Params.empty.update(required: [param_type]),
2279
+ params: Interface::Function::Params.empty.with_first_param(
2280
+ Interface::Function::Params::PositionalParams::Required.new(param_type)
2281
+ ),
2254
2282
  return_type: AST::Types::Union.build(types: return_types),
2255
2283
  location: nil
2256
2284
  ),
@@ -2348,6 +2376,7 @@ module Steep
2348
2376
 
2349
2377
  else
2350
2378
  typing.add_error(Diagnostic::Ruby::UnsupportedSyntax.new(node: node))
2379
+ add_typing(node, type: AST::Builtin.any_type)
2351
2380
 
2352
2381
  end.tap do |pair|
2353
2382
  unless pair.is_a?(Pair) && !pair.type.is_a?(Pair)
@@ -2697,11 +2726,10 @@ module Steep
2697
2726
  method = interface.methods[method_name]
2698
2727
 
2699
2728
  if method
2700
- args = TypeInference::SendArgs.from_nodes(arguments)
2701
2729
  call, constr = type_method_call(node,
2702
2730
  method: method,
2703
2731
  method_name: method_name,
2704
- args: args,
2732
+ arguments: arguments,
2705
2733
  block_params: block_params,
2706
2734
  block_body: block_body,
2707
2735
  receiver_type: receiver_type,
@@ -2881,27 +2909,22 @@ module Steep
2881
2909
  end
2882
2910
  end
2883
2911
 
2884
- def type_method_call(node, method_name:, receiver_type:, method:, args:, block_params:, block_body:, topdown_hint:)
2912
+ def type_method_call(node, method_name:, receiver_type:, method:, arguments:, block_params:, block_body:, topdown_hint:)
2885
2913
  node_range = node.loc.expression.yield_self {|l| l.begin_pos..l.end_pos }
2886
2914
 
2887
- results = method.method_types.flat_map do |method_type|
2915
+ results = method.method_types.map do |method_type|
2888
2916
  Steep.logger.tagged method_type.to_s do
2889
- zips = args.zips(method_type.type.params, method_type.block&.type)
2890
-
2891
- zips.map do |arg_pairs|
2892
- typing.new_child(node_range) do |child_typing|
2893
- self.with_new_typing(child_typing).try_method_type(
2894
- node,
2895
- receiver_type: receiver_type,
2896
- method_name: method_name,
2897
- method_type: method_type,
2898
- args: args,
2899
- arg_pairs: arg_pairs,
2900
- block_params: block_params,
2901
- block_body: block_body,
2902
- topdown_hint: topdown_hint
2903
- )
2904
- end
2917
+ typing.new_child(node_range) do |child_typing|
2918
+ self.with_new_typing(child_typing).try_method_type(
2919
+ node,
2920
+ receiver_type: receiver_type,
2921
+ method_name: method_name,
2922
+ method_type: method_type,
2923
+ arguments: arguments,
2924
+ block_params: block_params,
2925
+ block_body: block_body,
2926
+ topdown_hint: topdown_hint
2927
+ )
2905
2928
  end
2906
2929
  end
2907
2930
  end
@@ -2941,138 +2964,34 @@ module Steep
2941
2964
  ]
2942
2965
  end
2943
2966
 
2944
- def check_keyword_arg(receiver_type:, node:, method_type:, constraints:)
2945
- params = method_type.type.params
2946
-
2947
- case node.type
2948
- when :hash
2949
- keyword_hash_type = AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type,
2950
- AST::Builtin.any_type)
2951
- add_typing node, type: keyword_hash_type
2952
-
2953
- given_keys = Set.new()
2954
-
2955
- node.children.each do |element|
2956
- case element.type
2957
- when :pair
2958
- key_node, value_node = element.children
2959
-
2960
- case key_node.type
2961
- when :sym
2962
- key_symbol = key_node.children[0]
2963
- keyword_type = case
2964
- when params.required_keywords.key?(key_symbol)
2965
- params.required_keywords[key_symbol]
2966
- when params.optional_keywords.key?(key_symbol)
2967
- AST::Types::Union.build(
2968
- types: [params.optional_keywords[key_symbol],
2969
- AST::Builtin.nil_type]
2970
- )
2971
- when params.rest_keywords
2972
- params.rest_keywords
2973
- end
2974
-
2975
- add_typing key_node, type: AST::Builtin::Symbol.instance_type
2976
-
2977
- given_keys << key_symbol
2978
-
2979
- if keyword_type
2980
- check(value_node, keyword_type, constraints: constraints) do |expected, actual, result|
2981
- return Diagnostic::Ruby::IncompatibleAssignment.new(
2982
- node: value_node,
2983
- lhs_type: expected,
2984
- rhs_type: actual,
2985
- result: result
2986
- )
2987
- end
2988
- else
2989
- synthesize(value_node)
2990
- end
2991
-
2992
- else
2993
- check(key_node, AST::Builtin::Symbol.instance_type, constraints: constraints) do |expected, actual, result|
2994
- return Diagnostic::Ruby::IncompatibleAssignment.new(
2995
- node: key_node,
2996
- lhs_type: expected,
2997
- rhs_type: actual,
2998
- result: result
2999
- )
3000
- end
3001
- end
3002
-
3003
- when :kwsplat
3004
- Steep.logger.warn("Keyword arg with kwsplat(**) node are not supported.")
3005
-
3006
- check(element.children[0], keyword_hash_type, constraints: constraints) do |expected, actual, result|
3007
- return Diagnostic::Ruby::IncompatibleAssignment.new(
3008
- node: node,
3009
- lhs_type: expected,
3010
- rhs_type: actual,
3011
- result: result
3012
- )
3013
- end
3014
-
3015
- given_keys = true
3016
- end
3017
- end
2967
+ def inspect
2968
+ "#<#{self.class}>"
2969
+ end
3018
2970
 
3019
- case given_keys
3020
- when Set
3021
- missing_keywords = Set.new(params.required_keywords.keys) - given_keys
3022
- unless missing_keywords.empty?
3023
- return Diagnostic::Ruby::MissingKeyword.new(
3024
- node: node,
3025
- missing_keywords: missing_keywords
3026
- )
3027
- end
2971
+ def with_child_typing(range:)
2972
+ constr = with_new_typing(typing.new_child(range: range))
3028
2973
 
3029
- extra_keywords = given_keys - Set.new(params.required_keywords.keys) - Set.new(params.optional_keywords.keys)
3030
- if extra_keywords.any? && !params.rest_keywords
3031
- return Diagnostic::Ruby::UnexpectedKeyword.new(
3032
- node: node,
3033
- unexpected_keywords: extra_keywords
3034
- )
3035
- end
3036
- end
2974
+ if block_given?
2975
+ yield constr
3037
2976
  else
3038
- if params.rest_keywords
3039
- Steep.logger.warn("Method call with rest keywords type is detected. Rough approximation to be improved.")
3040
-
3041
- value_types = params.required_keywords.values +
3042
- params.optional_keywords.values.map {|type| AST::Types::Union.build(types: [type, AST::Builtin.nil_type])} +
3043
- [params.rest_keywords]
3044
-
3045
- hash_type = AST::Builtin::Hash.instance_type(
3046
- AST::Builtin::Symbol.instance_type,
3047
- AST::Types::Union.build(types: value_types)
3048
- )
3049
- else
3050
- hash_elements = params.required_keywords.merge(
3051
- params.optional_keywords.transform_values do |type|
3052
- AST::Types::Union.build(types: [type, AST::Builtin.nil_type])
3053
- end
3054
- )
3055
-
3056
- hash_type = AST::Types::Record.new(elements: hash_elements)
3057
- end
2977
+ constr
2978
+ end
2979
+ end
3058
2980
 
3059
- node_type = synthesize(node, hint: hash_type).type
2981
+ # Bypass :splat and :kwsplat
2982
+ def bypass_splat(node)
2983
+ splat = node.type == :splat || node.type == :kwsplat
3060
2984
 
3061
- check_relation(sub_type: node_type, super_type: hash_type).else do |result|
3062
- return Diagnostic::Ruby::ArgumentTypeMismatch.new(
3063
- node: node,
3064
- receiver_type: receiver_type,
3065
- expected: hash_type,
3066
- actual: node_type,
3067
- result: result
3068
- )
3069
- end
2985
+ if splat
2986
+ pair = yield(node.children[0])
2987
+ pair.constr.add_typing(node, type: pair.type)
2988
+ pair
2989
+ else
2990
+ yield node
3070
2991
  end
3071
-
3072
- nil
3073
2992
  end
3074
2993
 
3075
- def try_method_type(node, receiver_type:, method_name:, method_type:, args:, arg_pairs:, block_params:, block_body:, topdown_hint:)
2994
+ def try_method_type(node, receiver_type:, method_name:, method_type:, arguments:, block_params:, block_body:, topdown_hint:)
3076
2995
  fresh_types = method_type.type_params.map {|x| AST::Types::Var.fresh(x)}
3077
2996
  fresh_vars = Set.new(fresh_types.map(&:name))
3078
2997
  instantiation = Interface::Substitution.build(method_type.type_params, fresh_types)
@@ -3087,38 +3006,90 @@ module Steep
3087
3006
 
3088
3007
  errors = []
3089
3008
 
3090
- arg_pairs.each do |pair|
3091
- case pair
3092
- when Array
3093
- arg_node, param_type = pair
3094
- param_type = param_type.subst(instantiation)
3009
+ args = TypeInference::SendArgs.new(node: node, arguments: arguments, method_name: method_name, method_type: method_type)
3010
+ es = args.each do |arg|
3011
+ case arg
3012
+ when TypeInference::SendArgs::PositionalArgs::NodeParamPair
3013
+ _, constr = constr.type_check_argument(
3014
+ arg.node,
3015
+ type: arg.param.type,
3016
+ receiver_type: receiver_type,
3017
+ constraints: constraints,
3018
+ errors: errors
3019
+ )
3095
3020
 
3096
- arg_type, constr = if arg_node.type == :splat
3097
- constr.synthesize(arg_node.children[0])
3098
- else
3099
- constr.synthesize(arg_node, hint: topdown_hint ? param_type : nil)
3100
- end
3021
+ when TypeInference::SendArgs::PositionalArgs::NodeTypePair
3022
+ _, constr = bypass_splat(arg.node) do |n|
3023
+ constr.type_check_argument(
3024
+ n,
3025
+ type: arg.node_type,
3026
+ receiver_type: receiver_type,
3027
+ constraints: constraints,
3028
+ report_node: arg.node,
3029
+ errors: errors
3030
+ )
3031
+ end
3101
3032
 
3102
- check_relation(sub_type: arg_type, super_type: param_type, constraints: constraints).else do |result|
3103
- errors << Diagnostic::Ruby::ArgumentTypeMismatch.new(node: arg_node,
3104
- receiver_type: receiver_type,
3105
- expected: param_type,
3106
- actual: arg_type,
3107
- result: result)
3033
+ when TypeInference::SendArgs::PositionalArgs::UnexpectedArg
3034
+ _, constr = bypass_splat(arg.node) do |n|
3035
+ constr.synthesize(n)
3036
+ end
3037
+
3038
+ when TypeInference::SendArgs::PositionalArgs::SplatArg
3039
+ arg_type, _ = constr
3040
+ .with_child_typing(range: arg.node.loc.expression.begin_pos ... arg.node.loc.expression.end_pos)
3041
+ .try_tuple_type!(arg.node.children[0])
3042
+ arg.type = arg_type
3043
+
3044
+ when TypeInference::SendArgs::PositionalArgs::MissingArg
3045
+ # ignore
3046
+
3047
+ when TypeInference::SendArgs::KeywordArgs::ArgTypePairs
3048
+ arg.pairs.each do |node, type|
3049
+ _, constr = bypass_splat(node) do |node|
3050
+ constr.type_check_argument(
3051
+ node,
3052
+ type: type,
3053
+ receiver_type: receiver_type,
3054
+ constraints: constraints,
3055
+ errors: errors
3056
+ )
3057
+ end
3058
+ end
3059
+
3060
+ when TypeInference::SendArgs::KeywordArgs::UnexpectedKeyword
3061
+ if arg.node.type == :pair
3062
+ arg.node.children.each do |nn|
3063
+ _, constr = constr.synthesize(nn)
3064
+ end
3065
+ else
3066
+ _, constr = bypass_splat(arg.node) do |n|
3067
+ constr.synthesize(n)
3068
+ end
3108
3069
  end
3109
- else
3110
- # keyword
3111
- result = constr.check_keyword_arg(receiver_type: receiver_type,
3112
- node: pair,
3113
- method_type: method_type,
3114
- constraints: constraints)
3115
3070
 
3116
- if result.is_a?(Diagnostic::Ruby::Base)
3117
- errors << result
3071
+ when TypeInference::SendArgs::KeywordArgs::SplatArg
3072
+ type, _ = bypass_splat(arg.node) do |sp_node|
3073
+ if sp_node.type == :hash
3074
+ pair = constr.type_hash_record(sp_node, nil) and break pair
3075
+ end
3076
+
3077
+ constr.synthesize(sp_node)
3118
3078
  end
3079
+
3080
+ arg.type = type
3081
+
3082
+ when TypeInference::SendArgs::KeywordArgs::MissingKeyword
3083
+ # ignore
3084
+ else
3085
+ raise arg.inspect
3119
3086
  end
3087
+
3088
+ constr
3120
3089
  end
3121
3090
 
3091
+ errors.push(*es)
3092
+
3122
3093
  if block_params
3123
3094
  # block is given
3124
3095
  block_annotations = source.annotations(block: node, factory: checker.factory, current_module: current_namespace)
@@ -3261,104 +3232,105 @@ module Steep
3261
3232
  )
3262
3233
  end
3263
3234
  else
3264
- # block is not given
3265
- if (!method_type.block || method_type.block.optional?)
3266
- # Method call without block is allowed
3267
- unless args.block_pass_arg
3268
- # OK, without block
3269
- s = constraints.solution(
3235
+ arg = args.block_pass_arg
3236
+
3237
+ case
3238
+ when arg.compatible?
3239
+ if arg.node
3240
+ subst = constraints.solution(
3270
3241
  checker,
3271
- variance: variance,
3272
- variables: fresh_vars,
3273
3242
  self_type: self_type,
3274
3243
  instance_type: module_context.instance_type,
3275
- class_type: module_context.module_type
3244
+ class_type: module_context.module_type,
3245
+ variance: variance,
3246
+ variables: occurence.params
3276
3247
  )
3277
- method_type = method_type.subst(s)
3278
- else
3279
- # &block arg is given
3280
- s = constraints.solution(
3248
+
3249
+ block_type = arg.node_type.subst(subst)
3250
+
3251
+ node_type, constr = constr.synthesize(arg.node, hint: block_type)
3252
+ nil_block =
3253
+ constr.check_relation(sub_type: node_type, super_type: AST::Builtin.nil_type).success? &&
3254
+ !node_type.is_a?(AST::Types::Any)
3255
+
3256
+ unless nil_block
3257
+ constr.check_relation(sub_type: node_type, super_type: block_type, constraints: constraints).else do |result|
3258
+ errors << Diagnostic::Ruby::BlockTypeMismatch.new(
3259
+ node: arg.node,
3260
+ expected: block_type,
3261
+ actual: node_type,
3262
+ result: result
3263
+ )
3264
+ end
3265
+ end
3266
+
3267
+ subst = constraints.solution(
3281
3268
  checker,
3282
- variance: variance,
3283
- variables: fresh_vars,
3284
3269
  self_type: self_type,
3285
3270
  instance_type: module_context.instance_type,
3286
- class_type: module_context.module_type
3271
+ class_type: module_context.module_type,
3272
+ variance: variance,
3273
+ variables: method_type.free_variables
3287
3274
  )
3288
- method_type = method_type.subst(s)
3289
3275
 
3290
- type, constr = constr.synthesize(args.block_pass_arg, hint: AST::Builtin.nil_type)
3291
- type = expand_alias(type)
3292
- unless type.is_a?(AST::Types::Nil)
3293
- errors << Diagnostic::Ruby::UnexpectedBlockGiven.new(
3276
+ method_type = method_type.subst(subst)
3277
+
3278
+ if nil_block && arg.block.required?
3279
+ # Passing no block
3280
+ errors << Diagnostic::Ruby::RequiredBlockMissing.new(
3294
3281
  node: node,
3295
3282
  method_type: method_type
3296
3283
  )
3297
3284
  end
3298
- end
3299
- else
3300
- unless args.block_pass_arg
3301
- # Required block is missing
3302
- errors << Diagnostic::Ruby::RequiredBlockMissing.new(
3303
- node: node,
3304
- method_type: method_type
3305
- )
3306
-
3307
- s = constraints.solution(
3285
+ else
3286
+ subst = constraints.solution(
3308
3287
  checker,
3309
- variance: variance,
3310
- variables: fresh_vars,
3311
3288
  self_type: self_type,
3312
3289
  instance_type: module_context.instance_type,
3313
- class_type: module_context.module_type
3290
+ class_type: module_context.module_type,
3291
+ variance: variance,
3292
+ variables: method_type.free_variables
3314
3293
  )
3315
- method_type = method_type.subst(s)
3316
- else
3317
- begin
3318
- method_type = method_type.subst(
3319
- constraints.solution(
3320
- checker,
3321
- self_type: self_type,
3322
- instance_type: module_context.instance_type,
3323
- class_type: module_context.module_type,
3324
- variance: variance,
3325
- variables: occurence.params
3326
- )
3327
- )
3328
- hint_type = if topdown_hint
3329
- AST::Types::Proc.new(type: method_type.block.type, block: nil)
3330
- end
3331
- given_block_type, constr = constr.synthesize(args.block_pass_arg, hint: hint_type)
3332
- method_block_type = method_type.block.yield_self {|expected_block|
3333
- proc_type = AST::Types::Proc.new(type: expected_block.type, block: nil)
3334
- if expected_block.optional?
3335
- AST::Builtin.optional(proc_type)
3336
- else
3337
- proc_type
3338
- end
3339
- }
3340
3294
 
3341
- result = check_relation(sub_type: given_block_type, super_type: method_block_type, constraints: constraints)
3342
- result.else do |result|
3343
- errors << Diagnostic::Ruby::BlockTypeMismatch.new(
3344
- node: args.block_pass_arg,
3345
- expected: method_block_type,
3346
- actual: given_block_type,
3347
- result: result
3348
- )
3349
- end
3295
+ method_type = method_type.subst(subst)
3296
+ end
3350
3297
 
3351
- method_type = method_type.subst(
3352
- constraints.solution(
3353
- checker,
3354
- self_type: self_type,
3355
- instance_type: module_context.instance_type,
3356
- class_type: module_context.module_type,
3357
- variance: variance,
3358
- variables: method_type.free_variables
3359
- )
3360
- )
3361
- end
3298
+ when arg.block_missing?
3299
+ subst = constraints.solution(
3300
+ checker,
3301
+ self_type: self_type,
3302
+ instance_type: module_context.instance_type,
3303
+ class_type: module_context.module_type,
3304
+ variance: variance,
3305
+ variables: method_type.free_variables
3306
+ )
3307
+
3308
+ method_type = method_type.subst(subst)
3309
+
3310
+ errors << Diagnostic::Ruby::RequiredBlockMissing.new(
3311
+ node: node,
3312
+ method_type: method_type
3313
+ )
3314
+
3315
+ when arg.unexpected_block?
3316
+ subst = constraints.solution(
3317
+ checker,
3318
+ self_type: self_type,
3319
+ instance_type: module_context.instance_type,
3320
+ class_type: module_context.module_type,
3321
+ variance: variance,
3322
+ variables: method_type.free_variables
3323
+ )
3324
+
3325
+ method_type = method_type.subst(subst)
3326
+
3327
+ node_type, constr = constr.synthesize(arg.node)
3328
+
3329
+ unless constr.check_relation(sub_type: node_type, super_type: AST::Builtin.nil_type).success?
3330
+ errors << Diagnostic::Ruby::UnexpectedBlockGiven.new(
3331
+ node: node,
3332
+ method_type: method_type
3333
+ )
3362
3334
  end
3363
3335
  end
3364
3336
  end
@@ -3391,6 +3363,18 @@ module Steep
3391
3363
  ]
3392
3364
  end
3393
3365
 
3366
+ def type_check_argument(node, receiver_type:, type:, constraints:, report_node: node, errors:)
3367
+ check(node, type, constraints: constraints) do |expected, actual, result|
3368
+ errors << Diagnostic::Ruby::ArgumentTypeMismatch.new(
3369
+ node: report_node,
3370
+ receiver_type: receiver_type,
3371
+ expected: expected,
3372
+ actual: actual,
3373
+ result: result
3374
+ )
3375
+ end
3376
+ end
3377
+
3394
3378
  def type_block_without_hint(node:, block_annotations:, block_params:, block_body:, &block)
3395
3379
  unless block_params
3396
3380
  typing.add_error(
@@ -3531,68 +3515,6 @@ module Steep
3531
3515
  end
3532
3516
  end
3533
3517
 
3534
- def self.parameter_types(nodes, type)
3535
- nodes = nodes.dup
3536
-
3537
- env = {}
3538
-
3539
- type.params.required.each do |type|
3540
- a = nodes.first
3541
- if a&.type == :arg
3542
- env[a.children.first] = type
3543
- nodes.shift
3544
- else
3545
- break
3546
- end
3547
- end
3548
-
3549
- type.params.optional.each do |type|
3550
- a = nodes.first
3551
-
3552
- if a&.type == :optarg
3553
- env[a.children.first] = type
3554
- nodes.shift
3555
- else
3556
- break
3557
- end
3558
- end
3559
-
3560
- if type.params.rest
3561
- a = nodes.first
3562
- if a&.type == :restarg
3563
- env[a.children.first] = AST::Builtin::Array.instance_type(type.params.rest)
3564
- nodes.shift
3565
- end
3566
- end
3567
-
3568
- nodes.each do |node|
3569
- if node.type == :kwarg
3570
- name = node.children[0]
3571
- ty = type.params.required_keywords[name]
3572
- env[name] = ty if ty
3573
- end
3574
-
3575
- if node.type == :kwoptarg
3576
- name = node.children[0]
3577
- ty = type.params.optional_keywords[name]
3578
- env[name] = ty if ty
3579
- end
3580
-
3581
- if node.type == :kwrestarg
3582
- ty = type.params.rest_keywords
3583
- if ty
3584
- env[node.children[0]] = AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type, ty)
3585
- end
3586
- end
3587
- end
3588
-
3589
- env
3590
- end
3591
-
3592
- def self.valid_parameter_env?(env, nodes, params)
3593
- env.size == nodes.size && env.size == params.size
3594
- end
3595
-
3596
3518
  def current_namespace
3597
3519
  module_context&.current_namespace || AST::Namespace.root
3598
3520
  end
@@ -3700,25 +3622,6 @@ module Steep
3700
3622
  end
3701
3623
  end
3702
3624
 
3703
- def flatten_const_name(node)
3704
- path = []
3705
-
3706
- while node
3707
- case node.type
3708
- when :const, :casgn
3709
- path.unshift(node.children[1])
3710
- node = node.children[0]
3711
- when :cbase
3712
- path.unshift("")
3713
- break
3714
- else
3715
- return nil
3716
- end
3717
- end
3718
-
3719
- path.join("::").to_sym
3720
- end
3721
-
3722
3625
  def fallback_to_any(node)
3723
3626
  if block_given?
3724
3627
  typing.add_error yield
@@ -3760,16 +3663,6 @@ module Steep
3760
3663
  Pair.new(type: AST::Builtin.any_type, constr: self)
3761
3664
  end
3762
3665
 
3763
- def fallback_any_rec(node)
3764
- fallback_to_any(node) unless typing.has_type?(node)
3765
-
3766
- each_child_node(node) do |child|
3767
- fallback_any_rec(child)
3768
- end
3769
-
3770
- typing.type_of(node: node)
3771
- end
3772
-
3773
3666
  def unwrap(type)
3774
3667
  expand_alias(type) do |expanded|
3775
3668
  case
@@ -3782,19 +3675,6 @@ module Steep
3782
3675
  end
3783
3676
  end
3784
3677
 
3785
- def self.value_variables(node)
3786
- case node&.type
3787
- when :lvar
3788
- Set.new([node.children.first])
3789
- when :lvasgn
3790
- Set.new([node.children.first]) + value_variables(node.children[1])
3791
- when :begin
3792
- value_variables(node.children.last)
3793
- else
3794
- Set.new
3795
- end
3796
- end
3797
-
3798
3678
  def deep_expand_alias(type, &block)
3799
3679
  checker.factory.deep_expand_alias(type, &block)
3800
3680
  end
@@ -3841,24 +3721,6 @@ module Steep
3841
3721
  end
3842
3722
  end
3843
3723
 
3844
- def select_super_type(sub_type, super_type)
3845
- if super_type
3846
- result = check_relation(sub_type: sub_type, super_type: super_type)
3847
-
3848
- if result.success?
3849
- super_type
3850
- else
3851
- if block_given?
3852
- yield result
3853
- else
3854
- sub_type
3855
- end
3856
- end
3857
- else
3858
- sub_type
3859
- end
3860
- end
3861
-
3862
3724
  def to_instance_type(type, args: nil)
3863
3725
  args = args || case type
3864
3726
  when AST::Types::Name::Singleton
@@ -3870,16 +3732,35 @@ module Steep
3870
3732
  AST::Types::Name::Instance.new(name: type.name, args: args)
3871
3733
  end
3872
3734
 
3735
+ def try_tuple_type!(node, hint: nil)
3736
+ if node.type == :array && (hint.nil? || hint.is_a?(AST::Types::Tuple))
3737
+ node_range = node.loc.expression.yield_self {|l| l.begin_pos..l.end_pos }
3738
+
3739
+ typing.new_child(node_range) do |child_typing|
3740
+ if pair = with_new_typing(child_typing).try_tuple_type(node, hint)
3741
+ return pair.with(constr: pair.constr.save_typing)
3742
+ end
3743
+ end
3744
+ end
3745
+
3746
+ synthesize(node, hint: hint)
3747
+ end
3748
+
3873
3749
  def try_tuple_type(node, hint)
3874
- if node.children.size != hint.types.size
3875
- return
3750
+ if hint
3751
+ if node.children.size != hint.types.size
3752
+ return
3753
+ end
3876
3754
  end
3877
3755
 
3878
3756
  constr = self
3879
3757
  element_types = []
3880
3758
 
3881
3759
  each_child_node(node).with_index do |child, index|
3882
- type, constr = constr.synthesize(child, hint: hint.types[index])
3760
+ child_hint = if hint
3761
+ hint.types[index]
3762
+ end
3763
+ type, constr = constr.synthesize(child, hint: child_hint)
3883
3764
  element_types << type
3884
3765
  end
3885
3766
 
@@ -3911,55 +3792,152 @@ module Steep
3911
3792
  constr.add_typing(node, type: AST::Builtin::Array.instance_type(element_type))
3912
3793
  end
3913
3794
 
3914
- def try_hash_type(node, hint)
3915
- hint = expand_alias(hint)
3795
+ # Try to give record type to hash_node.
3796
+ #
3797
+ # Returns nil when it cannot have a record type.
3798
+ # `record_type` can be nil when the keys are not specified.
3799
+ #
3800
+ def type_hash_record(hash_node, record_type)
3801
+ raise unless hash_node.type == :hash
3802
+
3803
+ constr = self
3804
+
3805
+ if record_type
3806
+ elements = record_type.elements.dup
3807
+ else
3808
+ elements = {}
3809
+ end
3810
+
3811
+ elems = {}
3812
+
3813
+ each_child_node(hash_node) do |child|
3814
+ if child.type == :pair
3815
+ case child.children[0].type
3816
+ when :sym, :str, :int
3817
+ key_node = child.children[0]
3818
+ value_node = child.children[1]
3819
+
3820
+ key = key_node.children[0]
3821
+
3822
+ _, constr = constr.synthesize(key_node, hint: AST::Types::Literal.new(value: key))
3823
+ value_type, constr = constr.synthesize(value_node, hint: elements[key])
3824
+
3825
+ elems[key] = value_type
3826
+ else
3827
+ return
3828
+ end
3829
+ else
3830
+ return
3831
+ end
3832
+ end
3833
+
3834
+ type = AST::Types::Record.new(elements: elems)
3835
+ constr.add_typing(hash_node, type: type)
3836
+ end
3837
+
3838
+ # Give hash_node a type based on hint.
3839
+ #
3840
+ # * When hint is Record type, it may have record type.
3841
+ # * When hint is union type, it tries recursively with the union cases.
3842
+ # * Otherwise, it tries to be a hash instance.
3843
+ #
3844
+ def type_hash(hash_node, hint:)
3845
+ hint = deep_expand_alias(hint)
3846
+ range = hash_node.loc.expression.yield_self {|l| l.begin_pos..l.end_pos }
3916
3847
 
3917
3848
  case hint
3918
3849
  when AST::Types::Record
3919
- typing.new_child(node.loc.expression.yield_self {|l| l.begin_pos..l.end_pos }) do |child_typing|
3920
- new_construction = with_new_typing(child_typing)
3921
- elements = {}
3850
+ with_child_typing(range: range) do |constr|
3851
+ pair = constr.type_hash_record(hash_node, hint)
3852
+ if pair
3853
+ return pair.with(constr: pair.constr.save_typing)
3854
+ end
3855
+ end
3856
+ when AST::Types::Union
3857
+ pair = pick_one_of(hint.types, range: range) do |type, constr|
3858
+ constr.type_hash(hash_node, hint: type)
3859
+ end
3922
3860
 
3923
- each_child_node(node) do |child|
3924
- case child.type
3925
- when :pair
3926
- key, value = child.children
3861
+ if pair
3862
+ return pair
3863
+ end
3864
+ end
3927
3865
 
3928
- key_value = case key.type
3929
- when :str, :int, :sym
3930
- key.children[0]
3931
- else
3932
- return nil
3933
- end
3866
+ key_types = []
3867
+ value_types = []
3934
3868
 
3935
- value_hint = hint.elements[key_value]
3936
- value_type = new_construction.synthesize(value, hint: value_hint).type
3869
+ if AST::Builtin::Hash.instance_type?(hint)
3870
+ key_hint, value_hint = hint.args
3871
+ end
3937
3872
 
3938
- if value_hint
3939
- if check_relation(sub_type: value_type, super_type: value_hint).success?
3940
- value_type = value_hint
3941
- end
3873
+ hint_hash = AST::Builtin::Hash.instance_type(
3874
+ key_hint || AST::Builtin.any_type,
3875
+ value_hint || AST::Builtin.any_type
3876
+ )
3877
+
3878
+ constr = self
3879
+
3880
+ if hash_node.children.empty?
3881
+ key_types << key_hint if key_hint
3882
+ value_types << value_hint if value_hint
3883
+ else
3884
+ hash_node.children.each do |elem|
3885
+ case elem.type
3886
+ when :pair
3887
+ key_node, value_node = elem.children
3888
+ key_type, constr = constr.synthesize(key_node, hint: key_hint)
3889
+ value_type, constr = constr.synthesize(value_node, hint: value_hint)
3890
+
3891
+ key_types << key_type
3892
+ value_types << value_type
3893
+ when :kwsplat
3894
+ bypass_splat(elem) do |elem_|
3895
+ pair = constr.synthesize(elem_, hint: hint_hash)
3896
+
3897
+ if AST::Builtin::Hash.instance_type?(pair.type)
3898
+ key_types << pair.type.args[0]
3899
+ value_types << pair.type.args[1]
3942
3900
  end
3943
3901
 
3944
- elements[key_value] = value_type
3945
- else
3946
- return nil
3902
+ pair
3947
3903
  end
3904
+ else
3905
+ raise
3948
3906
  end
3907
+ end
3908
+ end
3949
3909
 
3950
- child_typing.save!
3910
+ key_types.reject! {|ty| ty.is_a?(AST::Types::Any) }
3911
+ value_types.reject! {|ty| ty.is_a?(AST::Types::Any) }
3951
3912
 
3952
- hash = AST::Types::Record.new(elements: elements)
3953
- add_typing(node, type: hash)
3954
- end
3955
- when AST::Types::Union
3956
- hint.types.each do |type|
3957
- if pair = try_hash_type(node, type)
3958
- return pair
3913
+ key_types << AST::Builtin.any_type if key_types.empty?
3914
+ value_types << AST::Builtin.any_type if value_types.empty?
3915
+
3916
+ hash_type = AST::Builtin::Hash.instance_type(
3917
+ AST::Types::Union.build(types: key_types),
3918
+ AST::Types::Union.build(types: value_types)
3919
+ )
3920
+ constr.add_typing(hash_node, type: hash_type)
3921
+ end
3922
+
3923
+ def pick_one_of(types, range:)
3924
+ types.each do |type|
3925
+ with_child_typing(range: range) do |constr|
3926
+ type_, constr = yield type, constr
3927
+
3928
+ constr.check_relation(sub_type: type_, super_type: type).then do
3929
+ constr = constr.save_typing
3930
+ return Pair.new(type: type, constr: constr)
3959
3931
  end
3960
3932
  end
3961
- nil
3962
3933
  end
3934
+
3935
+ nil
3936
+ end
3937
+
3938
+ def save_typing
3939
+ typing.save!
3940
+ with_new_typing(typing.parent)
3963
3941
  end
3964
3942
  end
3965
3943
  end