steep 0.1.0.pre
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 +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/README.md +95 -0
- data/Rakefile +23 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/smoke_runner.rb +106 -0
- data/exe/steep +18 -0
- data/lib/steep.rb +33 -0
- data/lib/steep/annotation.rb +223 -0
- data/lib/steep/cli.rb +79 -0
- data/lib/steep/drivers/check.rb +141 -0
- data/lib/steep/errors.rb +207 -0
- data/lib/steep/interface.rb +280 -0
- data/lib/steep/parser.y +311 -0
- data/lib/steep/signature/class.rb +358 -0
- data/lib/steep/signature/errors.rb +78 -0
- data/lib/steep/signature/extension.rb +51 -0
- data/lib/steep/signature/interface.rb +48 -0
- data/lib/steep/source.rb +98 -0
- data/lib/steep/type_assignability.rb +362 -0
- data/lib/steep/type_construction.rb +993 -0
- data/lib/steep/type_name.rb +37 -0
- data/lib/steep/types.rb +4 -0
- data/lib/steep/types/any.rb +31 -0
- data/lib/steep/types/class.rb +27 -0
- data/lib/steep/types/instance.rb +27 -0
- data/lib/steep/types/merge.rb +32 -0
- data/lib/steep/types/name.rb +57 -0
- data/lib/steep/types/union.rb +42 -0
- data/lib/steep/types/var.rb +38 -0
- data/lib/steep/typing.rb +70 -0
- data/lib/steep/version.rb +3 -0
- data/manual/annotations.md +144 -0
- data/sig/signature.rbi +54 -0
- data/sig/types.rbi +43 -0
- data/smoke/and/a.rb +9 -0
- data/smoke/array/a.rb +22 -0
- data/smoke/block/a.rb +12 -0
- data/smoke/block/a.rbi +4 -0
- data/smoke/case/a.rb +20 -0
- data/smoke/class/a.rb +31 -0
- data/smoke/class/a.rbi +9 -0
- data/smoke/class/b.rb +7 -0
- data/smoke/class/c.rb +10 -0
- data/smoke/const/a.rb +30 -0
- data/smoke/dstr/a.rb +6 -0
- data/smoke/extension/a.rb +11 -0
- data/smoke/extension/a.rbi +8 -0
- data/smoke/extension/b.rb +12 -0
- data/smoke/extension/c.rb +9 -0
- data/smoke/hello/hello.rb +13 -0
- data/smoke/hello/hello.rbi +7 -0
- data/smoke/if/a.rb +20 -0
- data/smoke/implements/a.rb +14 -0
- data/smoke/implements/a.rbi +6 -0
- data/smoke/literal/a.rb +16 -0
- data/smoke/map/a.rb +5 -0
- data/smoke/method/a.rb +26 -0
- data/smoke/method/a.rbi +0 -0
- data/smoke/module/a.rb +21 -0
- data/smoke/module/a.rbi +7 -0
- data/smoke/module/b.rb +8 -0
- data/smoke/self/a.rb +23 -0
- data/smoke/self/a.rbi +4 -0
- data/smoke/super/a.rb +34 -0
- data/smoke/super/a.rbi +10 -0
- data/smoke/yield/a.rb +18 -0
- data/stdlib/builtin.rbi +89 -0
- data/steep.gemspec +32 -0
- metadata +214 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
module Steep
|
2
|
+
module TypeName
|
3
|
+
class Base
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(name:)
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
other.is_a?(self.class) && other.name == name
|
12
|
+
end
|
13
|
+
|
14
|
+
def hash
|
15
|
+
name.hash
|
16
|
+
end
|
17
|
+
|
18
|
+
def eql?(other)
|
19
|
+
self == other
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
name.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Interface < Base; end
|
28
|
+
|
29
|
+
class Module < Base
|
30
|
+
def to_s
|
31
|
+
"#{name}.module"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Instance < Base; end
|
36
|
+
end
|
37
|
+
end
|
data/lib/steep/types.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Steep
|
2
|
+
module Types
|
3
|
+
class Any
|
4
|
+
# @implements Steep__Types__Any
|
5
|
+
|
6
|
+
def ==(other)
|
7
|
+
other.is_a?(Any)
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash
|
11
|
+
self.class.hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def eql?(other)
|
15
|
+
other == self
|
16
|
+
end
|
17
|
+
|
18
|
+
def closed?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def substitute(klass:, instance:, params:)
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"any"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Steep
|
2
|
+
module Types
|
3
|
+
class Class
|
4
|
+
# @implements Steep__Types__Class
|
5
|
+
|
6
|
+
def ==(other)
|
7
|
+
other.is_a?(Class)
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash
|
11
|
+
self.class.hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def eql?(other)
|
15
|
+
self == other
|
16
|
+
end
|
17
|
+
|
18
|
+
def closed?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def substitute(klass:, instance:, params:)
|
23
|
+
klass
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Steep
|
2
|
+
module Types
|
3
|
+
class Instance
|
4
|
+
# @implements Steep__Types__Instance
|
5
|
+
|
6
|
+
def ==(other)
|
7
|
+
other.is_a?(Instance)
|
8
|
+
end
|
9
|
+
|
10
|
+
def eql?(other)
|
11
|
+
self == other
|
12
|
+
end
|
13
|
+
|
14
|
+
def hash
|
15
|
+
self.class.hash
|
16
|
+
end
|
17
|
+
|
18
|
+
def closed?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def substitute(klass:, instance:, params:)
|
23
|
+
instance
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Steep
|
2
|
+
module Types
|
3
|
+
class Merge
|
4
|
+
# @implements Steep__Types__Merge
|
5
|
+
|
6
|
+
# @dynamic types
|
7
|
+
attr_reader :types
|
8
|
+
|
9
|
+
def initialize(types:)
|
10
|
+
@types = types
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
# @type var other_: Steep__Types__Merge
|
15
|
+
other_ = other
|
16
|
+
other_.is_a?(self.class) && other_.types == types
|
17
|
+
end
|
18
|
+
|
19
|
+
def hash
|
20
|
+
self.class.hash ^ types.hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def eql?(other)
|
24
|
+
other == self
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
types.join(" + ")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Steep
|
2
|
+
module Types
|
3
|
+
class Name
|
4
|
+
# @implements Steep__Types__Name
|
5
|
+
# @type const TypeName::Interface: Steep__TypeName.class
|
6
|
+
# @type const TypeName::Module: Steep__TypeName.class
|
7
|
+
# @type const TypeName::Instance: Steep__TypeName.class
|
8
|
+
|
9
|
+
# @dynamic name
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @dynamic params
|
13
|
+
attr_reader :params
|
14
|
+
|
15
|
+
def initialize(name:, params:)
|
16
|
+
@name = name
|
17
|
+
@params = params
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.interface(name:, params: [])
|
21
|
+
self.new(name: TypeName::Interface.new(name: name), params: params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.module(name:, params: [])
|
25
|
+
self.new(name: TypeName::Module.new(name: name), params: params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.instance(name:, params: [])
|
29
|
+
self.new(name: TypeName::Instance.new(name: name), params: params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other)
|
33
|
+
other.is_a?(Name) && name == other.name && other.params == params
|
34
|
+
end
|
35
|
+
|
36
|
+
def hash
|
37
|
+
name.hash ^ params.hash
|
38
|
+
end
|
39
|
+
|
40
|
+
def eql?(other)
|
41
|
+
other == self
|
42
|
+
end
|
43
|
+
|
44
|
+
def closed?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def substitute(klass:, instance:, params:)
|
49
|
+
self.class.new(name: name, params: self.params.map {|t| t.substitute(klass: klass, instance: instance, params: params) })
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"#{name}" + (params.empty? ? "" : "<#{params.map {|x| x.to_s }.join(", ")}>")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Steep
|
2
|
+
module Types
|
3
|
+
class Union
|
4
|
+
# @implements Steep__Types__Union
|
5
|
+
# @type const Union: Steep__Types__Union.module
|
6
|
+
|
7
|
+
# @dynamic types
|
8
|
+
attr_reader :types
|
9
|
+
|
10
|
+
def initialize(types:)
|
11
|
+
@types = types
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other_)
|
15
|
+
other = other_
|
16
|
+
|
17
|
+
# @type var other: Steep__Types__Union
|
18
|
+
other.is_a?(Union) && other.types.sort_by {|x| x.__id__ } == types.sort_by {|x| x.__id__ }
|
19
|
+
end
|
20
|
+
|
21
|
+
def hash
|
22
|
+
types.hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def eql?(other)
|
26
|
+
self == other
|
27
|
+
end
|
28
|
+
|
29
|
+
def closed?
|
30
|
+
types.all? {|x| x.closed? }
|
31
|
+
end
|
32
|
+
|
33
|
+
def substitute(klass:, instance:, params:)
|
34
|
+
self.class.new(types: types.map {|t| t.substitute(klass: klass, instance: instance, params: params) })
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
types.join(" | ")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Steep
|
2
|
+
module Types
|
3
|
+
class Var
|
4
|
+
# @implements Steep__Types__Var
|
5
|
+
|
6
|
+
# @dynamic name
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
def initialize(name:)
|
10
|
+
@name = name
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
other.is_a?(Var) && other.name == name
|
15
|
+
end
|
16
|
+
|
17
|
+
def hash
|
18
|
+
name.hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def eql?(other)
|
22
|
+
self == other
|
23
|
+
end
|
24
|
+
|
25
|
+
def closed?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def substitute(klass:, instance:, params:)
|
30
|
+
params[name] || self
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"'#{name}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/steep/typing.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module Steep
|
2
|
+
class Typing
|
3
|
+
attr_reader :errors
|
4
|
+
attr_reader :typing
|
5
|
+
attr_reader :nodes
|
6
|
+
attr_reader :var_typing
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@errors = []
|
10
|
+
@nodes = {}
|
11
|
+
@var_typing = {}
|
12
|
+
@typing = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_error(error)
|
16
|
+
errors << error
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_typing(node, type)
|
20
|
+
typing[node.__id__] = type
|
21
|
+
nodes[node.__id__] = node
|
22
|
+
|
23
|
+
type
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_var_type(variable, type)
|
27
|
+
if var_typing.key?(variable)
|
28
|
+
unless var_typing[variable] == type
|
29
|
+
raise "Unexpected variable typing: existing=#{var_typing[variable]}, new=#{type}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
var_typing[variable] = type
|
34
|
+
end
|
35
|
+
|
36
|
+
def type_of(node:)
|
37
|
+
typing[node.__id__] or raise "Unknown node for typing: #{node.inspect}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def type_of_variable(name: nil, label: nil)
|
41
|
+
var_typing.each do |var, type|
|
42
|
+
if (!name || var.name == name) && (!label || var.label == label)
|
43
|
+
return type
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
raise "Unknown variable for typing: #{name}@#{label}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def dump(io)
|
51
|
+
io.puts "Typing: "
|
52
|
+
nodes.each_value do |node|
|
53
|
+
io.puts " #{Typing.summary(node)} => #{type_of(node: node).inspect}"
|
54
|
+
end
|
55
|
+
|
56
|
+
io.puts "Errors: "
|
57
|
+
errors.each do |error|
|
58
|
+
io.puts " #{Typing.summary(error.node)} => #{error.inspect}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.summary(node)
|
63
|
+
src = node.loc.expression.source.split(/\n/).first
|
64
|
+
line = node.loc.first_line
|
65
|
+
col = node.loc.column
|
66
|
+
|
67
|
+
"#{line}:#{col}:#{src}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# Annotations
|
2
|
+
|
3
|
+
## Core Annotations
|
4
|
+
|
5
|
+
### Variable type
|
6
|
+
|
7
|
+
Variable type annotation tells type of local variable.
|
8
|
+
|
9
|
+
#### Example
|
10
|
+
|
11
|
+
```
|
12
|
+
# @type var x: String
|
13
|
+
# @type var klass: Class
|
14
|
+
```
|
15
|
+
|
16
|
+
#### Syntax
|
17
|
+
|
18
|
+
* `@type` `var` *x* `:` *type*
|
19
|
+
|
20
|
+
### Self type
|
21
|
+
|
22
|
+
Self type annotation tells type of `self`.
|
23
|
+
|
24
|
+
#### Example
|
25
|
+
|
26
|
+
```
|
27
|
+
# @type self: Object
|
28
|
+
```
|
29
|
+
|
30
|
+
#### Syntax
|
31
|
+
|
32
|
+
* `@type` `self` `:` *type*
|
33
|
+
|
34
|
+
### Instance variable type
|
35
|
+
|
36
|
+
Instance variable type annotation tells type of instance variable.
|
37
|
+
This annotation applies to instance variable of current context.
|
38
|
+
If it's written in `module` declaration, it applies to instance variable of the module, not its instance.
|
39
|
+
|
40
|
+
#### Example
|
41
|
+
|
42
|
+
```
|
43
|
+
# @type ivar @owner: Person
|
44
|
+
```
|
45
|
+
|
46
|
+
#### Syntax
|
47
|
+
|
48
|
+
* `@type` `ivar` *ivar* `:` *type*
|
49
|
+
|
50
|
+
### Global variable type
|
51
|
+
|
52
|
+
Global variable type annotation tells type of global variable.
|
53
|
+
|
54
|
+
#### Example
|
55
|
+
|
56
|
+
```
|
57
|
+
# @type gvar $LOAD_PATH: Array<String>
|
58
|
+
```
|
59
|
+
|
60
|
+
#### Syntax
|
61
|
+
|
62
|
+
* `@type` `gvar` *gvar* `:` *type*
|
63
|
+
|
64
|
+
### Constant type
|
65
|
+
|
66
|
+
Constant type annotation tells type of constant.
|
67
|
+
Note that constant resolution is done syntactically.
|
68
|
+
Annotation on `File::Append` does not apply to `::File::Append`.
|
69
|
+
|
70
|
+
#### Example
|
71
|
+
|
72
|
+
```
|
73
|
+
# @type const File::Append : Integer
|
74
|
+
```
|
75
|
+
|
76
|
+
#### Syntax
|
77
|
+
|
78
|
+
* `@type` `const` *const* `:` *type*
|
79
|
+
|
80
|
+
### Method type annotation
|
81
|
+
|
82
|
+
Method type annotation tells type of method being implemented in current scope.
|
83
|
+
|
84
|
+
This annotation is used to tell types of method parameters and its body.
|
85
|
+
Union method type cannot be written.
|
86
|
+
|
87
|
+
#### Example
|
88
|
+
|
89
|
+
```
|
90
|
+
# @type method foo: (String) -> any
|
91
|
+
```
|
92
|
+
|
93
|
+
#### Syntax
|
94
|
+
|
95
|
+
* `@type` `method` *method* `:` *single method type*
|
96
|
+
|
97
|
+
## Module Annotations
|
98
|
+
|
99
|
+
Module annotations is about defining modules and classes in Ruby.
|
100
|
+
This kind of annotations should be written in module context.
|
101
|
+
|
102
|
+
### Instance type annotation
|
103
|
+
|
104
|
+
Instance type annotation tells type of instance of class or module which is being defined.
|
105
|
+
|
106
|
+
#### Example
|
107
|
+
|
108
|
+
```
|
109
|
+
# @type instance: Foo
|
110
|
+
```
|
111
|
+
|
112
|
+
#### Syntax
|
113
|
+
|
114
|
+
* `@type` `instance` `:` *type*
|
115
|
+
|
116
|
+
### Module type annotation
|
117
|
+
|
118
|
+
Module type annotation tells type of module of class or module which is being defined.
|
119
|
+
|
120
|
+
#### Example
|
121
|
+
|
122
|
+
```
|
123
|
+
# @type module: Foo.class
|
124
|
+
```
|
125
|
+
|
126
|
+
#### Syntax
|
127
|
+
|
128
|
+
* `@type` `module` `:` *type*
|
129
|
+
|
130
|
+
### Instance/module ivar type annotation
|
131
|
+
|
132
|
+
This annotation tells instance variable of instance.
|
133
|
+
|
134
|
+
#### Example
|
135
|
+
|
136
|
+
```
|
137
|
+
# @type instance ivar @x: String
|
138
|
+
# @type module ivar @klass: String.class
|
139
|
+
```
|
140
|
+
|
141
|
+
#### Syntax
|
142
|
+
|
143
|
+
* `@type` `instance` `ivar` *ivar* `:` *type*
|
144
|
+
* `@type` `module` `ivar` *ivar* `:` *type*
|