steep 0.44.1 → 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 (43) 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 +14 -0
  6. data/Gemfile +0 -2
  7. data/Gemfile.lock +77 -0
  8. data/lib/steep.rb +3 -1
  9. data/lib/steep/ast/builtin.rb +7 -1
  10. data/lib/steep/ast/types/factory.rb +19 -25
  11. data/lib/steep/diagnostic/ruby.rb +137 -60
  12. data/lib/steep/diagnostic/signature.rb +34 -0
  13. data/lib/steep/equatable.rb +21 -0
  14. data/lib/steep/interface/function.rb +798 -579
  15. data/lib/steep/server/interaction_worker.rb +238 -19
  16. data/lib/steep/services/file_loader.rb +26 -19
  17. data/lib/steep/services/hover_content.rb +131 -79
  18. data/lib/steep/source.rb +7 -10
  19. data/lib/steep/type_construction.rb +435 -502
  20. data/lib/steep/type_inference/block_params.rb +2 -5
  21. data/lib/steep/type_inference/method_params.rb +483 -0
  22. data/lib/steep/type_inference/send_args.rb +610 -128
  23. data/lib/steep/typing.rb +46 -21
  24. data/lib/steep/version.rb +1 -1
  25. data/sig/steep/type_inference/send_args.rbs +42 -0
  26. data/smoke/array/test_expectations.yml +3 -3
  27. data/smoke/block/c.rb +0 -1
  28. data/smoke/class/test_expectations.yml +12 -15
  29. data/smoke/diagnostics-rbs/mixin-class-error.rbs +6 -0
  30. data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
  31. data/smoke/diagnostics/different_method_parameter_kind.rb +9 -0
  32. data/smoke/diagnostics/method_arity_mismatch.rb +2 -2
  33. data/smoke/diagnostics/method_parameter_mismatch.rb +10 -0
  34. data/smoke/diagnostics/test_expectations.yml +108 -31
  35. data/smoke/ensure/test_expectations.yml +3 -3
  36. data/smoke/enumerator/test_expectations.yml +1 -1
  37. data/smoke/literal/test_expectations.yml +2 -2
  38. data/smoke/method/test_expectations.yml +11 -10
  39. data/smoke/rescue/test_expectations.yml +3 -3
  40. data/smoke/toplevel/test_expectations.yml +3 -3
  41. data/smoke/tsort/test_expectations.yml +2 -2
  42. data/steep.gemspec +1 -1
  43. metadata +13 -5
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,6 +936,25 @@ 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
@@ -999,10 +1014,13 @@ module Steep
999
1014
  end
1000
1015
 
1001
1016
  if body_node
1002
- begin_pos = body_node.loc.expression.end_pos
1003
- end_pos = node.loc.end.begin_pos
1004
-
1005
- 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
1006
1024
  end
1007
1025
 
1008
1026
  if module_context
@@ -1143,15 +1161,16 @@ module Steep
1143
1161
  )
1144
1162
  end
1145
1163
  else
1146
- check_relation(sub_type: AST::Builtin.nil_type, super_type: break_type).else do |result|
1147
- typing.add_error(
1148
- Diagnostic::Ruby::BreakTypeMismatch.new(
1149
- node: node,
1150
- expected: break_type,
1151
- actual: AST::Builtin.nil_type,
1152
- 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
+ )
1153
1172
  )
1154
- )
1173
+ end
1155
1174
  end
1156
1175
  end
1157
1176
  else
@@ -1363,52 +1382,20 @@ module Steep
1363
1382
  add_typing(node, type: AST::Types::Boolean.new)
1364
1383
  end
1365
1384
 
