rbs 0.12.2 → 0.16.0
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/.gitignore +0 -1
- data/CHANGELOG.md +32 -0
- data/Gemfile +4 -0
- data/README.md +8 -2
- data/Rakefile +9 -2
- data/Steepfile +1 -1
- data/bin/annotate-with-rdoc +1 -1
- data/bin/setup +0 -2
- data/bin/test_runner.rb +15 -1
- data/{stdlib/builtin → core}/array.rbs +124 -120
- data/{stdlib/builtin → core}/basic_object.rbs +54 -54
- data/{stdlib/builtin → core}/binding.rbs +42 -42
- data/core/builtin.rbs +70 -0
- data/{stdlib/builtin → core}/class.rbs +33 -33
- data/{stdlib/builtin → core}/comparable.rbs +0 -0
- data/{stdlib/builtin → core}/complex.rbs +90 -90
- 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 +0 -0
- data/{stdlib/builtin → core}/encoding.rbs +33 -33
- data/{stdlib/builtin → core}/enumerable.rbs +58 -52
- data/{stdlib/builtin → core}/enumerator.rbs +35 -35
- data/{stdlib/builtin → core}/errno.rbs +0 -0
- data/{stdlib/builtin → core}/errors.rbs +2 -2
- data/{stdlib/builtin → core}/exception.rbs +50 -50
- data/{stdlib/builtin → core}/false_class.rbs +6 -6
- data/{stdlib/builtin → core}/fiber.rbs +14 -14
- data/{stdlib/builtin → core}/fiber_error.rbs +1 -1
- data/{stdlib/builtin → core}/file.rbs +0 -0
- data/{stdlib/builtin → core}/file_test.rbs +0 -0
- data/{stdlib/builtin → core}/float.rbs +161 -161
- data/{stdlib/builtin → core}/gc.rbs +3 -3
- data/{stdlib/builtin → core}/hash.rbs +7 -7
- data/{stdlib/builtin → core}/integer.rbs +0 -0
- data/{stdlib/builtin → core}/io.rbs +88 -88
- data/{stdlib/builtin → core}/kernel.rbs +71 -153
- data/{stdlib/builtin → core}/marshal.rbs +0 -0
- data/{stdlib/builtin → core}/match_data.rbs +1 -1
- data/{stdlib/builtin → core}/math.rbs +0 -0
- data/{stdlib/builtin → core}/method.rbs +19 -19
- data/{stdlib/builtin → core}/module.rbs +13 -13
- data/{stdlib/builtin → core}/nil_class.rbs +20 -20
- data/{stdlib/builtin → core}/numeric.rbs +101 -101
- data/{stdlib/builtin → core}/object.rbs +173 -173
- data/{stdlib/builtin → core}/proc.rbs +91 -91
- data/{stdlib/builtin → core}/process.rbs +0 -0
- data/{stdlib/builtin → core}/random.rbs +1 -1
- data/{stdlib/builtin → core}/range.rbs +3 -5
- data/{stdlib/builtin → core}/rational.rbs +83 -83
- 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 +7 -7
- data/{stdlib/builtin → core}/string.rbs +10 -10
- data/{stdlib/builtin → core}/string_io.rbs +8 -8
- data/{stdlib/builtin → core}/struct.rbs +1 -1
- data/{stdlib/builtin → core}/symbol.rbs +1 -1
- data/{stdlib/builtin → core}/thread.rbs +189 -189
- data/{stdlib/builtin → core}/thread_group.rbs +2 -2
- data/{stdlib/builtin → core}/time.rbs +0 -0
- data/{stdlib/builtin → core}/trace_point.rbs +0 -0
- data/{stdlib/builtin → core}/true_class.rbs +10 -10
- data/{stdlib/builtin → core}/unbound_method.rbs +0 -0
- data/{stdlib/builtin → core}/warning.rbs +1 -1
- data/docs/CONTRIBUTING.md +1 -0
- data/docs/repo.md +125 -0
- data/docs/syntax.md +50 -6
- data/goodcheck.yml +22 -5
- data/lib/rbs.rb +1 -0
- data/lib/rbs/ast/comment.rb +1 -1
- data/lib/rbs/cli.rb +117 -107
- data/lib/rbs/constant.rb +1 -1
- data/lib/rbs/constant_table.rb +9 -8
- data/lib/rbs/definition_builder.rb +6 -7
- data/lib/rbs/environment.rb +5 -1
- data/lib/rbs/environment_loader.rb +79 -105
- data/lib/rbs/namespace.rb +1 -1
- data/lib/rbs/parser.rb +3148 -0
- data/lib/rbs/parser.y +10 -3
- data/lib/rbs/prototype/rb.rb +38 -6
- data/lib/rbs/prototype/runtime.rb +17 -7
- data/lib/rbs/repository.rb +121 -0
- data/lib/rbs/test/hook.rb +2 -0
- data/lib/rbs/test/setup.rb +5 -3
- data/lib/rbs/test/setup_helper.rb +4 -4
- data/lib/rbs/test/tester.rb +4 -1
- data/lib/rbs/test/type_check.rb +12 -6
- data/lib/rbs/type_name.rb +3 -2
- data/lib/rbs/variance_calculator.rb +2 -2
- data/lib/rbs/vendorer.rb +38 -16
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/writer.rb +25 -15
- data/sig/cli.rbs +58 -0
- data/sig/constant.rbs +21 -0
- data/sig/constant_table.rbs +30 -0
- data/sig/declarations.rbs +2 -2
- data/sig/definition.rbs +2 -2
- data/sig/definition_builder.rbs +6 -5
- data/sig/environment_loader.rbs +100 -0
- data/sig/members.rbs +2 -2
- data/sig/method_types.rbs +1 -1
- data/sig/namespace.rbs +4 -4
- data/sig/parser.rbs +25 -0
- data/sig/polyfill.rbs +42 -0
- data/sig/rbs.rbs +8 -0
- data/sig/repository.rbs +79 -0
- data/sig/substitution.rbs +3 -3
- data/sig/typename.rbs +1 -1
- data/sig/types.rbs +1 -1
- data/sig/vendorer.rbs +44 -0
- data/sig/version.rbs +3 -0
- data/sig/writer.rbs +40 -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} +2 -2
- 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} +1 -1
- data/stdlib/date/{date.rbs → 0/date.rbs} +4 -4
- 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} +12 -12
- 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} +41 -39
- data/stdlib/prime/{integer-extension.rbs → 0/integer-extension.rbs} +0 -0
- data/stdlib/prime/{prime.rbs → 0/prime.rbs} +0 -0
- 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} +0 -0
- data/stdlib/tmpdir/{tmpdir.rbs → 0/tmpdir.rbs} +12 -12
- data/stdlib/uri/{file.rbs → 0/file.rbs} +0 -0
- data/stdlib/uri/{generic.rbs → 0/generic.rbs} +2 -2
- data/stdlib/uri/0/http.rbs +158 -0
- data/stdlib/uri/0/https.rbs +108 -0
- data/stdlib/uri/0/ldap.rbs +224 -0
- data/stdlib/uri/0/ldaps.rbs +108 -0
- data/stdlib/zlib/{zlib.rbs → 0/zlib.rbs} +0 -0
- data/steep/Gemfile.lock +13 -17
- metadata +105 -89
- data/stdlib/builtin/builtin.rbs +0 -42
@@ -1,11 +1,11 @@
|
|
1
1
|
# [ThreadGroup](ThreadGroup) provides a means of
|
2
2
|
# keeping track of a number of threads as a group.
|
3
|
-
#
|
3
|
+
#
|
4
4
|
# A given [Thread](https://ruby-doc.org/core-2.6.3/Thread.html) object can
|
5
5
|
# only belong to one [ThreadGroup](ThreadGroup) at a
|
6
6
|
# time; adding a thread to a new group will remove it from any previous
|
7
7
|
# group.
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# Newly created threads belong to the same group as the thread from which
|
10
10
|
# they were created.
|
11
11
|
class ThreadGroup < Object
|
File without changes
|
File without changes
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# The global value `true` is the only instance of class TrueClass and represents
|
2
2
|
# a logically true value in boolean expressions. The class provides operators
|
3
3
|
# allowing `true` to be used in logical expressions.
|
4
|
-
#
|
4
|
+
#
|
5
5
|
class TrueClass
|
6
6
|
public
|
7
7
|
|
8
8
|
def !: () -> bool
|
9
9
|
|
10
10
|
# And---Returns `false` if *obj* is `nil` or `false`, `true` otherwise.
|
11
|
-
#
|
11
|
+
#
|
12
12
|
def &: (nil) -> false
|
13
13
|
| (false) -> false
|
14
14
|
| (untyped obj) -> bool
|
@@ -16,12 +16,12 @@ class TrueClass
|
|
16
16
|
# Case Equality -- For class Object, effectively the same as calling `#==`, but
|
17
17
|
# typically overridden by descendants to provide meaningful semantics in `case`
|
18
18
|
# statements.
|
19
|
-
#
|
19
|
+
#
|
20
20
|
def ===: (true) -> true
|
21
21
|
| (untyped obj) -> bool
|
22
22
|
|
23
23
|
# Exclusive Or---Returns `true` if *obj* is `nil` or `false`, `false` otherwise.
|
24
|
-
#
|
24
|
+
#
|
25
25
|
def ^: (nil) -> true
|
26
26
|
| (false) -> true
|
27
27
|
| (untyped obj) -> bool
|
@@ -29,18 +29,18 @@ class TrueClass
|
|
29
29
|
alias inspect to_s
|
30
30
|
|
31
31
|
# The string representation of `true` is "true".
|
32
|
-
#
|
32
|
+
#
|
33
33
|
def to_s: () -> "true"
|
34
34
|
|
35
35
|
# Or---Returns `true`. As *obj* is an argument to a method call, it is always
|
36
36
|
# evaluated; there is no short-circuit evaluation in this case.
|
37
|
-
#
|
37
|
+
#
|
38
38
|
# true | puts("or")
|
39
39
|
# true || puts("logical or")
|
40
|
-
#
|
40
|
+
#
|
41
41
|
# *produces:*
|
42
|
-
#
|
42
|
+
#
|
43
43
|
# or
|
44
|
-
#
|
45
|
-
def |: (
|
44
|
+
#
|
45
|
+
def |: (boolish obj) -> bool
|
46
46
|
end
|
File without changes
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# module extends itself, making `Warning.warn` available.
|
4
4
|
# [\#warn](Warning#method-i-warn) is called for all
|
5
5
|
# warnings issued by Ruby. By default, warnings are printed to $stderr.
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# By overriding [\#warn](Warning#method-i-warn), you
|
8
8
|
# can change how warnings are handled by Ruby, either filtering some
|
9
9
|
# warnings, and/or outputting warnings somewhere other than $stderr. When
|
data/docs/CONTRIBUTING.md
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
- Use `rbs prototype runtime --merge CLASS_NAME` command to generate the missing method definitions.
|
18
18
|
- Committing the auto generated signatures is recommended.
|
19
19
|
5. Annotate with RDoc.
|
20
|
+
- You'll need RDoc installed. Rvm users should use `rvm docs generate` first.
|
20
21
|
- Use `bin/annotate-with-rdoc stdlib/path/to/signature.rbs` to annotate the RBS files.
|
21
22
|
- Committing the generated annotations is recommended.
|
22
23
|
6. Fix method types and comments.
|
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/goodcheck.yml
CHANGED
@@ -6,12 +6,12 @@ rules:
|
|
6
6
|
- "stdlib/**/*.rbs"
|
7
7
|
fail:
|
8
8
|
- |
|
9
|
-
# arglists 💪👽🚨 << Delete this section
|
10
|
-
# File.absolute_path?(file_name) -> true or false
|
9
|
+
# arglists 💪👽🚨 << Delete this section
|
10
|
+
# File.absolute_path?(file_name) -> true or false
|
11
11
|
#
|
12
12
|
|
13
13
|
- id: rbs.no_arg
|
14
|
-
pattern:
|
14
|
+
pattern:
|
15
15
|
regexp: arg\d+
|
16
16
|
message: |
|
17
17
|
Stop using parameter names like `arg0` or `arg1`
|
@@ -23,7 +23,7 @@ rules:
|
|
23
23
|
- Documents (comments) may contain that pattern.
|
24
24
|
glob:
|
25
25
|
- "stdlib/**/*.rbs"
|
26
|
-
fail:
|
26
|
+
fail:
|
27
27
|
- "def `send`: (String | Symbol arg0, *untyped arg1) -> untyped"
|
28
28
|
pass:
|
29
29
|
- "def `send`: (String | Symbol, *untyped) -> untyped"
|
@@ -62,4 +62,21 @@ rules:
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
|
65
|
+
- id: no_trailing_whitespace
|
66
|
+
pattern:
|
67
|
+
regexp: '[ \t]+$'
|
68
|
+
message: |
|
69
|
+
Trim trailing whitespaces
|
70
|
+
glob:
|
71
|
+
- '**/*.rb'
|
72
|
+
- '**/*.rbs'
|
73
|
+
- '**/*.md'
|
74
|
+
justification:
|
75
|
+
- Let the maintainers know if it is an autogenerated files.
|
76
|
+
pass:
|
77
|
+
- "Hello world"
|
78
|
+
- "Hello\nworld"
|
79
|
+
- "Hello\n\nworld"
|
80
|
+
fail:
|
81
|
+
- "Hello world "
|
82
|
+
- "Hello \nworld"
|
data/lib/rbs.rb
CHANGED
data/lib/rbs/ast/comment.rb
CHANGED
data/lib/rbs/cli.rb
CHANGED
@@ -1,32 +1,63 @@
|
|
1
|
+
require "open3"
|
1
2
|
require "optparse"
|
2
3
|
require "shellwords"
|
3
4
|
|
4
5
|
module RBS
|
5
6
|
class CLI
|
6
7
|
class LibraryOptions
|
8
|
+
attr_accessor :core_root
|
9
|
+
attr_reader :repos
|
7
10
|
attr_reader :libs
|
8
11
|
attr_reader :dirs
|
9
|
-
attr_accessor :no_stdlib
|
10
12
|
|
11
13
|
def initialize()
|
14
|
+
@core_root = EnvironmentLoader::DEFAULT_CORE_ROOT
|
15
|
+
@repos = []
|
16
|
+
|
12
17
|
@libs = []
|
13
18
|
@dirs = []
|
14
|
-
@no_stdlib = false
|
15
19
|
end
|
16
20
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
21
|
+
def loader
|
22
|
+
repository = Repository.new(no_stdlib: core_root.nil?)
|
23
|
+
repos.each do |repo|
|
24
|
+
repository.add(Pathname(repo))
|
20
25
|
end
|
21
26
|
|
27
|
+
loader = EnvironmentLoader.new(core_root: core_root, repository: repository)
|
28
|
+
|
22
29
|
dirs.each do |dir|
|
23
30
|
loader.add(path: Pathname(dir))
|
24
31
|
end
|
25
32
|
|
26
|
-
|
33
|
+
libs.each do |lib|
|
34
|
+
name, version = lib.split(/:/, 2)
|
35
|
+
next unless name
|
36
|
+
loader.add(library: name, version: version)
|
37
|
+
end
|
27
38
|
|
28
39
|
loader
|
29
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
|
30
61
|
end
|
31
62
|
|
32
63
|
attr_reader :stdout
|
@@ -39,29 +70,14 @@ module RBS
|
|
39
70
|
|
40
71
|
COMMANDS = [:ast, :list, :ancestors, :methods, :method, :validate, :constant, :paths, :prototype, :vendor, :parse, :test]
|
41
72
|
|
42
|
-
def library_parse(opts, options:)
|
43
|
-
opts.on("-r LIBRARY", "Load RBS files of the library") do |lib|
|
44
|
-
options.libs << lib
|
45
|
-
end
|
46
|
-
|
47
|
-
opts.on("-I DIR", "Load RBS files from the directory") do |dir|
|
48
|
-
options.dirs << dir
|
49
|
-
end
|
50
|
-
|
51
|
-
opts.on("--no-stdlib", "Skip loading standard library signatures") do
|
52
|
-
options.no_stdlib = true
|
53
|
-
end
|
54
|
-
|
55
|
-
opts
|
56
|
-
end
|
57
|
-
|
58
73
|
def parse_logging_options(opts)
|
59
74
|
opts.on("--log-level LEVEL", "Specify log level (defaults to `warn`)") do |level|
|
60
75
|
RBS.logger_level = level
|
61
76
|
end
|
62
77
|
|
63
78
|
opts.on("--log-output OUTPUT", "Specify the file to output log (defaults to stderr)") do |output|
|
64
|
-
|
79
|
+
io = File.open(output, "a") or raise
|
80
|
+
RBS.logger_output = io
|
65
81
|
end
|
66
82
|
|
67
83
|
opts
|
@@ -82,7 +98,7 @@ module RBS
|
|
82
98
|
|
83
99
|
Options:
|
84
100
|
USAGE
|
85
|
-
|
101
|
+
options.setup_library_options(opts)
|
86
102
|
parse_logging_options(opts)
|
87
103
|
opts.version = RBS::VERSION
|
88
104
|
|
@@ -128,13 +144,14 @@ EOB
|
|
128
144
|
end
|
129
145
|
end
|
130
146
|
|
131
|
-
loader =
|
132
|
-
options.setup(loader)
|
147
|
+
loader = options.loader()
|
133
148
|
|
134
149
|
env = Environment.from_loader(loader).resolve_type_names
|
135
150
|
|
136
151
|
decls = env.declarations.select do |decl|
|
137
|
-
|
152
|
+
loc = decl.location or raise
|
153
|
+
# @type var name: String
|
154
|
+
name = loc.buffer.name
|
138
155
|
|
139
156
|
patterns.empty? || patterns.any? do |pat|
|
140
157
|
case pat
|
@@ -151,6 +168,7 @@ EOB
|
|
151
168
|
end
|
152
169
|
|
153
170
|
def run_list(args, options)
|
171
|
+
# @type var list: Set[:class | :module | :interface]
|
154
172
|
list = Set[]
|
155
173
|
|
156
174
|
OptionParser.new do |opts|
|
@@ -171,10 +189,9 @@ EOB
|
|
171
189
|
opts.on("--interface", "List interfaces") { list << :interface }
|
172
190
|
end.order!(args)
|
173
191
|
|
174
|
-
list.merge([:class, :module, :interface]) if list.empty?
|
192
|
+
list.merge(_ = [:class, :module, :interface]) if list.empty?
|
175
193
|
|
176
|
-
loader =
|
177
|
-
options.setup(loader)
|
194
|
+
loader = options.loader()
|
178
195
|
|
179
196
|
env = Environment.from_loader(loader).resolve_type_names
|
180
197
|
|
@@ -201,6 +218,7 @@ EOB
|
|
201
218
|
end
|
202
219
|
|
203
220
|
def run_ancestors(args, options)
|
221
|
+
# @type var kind: :instance | :singleton
|
204
222
|
kind = :instance
|
205
223
|
|
206
224
|
OptionParser.new do |opts|
|
@@ -220,13 +238,12 @@ EOU
|
|
220
238
|
opts.on("--singleton", "Ancestors of singleton of the given type_name") { kind = :singleton }
|
221
239
|
end.order!(args)
|
222
240
|
|
223
|
-
loader =
|
224
|
-
options.setup(loader)
|
241
|
+
loader = options.loader()
|
225
242
|
|
226
243
|
env = Environment.from_loader(loader).resolve_type_names
|
227
244
|
|
228
245
|
builder = DefinitionBuilder.new(env: env)
|
229
|
-
type_name =
|
246
|
+
type_name = TypeName(args[0]).absolute!
|
230
247
|
|
231
248
|
if env.class_decls.key?(type_name)
|
232
249
|
ancestors = case kind
|
@@ -234,6 +251,8 @@ EOU
|
|
234
251
|
builder.instance_ancestors(type_name)
|
235
252
|
when :singleton
|
236
253
|
builder.singleton_ancestors(type_name)
|
254
|
+
else
|
255
|
+
raise
|
237
256
|
end
|
238
257
|
|
239
258
|
ancestors.ancestors.each do |ancestor|
|
@@ -254,6 +273,7 @@ EOU
|
|
254
273
|
end
|
255
274
|
|
256
275
|
def run_methods(args, options)
|
276
|
+
# @type var kind: :instance | :singleton
|
257
277
|
kind = :instance
|
258
278
|
inherit = true
|
259
279
|
|
@@ -280,13 +300,12 @@ EOU
|
|
280
300
|
return
|
281
301
|
end
|
282
302
|
|
283
|
-
loader =
|
284
|
-
options.setup(loader)
|
303
|
+
loader = options.loader()
|
285
304
|
|
286
305
|
env = Environment.from_loader(loader).resolve_type_names
|
287
306
|
|
288
307
|
builder = DefinitionBuilder.new(env: env)
|
289
|
-
type_name =
|
308
|
+
type_name = TypeName(args[0]).absolute!
|
290
309
|
|
291
310
|
if env.class_decls.key?(type_name)
|
292
311
|
definition = case kind
|
@@ -294,6 +313,8 @@ EOU
|
|
294
313
|
builder.build_instance(type_name)
|
295
314
|
when :singleton
|
296
315
|
builder.build_singleton(type_name)
|
316
|
+
else
|
317
|
+
raise
|
297
318
|
end
|
298
319
|
|
299
320
|
definition.methods.keys.sort.each do |name|
|
@@ -308,6 +329,7 @@ EOU
|
|
308
329
|
end
|
309
330
|
|
310
331
|
def run_method(args, options)
|
332
|
+
# @type var kind: :instance | :singleton
|
311
333
|
kind = :instance
|
312
334
|
|
313
335
|
OptionParser.new do |opts|
|
@@ -332,13 +354,11 @@ EOU
|
|
332
354
|
return
|
333
355
|
end
|
334
356
|
|
335
|
-
loader =
|
336
|
-
options.setup(loader)
|
337
|
-
|
357
|
+
loader = options.loader()
|
338
358
|
env = Environment.from_loader(loader).resolve_type_names
|
339
359
|
|
340
360
|
builder = DefinitionBuilder.new(env: env)
|
341
|
-
type_name =
|
361
|
+
type_name = TypeName(args[0]).absolute!
|
342
362
|
method_name = args[1].to_sym
|
343
363
|
|
344
364
|
unless env.class_decls.key?(type_name)
|
@@ -351,6 +371,8 @@ EOU
|
|
351
371
|
builder.build_instance(type_name)
|
352
372
|
when :singleton
|
353
373
|
builder.build_singleton(type_name)
|
374
|
+
else
|
375
|
+
raise
|
354
376
|
end
|
355
377
|
|
356
378
|
method = definition.methods[method_name]
|
@@ -383,12 +405,14 @@ Examples:
|
|
383
405
|
|
384
406
|
$ rbs validate
|
385
407
|
EOU
|
386
|
-
end.parse!(args)
|
387
|
-
|
388
|
-
loader = EnvironmentLoader.new()
|
389
408
|
|
390
|
-
|
409
|
+
opts.on("--silent") do
|
410
|
+
require "stringio"
|
411
|
+
@stdout = StringIO.new
|
412
|
+
end
|
413
|
+
end.parse!(args)
|
391
414
|
|
415
|
+
loader = options.loader()
|
392
416
|
env = Environment.from_loader(loader).resolve_type_names
|
393
417
|
|
394
418
|
builder = DefinitionBuilder.new(env: env)
|
@@ -431,6 +455,7 @@ EOU
|
|
431
455
|
end
|
432
456
|
|
433
457
|
def run_constant(args, options)
|
458
|
+
# @type var context: String?
|
434
459
|
context = nil
|
435
460
|
|
436
461
|
OptionParser.new do |opts|
|
@@ -455,10 +480,7 @@ EOU
|
|
455
480
|
return
|
456
481
|
end
|
457
482
|
|
458
|
-
loader =
|
459
|
-
|
460
|
-
options.setup(loader)
|
461
|
-
|
483
|
+
loader = options.loader()
|
462
484
|
env = Environment.from_loader(loader).resolve_type_names
|
463
485
|
|
464
486
|
builder = DefinitionBuilder.new(env: env)
|
@@ -488,15 +510,14 @@ Show paths to directories where the RBS files are loaded from.
|
|
488
510
|
Examples:
|
489
511
|
|
490
512
|
$ rbs paths
|
491
|
-
$
|
513
|
+
$ rbs -r set paths
|
492
514
|
EOU
|
493
515
|
end.parse!(args)
|
494
516
|
|
495
|
-
loader =
|
496
|
-
|
497
|
-
options.setup(loader)
|
517
|
+
loader = options.loader()
|
498
518
|
|
499
519
|
kind_of = -> (path) {
|
520
|
+
# @type var path: Pathname
|
500
521
|
case
|
501
522
|
when path.file?
|
502
523
|
"file"
|
@@ -509,19 +530,14 @@ EOU
|
|
509
530
|
end
|
510
531
|
}
|
511
532
|
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
loader.paths.each do |path|
|
518
|
-
case path
|
533
|
+
loader.each_dir do |source, dir|
|
534
|
+
case source
|
535
|
+
when :core
|
536
|
+
stdout.puts "#{dir} (#{kind_of[dir]}, core)"
|
519
537
|
when Pathname
|
520
|
-
stdout.puts "#{
|
521
|
-
when EnvironmentLoader::
|
522
|
-
stdout.puts "#{
|
523
|
-
when EnvironmentLoader::LibraryPath
|
524
|
-
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})"
|
525
541
|
end
|
526
542
|
end
|
527
543
|
end
|
@@ -543,7 +559,7 @@ EOU
|
|
543
559
|
Usage: rbs prototype runtime [options...] [pattern...]
|
544
560
|
|
545
561
|
Generate RBS prototype based on runtime introspection.
|
546
|
-
It loads Ruby code specified in [options] and generates RBS prototypes for classes matches to [pattern].
|
562
|
+
It loads Ruby code specified in [options] and generates RBS prototypes for classes matches to [pattern].
|
547
563
|
|
548
564
|
Examples:
|
549
565
|
|
@@ -567,10 +583,7 @@ EOU
|
|
567
583
|
end
|
568
584
|
end.parse!(args)
|
569
585
|
|
570
|
-
loader =
|
571
|
-
|
572
|
-
options.setup(loader)
|
573
|
-
|
586
|
+
loader = options.loader()
|
574
587
|
env = Environment.from_loader(loader).resolve_type_names
|
575
588
|
|
576
589
|
require_libs.each do |lib|
|
@@ -653,7 +666,6 @@ EOU
|
|
653
666
|
|
654
667
|
def run_vendor(args, options)
|
655
668
|
clean = false
|
656
|
-
vendor_stdlib = false
|
657
669
|
vendor_dir = Pathname("vendor/sigs")
|
658
670
|
|
659
671
|
OptionParser.new do |opts|
|
@@ -676,10 +688,6 @@ Options:
|
|
676
688
|
clean = v
|
677
689
|
end
|
678
690
|
|
679
|
-
opts.on("--[no-]stdlib", "Vendor stdlib signatures or not (default: no)") do |v|
|
680
|
-
vendor_stdlib = v
|
681
|
-
end
|
682
|
-
|
683
691
|
opts.on("--vendor-dir [DIR]", "Specify the directory for vendored signatures (default: vendor/sigs)") do |path|
|
684
692
|
vendor_dir = Pathname(path)
|
685
693
|
end
|
@@ -687,28 +695,26 @@ Options:
|
|
687
695
|
|
688
696
|
stdout.puts "Vendoring signatures to #{vendor_dir}..."
|
689
697
|
|
690
|
-
|
698
|
+
loader = options.loader()
|
691
699
|
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
700
|
+
args.each do |gem|
|
701
|
+
name, version = gem.split(/:/, 2)
|
702
|
+
|
703
|
+
next unless name
|
696
704
|
|
697
|
-
|
698
|
-
|
699
|
-
vendorer.stdlib!
|
705
|
+
stdout.puts " Loading library: #{name}, version=#{version}..."
|
706
|
+
loader.add(library: name, version: version)
|
700
707
|
end
|
701
708
|
|
702
|
-
|
703
|
-
name, version = EnvironmentLoader.parse_library(gem)
|
709
|
+
vendorer = Vendorer.new(vendor_dir: vendor_dir, loader: loader)
|
704
710
|
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
stdout.puts " Vendoring gem: name=#{name}, version=#{version}..."
|
709
|
-
vendorer.gem!(name, version)
|
710
|
-
end
|
711
|
+
if clean
|
712
|
+
stdout.puts " Deleting #{vendor_dir}..."
|
713
|
+
vendorer.clean!
|
711
714
|
end
|
715
|
+
|
716
|
+
stdout.puts " Copying RBS files..."
|
717
|
+
vendorer.copy!
|
712
718
|
end
|
713
719
|
|
714
720
|
def run_parse(args, options)
|
@@ -724,20 +730,20 @@ Examples:
|
|
724
730
|
EOB
|
725
731
|
end.parse!(args)
|
726
732
|
|
727
|
-
loader =
|
733
|
+
loader = options.loader()
|
728
734
|
|
729
735
|
syntax_error = false
|
730
736
|
args.each do |path|
|
731
737
|
path = Pathname(path)
|
732
|
-
loader.
|
733
|
-
Parser.parse_signature(
|
738
|
+
loader.each_file(path, skip_hidden: false, immediate: true) do |file_path|
|
739
|
+
Parser.parse_signature(file_path.read)
|
734
740
|
rescue RBS::Parser::SyntaxError => ex
|
735
741
|
loc = ex.error_value.location
|
736
|
-
stdout.puts "#{
|
742
|
+
stdout.puts "#{file_path}:#{loc.start_line}:#{loc.start_column}: parse error on value: (#{ex.token_str})"
|
737
743
|
syntax_error = true
|
738
744
|
rescue RBS::Parser::SemanticsError => ex
|
739
745
|
loc = ex.location
|
740
|
-
stdout.puts "#{
|
746
|
+
stdout.puts "#{file_path}:#{loc.start_line}:#{loc.start_column}: #{ex.original_message}"
|
741
747
|
syntax_error = true
|
742
748
|
end
|
743
749
|
end
|
@@ -745,22 +751,24 @@ Examples:
|
|
745
751
|
exit 1 if syntax_error
|
746
752
|
end
|
747
753
|
|
748
|
-
def parse_type_name(string)
|
749
|
-
Namespace.parse(string).yield_self do |namespace|
|
750
|
-
last = namespace.path.last
|
751
|
-
TypeName.new(name: last, namespace: namespace.parent)
|
752
|
-
end
|
753
|
-
end
|
754
|
-
|
755
754
|
def test_opt options
|
756
|
-
|
757
|
-
|
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(" ")
|
758
762
|
end
|
759
763
|
|
760
764
|
def run_test(args, options)
|
765
|
+
# @type var unchecked_classes: Array[String]
|
761
766
|
unchecked_classes = []
|
767
|
+
# @type var targets: Array[String]
|
762
768
|
targets = []
|
769
|
+
# @type var sample_size: String?
|
763
770
|
sample_size = nil
|
771
|
+
# @type var double_suite: String?
|
764
772
|
double_suite = nil
|
765
773
|
|
766
774
|
(opts = OptionParser.new do |opts|
|
@@ -790,7 +798,6 @@ EOB
|
|
790
798
|
opts.on("--double-suite DOUBLE_SUITE", "Sets the double suite in use (currently supported: rspec | minitest)") do |suite|
|
791
799
|
double_suite = suite
|
792
800
|
end
|
793
|
-
|
794
801
|
end).order!(args)
|
795
802
|
|
796
803
|
if args.length.zero?
|
@@ -798,6 +805,7 @@ EOB
|
|
798
805
|
exit 1
|
799
806
|
end
|
800
807
|
|
808
|
+
# @type var env_hash: Hash[String, String?]
|
801
809
|
env_hash = {
|
802
810
|
'RUBYOPT' => "#{ENV['RUBYOPT']} -rrbs/test/setup",
|
803
811
|
'RBS_TEST_OPT' => test_opt(options),
|
@@ -808,8 +816,10 @@ EOB
|
|
808
816
|
'RBS_TEST_TARGET' => (targets.join(',') unless targets.empty?)
|
809
817
|
}
|
810
818
|
|
811
|
-
|
812
|
-
|
819
|
+
out, err, status = Open3.capture3(env_hash, *args)
|
820
|
+
stdout.print(out)
|
821
|
+
stderr.print(err)
|
822
|
+
status
|
813
823
|
end
|
814
824
|
end
|
815
825
|
end
|