rbs 0.12.2 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +8 -4
  3. data/.gitignore +0 -1
  4. data/CHANGELOG.md +32 -0
  5. data/Gemfile +4 -0
  6. data/README.md +8 -2
  7. data/Rakefile +9 -2
  8. data/Steepfile +1 -1
  9. data/bin/annotate-with-rdoc +1 -1
  10. data/bin/setup +0 -2
  11. data/bin/test_runner.rb +15 -1
  12. data/{stdlib/builtin → core}/array.rbs +124 -120
  13. data/{stdlib/builtin → core}/basic_object.rbs +54 -54
  14. data/{stdlib/builtin → core}/binding.rbs +42 -42
  15. data/core/builtin.rbs +70 -0
  16. data/{stdlib/builtin → core}/class.rbs +33 -33
  17. data/{stdlib/builtin → core}/comparable.rbs +0 -0
  18. data/{stdlib/builtin → core}/complex.rbs +90 -90
  19. data/{stdlib/builtin → core}/constants.rbs +0 -0
  20. data/{stdlib/builtin → core}/data.rbs +0 -0
  21. data/{stdlib/builtin → core}/deprecated.rbs +0 -0
  22. data/{stdlib/builtin → core}/dir.rbs +0 -0
  23. data/{stdlib/builtin → core}/encoding.rbs +33 -33
  24. data/{stdlib/builtin → core}/enumerable.rbs +58 -52
  25. data/{stdlib/builtin → core}/enumerator.rbs +35 -35
  26. data/{stdlib/builtin → core}/errno.rbs +0 -0
  27. data/{stdlib/builtin → core}/errors.rbs +2 -2
  28. data/{stdlib/builtin → core}/exception.rbs +50 -50
  29. data/{stdlib/builtin → core}/false_class.rbs +6 -6
  30. data/{stdlib/builtin → core}/fiber.rbs +14 -14
  31. data/{stdlib/builtin → core}/fiber_error.rbs +1 -1
  32. data/{stdlib/builtin → core}/file.rbs +0 -0
  33. data/{stdlib/builtin → core}/file_test.rbs +0 -0
  34. data/{stdlib/builtin → core}/float.rbs +161 -161
  35. data/{stdlib/builtin → core}/gc.rbs +3 -3
  36. data/{stdlib/builtin → core}/hash.rbs +7 -7
  37. data/{stdlib/builtin → core}/integer.rbs +0 -0
  38. data/{stdlib/builtin → core}/io.rbs +88 -88
  39. data/{stdlib/builtin → core}/kernel.rbs +71 -153
  40. data/{stdlib/builtin → core}/marshal.rbs +0 -0
  41. data/{stdlib/builtin → core}/match_data.rbs +1 -1
  42. data/{stdlib/builtin → core}/math.rbs +0 -0
  43. data/{stdlib/builtin → core}/method.rbs +19 -19
  44. data/{stdlib/builtin → core}/module.rbs +13 -13
  45. data/{stdlib/builtin → core}/nil_class.rbs +20 -20
  46. data/{stdlib/builtin → core}/numeric.rbs +101 -101
  47. data/{stdlib/builtin → core}/object.rbs +173 -173
  48. data/{stdlib/builtin → core}/proc.rbs +91 -91
  49. data/{stdlib/builtin → core}/process.rbs +0 -0
  50. data/{stdlib/builtin → core}/random.rbs +1 -1
  51. data/{stdlib/builtin → core}/range.rbs +3 -5
  52. data/{stdlib/builtin → core}/rational.rbs +83 -83
  53. data/{stdlib/builtin → core}/rb_config.rbs +0 -0
  54. data/{stdlib/builtin → core}/regexp.rbs +0 -0
  55. data/{stdlib/builtin → core}/ruby_vm.rbs +0 -0
  56. data/{stdlib/builtin → core}/signal.rbs +7 -7
  57. data/{stdlib/builtin → core}/string.rbs +10 -10
  58. data/{stdlib/builtin → core}/string_io.rbs +8 -8
  59. data/{stdlib/builtin → core}/struct.rbs +1 -1
  60. data/{stdlib/builtin → core}/symbol.rbs +1 -1
  61. data/{stdlib/builtin → core}/thread.rbs +189 -189
  62. data/{stdlib/builtin → core}/thread_group.rbs +2 -2
  63. data/{stdlib/builtin → core}/time.rbs +0 -0
  64. data/{stdlib/builtin → core}/trace_point.rbs +0 -0
  65. data/{stdlib/builtin → core}/true_class.rbs +10 -10
  66. data/{stdlib/builtin → core}/unbound_method.rbs +0 -0
  67. data/{stdlib/builtin → core}/warning.rbs +1 -1
  68. data/docs/CONTRIBUTING.md +1 -0
  69. data/docs/repo.md +125 -0
  70. data/docs/syntax.md +50 -6
  71. data/goodcheck.yml +22 -5
  72. data/lib/rbs.rb +1 -0
  73. data/lib/rbs/ast/comment.rb +1 -1
  74. data/lib/rbs/cli.rb +117 -107
  75. data/lib/rbs/constant.rb +1 -1
  76. data/lib/rbs/constant_table.rb +9 -8
  77. data/lib/rbs/definition_builder.rb +6 -7
  78. data/lib/rbs/environment.rb +5 -1
  79. data/lib/rbs/environment_loader.rb +79 -105
  80. data/lib/rbs/namespace.rb +1 -1
  81. data/lib/rbs/parser.rb +3148 -0
  82. data/lib/rbs/parser.y +10 -3
  83. data/lib/rbs/prototype/rb.rb +38 -6
  84. data/lib/rbs/prototype/runtime.rb +17 -7
  85. data/lib/rbs/repository.rb +121 -0
  86. data/lib/rbs/test/hook.rb +2 -0
  87. data/lib/rbs/test/setup.rb +5 -3
  88. data/lib/rbs/test/setup_helper.rb +4 -4
  89. data/lib/rbs/test/tester.rb +4 -1
  90. data/lib/rbs/test/type_check.rb +12 -6
  91. data/lib/rbs/type_name.rb +3 -2
  92. data/lib/rbs/variance_calculator.rb +2 -2
  93. data/lib/rbs/vendorer.rb +38 -16
  94. data/lib/rbs/version.rb +1 -1
  95. data/lib/rbs/writer.rb +25 -15
  96. data/sig/cli.rbs +58 -0
  97. data/sig/constant.rbs +21 -0
  98. data/sig/constant_table.rbs +30 -0
  99. data/sig/declarations.rbs +2 -2
  100. data/sig/definition.rbs +2 -2
  101. data/sig/definition_builder.rbs +6 -5
  102. data/sig/environment_loader.rbs +100 -0
  103. data/sig/members.rbs +2 -2
  104. data/sig/method_types.rbs +1 -1
  105. data/sig/namespace.rbs +4 -4
  106. data/sig/parser.rbs +25 -0
  107. data/sig/polyfill.rbs +42 -0
  108. data/sig/rbs.rbs +8 -0
  109. data/sig/repository.rbs +79 -0
  110. data/sig/substitution.rbs +3 -3
  111. data/sig/typename.rbs +1 -1
  112. data/sig/types.rbs +1 -1
  113. data/sig/vendorer.rbs +44 -0
  114. data/sig/version.rbs +3 -0
  115. data/sig/writer.rbs +40 -0
  116. data/stdlib/abbrev/{abbrev.rbs → 0/abbrev.rbs} +0 -0
  117. data/stdlib/base64/{base64.rbs → 0/base64.rbs} +1 -1
  118. data/stdlib/benchmark/{benchmark.rbs → 0/benchmark.rbs} +2 -2
  119. data/stdlib/{bigdecimal/math → bigdecimal-math/0}/big_math.rbs +0 -0
  120. data/stdlib/bigdecimal/{big_decimal.rbs → 0/big_decimal.rbs} +0 -0
  121. data/stdlib/coverage/{coverage.rbs → 0/coverage.rbs} +2 -2
  122. data/stdlib/csv/{csv.rbs → 0/csv.rbs} +1 -1
  123. data/stdlib/date/{date.rbs → 0/date.rbs} +4 -4
  124. data/stdlib/date/{date_time.rbs → 0/date_time.rbs} +1 -1
  125. data/stdlib/dbm/0/dbm.rbs +277 -0
  126. data/stdlib/erb/{erb.rbs → 0/erb.rbs} +0 -0
  127. data/stdlib/fiber/{fiber.rbs → 0/fiber.rbs} +0 -0
  128. data/stdlib/find/{find.rbs → 0/find.rbs} +12 -12
  129. data/stdlib/forwardable/{forwardable.rbs → 0/forwardable.rbs} +0 -0
  130. data/stdlib/ipaddr/{ipaddr.rbs → 0/ipaddr.rbs} +0 -0
  131. data/stdlib/json/{json.rbs → 0/json.rbs} +0 -0
  132. data/stdlib/logger/{formatter.rbs → 0/formatter.rbs} +0 -0
  133. data/stdlib/logger/{log_device.rbs → 0/log_device.rbs} +1 -1
  134. data/stdlib/logger/{logger.rbs → 0/logger.rbs} +1 -1
  135. data/stdlib/logger/{period.rbs → 0/period.rbs} +0 -0
  136. data/stdlib/logger/{severity.rbs → 0/severity.rbs} +0 -0
  137. data/stdlib/mutex_m/{mutex_m.rbs → 0/mutex_m.rbs} +0 -0
  138. data/stdlib/pathname/{pathname.rbs → 0/pathname.rbs} +41 -39
  139. data/stdlib/prime/{integer-extension.rbs → 0/integer-extension.rbs} +0 -0
  140. data/stdlib/prime/{prime.rbs → 0/prime.rbs} +0 -0
  141. data/stdlib/pstore/0/pstore.rbs +287 -0
  142. data/stdlib/pty/{pty.rbs → 0/pty.rbs} +1 -1
  143. data/stdlib/securerandom/{securerandom.rbs → 0/securerandom.rbs} +0 -0
  144. data/stdlib/set/{set.rbs → 0/set.rbs} +0 -0
  145. data/stdlib/tmpdir/{tmpdir.rbs → 0/tmpdir.rbs} +12 -12
  146. data/stdlib/uri/{file.rbs → 0/file.rbs} +0 -0
  147. data/stdlib/uri/{generic.rbs → 0/generic.rbs} +2 -2
  148. data/stdlib/uri/0/http.rbs +158 -0
  149. data/stdlib/uri/0/https.rbs +108 -0
  150. data/stdlib/uri/0/ldap.rbs +224 -0
  151. data/stdlib/uri/0/ldaps.rbs +108 -0
  152. data/stdlib/zlib/{zlib.rbs → 0/zlib.rbs} +0 -0
  153. data/steep/Gemfile.lock +13 -17
  154. metadata +105 -89
  155. 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 |: (bool obj) -> bool