1366
- when :hash
1367
- yield_self do
1368
- ty = try_hash_type(node, hint) and return ty
1369
-
1370
- if AST::Builtin::Hash.instance_type?(hint)
1371
- key_hint = hint.args[0]
1372
- value_hint = hint.args[1]
1373
- end
1374
-
1375
- key_types = []
1376
- value_types = []
1377
-
1378
- each_child_node(node) do |child|
1379
- case child.type
1380
- when :pair
1381
- key, value = child.children
1382
- key_types << synthesize(key, hint: key_hint).type.yield_self do |type|
1383
- select_super_type(type, key_hint)
1384
- end
1385
- value_types << synthesize(value, hint: value_hint).type.yield_self do |type|
1386
- select_super_type(type, value_hint)
1387
- end
1388
- when :kwsplat
1389
- expand_alias(synthesize(child.children[0]).type) do |splat_type, original_type|
1390
- if AST::Builtin::Hash.instance_type?(splat_type)
1391
- key_types << splat_type.args[0]
1392
- value_types << splat_type.args[1]
1393
- else
1394
- typing.add_error Diagnostic::Ruby::UnexpectedSplat.new(node: child, type: original_type)
1395
- key_types << AST::Builtin.any_type
1396
- value_types << AST::Builtin.any_type
1397
- end
1398
- 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
1399
1395
  else
1400
- raise "Unexpected non pair: #{child.inspect}" unless child.type == :pair
1396
+ pair.constr.typing.add_error Diagnostic::Ruby::FallbackAny.new(node: node)
1401
1397
  end
1402
1398
  end
1403
-
1404
- key_type = key_types.empty? ? AST::Builtin.any_type : AST::Types::Union.build(types: key_types)
1405
- value_type = value_types.empty? ? AST::Builtin.any_type : AST::Types::Union.build(types: value_types)
1406
-
1407
- if key_types.empty? && value_types.empty? && !hint
1408
- typing.add_error Diagnostic::Ruby::FallbackAny.new(node: node)
1409
- end
1410
-
1411
- add_typing(node, type: AST::Builtin::Hash.instance_type(key_type, value_type))
1412
1399
  end
1413
1400
 
1414
1401
  when :dstr, :xstr
@@ -1683,8 +1670,7 @@ module Steep
1683
1670
  tuples.each do |tuple|
1684
1671
  typing.new_child(node_range) do |child_typing|
1685
1672
  if pair = with_new_typing(child_typing).try_tuple_type(node, tuple)
1686
- child_typing.save!
1687
- return pair.with(constr: pair.constr.with_new_typing(typing))
1673
+ return pair.with(constr: pair.constr.save_typing)
1688
1674
  end
1689
1675
  end
1690
1676
  end
@@ -1695,8 +1681,7 @@ module Steep
1695
1681
  typing.new_child(node_range) do |child_typing|
1696
1682
  pair = with_new_typing(child_typing).try_array_type(node, array)
1697
1683
  if pair.constr.check_relation(sub_type: pair.type, super_type: hint).success?
1698
- child_typing.save!
1699
- return pair.with(constr: pair.constr.with_new_typing(typing))
1684
+ return pair.with(constr: pair.constr.save_typing)
1700
1685
  end
1701
1686
  end
1702
1687
  end
@@ -2291,7 +2276,9 @@ module Steep
2291
2276
  unless return_types.empty?
2292
2277
  type = AST::Types::Proc.new(
2293
2278
  type: Interface::Function.new(
2294
- 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
+ ),
2295
2282
  return_type: AST::Types::Union.build(types: return_types),
2296
2283
  location: nil
2297
2284
  ),
@@ -2389,6 +2376,7 @@ module Steep
2389
2376
 
2390
2377
  else
2391
2378
  typing.add_error(Diagnostic::Ruby::UnsupportedSyntax.new(node: node))
2379
+ add_typing(node, type: AST::Builtin.any_type)
2392
2380
 
2393
2381
  end.tap do |pair|
2394
2382
  unless pair.is_a?(Pair) && !pair.type.is_a?(Pair)
@@ -2738,11 +2726,10 @@ module Steep
2738
2726
  method = interface.methods[method_name]
2739
2727
 
2740
2728
  if method
2741
- args = TypeInference::SendArgs.from_nodes(arguments)
2742
2729
  call, constr = type_method_call(node,
2743
2730
  method: method,
2744
2731
  method_name: method_name,
2745
- args: args,
2732
+ arguments: arguments,
2746
2733
  block_params: block_params,
2747
2734
  block_body: block_body,
2748
2735
  receiver_type: receiver_type,
@@ -2922,27 +2909,22 @@ module Steep
2922
2909
  end
2923
2910
  end
2924
2911
 
2925
- 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:)
2926
2913
  node_range = node.loc.expression.yield_self {|l| l.begin_pos..l.end_pos }
