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