rbs 0.14.0 → 0.18.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +8 -4
- data/CHANGELOG.md +35 -0
- data/Gemfile +1 -0
- data/README.md +1 -1
- data/Rakefile +19 -1
- data/Steepfile +0 -1
- data/bin/test_runner.rb +15 -1
- data/{stdlib/builtin → core}/array.rbs +124 -120
- data/{stdlib/builtin → core}/basic_object.rbs +0 -0
- data/{stdlib/builtin → core}/binding.rbs +0 -0
- data/core/builtin.rbs +70 -0
- data/{stdlib/builtin → core}/class.rbs +0 -0
- data/{stdlib/builtin → core}/comparable.rbs +0 -0
- data/{stdlib/builtin → core}/complex.rbs +0 -0
- data/{stdlib/builtin → core}/constants.rbs +0 -0
- data/{stdlib/builtin → core}/data.rbs +0 -0
- data/{stdlib/builtin → core}/deprecated.rbs +0 -0
- data/{stdlib/builtin → core}/dir.rbs +1 -1
- data/{stdlib/builtin → core}/encoding.rbs +0 -0
- data/{stdlib/builtin → core}/enumerable.rbs +67 -60
- data/{stdlib/builtin → core}/enumerator.rbs +5 -5
- data/{stdlib/builtin → core}/errno.rbs +0 -0
- data/{stdlib/builtin → core}/errors.rbs +1 -1
- data/{stdlib/builtin → core}/exception.rbs +0 -0
- data/{stdlib/builtin → core}/false_class.rbs +0 -0
- data/{stdlib/builtin → core}/fiber.rbs +0 -0
- data/{stdlib/builtin → core}/fiber_error.rbs +0 -0
- data/{stdlib/builtin → core}/file.rbs +0 -0
- data/{stdlib/builtin → core}/file_test.rbs +0 -0
- data/{stdlib/builtin → core}/float.rbs +0 -0
- data/{stdlib/builtin → core}/gc.rbs +2 -2
- data/{stdlib/builtin → core}/hash.rbs +15 -15
- data/{stdlib/builtin → core}/integer.rbs +0 -0
- data/{stdlib/builtin → core}/io.rbs +6 -6
- data/{stdlib/builtin → core}/kernel.rbs +1 -85
- data/{stdlib/builtin → core}/marshal.rbs +0 -0
- data/{stdlib/builtin → core}/match_data.rbs +0 -0
- data/{stdlib/builtin → core}/math.rbs +0 -0
- data/{stdlib/builtin → core}/method.rbs +0 -0
- data/{stdlib/builtin → core}/module.rbs +13 -13
- data/{stdlib/builtin → core}/nil_class.rbs +0 -0
- data/{stdlib/builtin → core}/numeric.rbs +0 -0
- data/{stdlib/builtin → core}/object.rbs +1 -1
- data/core/object_space.rbs +98 -0
- data/{stdlib/builtin → core}/proc.rbs +0 -0
- data/{stdlib/builtin → core}/process.rbs +0 -0
- data/{stdlib/builtin → core}/random.rbs +1 -1
- data/{stdlib/builtin → core}/range.rbs +3 -3
- data/{stdlib/builtin → core}/rational.rbs +0 -0
- data/{stdlib/builtin → core}/rb_config.rbs +0 -0
- data/{stdlib/builtin → core}/regexp.rbs +0 -0
- data/{stdlib/builtin → core}/ruby_vm.rbs +0 -0
- data/{stdlib/builtin → core}/signal.rbs +0 -0
- data/{stdlib/builtin → core}/string.rbs +6 -6
- data/{stdlib/builtin → core}/string_io.rbs +7 -7
- data/{stdlib/builtin → core}/struct.rbs +2 -2
- data/{stdlib/builtin → core}/symbol.rbs +1 -1
- data/{stdlib/builtin → core}/thread.rbs +4 -4
- data/{stdlib/builtin → core}/thread_group.rbs +0 -0
- data/{stdlib/builtin → core}/time.rbs +0 -0
- data/{stdlib/builtin → core}/trace_point.rbs +0 -0
- data/{stdlib/builtin → core}/true_class.rbs +1 -1
- data/{stdlib/builtin → core}/unbound_method.rbs +0 -0
- data/{stdlib/builtin → core}/warning.rbs +0 -0
- data/docs/repo.md +125 -0
- data/docs/syntax.md +50 -6
- data/lib/rbs.rb +1 -0
- data/lib/rbs/cli.rb +105 -103
- data/lib/rbs/definition.rb +9 -4
- data/lib/rbs/definition_builder.rb +50 -17
- data/lib/rbs/environment_loader.rb +79 -105
- data/lib/rbs/environment_walker.rb +70 -35
- data/lib/rbs/parser.rb +404 -397
- data/lib/rbs/parser.y +18 -9
- data/lib/rbs/prototype/rb.rb +186 -25
- data/lib/rbs/prototype/runtime.rb +18 -7
- data/lib/rbs/repository.rb +121 -0
- data/lib/rbs/test/hook.rb +27 -15
- data/lib/rbs/test/setup.rb +5 -3
- data/lib/rbs/test/tester.rb +4 -1
- data/lib/rbs/test/type_check.rb +16 -5
- data/lib/rbs/type_name.rb +2 -1
- data/lib/rbs/vendorer.rb +38 -16
- data/lib/rbs/version.rb +1 -1
- data/sig/cli.rbs +58 -0
- data/sig/constant_table.rbs +1 -1
- data/sig/declarations.rbs +1 -1
- data/sig/definition.rbs +9 -4
- data/sig/definition_builder.rbs +4 -1
- data/sig/environment_loader.rbs +92 -46
- data/sig/members.rbs +2 -2
- data/sig/method_types.rbs +1 -1
- data/sig/namespace.rbs +1 -1
- data/sig/polyfill.rbs +42 -0
- data/sig/rbs.rbs +8 -0
- data/sig/repository.rbs +79 -0
- data/sig/vendorer.rbs +44 -0
- data/stdlib/abbrev/{abbrev.rbs → 0/abbrev.rbs} +0 -0
- data/stdlib/base64/{base64.rbs → 0/base64.rbs} +1 -1
- data/stdlib/benchmark/{benchmark.rbs → 0/benchmark.rbs} +0 -0
- data/stdlib/{bigdecimal/math → bigdecimal-math/0}/big_math.rbs +0 -0
- data/stdlib/bigdecimal/{big_decimal.rbs → 0/big_decimal.rbs} +0 -0
- data/stdlib/coverage/{coverage.rbs → 0/coverage.rbs} +2 -2
- data/stdlib/csv/{csv.rbs → 0/csv.rbs} +4 -4
- data/stdlib/date/{date.rbs → 0/date.rbs} +2 -2
- data/stdlib/date/{date_time.rbs → 0/date_time.rbs} +1 -1
- data/stdlib/dbm/0/dbm.rbs +277 -0
- data/stdlib/erb/{erb.rbs → 0/erb.rbs} +0 -0
- data/stdlib/fiber/{fiber.rbs → 0/fiber.rbs} +0 -0
- data/stdlib/find/{find.rbs → 0/find.rbs} +2 -2
- data/stdlib/forwardable/{forwardable.rbs → 0/forwardable.rbs} +0 -0
- data/stdlib/ipaddr/{ipaddr.rbs → 0/ipaddr.rbs} +0 -0
- data/stdlib/json/{json.rbs → 0/json.rbs} +0 -0
- data/stdlib/logger/{formatter.rbs → 0/formatter.rbs} +0 -0
- data/stdlib/logger/{log_device.rbs → 0/log_device.rbs} +1 -1
- data/stdlib/logger/{logger.rbs → 0/logger.rbs} +1 -1
- data/stdlib/logger/{period.rbs → 0/period.rbs} +0 -0
- data/stdlib/logger/{severity.rbs → 0/severity.rbs} +0 -0
- data/stdlib/mutex_m/{mutex_m.rbs → 0/mutex_m.rbs} +0 -0
- data/stdlib/pathname/{pathname.rbs → 0/pathname.rbs} +39 -39
- data/stdlib/prime/{integer-extension.rbs → 0/integer-extension.rbs} +0 -0
- data/stdlib/prime/{prime.rbs → 0/prime.rbs} +1 -1
- data/stdlib/pstore/0/pstore.rbs +287 -0
- data/stdlib/pty/{pty.rbs → 0/pty.rbs} +1 -1
- data/stdlib/securerandom/{securerandom.rbs → 0/securerandom.rbs} +0 -0
- data/stdlib/set/{set.rbs → 0/set.rbs} +10 -10
- data/stdlib/singleton/0/singleton.rbs +111 -0
- data/stdlib/tmpdir/{tmpdir.rbs → 0/tmpdir.rbs} +0 -0
- data/stdlib/tsort/0/cyclic.rbs +4 -0
- data/stdlib/tsort/0/interfaces.rbs +19 -0
- data/stdlib/tsort/0/tsort.rbs +363 -0
- data/stdlib/uri/{file.rbs → 0/file.rbs} +0 -0
- data/stdlib/uri/{generic.rbs → 0/generic.rbs} +1 -1
- data/stdlib/uri/{http.rbs → 0/http.rbs} +0 -0
- data/stdlib/uri/{https.rbs → 0/https.rbs} +0 -0
- data/stdlib/uri/{ldap.rbs → 0/ldap.rbs} +0 -0
- data/stdlib/uri/{ldaps.rbs → 0/ldaps.rbs} +0 -0
- data/stdlib/yaml/0/dbm.rbs +221 -0
- data/stdlib/yaml/0/store.rbs +53 -0
- data/stdlib/zlib/{zlib.rbs → 0/zlib.rbs} +0 -0
- data/steep/Gemfile.lock +9 -9
- metadata +108 -94
- data/stdlib/builtin/builtin.rbs +0 -42
@@ -63,8 +63,8 @@ class StringIO
|
|
63
63
|
|
64
64
|
# See IO#each.
|
65
65
|
#
|
66
|
-
def each: (?String sep, ?Integer limit, ?chomp:
|
67
|
-
| (?String sep, ?Integer limit, ?chomp:
|
66
|
+
def each: (?String sep, ?Integer limit, ?chomp: boolish) { (String) -> untyped } -> self
|
67
|
+
| (?String sep, ?Integer limit, ?chomp: boolish) -> ::Enumerator[String, self]
|
68
68
|
|
69
69
|
# See IO#each_byte.
|
70
70
|
#
|
@@ -112,7 +112,7 @@ class StringIO
|
|
112
112
|
|
113
113
|
# See IO#gets.
|
114
114
|
#
|
115
|
-
def gets: (?String sep, ?Integer limit, ?chomp:
|
115
|
+
def gets: (?String sep, ?Integer limit, ?chomp: boolish) -> String?
|
116
116
|
|
117
117
|
# Returns the Encoding of the internal string if conversion is specified.
|
118
118
|
# Otherwise returns `nil`.
|
@@ -177,7 +177,7 @@ class StringIO
|
|
177
177
|
|
178
178
|
# See IO#readlines.
|
179
179
|
#
|
180
|
-
def readlines: (?String sep, ?Integer limit, ?chomp:
|
180
|
+
def readlines: (?String sep, ?Integer limit, ?chomp: boolish) -> ::Array[String]
|
181
181
|
|
182
182
|
def readpartial: (Integer maxlen) -> String
|
183
183
|
| (Integer maxlen, ?String outbuf) -> String
|
@@ -222,7 +222,7 @@ class StringIO
|
|
222
222
|
|
223
223
|
# Returns the argument unchanged. Just for compatibility to IO.
|
224
224
|
#
|
225
|
-
def sync=: (
|
225
|
+
def sync=: (boolish) -> bool
|
226
226
|
|
227
227
|
def sysread: (Integer maxlen, String outbuf) -> String
|
228
228
|
|
@@ -269,8 +269,8 @@ class StringIO
|
|
269
269
|
|
270
270
|
# See IO#each.
|
271
271
|
#
|
272
|
-
def each_line: (?String sep, ?Integer limit, ?chomp:
|
273
|
-
| (?String sep, ?Integer limit, ?chomp:
|
272
|
+
def each_line: (?String sep, ?Integer limit, ?chomp: boolish) { (String) -> untyped } -> self
|
273
|
+
| (?String sep, ?Integer limit, ?chomp: boolish) -> ::Enumerator[String, self]
|
274
274
|
|
275
275
|
# Returns true if the stream is at the end of the data (underlying string). The
|
276
276
|
# stream must be opened for reading or an `IOError` will be raised.
|
@@ -27,11 +27,11 @@
|
|
27
27
|
# struct member which is either a quoted string ( `"name"` ) or a
|
28
28
|
# [Symbol](https://ruby-doc.org/core-2.6.3/Symbol.html) ( `:name` ).
|
29
29
|
class Struct[Elem] < Object
|
30
|
-
include Enumerable[Elem
|
30
|
+
include Enumerable[Elem]
|
31
31
|
|
32
32
|
type attribute_name = Symbol | String
|
33
33
|
|
34
|
-
def initialize: (attribute_name, *attribute_name, ?keyword_init:
|
34
|
+
def initialize: (attribute_name, *attribute_name, ?keyword_init: boolish) ?{ () -> void } -> void
|
35
35
|
|
36
36
|
def each: () { (Elem) -> untyped } -> untyped
|
37
37
|
|
@@ -109,7 +109,7 @@ class Symbol
|
|
109
109
|
# :foo.casecmp?(2) #=> nil
|
110
110
|
# "\u{e4 f6 fc}".encode("ISO-8859-1").to_sym.casecmp?(:"\u{c4 d6 dc}") #=> nil
|
111
111
|
#
|
112
|
-
def casecmp?: (untyped other) -> bool
|
112
|
+
def casecmp?: (untyped other) -> bool?
|
113
113
|
|
114
114
|
# Same as `sym.to_s.downcase.intern`.
|
115
115
|
#
|
@@ -240,7 +240,7 @@ class Thread < Object
|
|
240
240
|
# There is also a class level method to set this for all threads, see
|
241
241
|
# [::abort\_on\_exception=](Thread.downloaded.ruby_doc#method-c-abort_on_exception-3D)
|
242
242
|
# .
|
243
|
-
def abort_on_exception=: (
|
243
|
+
def abort_on_exception=: (boolish abort_on_exception) -> untyped
|
244
244
|
|
245
245
|
# Adds *proc* as a handler for tracing.
|
246
246
|
#
|
@@ -424,7 +424,7 @@ class Thread < Object
|
|
424
424
|
# There is also a class level method to set this for all new threads, see
|
425
425
|
# [::report\_on\_exception=](Thread.downloaded.ruby_doc#method-c-report_on_exception-3D)
|
426
426
|
# .
|
427
|
-
def report_on_exception=: (
|
427
|
+
def report_on_exception=: (boolish report_on_exception) -> untyped
|
428
428
|
|
429
429
|
# Wakes up `thr`, making it eligible for scheduling.
|
430
430
|
#
|
@@ -1049,7 +1049,7 @@ class Thread::Queue < Object
|
|
1049
1049
|
#
|
1050
1050
|
# Also aliased as: [deq](Queue.downloaded.ruby_doc#method-i-deq),
|
1051
1051
|
# [shift](Queue.downloaded.ruby_doc#method-i-shift)
|
1052
|
-
def pop: (?
|
1052
|
+
def pop: (?boolish non_block) -> untyped
|
1053
1053
|
|
1054
1054
|
# Pushes the given `object` to the queue.
|
1055
1055
|
#
|
@@ -1096,7 +1096,7 @@ class Thread::SizedQueue < Thread::Queue
|
|
1096
1096
|
#
|
1097
1097
|
# Also aliased as: [enq](SizedQueue.downloaded.ruby_doc#method-i-enq),
|
1098
1098
|
# [\<\<](SizedQueue.downloaded.ruby_doc#method-i-3C-3C)
|
1099
|
-
def push: (untyped obj, ?
|
1099
|
+
def push: (untyped obj, ?boolish non_block) -> void
|
1100
1100
|
end
|
1101
1101
|
|
1102
1102
|
ConditionVariable: singleton(Thread::ConditionVariable)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/docs/repo.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Third-party RBS Repository
|
2
|
+
|
3
|
+
This is the spec of the directory structure for RBS files of gems without RBS files. It allows distributing RBS type definitions of gems separately from the `.gemspec` files so that the Ruby developers can type check their Ruby programs even if the dependent gems don't ship with their type definitions.
|
4
|
+
|
5
|
+
The spec includes:
|
6
|
+
|
7
|
+
* The directory structure, and
|
8
|
+
* The RBS file lookup rules given _repository root_, gem name, and version.
|
9
|
+
|
10
|
+
## Motivating Example
|
11
|
+
|
12
|
+
Assume there is a rubygem called `bug-free-doodle` and our application depends on the library. We are trying to type check our application and we need RBS files of `bug-free-doodle`. The problem is that the `bug-free-doodle` gem doesn't ship with RBS files. The type checkers cannot resolve the type of constant `Bug::Free::Doodle` and its methods.
|
13
|
+
|
14
|
+
One workaround is to add type definitions of the library in the application signatures.
|
15
|
+
|
16
|
+
```
|
17
|
+
# sig/polyfill/bug-free-doodle.rbs
|
18
|
+
|
19
|
+
module Bug
|
20
|
+
module Free
|
21
|
+
class Doodle
|
22
|
+
attr_reader name: Symbol
|
23
|
+
attr_reader strokes: Array[Stroke]
|
24
|
+
|
25
|
+
def initialize: (name: Symbol) -> void
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
You may want to distribute the RBS file to anyone who needs it. Which version do we support? Testing it? How to load the RBS files? This is the spec you need!
|
32
|
+
|
33
|
+
### Third-party RBS repository
|
34
|
+
|
35
|
+
Make a directory (or you may want to `git init`) to put your _third party RBSs_.
|
36
|
+
|
37
|
+
```sh
|
38
|
+
$ make my-rbs # Or you may want a git repository: git init my-rbs
|
39
|
+
$ cd my-rbs
|
40
|
+
$ mkdir gems
|
41
|
+
```
|
42
|
+
|
43
|
+
We call the `my-rbs/gems` directory _repository root_. Note that it is different from the root of the git repository. The RBS repository root is the directory that contains directories of gem names.
|
44
|
+
|
45
|
+
Make a directory for the gem and the version.
|
46
|
+
|
47
|
+
```sh
|
48
|
+
$ mkdir gems/bug-free-doodle
|
49
|
+
$ mkdir gems/bug-free-doodle/1.2.3
|
50
|
+
```
|
51
|
+
|
52
|
+
And copy the RBS file in it.
|
53
|
+
|
54
|
+
```sh
|
55
|
+
$ cp your-app/sig/polyfill/bug-free-doodle.rbs gems/bug-free-doodle/1.2.3
|
56
|
+
```
|
57
|
+
|
58
|
+
### Reading Third-party RBS
|
59
|
+
|
60
|
+
`rbs` command accepts `--repo` option which points to a _repository root_. You can specify `-r` option to let the command know which gems you want to load.
|
61
|
+
|
62
|
+
In this case, the _repository root_ is `./gems` and we are trying to load `bug-free-doodle` gem.
|
63
|
+
|
64
|
+
```sh
|
65
|
+
$ rbs --repo=gems -r bug-free-doodle paths
|
66
|
+
```
|
67
|
+
|
68
|
+
The `-r` option also accepts gem name with version.
|
69
|
+
|
70
|
+
```sh
|
71
|
+
$ rbs --repo=gems -r bug-free-doodle:1.2.3 paths
|
72
|
+
```
|
73
|
+
|
74
|
+
Note that the version resolution is not compatible with semantic versioning. It is optimistic. It resolves to some version unless no version for the gem is available.
|
75
|
+
|
76
|
+
## Directory Structure
|
77
|
+
|
78
|
+
There are directories for each gem under _repository root_. We also have directories for each version of each gem.
|
79
|
+
|
80
|
+
- $REPO_ROOT/bug-free-doodle/0.2.0
|
81
|
+
- $REPO_ROOT/bug-free-doodle/1.0
|
82
|
+
- $REPO_ROOT/bug-free-doodle/1.2.3
|
83
|
+
- $REPO_ROOT/bug-free-doodle/2.0
|
84
|
+
|
85
|
+
Note that we assume that we have git repositories for each RBS repository, and we have a directory at the root of the git repository for _repository root_.
|
86
|
+
|
87
|
+
So the git repository structure would be something like the following:
|
88
|
+
|
89
|
+
- /Gemfile
|
90
|
+
- /Gemfile.lock
|
91
|
+
- /README.md
|
92
|
+
- /LICENSE
|
93
|
+
- /gems/bug-free-doodle/1.2.3/bug-free-doodle.rbs
|
94
|
+
|
95
|
+
You should have `Gemfile` and `Gemfile.lock` to manage dependencies, `README.md` and `LICENSE` to documentation, and `gems` directory as _repository root_.
|
96
|
+
|
97
|
+
(We call _repository root_ `gems` in this doc, but the name can be anything you like.)
|
98
|
+
|
99
|
+
## Version Resolution
|
100
|
+
|
101
|
+
The version resolution in RBS is optimistic. We don't block loading RBS files for _incompatible_ version in terms of semantic versioning.
|
102
|
+
|
103
|
+
It tries to resolve version _n_ as follows:
|
104
|
+
|
105
|
+
1. It resolves to _m_ such that _m_ is the latest version available and _m_ <= _n_ holds.
|
106
|
+
2. It resolves to the oldest version when rule 1 cannot find version _m_.
|
107
|
+
|
108
|
+
If two versions, `0.4.0`, `1.0.0` are available for a gem:
|
109
|
+
|
110
|
+
| Requested version | Resolved version |
|
111
|
+
|-------------------|------------------|
|
112
|
+
| `0.3.0` | `0.4.0` (Rule 2) |
|
113
|
+
| `0.4.0` | `0.4.0` |
|
114
|
+
| `0.5.0` | `0.4.0` |
|
115
|
+
| `1.0.0` | `1.0.0` |
|
116
|
+
| `2.0.0` | `1.0.0` |
|
117
|
+
|
118
|
+
This is not compatible with the concept of semantic versioning. We don't want to block users to load RBS even for incompatible versions of gems.
|
119
|
+
|
120
|
+
We believe this makes more sense because:
|
121
|
+
|
122
|
+
* Using (potentially) incompatible type definitions are better than no type definition.
|
123
|
+
* Users can stop loading RBS if incompatibility causes an issue and falling back to hand-written polyfills.
|
124
|
+
|
125
|
+
|
data/docs/syntax.md
CHANGED
@@ -179,7 +179,7 @@ Proc type denotes type of procedures, `Proc` instances.
|
|
179
179
|
|
180
180
|
`instance` denotes the type of instance of the class. `class` is the singleton of the class.
|
181
181
|
|
182
|
-
`bool` is an
|
182
|
+
`bool` is an alias of `true | false`.
|
183
183
|
|
184
184
|
`untyped` is for _a type without type checking_. It is `?` in gradual typing, _dynamic_ in some languages like C#, and _any_ in TypeScript. It is both subtype _and_ supertype of all of the types. (The type was `any` but renamed to `untyped`.)
|
185
185
|
|
@@ -193,15 +193,34 @@ Proc type denotes type of procedures, `Proc` instances.
|
|
193
193
|
|
194
194
|
We recommend using `nil`.
|
195
195
|
|
196
|
-
#### `bool` or `
|
196
|
+
#### `bool` or `boolish`
|
197
197
|
|
198
|
-
We
|
198
|
+
We have a builtin type alias called `boolish`.
|
199
|
+
It is an alias of `top` type, and you can use `boolish` if we want to allow any object of any type.
|
199
200
|
|
200
|
-
|
201
|
+
We can see an example at the definition of `Enumerable#find`:
|
202
|
+
|
203
|
+
```
|
204
|
+
module Enumerable[Elem, Return]
|
205
|
+
def find: () { (Elem) -> boolish } -> Elem?
|
206
|
+
...
|
207
|
+
end
|
208
|
+
```
|
209
|
+
|
210
|
+
We want to write something like:
|
211
|
+
|
212
|
+
```
|
213
|
+
array.find {|x| x && x.some_test? } # The block will return (bool | nil)
|
214
|
+
```
|
215
|
+
|
216
|
+
We recommend using `boolish` for method arguments and block return values, if you only use the values for conditions.
|
217
|
+
You can write `bool` if you strictly want `true | false`.
|
218
|
+
|
219
|
+
#### `void`, `boolish`, or `top`?
|
201
220
|
|
202
221
|
They are all equivalent for the type system; they are all _top type_.
|
203
222
|
|
204
|
-
`void` tells developers a hint that _the value should not be used_. `
|
223
|
+
`void` tells developers a hint that _the value should not be used_. `boolish` implies the value is used as a truth value. `top` is anything else.
|
205
224
|
|
206
225
|
## Method Types
|
207
226
|
|
@@ -433,8 +452,10 @@ _global-name_ ::= /$[a-zA-Z]\w+/ | ...
|
|
433
452
|
_module-type-parameters_ ::= # Empty
|
434
453
|
| `[` _module-type-parameter_ `,` ... `]`
|
435
454
|
|
436
|
-
_module-type-parameter_ ::= _variance_ _type-variable_
|
455
|
+
_module-type-parameter_ ::= _check_ _variance_ _type-variable_
|
437
456
|
_variance_ ::= `out` | `in`
|
457
|
+
_check_ ::= # Empty
|
458
|
+
| `unchecked`
|
438
459
|
```
|
439
460
|
|
440
461
|
### Class declaration
|
@@ -448,6 +469,29 @@ class Ref[A] < Object
|
|
448
469
|
end
|
449
470
|
```
|
450
471
|
|
472
|
+
For classes with type parameters, you may specify if they are "invariant" (default), "covariant" (`out`) or "contravariant" (`in`). See [this definition of covariance and contravariance](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)).
|
473
|
+
|
474
|
+
For example, an `Array` of `String` can almost be considered to be an `Array` of `Object`, but not the reverse, so we can think of:
|
475
|
+
|
476
|
+
```
|
477
|
+
class Array[out T]
|
478
|
+
# ...
|
479
|
+
end
|
480
|
+
```
|
481
|
+
|
482
|
+
There's a limitation with this is for mutable objects (like arrays): a mutation could invalidate this.
|
483
|
+
If an array of String is passed to a method as an array of Objects, and that method adds an Integer to the array, the promise is broken.
|
484
|
+
|
485
|
+
In those cases, one must use the `unchecked` keyword:
|
486
|
+
|
487
|
+
```
|
488
|
+
class Array[unchecked out T]
|
489
|
+
# ...
|
490
|
+
end
|
491
|
+
```
|
492
|
+
|
493
|
+
This is how `Array` is actually defined in RBS.
|
494
|
+
|
451
495
|
### Module declaration
|
452
496
|
|
453
497
|
Module declaration takes optional _self type_ parameter, which defines a constraint about a class when the module is mixed.
|
data/lib/rbs.rb
CHANGED
data/lib/rbs/cli.rb
CHANGED
@@ -5,29 +5,59 @@ require "shellwords"
|
|
5
5
|
module RBS
|
6
6
|
class CLI
|
7
7
|
class LibraryOptions
|
8
|
+
attr_accessor :core_root
|
9
|
+
attr_reader :repos
|
8
10
|
attr_reader :libs
|
9
11
|
attr_reader :dirs
|
10
|
-
attr_accessor :no_stdlib
|
11
12
|
|
12
13
|
def initialize()
|
14
|
+
@core_root = EnvironmentLoader::DEFAULT_CORE_ROOT
|
15
|
+
@repos = []
|
16
|
+
|
13
17
|
@libs = []
|
14
18
|
@dirs = []
|
15
|
-
@no_stdlib = false
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
+
def loader
|
22
|
+
repository = Repository.new(no_stdlib: core_root.nil?)
|
23
|
+
repos.each do |repo|
|
24
|
+
repository.add(Pathname(repo))
|
21
25
|
end
|
22
26
|
|
27
|
+
loader = EnvironmentLoader.new(core_root: core_root, repository: repository)
|
28
|
+
|
23
29
|
dirs.each do |dir|
|
24
30
|
loader.add(path: Pathname(dir))
|
25
31
|
end
|
26
32
|
|
27
|
-
|
33
|
+
libs.each do |lib|
|
34
|
+
name, version = lib.split(/:/, 2)
|
35
|
+
next unless name
|
36
|
+
loader.add(library: name, version: version)
|
37
|
+
end
|
28
38
|
|
29
39
|
loader
|
30
40
|
end
|
41
|
+
|
42
|
+
def setup_library_options(opts)
|
43
|
+
opts.on("-r LIBRARY", "Load RBS files of the library") do |lib|
|
44
|
+
libs << lib
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("-I DIR", "Load RBS files from the directory") do |dir|
|
48
|
+
dirs << dir
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on("--no-stdlib", "Skip loading standard library signatures") do
|
52
|
+
self.core_root = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on("--repo DIR", "Add RBS repository") do |dir|
|
56
|
+
repos << dir
|
57
|
+
end
|
58
|
+
|
59
|
+
opts
|
60
|
+
end
|
31
61
|
end
|
32
62
|
|
33
63
|
attr_reader :stdout
|
@@ -40,29 +70,14 @@ module RBS
|
|
40
70
|
|
41
71
|
COMMANDS = [:ast, :list, :ancestors, :methods, :method, :validate, :constant, :paths, :prototype, :vendor, :parse, :test]
|
42
72
|
|
43
|
-
def library_parse(opts, options:)
|
44
|
-
opts.on("-r LIBRARY", "Load RBS files of the library") do |lib|
|
45
|
-
options.libs << lib
|
46
|
-
end
|
47
|
-
|
48
|
-
opts.on("-I DIR", "Load RBS files from the directory") do |dir|
|
49
|
-
options.dirs << dir
|
50
|
-
end
|
51
|
-
|
52
|
-
opts.on("--no-stdlib", "Skip loading standard library signatures") do
|
53
|
-
options.no_stdlib = true
|
54
|
-
end
|
55
|
-
|
56
|
-
opts
|
57
|
-
end
|
58
|
-
|
59
73
|
def parse_logging_options(opts)
|
60
74
|
opts.on("--log-level LEVEL", "Specify log level (defaults to `warn`)") do |level|
|
61
75
|
RBS.logger_level = level
|
62
76
|
end
|
63
77
|
|
64
78
|
opts.on("--log-output OUTPUT", "Specify the file to output log (defaults to stderr)") do |output|
|
65
|
-
|
79
|
+
io = File.open(output, "a") or raise
|
80
|
+
RBS.logger_output = io
|
66
81
|
end
|
67
82
|
|
68
83
|
opts
|
@@ -83,7 +98,7 @@ module RBS
|
|
83
98
|
|
84
99
|
Options:
|
85
100
|
USAGE
|
86
|
-
|
101
|
+
options.setup_library_options(opts)
|
87
102
|
parse_logging_options(opts)
|
88
103
|
opts.version = RBS::VERSION
|
89
104
|
|
@@ -129,13 +144,14 @@ EOB
|
|
129
144
|
end
|
130
145
|
end
|
131
146
|
|
132
|
-
loader =
|
133
|
-
options.setup(loader)
|
147
|
+
loader = options.loader()
|
134
148
|
|
135
149
|
env = Environment.from_loader(loader).resolve_type_names
|
136
150
|
|
137
151
|
decls = env.declarations.select do |decl|
|
138
|
-
|
152
|
+
loc = decl.location or raise
|
153
|
+
# @type var name: String
|
154
|
+
name = loc.buffer.name
|
139
155
|
|
140
156
|
patterns.empty? || patterns.any? do |pat|
|
141
157
|
case pat
|
@@ -152,6 +168,7 @@ EOB
|
|
152
168
|
end
|
153
169
|
|
154
170
|
def run_list(args, options)
|
171
|
+
# @type var list: Set[:class | :module | :interface]
|
155
172
|
list = Set[]
|
156
173
|
|
157
174
|
OptionParser.new do |opts|
|
@@ -172,10 +189,9 @@ EOB
|
|
172
189
|
opts.on("--interface", "List interfaces") { list << :interface }
|
173
190
|
end.order!(args)
|
174
191
|
|
175
|
-
list.merge([:class, :module, :interface]) if list.empty?
|
192
|
+
list.merge(_ = [:class, :module, :interface]) if list.empty?
|
176
193
|
|
177
|
-
loader =
|
178
|
-
options.setup(loader)
|
194
|
+
loader = options.loader()
|
179
195
|
|
180
196
|
env = Environment.from_loader(loader).resolve_type_names
|
181
197
|
|
@@ -202,6 +218,7 @@ EOB
|
|
202
218
|
end
|
203
219
|
|
204
220
|
def run_ancestors(args, options)
|
221
|
+
# @type var kind: :instance | :singleton
|
205
222
|
kind = :instance
|
206
223
|
|
207
224
|
OptionParser.new do |opts|
|
@@ -221,13 +238,12 @@ EOU
|
|
221
238
|
opts.on("--singleton", "Ancestors of singleton of the given type_name") { kind = :singleton }
|
222
239
|
end.order!(args)
|
223
240
|
|
224
|
-
loader =
|
225
|
-
options.setup(loader)
|
241
|
+
loader = options.loader()
|
226
242
|
|
227
243
|
env = Environment.from_loader(loader).resolve_type_names
|
228
244
|
|
229
245
|
builder = DefinitionBuilder.new(env: env)
|
230
|
-
type_name =
|
246
|
+
type_name = TypeName(args[0]).absolute!
|
231
247
|
|
232
248
|
if env.class_decls.key?(type_name)
|
233
249
|
ancestors = case kind
|
@@ -235,6 +251,8 @@ EOU
|
|
235
251
|
builder.instance_ancestors(type_name)
|
236
252
|
when :singleton
|
237
253
|
builder.singleton_ancestors(type_name)
|
254
|
+
else
|
255
|
+
raise
|
238
256
|
end
|
239
257
|
|
240
258
|
ancestors.ancestors.each do |ancestor|
|
@@ -255,6 +273,7 @@ EOU
|
|
255
273
|
end
|
256
274
|
|
257
275
|
def run_methods(args, options)
|
276
|
+
# @type var kind: :instance | :singleton
|
258
277
|
kind = :instance
|
259
278
|
inherit = true
|
260
279
|
|
@@ -281,13 +300,12 @@ EOU
|
|
281
300
|
return
|
282
301
|
end
|
283
302
|
|
284
|
-
loader =
|
285
|
-
options.setup(loader)
|
303
|
+
loader = options.loader()
|
286
304
|
|
287
305
|
env = Environment.from_loader(loader).resolve_type_names
|
288
306
|
|
289
307
|
builder = DefinitionBuilder.new(env: env)
|
290
|
-
type_name =
|
308
|
+
type_name = TypeName(args[0]).absolute!
|
291
309
|
|
292
310
|
if env.class_decls.key?(type_name)
|
293
311
|
definition = case kind
|
@@ -295,6 +313,8 @@ EOU
|
|
295
313
|
builder.build_instance(type_name)
|
296
314
|
when :singleton
|
297
315
|
builder.build_singleton(type_name)
|
316
|
+
else
|
317
|
+
raise
|
298
318
|
end
|
299
319
|
|
300
320
|
definition.methods.keys.sort.each do |name|
|
@@ -309,6 +329,7 @@ EOU
|
|
309
329
|
end
|
310
330
|
|
311
331
|
def run_method(args, options)
|
332
|
+
# @type var kind: :instance | :singleton
|
312
333
|
kind = :instance
|
313
334
|
|
314
335
|
OptionParser.new do |opts|
|
@@ -333,13 +354,11 @@ EOU
|
|
333
354
|
return
|
334
355
|
end
|
335
356
|
|
336
|
-
loader =
|
337
|
-
options.setup(loader)
|
338
|
-
|
357
|
+
loader = options.loader()
|
339
358
|
env = Environment.from_loader(loader).resolve_type_names
|
340
359
|
|
341
360
|
builder = DefinitionBuilder.new(env: env)
|
342
|
-
type_name =
|
361
|
+
type_name = TypeName(args[0]).absolute!
|
343
362
|
method_name = args[1].to_sym
|
344
363
|
|
345
364
|
unless env.class_decls.key?(type_name)
|
@@ -352,6 +371,8 @@ EOU
|
|
352
371
|
builder.build_instance(type_name)
|
353
372
|
when :singleton
|
354
373
|
builder.build_singleton(type_name)
|
374
|
+
else
|
375
|
+
raise
|
355
376
|
end
|
356
377
|
|
357
378
|
method = definition.methods[method_name]
|
@@ -391,10 +412,7 @@ EOU
|
|
391
412
|
end
|
392
413
|
end.parse!(args)
|
393
414
|
|
394
|
-
loader =
|
395
|
-
|
396
|
-
options.setup(loader)
|
397
|
-
|
415
|
+
loader = options.loader()
|
398
416
|
env = Environment.from_loader(loader).resolve_type_names
|
399
417
|
|
400
418
|
builder = DefinitionBuilder.new(env: env)
|
@@ -437,6 +455,7 @@ EOU
|
|
437
455
|
end
|
438
456
|
|
439
457
|
def run_constant(args, options)
|
458
|
+
# @type var context: String?
|
440
459
|
context = nil
|
441
460
|
|
442
461
|
OptionParser.new do |opts|
|
@@ -461,10 +480,7 @@ EOU
|
|
461
480
|
return
|
462
481
|
end
|
463
482
|
|
464
|
-
loader =
|
465
|
-
|
466
|
-
options.setup(loader)
|
467
|
-
|
483
|
+
loader = options.loader()
|
468
484
|
env = Environment.from_loader(loader).resolve_type_names
|
469
485
|
|
470
486
|
builder = DefinitionBuilder.new(env: env)
|
@@ -498,11 +514,10 @@ Examples:
|
|
498
514
|
EOU
|
499
515
|
end.parse!(args)
|
500
516
|
|
501
|
-
loader =
|
502
|
-
|
503
|
-
options.setup(loader)
|
517
|
+
loader = options.loader()
|
504
518
|
|
505
519
|
kind_of = -> (path) {
|
520
|
+
# @type var path: Pathname
|
506
521
|
case
|
507
522
|
when path.file?
|
508
523
|
"file"
|
@@ -515,19 +530,14 @@ EOU
|
|
515
530
|
end
|
516
531
|
}
|
517
532
|
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
loader.paths.each do |path|
|
524
|
-
case path
|
533
|
+
loader.each_dir do |source, dir|
|
534
|
+
case source
|
535
|
+
when :core
|
536
|
+
stdout.puts "#{dir} (#{kind_of[dir]}, core)"
|
525
537
|
when Pathname
|
526
|
-
stdout.puts "#{
|
527
|
-
when EnvironmentLoader::
|
528
|
-
stdout.puts "#{
|
529
|
-
when EnvironmentLoader::LibraryPath
|
530
|
-
stdout.puts "#{path.path} (#{kind_of[path.path]}, library, name=#{path.name})"
|
538
|
+
stdout.puts "#{dir} (#{kind_of[dir]})"
|
539
|
+
when EnvironmentLoader::Library
|
540
|
+
stdout.puts "#{dir} (#{kind_of[dir]}, library, name=#{source.name})"
|
531
541
|
end
|
532
542
|
end
|
533
543
|
end
|
@@ -573,10 +583,7 @@ EOU
|
|
573
583
|
end
|
574
584
|
end.parse!(args)
|
575
585
|
|
576
|
-
loader =
|
577
|
-
|
578
|
-
options.setup(loader)
|
579
|
-
|
586
|
+
loader = options.loader()
|
580
587
|
env = Environment.from_loader(loader).resolve_type_names
|
581
588
|
|
582
589
|
require_libs.each do |lib|
|
@@ -659,7 +666,6 @@ EOU
|
|
659
666
|
|
660
667
|
def run_vendor(args, options)
|
661
668
|
clean = false
|
662
|
-
vendor_stdlib = false
|
663
669
|
vendor_dir = Pathname("vendor/sigs")
|
664
670
|
|
665
671
|
OptionParser.new do |opts|
|
@@ -682,10 +688,6 @@ Options:
|
|
682
688
|
clean = v
|
683
689
|
end
|
684
690
|
|
685
|
-
opts.on("--[no-]stdlib", "Vendor stdlib signatures or not (default: no)") do |v|
|
686
|
-
vendor_stdlib = v
|
687
|
-
end
|
688
|
-
|
689
691
|
opts.on("--vendor-dir [DIR]", "Specify the directory for vendored signatures (default: vendor/sigs)") do |path|
|
690
692
|
vendor_dir = Pathname(path)
|
691
693
|
end
|
@@ -693,28 +695,26 @@ Options:
|
|
693
695
|
|
694
696
|
stdout.puts "Vendoring signatures to #{vendor_dir}..."
|
695
697
|
|
696
|
-
|
698
|
+
loader = options.loader()
|
697
699
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
700
|
+
args.each do |gem|
|
701
|
+
name, version = gem.split(/:/, 2)
|
702
|
+
|
703
|
+
next unless name
|
702
704
|
|
703
|
-
|
704
|
-
|
705
|
-
vendorer.stdlib!
|
705
|
+
stdout.puts " Loading library: #{name}, version=#{version}..."
|
706
|
+
loader.add(library: name, version: version)
|
706
707
|
end
|
707
708
|
|
708
|
-
|
709
|
-
name, version = EnvironmentLoader.parse_library(gem)
|
709
|
+
vendorer = Vendorer.new(vendor_dir: vendor_dir, loader: loader)
|
710
710
|
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
stdout.puts " Vendoring gem: name=#{name}, version=#{version}..."
|
715
|
-
vendorer.gem!(name, version)
|
716
|
-
end
|
711
|
+
if clean
|
712
|
+
stdout.puts " Deleting #{vendor_dir}..."
|
713
|
+
vendorer.clean!
|
717
714
|
end
|
715
|
+
|
716
|
+
stdout.puts " Copying RBS files..."
|
717
|
+
vendorer.copy!
|
718
718
|
end
|
719
719
|
|
720
720
|
def run_parse(args, options)
|
@@ -730,20 +730,20 @@ Examples:
|
|
730
730
|
EOB
|
731
731
|
end.parse!(args)
|
732
732
|
|
733
|
-
loader =
|
733
|
+
loader = options.loader()
|
734
734
|
|
735
735
|
syntax_error = false
|
736
736
|
args.each do |path|
|
737
737
|
path = Pathname(path)
|
738
|
-
loader.
|
739
|
-
Parser.parse_signature(
|
738
|
+
loader.each_file(path, skip_hidden: false, immediate: true) do |file_path|
|
739
|
+
Parser.parse_signature(file_path.read)
|
740
740
|
rescue RBS::Parser::SyntaxError => ex
|
741
741
|
loc = ex.error_value.location
|
742
|
-
stdout.puts "#{
|
742
|
+
stdout.puts "#{file_path}:#{loc.start_line}:#{loc.start_column}: parse error on value: (#{ex.token_str})"
|
743
743
|
syntax_error = true
|
744
744
|
rescue RBS::Parser::SemanticsError => ex
|
745
745
|
loc = ex.location
|
746
|
-
stdout.puts "#{
|
746
|
+
stdout.puts "#{file_path}:#{loc.start_line}:#{loc.start_column}: #{ex.original_message}"
|
747
747
|
syntax_error = true
|
748
748
|
end
|
749
749
|
end
|
@@ -751,22 +751,24 @@ Examples:
|
|
751
751
|
exit 1 if syntax_error
|
752
752
|
end
|
753
753
|
|
754
|
-
def parse_type_name(string)
|
755
|
-
Namespace.parse(string).yield_self do |namespace|
|
756
|
-
last = namespace.path.last
|
757
|
-
TypeName.new(name: last, namespace: namespace.parent)
|
758
|
-
end
|
759
|
-
end
|
760
|
-
|
761
754
|
def test_opt options
|
762
|
-
|
763
|
-
|
755
|
+
opts = []
|
756
|
+
|
757
|
+
opts.push(*options.repos.map {|dir| "--repo #{Shellwords.escape(dir)}"})
|
758
|
+
opts.push(*options.dirs.map {|dir| "-I #{Shellwords.escape(dir)}"})
|
759
|
+
opts.push(*options.libs.map {|lib| "-r#{Shellwords.escape(lib)}"})
|
760
|
+
|
761
|
+
opts.empty? ? nil : opts.join(" ")
|
764
762
|
end
|
765
763
|
|
766
764
|
def run_test(args, options)
|
765
|
+
# @type var unchecked_classes: Array[String]
|
767
766
|
unchecked_classes = []
|
767
|
+
# @type var targets: Array[String]
|
768
768
|
targets = []
|
769
|
+
# @type var sample_size: String?
|
769
770
|
sample_size = nil
|
771
|
+
# @type var double_suite: String?
|
770
772
|
double_suite = nil
|
771
773
|
|
772
774
|
(opts = OptionParser.new do |opts|
|
@@ -796,7 +798,6 @@ EOB
|
|
796
798
|
opts.on("--double-suite DOUBLE_SUITE", "Sets the double suite in use (currently supported: rspec | minitest)") do |suite|
|
797
799
|
double_suite = suite
|
798
800
|
end
|
799
|
-
|
800
801
|
end).order!(args)
|
801
802
|
|
802
803
|
if args.length.zero?
|
@@ -804,6 +805,7 @@ EOB
|
|
804
805
|
exit 1
|
805
806
|
end
|
806
807
|
|
808
|
+
# @type var env_hash: Hash[String, String?]
|
807
809
|
env_hash = {
|
808
810
|
'RUBYOPT' => "#{ENV['RUBYOPT']} -rrbs/test/setup",
|
809
811
|
'RBS_TEST_OPT' => test_opt(options),
|