2927
2914
 
2928
- results = method.method_types.flat_map do |method_type|
2915
+ results = method.method_types.map do |method_type|
2929
2916
  Steep.logger.tagged method_type.to_s do
2930
- zips = args.zips(method_type.type.params, method_type.block&.type)
2931
-
2932
- zips.map do |arg_pairs|
2933
- typing.new_child(node_range) do |child_typing|
2934
- self.with_new_typing(child_typing).try_method_type(
2935
- node,
2936
- receiver_type: receiver_type,
2937
- method_name: method_name,
2938
- method_type: method_type,
2939
- args: args,
2940
- arg_pairs: arg_pairs,
2941
- block_params: block_params,
2942
- block_body: block_body,
2943
- topdown_hint: topdown_hint
2944
- )
2945
- 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
+ )
2946
2928
  end
2947
2929
  end
2948
2930
  end
@@ -2982,138 +2964,34 @@ module Steep
2982
2964
  ]
2983
2965
  end
2984
2966
 
2985
- def check_keyword_arg(receiver_type:, node:, method_type:, constraints:)
2986
- params = method_type.type.params
2987
-
2988
- case node.type
2989
- when :hash
2990
- keyword_hash_type = AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type,
2991
- AST::Builtin.any_type)
2992
- add_typing node, type: keyword_hash_type
2993
-
2994
- given_keys = Set.new()
2995
-
2996
- node.children.each do |element|
2997
- case element.type
2998
- when :pair
2999
- key_node, value_node = element.children
3000
-
3001
- case key_node.type
3002
- when :sym
3003
- key_symbol = key_node.children[0]
3004
- keyword_type = case
3005
- when params.required_keywords.key?(key_symbol)
3006
- params.required_keywords[key_symbol]
3007
- when params.optional_keywords.key?(key_symbol)
3008
- AST::Types::Union.build(
3009
- types: [params.optional_keywords[key_symbol],
3010
- AST::Builtin.nil_type]
3011
- )
3012
- when params.rest_keywords
3013
- params.rest_keywords
3014
- end
3015
-
3016
- add_typing key_node, type: AST::Builtin::Symbol.instance_type
3017
-
3018
- given_keys << key_symbol
3019
-
3020
- if keyword_type
3021
- check(value_node, keyword_type, constraints: constraints) do |expected, actual, result|
3022
- return Diagnostic::Ruby::IncompatibleAssignment.new(
3023
- node: value_node,
3024
- lhs_type: expected,
3025
- rhs_type: actual,
3026
- result: result
3027
- )
3028
- end
3029
- else
3030
- synthesize(value_node)
3031
- end
3032
-
3033
- else
3034
- check(key_node, AST::Builtin::Symbol.instance_type, constraints: constraints) do |expected, actual, result|
3035
- return Diagnostic::Ruby::IncompatibleAssignment.new(
3036
- node: key_node,
3037
- lhs_type: expected,
3038
- rhs_type: actual,
3039
- result: result
3040
- )
3041
- end
3042
- end
3043
-
3044
- when :kwsplat
3045
- Steep.logger.warn("Keyword arg with kwsplat(**) node are not supported.")
3046
-
3047
- check(element.children[0], keyword_hash_type, constraints: constraints) do |expected, actual, result|
3048
- return Diagnostic::Ruby::IncompatibleAssignment.new(
3049
- node: node,
3050
- lhs_type: expected,
3051
- rhs_type: actual,
3052
- result: result
3053
- )
3054
- end
2967
+ def inspect
2968
+ "#<#{self.class}>"
2969
+ end
3055
2970
 
3056
- given_keys = true
3057
- end
3058
- end
2971
+ def with_child_typing(range:)
2972
+ constr = with_new_typing(typing.new_child(range: range))
3059
2973
 