44
+ #
45
+ def |: (boolish obj) -> bool
46
46
  end
@@ -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
@@ -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.
@@ -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
+
@@ -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 abstract type for truth value.
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 `TrueClass | FalseClass`
196
+ #### `bool` or `boolish`
197
197
 
198
- We recommend using `bool` because it is more close to Ruby's semantics. If the type of a parameter of a method is `bool`, we usually pass `true` and `false`, and also `nil` or any other values. `TrueClass | FalseClass` rejects other values than `true` and `false`.
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
- #### `void`, `bool`, or `top`?
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_. `bool` implies the value is used as a truth value. `top` is anything else.
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.
@@ -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
@@ -37,6 +37,7 @@ require "rbs/environment_walker"
37
37
  require "rbs/vendorer"
38
38
  require "rbs/validator"
39
39
  require "rbs/factory"
40
+ require "rbs/repository"
40
41
 
41
42
  begin
42
43
  require "rbs/parser"
@@ -23,7 +23,7 @@ module RBS
23
23
  { string: string, location: location }.to_json(*a)
24
24
  end
25
25
 
26
- def concat(string:, location:)
26
+ def concat(string:, location:)
27
27
  @string.concat string
28
28
 
29
29
  if loc = @location
@@ -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 setup(loader)
18
- libs.each do |lib|
19
- loader.add(library: lib)
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
- loader.no_builtin! if no_stdlib
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
- RBS.logger_output = File.open(output, "a")
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
- library_parse(opts, options: options)
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 = EnvironmentLoader.new()
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
- name = decl.location.buffer.name
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 = EnvironmentLoader.new()
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 = EnvironmentLoader.new()
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 = parse_type_name(args[0]).absolute!
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 = EnvironmentLoader.new()
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 = parse_type_name(args[0]).absolute!
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 = EnvironmentLoader.new()
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 = parse_type_name(args[0]).absolute!
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
- options.setup(loader)
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 = EnvironmentLoader.new()
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
- $ tbs -r set paths
513
+ $ rbs -r set paths
492
514
  EOU
