steep 0.7.0 → 0.7.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1aa31e85a07f6ac87650d401444230861764800154bfe367724f5cd9f0813ea0
4
- data.tar.gz: b7b38997f73d6ac3cf030e1114fb9f6d2c20afb9635a922fe86256ee6e5359b5
3
+ metadata.gz: 0c1966d8fc74abea789649fe945211c56d81da2fc655860d22849aacc7feae9f
4
+ data.tar.gz: f4b9793cb25421d5a9e031b78df1c29f1350f9d7cd184f12c772065607664afc
5
5
  SHA512:
6
- metadata.gz: 805a6bcc465e951a41d7742fb3ff82f6c8ccedc137aa3be4969539f4610086c6949f6fc04ca461533847aa745464827f763fb1c1943d0f13e5f309e0414fd64a
7
- data.tar.gz: 8677c727c5f7ffc4bb9994cc7eff5bf06c240df29d7fbbd8796a58666ac261a0137e206a397d5c0af0a7210ed12ef391019a0a209497520dc04a98d81948a63c
6
+ metadata.gz: 4d3309eb83780ad4f157a7c7cc9b4bfb1763aac6ff6a2f905838b5f9bebb462ff7fb7bb2d381af5dc6698287b57ee097301d60143e9385804ca2a4700b5f3466
7
+ data.tar.gz: 1e142225c3dfe0d2ed47831f97840ded1a24c5348e734b3767ce1784546a71bcf5bbb1fd57bfc0baad9ec51b6a3002dacc41535ae451a06b8d6a6e4931ec7e9d
data/.travis.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.5.0
4
+ - 2.5.3
5
5
  before_install: gem install bundler -v 1.13.7