3060
- case given_keys
3061
- when Set
3062
- missing_keywords = Set.new(params.required_keywords.keys) - given_keys
3063
- unless missing_keywords.empty?
3064
- return Diagnostic::Ruby::MissingKeyword.new(
3065
- node: node,
3066
- missing_keywords: missing_keywords
3067
- )
3068
- end
3069
-
3070
- extra_keywords = given_keys - Set.new(params.required_keywords.keys) - Set.new(params.optional_keywords.keys)
3071
- if extra_keywords.any? && !params.rest_keywords
3072
- return Diagnostic::Ruby::UnexpectedKeyword.new(
3073
- node: node,
3074
- unexpected_keywords: extra_keywords
3075
- )
3076
- end
3077
- end
2974
+ if block_given?
2975
+ yield constr
3078
2976
  else
3079
- if params.rest_keywords
3080
- Steep.logger.warn("Method call with rest keywords type is detected. Rough approximation to be improved.")
3081
-
3082
- value_types = params.required_keywords.values +
3083
- params.optional_keywords.values.map {|type| AST::Types::Union.build(types: [type, AST::Builtin.nil_type])} +
3084
- [params.rest_keywords]
3085
-
3086
- hash_type = AST::Builtin::Hash.instance_type(
3087
- AST::Builtin::Symbol.instance_type,
3088
- AST::Types::Union.build(types: value_types)
3089
- )
3090
- else
3091
- hash_elements = params.required_keywords.merge(
3092
- params.optional_keywords.transform_values do |type|
3093
- AST::Types::Union.build(types: [type, AST::Builtin.nil_type])
3094
- end
3095
- )
3096
-
3097
- hash_type = AST::Types::Record.new(elements: hash_elements)
3098
- end
2977
+ constr
2978
+ end
2979
+ end
3099
2980
 
3100
- 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
3101
2984
 
3102
- check_relation(sub_type: node_type, super_type: hash_type).else do |result|
3103
- return Diagnostic::Ruby::ArgumentTypeMismatch.new(
3104
- node: node,
3105
- receiver_type: receiver_type,
3106
- expected: hash_type,
3107
- actual: node_type,
3108
- result: result
3109
- )
3110
- 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
3111
2991
  end
3112
-
3113
- nil
3114
2992
  end
3115
2993
 
3116
- 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:)
3117
2995
  fresh_types = method_type.type_params.map {|x| AST::Types::Var.fresh(x)}
3118
2996
  fresh_vars = Set.new(fresh_types.map(&:name))
3119
2997
  instantiation = Interface::Substitution.build(method_type.type_params, fresh_types)
@@ -3128,38 +3006,90 @@ module Steep
3128
3006
 
3129
3007
  errors = []
3130
3008
 
3131
- arg_pairs.each do |pair|
3132
- case pair
3133
- when Array
3134
- arg_node, param_type = pair
3135
- 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
+ )
3136
3020
 
3137
- arg_type, constr = if arg_node.type == :splat
3138
- constr.synthesize(arg_node.children[0])
3139
- else
3140
- constr.synthesize(arg_node, hint: topdown_hint ? param_type : nil)
3141
- 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
3142
3032
 
3143
- check_relation(sub_type: arg_type, super_type: param_type, constraints: constraints).else do |result|
3144
- errors << Diagnostic::Ruby::ArgumentTypeMismatch.new(node: arg_node,
3145
- receiver_type: receiver_type,
3146
- expected: param_type,
3147
- actual: arg_type,
3148
- result: result)
3033
+ when TypeInference::SendArgs::PositionalArgs::UnexpectedArg
3034
+ _, constr = bypass_splat(arg.node) do |n|
3035
+ constr.synthesize(n)
3149
3036
  end
3150
- else
3151
- # keyword
3152
- result = constr.check_keyword_arg(receiver_type: receiver_type,
3153
- node: pair,
3154
- method_type: method_type,
3155
- constraints: constraints)
3156
3037
 
3157
- if result.is_a?(Diagnostic::Ruby::Base)
3158
- errors << result
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
3159
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
3069
+ end
3070
+
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)
3078
+ end
3079
+
3080
+ arg.type = type
3081
+
3082
+ when TypeInference::SendArgs::KeywordArgs::MissingKeyword
3083
+ # ignore
3084
+ else
3085
+ raise arg.inspect
3160
3086
  end
3087
+
3088
+ constr
3161
3089
  end
3162
3090
 
3091
+ errors.push(*es)
3092
+
3163
3093
  if block_params
