steep 0.44.1 → 0.45.0

Sign up to get free protection for your applications and to get access to all the features.
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