493
515
  end.parse!(args)
494
516
 
495
- loader = EnvironmentLoader.new()
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
- if loader.stdlib_root
513
- path = loader.stdlib_root
514
- stdout.puts "#{path}/builtin (#{kind_of[path]}, stdlib)"
515
- end
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 "#{path} (#{kind_of[path]})"
521
- when EnvironmentLoader::GemPath
522
- stdout.puts "#{path.path} (#{kind_of[path.path]}, gem, name=#{path.name}, version=#{path.version})"
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 = EnvironmentLoader.new()
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
- vendorer = Vendorer.new(vendor_dir: vendor_dir)
698
+ loader = options.loader()
691
699
 
692
- if clean
693
- stdout.puts " Deleting #{vendor_dir}..."
694
- vendorer.clean!
695
- end
700
+ args.each do |gem|
701
+ name, version = gem.split(/:/, 2)
702
+
703
+ next unless name
696
704
 
697
- if vendor_stdlib
698
- stdout.puts " Vendoring standard libraries..."
699
- vendorer.stdlib!
705
+ stdout.puts " Loading library: #{name}, version=#{version}..."
706
+ loader.add(library: name, version: version)
700
707
  end
701
708
 
702
- args.each do |gem|
703
- name, version = EnvironmentLoader.parse_library(gem)
709
+ vendorer = Vendorer.new(vendor_dir: vendor_dir, loader: loader)
704
710
 
705
- unless EnvironmentLoader.gem_sig_path(name, version)
706
- stdout.puts " ⚠️ Cannot find rubygem: name=#{name}, version=#{version} 🚨"
707
- else
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 = EnvironmentLoader.new()
733
+ loader = options.loader()
728
734
 
729
735
  syntax_error = false
730
736
  args.each do |path|
731
737
  path = Pathname(path)
732
- loader.each_signature(path) do |sig_path|
733
- Parser.parse_signature(sig_path.read)
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 "#{sig_path}:#{loc.start_line}:#{loc.start_column}: parse error on value: (#{ex.token_str})"
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 "#{sig_path}:#{loc.start_line}:#{loc.start_column}: #{ex.original_message}"
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
- opt_string = options.dirs.map { |dir| "-I #{Shellwords.escape(dir)}"}.concat(options.libs.map { |lib| "-r#{Shellwords.escape(lib)}"}).join(' ')
757
- opt_string.empty? ? nil : opt_string
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
- system(env_hash, *args)
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