3164
3094
  # block is given
3165
3095
  block_annotations = source.annotations(block: node, factory: checker.factory, current_module: current_namespace)
@@ -3302,108 +3232,105 @@ module Steep
3302
3232
  )
3303
3233
  end
3304
3234
  else
3305
- # block is not given
3306
- if !method_type.block
3307
- # Method doesn't accept blocks
3308
- unless args.block_pass_arg
3309
- # OK, without block
3310
- s = constraints.solution(
3235
+ arg = args.block_pass_arg
3236
+
3237
+ case
3238
+ when arg.compatible?
3239
+ if arg.node
3240
+ subst = constraints.solution(
3311
3241
  checker,
3312
- variance: variance,
3313
- variables: fresh_vars,
3314
3242
  self_type: self_type,
3315
3243
  instance_type: module_context.instance_type,
3316
- class_type: module_context.module_type
3244
+ class_type: module_context.module_type,
3245
+ variance: variance,
3246
+ variables: occurence.params
3317
3247
  )
3318
- method_type = method_type.subst(s)
3319
- else
3320
- # &block arg is given
3321
- 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(
3322
3268
  checker,
3323
- variance: variance,
3324
- variables: fresh_vars,
3325
3269
  self_type: self_type,
3326
3270
  instance_type: module_context.instance_type,
3327
- class_type: module_context.module_type
3271
+ class_type: module_context.module_type,
3272
+ variance: variance,
3273
+ variables: method_type.free_variables
3328
3274
  )
3329
- method_type = method_type.subst(s)
3330
3275
 
3331
- type, constr = constr.synthesize(args.block_pass_arg, hint: AST::Builtin.nil_type)
3332
- type = expand_alias(type)
3333
- unless type.is_a?(AST::Types::Nil)
3334
- errors << Diagnostic::Ruby::UnexpectedBlockGiven.new(
3335
- node: node,
3336
- method_type: method_type
3337
- )
3338
- end
3339
- end
3340
- else
3341
- # Method accepts block
3342
- if !args.block_pass_arg
3343
- # Block pass is not given
3344
- if method_type.block.required?
3345
- # Required block is missing
3276
+ method_type = method_type.subst(subst)
3277
+
3278
+ if nil_block && arg.block.required?
3279
+ # Passing no block
3346
3280
  errors << Diagnostic::Ruby::RequiredBlockMissing.new(
3347
3281
  node: node,
3348
3282
  method_type: method_type
3349
3283
  )
3350
3284
  end
3351
-
3352
- s = constraints.solution(
3285
+ else
3286
+ subst = constraints.solution(
3353
3287
  checker,
3354
- variance: variance,
3355
- variables: fresh_vars,
3356
3288
  self_type: self_type,
3357
3289
  instance_type: module_context.instance_type,
3358
- class_type: module_context.module_type
3290
+ class_type: module_context.module_type,
3291
+ variance: variance,
3292
+ variables: method_type.free_variables
3359
3293
  )
3360
- method_type = method_type.subst(s)
3361
- else
3362
- begin
3363
- method_type = method_type.subst(
3364
- constraints.solution(
3365
- checker,
3366
- self_type: self_type,
3367
- instance_type: module_context.instance_type,
3368
- class_type: module_context.module_type,
3369
- variance: variance,
3370
- variables: occurence.params
3371
- )
3372
- )
3373
- hint_type = if topdown_hint
3374
- AST::Types::Proc.new(type: method_type.block.type, block: nil)
3375
- end
3376
- given_block_type, constr = constr.synthesize(args.block_pass_arg, hint: hint_type)
3377
- method_block_type = method_type.block.yield_self {|expected_block|
3378
- proc_type = AST::Types::Proc.new(type: expected_block.type, block: nil)
3379
- if expected_block.optional?
3380
- AST::Builtin.optional(proc_type)
3381
- else
3382
- proc_type
3383
- end
3384
- }
3385
3294
 
3386
- result = check_relation(sub_type: given_block_type, super_type: method_block_type, constraints: constraints)
3387
- result.else do |result|
3388
- errors << Diagnostic::Ruby::BlockTypeMismatch.new(
3389
- node: args.block_pass_arg,
3390
- expected: method_block_type,
3391
- actual: given_block_type,
3392
- result: result
3393
- )
3394
- end
3295
+ method_type = method_type.subst(subst)
3296
+ end
3395
3297
 
3396
- method_type = method_type.subst(
3397
- constraints.solution(
3398
- checker,
3399
- self_type: self_type,
3400
- instance_type: module_context.instance_type,
3401
- class_type: module_context.module_type,
3402
- variance: variance,
3403
- variables: method_type.free_variables
3404
- )
3405
- )
3406
- 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
+ )
3407
3334
  end
3408
3335
  end
3409
3336
  end
@@ -3436,6 +3363,18 @@ module Steep
3436
3363
  ]
