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 +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +5 -0
- data/lib/steep/ast/types/{hash.rb → record.rb} +2 -2
- data/lib/steep/cli.rb +5 -1
- data/lib/steep/drivers/check.rb +0 -2
- data/lib/steep/drivers/print_interface.rb +1 -1
- data/lib/steep/errors.rb +26 -0
- data/lib/steep/interface/builder.rb +2 -2
- data/lib/steep/interface/method_type.rb +33 -4
- data/lib/steep/parser.rb +1 -1
- data/lib/steep/parser.y +1 -1
- data/lib/steep/subtyping/check.rb +20 -46
- data/lib/steep/type_construction.rb +214 -48
- data/lib/steep/type_inference/send_args.rb +117 -149
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +1 -1
- data/smoke/hash/d.rb +1 -1
- data/smoke/hash/e.rb +2 -0
- data/smoke/hash/e.rbi +3 -0
- data/smoke/literal/b.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c1966d8fc74abea789649fe945211c56d81da2fc655860d22849aacc7feae9f
|
4
|
+
data.tar.gz: f4b9793cb25421d5a9e031b78df1c29f1350f9d7cd184f12c772065607664afc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d3309eb83780ad4f157a7c7cc9b4bfb1763aac6ff6a2f905838b5f9bebb462ff7fb7bb2d381af5dc6698287b57ee097301d60143e9385804ca2a4700b5f3466
|
7
|
+
data.tar.gz: 1e142225c3dfe0d2ed47831f97840ded1a24c5348e734b3767ce1784546a71bcf5bbb1fd57bfc0baad9ec51b6a3002dacc41535ae451a06b8d6a6e4931ec7e9d
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Steep
|
2
2
|
module AST
|
3
3
|
module Types
|
4
|
-
class
|
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?(
|
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
|
data/lib/steep/drivers/check.rb
CHANGED
@@ -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
|
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::
|
89
|
-
AST::Types::
|
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::
|
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::
|
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::
|
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:
|
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
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
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 |
|
1949
|
-
|
2093
|
+
arg_pairs.each do |pair|
|
2094
|
+
case pair
|
2095
|
+
when Array
|
2096
|
+
(arg_node, param_type) = pair
|
1950
2097
|
|
1951
|
-
|
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
|
-
|
1959
|
-
|
1960
|
-
|
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
|
-
|
1964
|
-
|
1965
|
-
|
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::
|
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
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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:,
|
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
|
-
|
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
|
-
|
40
|
-
|
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
|
-
|
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
|
50
|
-
|
51
|
-
|
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
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
162
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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
|
-
(
|
181
|
-
types = arg_types[arg.object_id]
|
143
|
+
no_keyword = zip0(params.without_keywords, block_type)
|
182
144
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
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
|
-
|
171
|
+
zip0(params.drop_first, block_type)
|
215
172
|
end
|
216
|
-
end
|
217
|
-
end
|
218
173
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
args
|
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
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/
|
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
|
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
data/smoke/hash/e.rbi
ADDED
data/smoke/literal/b.rb
CHANGED
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.
|
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-
|
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
|