6
6
  script: bundle exec rake test smoke
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.7.1 (2018-10-22)
6
+
7
+ * Rename *hash type* to *record type* (#60)
8
+ * Fix keyword typing (#59)
9
+
5
10
  ## 0.7.0 (2018-09-24)
6
11
 
7
12
  * Add some builtin
@@ -1,7 +1,7 @@
1
1
  module Steep
2
2
  module AST
3
3
  module Types
4
- class Hash
4
+ class Record
5
5
  attr_reader :location
6
6
  attr_reader :elements
7
7
 
@@ -11,7 +11,7 @@ module Steep
11
11
  end
12
12
 
13
13
  def ==(other)
14
- other.is_a?(Hash) && other.elements == elements
14
+ other.is_a?(Record) && other.elements == elements
15
15
  end
16
16
 
17
17
  def hash
data/lib/steep/cli.rb CHANGED
@@ -16,7 +16,7 @@ module Steep
16
16
  end
17
17
 
18
18
  def self.available_commands
19
- [:check, :validate, :annotations, :scaffold, :interface]
19
+ [:check, :validate, :annotations, :scaffold, :interface, :version]
20
20
  end
21
21
 
22
22
  def setup_global_options
@@ -127,5 +127,9 @@ module Steep
127
127
 
128
128
  Drivers::PrintInterface.new(type_name: argv.first, signature_dirs: signature_dirs, stdout: stdout, stderr: stderr).run
129
129
  end
130
+
131
+ def process_version
132
+ stdout.puts Steep::VERSION
133
+ end
130
134
  end
131
135
  end
@@ -65,8 +65,6 @@ module Steep
65
65
  Steep.logger.debug "Typechecking..."
66
66
  annotations = source.annotations(block: source.node, builder: check.builder, current_module: AST::Namespace.root)
67
67
 
68
- pp annotations if verbose
69
-
70
68
  const_env = TypeInference::ConstantEnv.new(builder: check.builder, context: nil)
71
69
  type_env = TypeInference::TypeEnv.build(annotations: annotations,
72
70
  subtyping: check,
@@ -29,7 +29,7 @@ module Steep
29
29
  builder = Interface::Builder.new(signatures: env)
30
30
  check = Subtyping::Check.new(builder: builder)
31
31
 
32
- interface = check.resolve(type, with_initialize: true)
32
+ interface = check.resolve(type)
33
33
 
34
34
  stdout.puts "#{type}"
35
35
  stdout.puts "- Instance variables:"
data/lib/steep/errors.rb CHANGED
@@ -503,5 +503,31 @@ module Steep
503
503
  "#{location_to_str}: IncompatibleTuple: expected_tuple=#{expected_tuple}"
504
504
  end
505
505
  end
506
+
507
+ class UnexpectedKeyword < Base
508
+ attr_reader :unexpected_keywords
509
+
510
+ def initialize(node:, unexpected_keywords:)
511
+ super(node: node)
512
+ @unexpected_keywords = unexpected_keywords
513
+ end
514
+
515
+ def to_s
516
+ "#{location_to_str}: UnexpectedKeyword: #{unexpected_keywords.join(", ")}"
517
+ end
518
+ end
519
+
520
+ class MissingKeyword < Base
521
+ attr_reader :missing_keywords
522
+
523
+ def initialize(node:, missing_keywords:)
524
+ super(node: node)
525
+ @missing_keywords = missing_keywords
526
+ end
527
+
528
+ def to_s
529
+ "#{location_to_str}: MissingKeyword: #{missing_keywords.join(", ")}"
530
+ end
531
+ end
506
532
  end
507
533
  end
@@ -85,8 +85,8 @@ module Steep
85
85
  return_type: absolute_type(type.return_type, current: current),
86
86
  location: type.location
87
87
  )
88
- when AST::Types::Hash
89
- AST::Types::Hash.new(
88
+ when AST::Types::Record
89
+ AST::Types::Record.new(
90
90
  elements: type.elements.transform_values {|ty| absolute_type(ty, current: current) },
91
91
  location: type.location
92
92
  )
@@ -62,6 +62,35 @@ module Steep
62
62
  !required_keywords.empty? || !optional_keywords.empty? || rest_keywords
63
63
  end
64
64
 
65
+ def without_keywords
66
+ self.class.new(
67
+ required: required,
68
+ optional: optional,
69
+ rest: rest,
70
+ required_keywords: {},
71
+ optional_keywords: {},
72
+ rest_keywords: nil
73
+ )
74
+ end
75
+
76
+ def drop_first
77
+ case
78
+ when required.any? || optional.any? || rest
79
+ self.class.new(
80
+ required: required.any? ? required.drop(1) : [],
81
+ optional: required.empty? && optional.any? ? optional.drop(1) : optional,
82
+ rest: required.empty? && optional.empty? ? nil : rest,
83
+ required_keywords: required_keywords,
84
+ optional_keywords: optional_keywords,
85
+ rest_keywords: rest_keywords
86
+ )
87
+ when has_keywords?
88
+ without_keywords
89
+ else
90
+ raise "Cannot drop from empty params"
91
+ end
92
+ end
93
+
65
94
  def each_missing_argument(args)
66
95
  required.size.times do |index|
67
96
  if index >= args.size
@@ -160,10 +189,6 @@ module Steep
160
189
  required.all?(&:closed?) && optional.all?(&:closed?) && (!rest || rest.closed?) && required_keywords.values.all?(&:closed?) && optional_keywords.values.all?(&:closed?) && (!rest_keywords || rest_keywords.closed?)
161
190
  end
162
191
 
163
- def has_keyword?
164
- required_keywords.any? || optional_keywords.any? || rest_keywords
165
- end
166
-
167
192
  def subst(s)
168
193
  self.class.new(
169
194
  required: required.map {|t| t.subst(s) },
@@ -199,6 +224,10 @@ module Steep
199
224
  rest_keywords: rest_keywords && yield(rest_keywords)
200
225
  )
201
226
  end
227
+
228
+ def empty?
229
+ required.empty? && optional.empty? && !rest && !has_keywords?
230
+ end
202
231
  end
203
232
 
204
233
  class Block
data/lib/steep/parser.rb CHANGED
@@ -1846,7 +1846,7 @@ module_eval(<<'.,.,', 'parser.y', 399)
1846
1846
  module_eval(<<'.,.,', 'parser.y', 403)
1847
1847
  def _reduce_76(val, _values, result)
1848
1848
  location = val[0].location + val[2].location
1849
- result = AST::Types::Hash.new(elements: val[1], location: location)
1849
+ result = AST::Types::Record.new(elements: val[1], location: location)
1850
1850
 
1851
1851
  result
1852
1852
  end
data/lib/steep/parser.y CHANGED
@@ -402,7 +402,7 @@ rule
402
402
  | tLBRACE hash_elements tRBRACE
403
403
  {
404
404
  location = val[0].location + val[2].location
405
- result = AST::Types::Hash.new(elements: val[1], location: location)
405
+ result = AST::Types::Record.new(elements: val[1], location: location)
406
406
  }
407
407
  | simple_type
408
408
 
@@ -225,6 +225,25 @@ module Steep
225
225
  trace: trace,
226
226
  constraints: constraints)
227
227
 
228
+ when relation.sub_type.is_a?(AST::Types::Record) && relation.super_type.is_a?(AST::Types::Record)
229
+ if Set.new(relation.sub_type.elements.keys).superset?(Set.new(relation.super_type.elements.keys))
230
+ keys = relation.super_type.elements.keys
231
+ type_pairs = keys.map {|key| [relation.sub_type.elements[key], relation.super_type.elements[key]] }
232
+ results = type_pairs.flat_map do |t1, t2|
233
+ relation = Relation.new(sub_type: t1, super_type: t2)
234
+ [check0(relation, assumption: assumption, trace: trace, constraints: constraints),
235
+ check0(relation.flip, assumption: assumption, trace: trace, constraints: constraints)]
236
+ end
237
+
238
+ if results.all?(&:success?)
239
+ success(constraints: constraints)
240
+ else
241
+ results.find(&:failure?)
242
+ end
243
+ else
244
+ failure(error: Result::Failure::UnknownPairError.new(relation: relation),
245
+ trace: trace)
246
+ end
228
247
  else
229
248
  failure(error: Result::Failure::UnknownPairError.new(relation: relation),
230
249
  trace: trace)
@@ -575,51 +594,6 @@ module Steep
575
594
  end
576
595
  end
577
596
 
578
- def module_type(type)
579
- case type.name
580
- when TypeName::Instance
581
- case
582
- when builder.signatures.class_name?(type.name.name)
583
- type.class_type(constructor: nil)
584
- when builder.signatures.module_name?(type.name.name)
585
- type.module_type
586
- end
587
- else
588
- nil
589
- end
590
- end
591
-
592
- def compact(types)
593
- types = types.reject {|type| type.is_a?(AST::Types::Any) }
594
-
595
- if types.empty?
596
- [AST::Types::Any.new]
597
- else
598
- compact0(types)
599
- end
600
- end
601
-
602
- def compact0(types)
603
- if types.size == 1
604
- types
605
- else
606
- type, *types_ = types
607
- compacted = compact0(types_)
608
- compacted.flat_map do |type_|
609
- case
610
- when type == type_
611
- [type]
612
- when check(Relation.new(sub_type: type_, super_type: type), constraints: Constraints.empty).success?
613
- [type]
614
- when check(Relation.new(sub_type: type, super_type: type_), constraints: Constraints.empty).success?
615
- [type_]
616
- else
617
- [type, type_]
618
- end
619
- end.uniq
620
- end
621
- end
622
-
623
597
  class CannotResolveError < StandardError
624
598
  attr_reader :type
625
599
 
@@ -892,7 +866,7 @@ module Steep
892
866
  array_interface
893
867
  end
894
868
 
895
- when AST::Types::Hash
869
+ when AST::Types::Record
896
870
  yield_self do
897
871
  key_type = AST::Types::Union.build(types: type.elements.keys.map {|val| AST::Types::Literal.new(value: val) })
898
872
  value_type = AST::Types::Union.build(types: type.elements.values)
@@ -1555,13 +1555,13 @@ module Steep
1555
1555
  end
1556
1556
  end
1557
1557
 
1558
- def check(node, type)
1558
+ def check(node, type, constraints: Subtyping::Constraints.empty)
1559
1559
  type_ = synthesize(node, hint: type)
1560
1560
 
1561
1561
  result = checker.check(
1562
1562
  Subtyping::Relation.new(sub_type: type_,
1563
1563
  super_type: type),
1564
- constraints: Subtyping::Constraints.empty
1564
+ constraints: constraints
1565
1565
  )
1566
1566
  if result.failure?
1567
1567
  yield(type, type_, result)
@@ -1881,24 +1881,36 @@ module Steep
1881
1881
  def type_method_call(node, receiver_type:, method:, args:, block_params:, block_body:)
1882
1882
  results = method.types.map do |method_type|
1883
1883
  Steep.logger.tagged method_type.location&.source do
1884
- child_typing = typing.new_child
1885
- arg_pairs = args.zip(method_type.params, method_type.block&.type)
1886
-
1887
- type_or_error = if arg_pairs
1888
- self.with_new_typing(child_typing).try_method_type(
1889
- node,
1890
- receiver_type: receiver_type,
1891
- method_type: method_type,
1892
- args: args,
1893
- arg_pairs: arg_pairs,
1894
- block_params: block_params,
1895
- block_body: block_body,
1896
- child_typing: child_typing
1897
- )
1898
- else
1899
- Steep.logger.debug(node.inspect)
1900
- Errors::IncompatibleArguments.new(node: node, receiver_type: receiver_type, method_type: method_type)
1901
- end
1884
+ zips = args.zips(method_type.params, method_type.block&.type)
1885
+
1886
+ type_or_error, child_typing = if zips.any?
1887
+ zips.map do |arg_pairs|
1888
+ child_typing = typing.new_child
1889
+
1890
+ result = self.with_new_typing(child_typing).try_method_type(
1891
+ node,
1892
+ receiver_type: receiver_type,
1893
+ method_type: method_type,
1894
+ args: args,
1895
+ arg_pairs: arg_pairs,
1896
+ block_params: block_params,
1897
+ block_body: block_body,
1898
+ child_typing: child_typing
1899
+ )
1900
+
1901
+ unless result.is_a?(Errors::Base)
1902
+ break [[result, child_typing]]
1903
+ end
1904
+
1905
+ [result, child_typing]
1906
+ end.first
1907
+ else
1908
+ Steep.logger.debug(node.inspect)
1909
+ [
1910
+ Errors::IncompatibleArguments.new(node: node, receiver_type: receiver_type, method_type: method_type),
1911
+ typing.new_child
1912
+ ]
1913
+ end
1902
1914
 
1903
1915
  [child_typing, type_or_error, method_type]
1904
1916
  end
@@ -1922,6 +1934,139 @@ module Steep
1922
1934
  end
1923
1935
  end
1924
1936
 
1937
+ def check_keyword_arg(receiver_type:, node:, method_type:, constraints:)
1938
+ params = method_type.params
1939
+
1940
+ case node.type
1941
+ when :hash
1942
+ keyword_hash_type = AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type,
1943
+ AST::Builtin.any_type)
1944
+ typing.add_typing node, keyword_hash_type
1945
+
1946
+ given_keys = Set.new()
1947
+
1948
+ node.children.each do |element|
1949
+ case element.type
1950
+ when :pair
1951
+ key_node, value_node = element.children
1952
+
1953
+ case key_node.type
1954
+ when :sym
1955
+ key_symbol = key_node.children[0]
1956
+ keyword_type = case
1957
+ when params.required_keywords.key?(key_symbol)
1958
+ params.required_keywords[key_symbol]
1959
+ when params.optional_keywords.key?(key_symbol)
1960
+ AST::Types::Union.build(
1961
+ types: [params.optional_keywords[key_symbol],
1962
+ AST::Builtin.nil_type]
1963
+ )
1964
+ when params.rest_keywords
1965
+ params.rest_keywords
1966
+ end
1967
+
1968
+ typing.add_typing key_node, AST::Builtin::Symbol.instance_type
1969
+
1970
+ given_keys << key_symbol
1971
+
1972
+ if keyword_type
1973
+ check(value_node, keyword_type, constraints: constraints) do |expected, actual, result|
1974
+ return Errors::IncompatibleAssignment.new(
1975
+ node: value_node,
1976
+ lhs_type: expected,
1977
+ rhs_type: actual,
1978
+ result: result
1979
+ )
1980
+ end
1981
+ else
1982
+ synthesize(value_node)
1983
+ end
1984
+
1985
+ else
1986
+ check(key_node, AST::Builtin::Symbol.instance_type, constraints: constraints) do |expected, actual, result|
1987
+ return Errors::IncompatibleAssignment.new(
1988
+ node: key_node,
1989
+ lhs_type: expected,
1990
+ rhs_type: actual,
1991
+ result: result
1992
+ )
1993
+ end
1994
+ end
1995
+
1996
+ when :kwsplat
1997
+ Steep.logger.warn("Keyword arg with kwsplat(**) node are not supported.")
1998
+
1999
+ check(element.children[0], keyword_hash_type, constraints: constraints) do |expected, actual, result|
2000
+ return Errors::IncompatibleAssignment.new(
2001
+ node: node,
2002
+ lhs_type: expected,
2003
+ rhs_type: actual,
2004
+ result: result
2005
+ )
2006
+ end
2007
+
2008
+ given_keys = true
2009
+ end
2010
+ end
2011
+
2012
+ case given_keys
2013
+ when Set
2014
+ missing_keywords = Set.new(params.required_keywords.keys) - given_keys
2015
+ unless missing_keywords.empty?
2016
+ return Errors::MissingKeyword.new(node: node,
2017
+ missing_keywords: missing_keywords)
2018
+ end
2019
+
2020
+ extra_keywords = given_keys - Set.new(params.required_keywords.keys) - Set.new(params.optional_keywords.keys)
2021
+ if extra_keywords.any? && !params.rest_keywords
2022
+ return Errors::UnexpectedKeyword.new(node: node,
2023
+ unexpected_keywords: extra_keywords)
2024
+ end
2025
+ end
2026
+ else
2027
+ if params.rest_keywords
2028
+ Steep.logger.warn("Method call with rest keywords type is detected. Rough approximation to be improved.")
2029
+
2030
+ value_types = params.required_keywords.values +
2031
+ params.optional_keywords.values.map {|type| AST::Types::Union.build(types: [type, AST::Builtin.nil_type]) } +
2032
+ [params.rest_keywords]
2033
+
2034
+ hash_type = AST::Builtin::Hash.instance_type(
2035
+ AST::Builtin::Symbol.instance_type,
2036
+ AST::Types::Union.build(types: value_types,
2037
+ location: method_type.location)
2038
+ )
2039
+ else
2040
+ hash_elements = params.required_keywords.merge(
2041
+ method_type.optional_keywords.transform_values do |type|
2042
+ AST::Types::Union.build(types: [type, AST::Builtin.nil_type],
2043
+ location: method_type.location)
2044
+ end
2045
+ )
2046
+
2047
+ hash_type = AST::Types::Record.new(elements: hash_elements)
2048
+ end
2049
+
2050
+ node_type = synthesize(node, hint: hash_type)
2051
+
2052
+ relation = Subtyping::Relation.new(
2053
+ sub_type: node_type,
2054
+ super_type: hash_type
2055
+ )
2056
+
2057
+ checker.check(relation, constraints: constraints).else do
2058
+ return Errors::ArgumentTypeMismatch.new(
2059
+ node: node,
2060
+ receiver_type: receiver_type,
2061
+ expected: relation.super_type,
2062
+ actual: relation.sub_type
2063
+ )
2064
+ end
2065
+ end
2066
+
2067
+ nil
2068
+ end
2069
+
1925
2070
  def try_method_type(node, receiver_type:, method_type:, args:, arg_pairs:, block_params:, block_body:, child_typing:)
1926
2071
  fresh_types = method_type.type_params.map {|x| AST::Types::Var.fresh(x) }
1927
2072
  fresh_vars = Set.new(fresh_types.map(&:name))
@@ -1945,28 +2090,43 @@ module Steep
1945
2090
  variance = Subtyping::VariableVariance.from_method_type(method_type)
1946
2091
  occurence = Subtyping::VariableOccurence.from_method_type(method_type)
1947
2092
 
1948
- arg_pairs.each do |(arg_node, param_type)|
1949
- param_type = param_type.subst(instantiation)
2093
+ arg_pairs.each do |pair|
2094
+ case pair
2095
+ when Array
2096
+ (arg_node, param_type) = pair
1950
2097
 
1951
- arg_type = if arg_node.type == :splat
1952
- type = construction.synthesize(arg_node.children[0])
1953
- child_typing.add_typing(arg_node, type)
1954
- else
1955
- construction.synthesize(arg_node, hint: param_type)
1956
- end
2098
+ param_type = param_type.subst(instantiation)
1957
2099
 
1958
- relation = Subtyping::Relation.new(
1959
- sub_type: arg_type,
1960
- super_type: param_type
1961
- )
2100
+ arg_type = if arg_node.type == :splat
2101
+ type = construction.synthesize(arg_node.children[0])
2102
+ child_typing.add_typing(arg_node, type)
2103
+ else
2104
+ construction.synthesize(arg_node, hint: param_type)
2105
+ end
1962
2106
 
1963
- checker.check(relation, constraints: constraints).else do |result|
1964
- return Errors::ArgumentTypeMismatch.new(
1965
- node: arg_node,
1966
- receiver_type: receiver_type,
1967
- expected: relation.super_type,
1968
- actual: relation.sub_type
2107
+ relation = Subtyping::Relation.new(
2108
+ sub_type: arg_type,
2109
+ super_type: param_type
1969
2110
  )
2111
+
2112
+ checker.check(relation, constraints: constraints).else do |result|
2113
+ return Errors::ArgumentTypeMismatch.new(
2114
+ node: arg_node,
2115
+ receiver_type: receiver_type,
2116
+ expected: relation.super_type,
2117
+ actual: relation.sub_type
2118
+ )
2119
+ end
2120
+ else
2121
+ # keyword
2122
+ result = check_keyword_arg(receiver_type: receiver_type,
2123
+ node: pair,
2124
+ method_type: method_type,
2125
+ constraints: constraints)
2126
+
2127
+ if result.is_a?(Errors::Base)
2128
+ return result
2129
+ end
1970
2130
  end
1971
2131
  end
1972
2132
 
@@ -2496,10 +2656,10 @@ module Steep
2496
2656
  end
2497
2657
 
2498
2658
  def try_hash_type(node, hint)
2499
- if hint.is_a?(AST::Types::Hash)
2659
+ if hint.is_a?(AST::Types::Record)
2500
2660
  typing.new_child do |child_typing|
2501
2661
  new_construction = with_new_typing(child_typing)
2502
- elem_types = hint.elements.dup
2662
+ elements = {}
2503
2663
 
2504
2664
  each_child_node(node) do |child|
2505
2665
  case child.type
@@ -2513,22 +2673,28 @@ module Steep
2513
2673
  return nil
2514
2674
  end
2515
2675
 
2516
- return nil unless elem_types.key?(key_value)
2517
-
2518
2676
  key_hint = AST::Types::Literal.new(value: key_value)
2519
- value_hint = elem_types.delete(key_value)
2677
+ value_hint = hint.elements[key_value]
2678
+
2679
+ value_type = new_construction.synthesize(value, hint: value_hint)
2680
+
2681
+ if value_hint
2682
+ relation = Subtyping::Relation.new(sub_type: value_type, super_type: value_hint)
2683
+ if checker.check(relation, constraints: Subtyping::Constraints.empty).success?
2684
+ value_type = value_hint
2685
+ end
2686
+ end
2520
2687
 
2521
- new_construction.check(key, key_hint) { return nil }
2522
- new_construction.check(value, value_hint) { return nil }
2688
+ elements[key_value] = value_type
2523
2689
  else
2524
2690
  return nil
2525
2691
  end
2526
2692
  end
2527
2693
 
2528
- return nil unless elem_types.empty?
2529
-
2530
2694
  child_typing.save!
2531
- return typing.add_typing(node, hint)
2695
+
2696
+ hash = AST::Types::Record.new(elements: elements)
2697
+ typing.add_typing(node, hash)
2532
2698
  end
2533
2699
  end
2534
2700
  end
@@ -2,12 +2,10 @@ module Steep
2
2
  module TypeInference
3
3
  class SendArgs
4
4
  attr_reader :args
5
- attr_reader :kw_args
6
5
  attr_reader :block_pass_arg
7
6
 
8
- def initialize(args:, kw_args:, block_pass_arg:)
7
+ def initialize(args:, block_pass_arg:)
9
8
  @args = args
10
- @kw_args = kw_args
11
9
  @block_pass_arg = block_pass_arg
12
10
  end
13
11
 
@@ -15,7 +13,6 @@ module Steep
15
13
  nodes = nodes.dup
16
14
 
17
15
  args = []
18
- last_hash = nil
19
16
  block_pass_arg = nil
20
17
 
21
18
  if nodes.last&.type == :block_pass
@@ -23,38 +20,20 @@ module Steep
23
20
  end
24
21
 
25
22
  nodes.each do |node|
26
- if last_hash
27
- args << last_hash
28
- last_hash = nil
29
- end
30
-
31
- case node.type
32
- when :hash
33
- last_hash = node
34
- else
35
- args << node
36
- end
23
+ args << node
37
24
  end
38
25
 
39
- if last_hash
40
- unless kw_args?(last_hash)
41
- args << last_hash
42
- last_hash = nil
43
- end
44
- end
26
+ new(args: args, block_pass_arg: block_pass_arg)
27
+ end
45
28
 
46
- new(args: args, kw_args: last_hash, block_pass_arg: block_pass_arg)
29
+ def drop_first
30
+ raise "Cannot drop first from empty args" if args.empty?
31
+ self.class.new(args: args.drop(1), block_pass_arg: block_pass_arg)
47
32
  end
48
33
 
49
- def self.kw_args?(node)
50
- node.children.all? do |child|
51
- case child.type
52
- when :pair
53
- child.children[0].type == :sym
54
- when :kwsplat
55
- true
56
- end
57
- end
34
+ def drop_last
35
+ raise "Cannot drop last from empty args" if args.empty?
36
+ self.class.new(args: args.take(args.size - 1), block_pass_arg: block_pass_arg)
58
37
  end
59
38
 
60
39
  def each_keyword_arg
@@ -81,146 +60,135 @@ module Steep
81
60
  end
82
61
  end
83
62
 
84
- def zip(params, block)
85
- Set.new(
86
- [].tap do |pairs|
87
- consumed_keywords = Set.new
88
- rest_types = []
89
-
90
- params.required_keywords.each do |name, type|
91
- if (node = each_keyword_arg.find {|pair| pair.children[0].children[0] == name })
92
- pairs << [node.children[1], type]
93
- consumed_keywords << name
94
- else
95
- if kwsplat_nodes.any?
96
- rest_types << type
97
- else
98
- return
99
- end
100
- end
101
- end
63
+ def zips(params, block_type)
64
+ zip0(params, block_type).map do |pairs|
65
+ group_pairs(pairs)
66
+ end
67
+ end
102
68
 
103
- params.optional_keywords.each do |name, type|
104
- if (node = each_keyword_arg.find {|pair| pair.children[0].children[0] == name })
105
- pairs << [node.children[1], type]
106
- consumed_keywords << name
107
- else
108
- if kwsplat_nodes.any?
109
- rest_types << type
110
- end
111
- end
112
- end
69
+ def group_pairs(pairs)
70
+ types = pairs.each_with_object({}) do |pair, hash|
71
+ case pair
72
+ when Array
73
+ node, type = pair
74
+ hash[node.__id__] ||= [node]
75
+ hash[node.__id__] << type
76
+ else
77
+ hash[node.__id__] = pair
78
+ end
79
+ end
113
80
 
114
- if params.rest_keywords
115
- each_keyword_arg do |pair|
116
- name = pair.children[0].children[0]
117
- node = pair.children[1]
81
+ types.map do |_, array|
82
+ case array
83
+ when Array
84
+ node, *types_ = array
85
+ [node, AST::Types::Intersection.build(types: types_)]
86
+ else
87
+ array
88
+ end
89
+ end
90
+ end
118
91
 
119
- unless consumed_keywords.include?(name)
120
- pairs << [node, params.rest_keywords]
121
- end
122
- end
92
+ def add_pair(pairs, pair)
93
+ pairs.map do |ps|
94
+ if block_given?
95
+ yield ps, pair
96
+ else
97
+ [pair] + ps
98
+ end
99
+ end
100
+ end
123
101
 
124
- if kwsplat_nodes.any?
125
- pairs << [kw_args,
126
- AST::Builtin::Hash.instance_type(
127
- AST::Builtin::Symbol.instance_type,
128
- AST::Types::Union.build(types: rest_types + [params.rest_keywords])
129
- )]
130
- end
131
- end
102
+ def zip0(params, block_type)
103
+ case
104
+ when params.empty? && args.empty?
105
+ [[]]
132
106
 
133
- if params.has_keyword?
134
- if !params.rest_keywords
135
- if kwsplat_nodes.empty?
136
- if each_keyword_arg.any? {|pair| !consumed_keywords.include?(pair.children[0].children[0]) }
137
- return
138
- end
139
- end
140
- end
141
- end
107
+ when params.required.any?
108
+ if args.any?
109
+ first_arg = args[0]
142
110
 
143
- args = self.args.dup
144
- unless params.has_keyword?
145
- args << kw_args if kw_args
111
+ case first_arg.type
112
+ when :splat
113
+ []
114
+ else
115
+ rest = drop_first.zip0(params.drop_first, block_type)
116
+ pair = [first_arg, params.required[0]]
117
+
118
+ add_pair(rest, pair)
146
119
  end
120
+ else
121
+ []
122
+ end
147
123
 
148
- arg_types = {}
124
+ when params.has_keywords? && params.required_keywords.any?
125
+ if args.any?
126
+ rest = drop_last.zip0(params.without_keywords, block_type)
127
+ last_arg = args.last
149
128
 
150
- params.required.each do |param|
151
- if args.any?
152
- next_arg(args) do |arg|
153
- save_arg_type(arg, param, arg_types)
154
- end
155
- consume_arg(args)
156
- else
157
- return
158
- end
159
- end
129
+ return [] if last_arg.type == :splat
160
130
 
161
- params.optional.each do |param|
162
- next_arg(args) do |arg|
163
- save_arg_type(arg, param, arg_types)
164
- end
165
- consume_arg(args)
131
+ add_pair(rest, last_arg) do |ps, p|
132
+ ps + [p]
166
133
  end
134
+ else
135
+ []
136
+ end
167
137
 
168
- if args.any?
169
- if params.rest
170
- args.each do |arg|
171
- save_arg_type(arg, params.rest, arg_types)
172
- end
173
- else
174
- if args.none? {|arg| arg.type == :splat }
175
- return
176
- end
177
- end
178
- end
138
+ when params.has_keywords? && params.required_keywords.empty?
139
+ if args.any?
140
+ rest = drop_last.zip0(params.without_keywords, block_type)
141
+ last_arg = args.last
179
142
 
180
- (self.args + [kw_args].compact).each do |arg|
181
- types = arg_types[arg.object_id]
143
+ no_keyword = zip0(params.without_keywords, block_type)
182
144
 
183
- if types
184
- if arg.type == :splat
185
- type = AST::Builtin::Array.instance_type(AST::Types::Union.build(types: types))
186
- else
187
- type = AST::Types::Union.build(types: types)
188
- end
189
- pairs << [arg, type]
190
- end
145
+ if last_arg.type == :splat
146
+ no_keyword
147
+ else
148
+ add_pair(rest, last_arg) do |ps, p|
149
+ ps + [p]
150
+ end + no_keyword
191
151
  end
152
+ else
153
+ zip0(params.without_keywords, block_type)
192
154
  end
193
- )
194
- end
195
155
 
196
- def save_arg_type(arg, type, hash)
197
- if hash.key?(arg.object_id)
198
- types = hash[arg.object_id]
199
- else
200
- types = hash[arg.object_id] = []
201
- end
202
-
203
- types << type
204
- end
205
-
206
- def next_arg(args)
207
- if args.any?
208
- case args[0].type
209
- when :splat
210
- args.each do |arg|
211
- yield arg
156
+ when params.optional.any?
157
+ if args.any?
158
+ first_arg = args[0]
159
+
160
+ case first_arg.type
161
+ when :splat
162
+ rest = zip0(params.drop_first, block_type)
163
+ pair = [args[0], AST::Builtin::Array.instance_type(params.optional[0])]
164
+ else
165
+ rest = drop_first.zip0(params.drop_first, block_type)
166
+ pair = [args[0], params.optional[0]]
212
167
  end
168
+
169
+ add_pair(rest, pair)
213
170
  else
214
- yield args[0]
171
+ zip0(params.drop_first, block_type)
215
172
  end
216
- end
217
- end
218
173
 
219
- def consume_arg(args)
220
- if args.any?
221
- unless args[0].type == :splat
222
- args.shift
174
+ when params.rest
175
+ if args.any?
176
+ rest = drop_first.zip0(params, block_type)
177
+ first_arg = args[0]
178
+
179
+ case first_arg.type
180
+ when :splat
181
+ pair = [first_arg, AST::Builtin::Array.instance_type(params.rest)]
182
+ else
183
+ pair = [first_arg, params.rest]
184
+ end
185
+
186
+ add_pair(rest, pair)
187
+ else
188
+ zip0(params.drop_first, block_type)
223
189
  end
190
+ else
191
+ []
224
192
  end
225
193
  end
226
194
  end
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
data/lib/steep.rb CHANGED
@@ -28,7 +28,7 @@ require "steep/ast/types/literal"
28
28
  require "steep/ast/types/boolean"
29
29
  require "steep/ast/types/tuple"
30
30
  require "steep/ast/types/proc"
31
- require "steep/ast/types/hash"
31
+ require "steep/ast/types/record"
32
32
  require "steep/ast/method_type"
33
33
  require "steep/ast/type_params"
34
34
  require "steep/ast/signature/class"
data/smoke/hash/d.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  params = { id: 30, name: "Matz" }
4
4
 
5
- # !expects IncompatibleAssignment: lhs_type={ :name => ::String, :id => ::Integer }, rhs_type=::Hash<::Symbol, ::String>
5
+ # !expects IncompatibleAssignment: lhs_type={ :name => ::String, :id => ::Integer }, rhs_type={ :id => ::String, :name => ::String, :email => ::String }
6
6
  params = { id: "30", name: "foo", email: "matsumoto@soutaro.com" }
data/smoke/hash/e.rb ADDED
@@ -0,0 +1,2 @@
1
+ # !expects NoMethodError: type=::Integer, method=fffffffffffff
2
+ Foo.new.get({ foo: 3 }).fffffffffffff
data/smoke/hash/e.rbi ADDED
@@ -0,0 +1,3 @@
1
+ class Foo
2
+ def get: <'a> ({ foo: 'a }) -> 'a
3
+ end
data/smoke/literal/b.rb CHANGED
@@ -5,5 +5,5 @@ l.foo(3)
5
5
  l.foo(4)