3437
3364
  end
3438
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
+
3439
3378
  def type_block_without_hint(node:, block_annotations:, block_params:, block_body:, &block)
3440
3379
  unless block_params
3441
3380
  typing.add_error(
@@ -3576,68 +3515,6 @@ module Steep
3576
3515
  end
3577
3516
  end
3578
3517
 
3579
- def self.parameter_types(nodes, type)
3580
- nodes = nodes.dup
3581
-
3582
- env = {}
3583
-
3584
- type.params.required.each do |type|
3585
- a = nodes.first
3586
- if a&.type == :arg
3587
- env[a.children.first] = type
3588
- nodes.shift
3589
- else
3590
- break
3591
- end
3592
- end
3593
-
3594
- type.params.optional.each do |type|
3595
- a = nodes.first
3596
-
3597
- if a&.type == :optarg
3598
- env[a.children.first] = type
3599
- nodes.shift
3600
- else
3601
- break
3602
- end
3603
- end
3604
-
3605
- if type.params.rest
3606
- a = nodes.first
3607
- if a&.type == :restarg
3608
- env[a.children.first] = AST::Builtin::Array.instance_type(type.params.rest)
3609
- nodes.shift
3610
- end
3611
- end
3612
-
3613
- nodes.each do |node|
3614
- if node.type == :kwarg
3615
- name = node.children[0]
3616
- ty = type.params.required_keywords[name]
3617
- env[name] = ty if ty
3618
- end
3619
-
3620
- if node.type == :kwoptarg
3621
- name = node.children[0]
3622
- ty = type.params.optional_keywords[name]
3623
- env[name] = ty if ty
3624
- end
3625
-
3626
- if node.type == :kwrestarg
3627
- ty = type.params.rest_keywords
3628
- if ty
3629
- env[node.children[0]] = AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type, ty)
3630
- end
3631
- end
3632
- end
3633
-
3634
- env
3635
- end
3636
-
3637
- def self.valid_parameter_env?(env, nodes, params)
3638
- env.size == nodes.size && env.size == params.size
3639
- end
3640
-
3641
3518
  def current_namespace
3642
3519
  module_context&.current_namespace || AST::Namespace.root
3643
3520
  end
@@ -3745,25 +3622,6 @@ module Steep
3745
3622
  end
3746
3623
  end
3747
3624
 
3748
- def flatten_const_name(node)
3749
- path = []
3750
-
3751
- while node
3752
- case node.type
3753
- when :const, :casgn
3754
- path.unshift(node.children[1])
3755
- node = node.children[0]
3756
- when :cbase
3757
- path.unshift("")
3758
- break
3759
- else
3760
- return nil
3761
- end
3762
- end
3763
-
3764
- path.join("::").to_sym
3765
- end
3766
-
3767
3625
  def fallback_to_any(node)
3768
3626
  if block_given?
3769
3627
  typing.add_error yield
@@ -3805,16 +3663,6 @@ module Steep
3805
3663
  Pair.new(type: AST::Builtin.any_type, constr: self)
3806
3664
  end
3807
3665
 
3808
- def fallback_any_rec(node)
3809
- fallback_to_any(node) unless typing.has_type?(node)
3810
-
3811
- each_child_node(node) do |child|
3812
- fallback_any_rec(child)
3813
- end
3814
-
3815
- typing.type_of(node: node)
3816
- end
3817
-
3818
3666
  def unwrap(type)
3819
3667
  expand_alias(type) do |expanded|
3820
3668
  case
@@ -3827,19 +3675,6 @@ module Steep
3827
3675
  end
3828
3676
  end
3829
3677
 
3830
- def self.value_variables(node)
3831
- case node&.type
3832
- when :lvar
3833
- Set.new([node.children.first])
3834
- when :lvasgn
3835
- Set.new([node.children.first]) + value_variables(node.children[1])
3836
- when :begin
3837
- value_variables(node.children.last)
3838
- else
3839
- Set.new
3840
- end
3841
- end
3842
-
3843
3678
  def deep_expand_alias(type, &block)
3844
3679
  checker.factory.deep_expand_alias(type, &block)
3845
3680
  end
@@ -3886,24 +3721,6 @@ module Steep
3886
3721
  end
3887
3722
  end
3888
3723
 
3889
- def select_super_type(sub_type, super_type)
3890
- if super_type
3891
- result = check_relation(sub_type: sub_type, super_type: super_type)
3892
-
3893
- if result.success?
3894
- super_type
3895
- else
3896
- if block_given?
3897
- yield result
3898
- else
3899
- sub_type
3900
- end
3901
- end
3902
- else
3903
- sub_type
3904
- end
3905
- end
3906
-
3907
3724
  def to_instance_type(type, args: nil)
3908
3725
  args = args || case type
3909
3726
  when AST::Types::Name::Singleton
@@ -3915,16 +3732,35 @@ module Steep
3915
3732
  AST::Types::Name::Instance.new(name: type.name, args: args)
3916
3733
  end
3917
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
+
3918
3749
  def try_tuple_type(node, hint)
3919
- if node.children.size != hint.types.size
3920
- return
3750
+ if hint
3751
+ if node.children.size != hint.types.size
3752
+ return
3753
+ end
3921
3754
  end
3922
3755
 
3923
3756
  constr = self
3924
3757
  element_types = []
3925
3758
 
3926
3759
  each_child_node(node).with_index do |child, index|
3927
- 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)
3928
3764
  element_types << type
3929
3765
  end
3930
3766
 
@@ -3956,55 +3792,152 @@ module Steep
3956
3792
  constr.add_typing(node, type: AST::Builtin::Array.instance_type(element_type))
3957
3793
  end
3958
3794
 
3959
- def try_hash_type(node, hint)
3960
- 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 }
3961
3847
 
3962
3848
  case hint
3963
3849
  when AST::Types::Record
3964
- typing.new_child(node.loc.expression.yield_self {|l| l.begin_pos..l.end_pos }) do |child_typing|
3965
- new_construction = with_new_typing(child_typing)
3966
- 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
3967
3860
 
3968
- each_child_node(node) do |child|
3969
- case child.type
3970
- when :pair
3971
- key, value = child.children
3861
+ if pair
3862
+ return pair
3863
+ end
3864
+ end
3972
3865
 
3973
- key_value = case key.type
3974
- when :str, :int, :sym
3975
- key.children[0]
3976
- else
3977
- return nil
3978
- end
3866
+ key_types = []
3867
+ value_types = []
3979
3868
 
3980
- value_hint = hint.elements[key_value]
3981
- 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
3982
3872
 
3983
- if value_hint
3984
- if check_relation(sub_type: value_type, super_type: value_hint).success?
3985
- value_type = value_hint
3986
- 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]
3987
3900
  end
3988
3901
 
3989
- elements[key_value] = value_type
3990
- else
3991
- return nil
3902
+ pair
3992
3903
  end
3904
+ else
3905
+ raise
3993
3906
  end
3907
+ end
3908
+ end
3994
3909
 
3995
- 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) }
3996
3912
 
3997
- hash = AST::Types::Record.new(elements: elements)
3998
- add_typing(node, type: hash)
3999
- end
4000
- when AST::Types::Union
4001
- hint.types.each do |type|
4002
- if pair = try_hash_type(node, type)
4003
- 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)
4004
3931
  end
4005
3932
  end
4006
- nil
4007
3933
  end
3934
+
3935
+ nil
3936
+ end
3937
+
3938
+ def save_typing
3939
+ typing.save!
3940
+ with_new_typing(typing.parent)
4008
3941
  end
4009
3942
  end
4010
3943
  end