6
6
 
7
7
  l.bar(foo: :foo)
8
- # !expects ArgumentTypeMismatch: receiver=::LiteralMethods, expected=:foo, actual=::Symbol
8
+ # !expects IncompatibleAssignment: lhs_type=:foo, rhs_type=::Symbol
9
9
  l.bar(foo: :bar)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: steep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-23 00:00:00.000000000 Z
11
+ date: 2018-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -169,7 +169,6 @@ files:
169
169
  - lib/steep/ast/types/boolean.rb
170
170
  - lib/steep/ast/types/bot.rb
171
171
  - lib/steep/ast/types/class.rb
172
- - lib/steep/ast/types/hash.rb
173
172
  - lib/steep/ast/types/helper.rb
174
173
  - lib/steep/ast/types/instance.rb
175
174
  - lib/steep/ast/types/intersection.rb
@@ -177,6 +176,7 @@ files:
177
176
  - lib/steep/ast/types/name.rb
178
177
  - lib/steep/ast/types/nil.rb
179
178
  - lib/steep/ast/types/proc.rb
179
+ - lib/steep/ast/types/record.rb
180
180
  - lib/steep/ast/types/self.rb
181
181
  - lib/steep/ast/types/top.rb
182
182
  - lib/steep/ast/types/tuple.rb
@@ -264,6 +264,8 @@ files:
264
264
  - smoke/hash/b.rb
265
265
  - smoke/hash/c.rb
266
266
  - smoke/hash/d.rb
267
+ - smoke/hash/e.rb
268
+ - smoke/hash/e.rbi
267
269
  - smoke/hello/hello.rb
268
270
  - smoke/hello/hello.rbi
269
271
  - smoke/if/a.rb