rbs 4.0.3 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +37 -0
  3. data/.github/dependabot.yml +1 -1
  4. data/.github/workflows/bundle-update.yml +2 -2
  5. data/.github/workflows/c-check.yml +14 -6
  6. data/.github/workflows/comments.yml +2 -2
  7. data/.github/workflows/dependabot.yml +2 -2
  8. data/.github/workflows/jruby.yml +79 -0
  9. data/.github/workflows/release-gems.yml +164 -0
  10. data/.github/workflows/ruby.yml +43 -3
  11. data/.github/workflows/rust.yml +13 -10
  12. data/.github/workflows/truffleruby.yml +54 -0
  13. data/.github/workflows/typecheck.yml +5 -2
  14. data/.github/workflows/wasm.yml +55 -0
  15. data/.github/workflows/windows.yml +5 -2
  16. data/.gitignore +8 -0
  17. data/CHANGELOG.md +95 -4
  18. data/Dockerfile.jruby +53 -0
  19. data/README.md +3 -3
  20. data/Rakefile +429 -34
  21. data/Steepfile +9 -0
  22. data/config.yml +26 -0
  23. data/core/array.rbs +243 -180
  24. data/core/builtin.rbs +7 -6
  25. data/core/class.rbs +5 -3
  26. data/core/enumerable.rbs +109 -109
  27. data/core/enumerator/product.rbs +5 -5
  28. data/core/enumerator.rbs +28 -28
  29. data/core/file.rbs +24 -1018
  30. data/core/file_constants.rbs +463 -0
  31. data/core/file_stat.rbs +534 -0
  32. data/core/float.rbs +0 -24
  33. data/core/hash.rbs +117 -101
  34. data/core/integer.rbs +21 -58
  35. data/core/io.rbs +25 -7
  36. data/core/kernel.rbs +26 -11
  37. data/core/match_data.rbs +1 -1
  38. data/core/module.rbs +88 -74
  39. data/core/numeric.rbs +3 -0
  40. data/core/object_space/weak_key_map.rbs +7 -7
  41. data/core/pathname.rbs +0 -10
  42. data/core/ractor.rbs +0 -10
  43. data/core/range.rbs +23 -23
  44. data/core/rbs/ops.rbs +154 -0
  45. data/core/rbs/unnamed/argf.rbs +3 -3
  46. data/core/ruby_vm.rbs +40 -0
  47. data/core/rubygems/errors.rbs +4 -1
  48. data/core/rubygems/requirement.rbs +0 -10
  49. data/core/rubygems/rubygems.rbs +4 -1
  50. data/core/rubygems/specification.rbs +8 -0
  51. data/core/rubygems/version.rbs +0 -160
  52. data/core/set.rbs +3 -3
  53. data/core/struct.rbs +16 -16
  54. data/core/thread.rbs +9 -14
  55. data/docs/CONTRIBUTING.md +2 -1
  56. data/docs/inline.md +65 -7
  57. data/docs/rbs_by_example.md +20 -20
  58. data/docs/release.md +151 -0
  59. data/docs/syntax.md +2 -2
  60. data/docs/wasm_serialization.md +80 -0
  61. data/ext/rbs_extension/ast_translation.c +1298 -956
  62. data/ext/rbs_extension/ast_translation.h +4 -0
  63. data/ext/rbs_extension/class_constants.c +2 -0
  64. data/ext/rbs_extension/class_constants.h +1 -0
  65. data/ext/rbs_extension/extconf.rb +1 -0
  66. data/ext/rbs_extension/legacy_location.c +11 -6
  67. data/ext/rbs_extension/main.c +139 -4
  68. data/include/rbs/ast.h +323 -298
  69. data/include/rbs/defines.h +13 -0
  70. data/include/rbs/lexer.h +1 -0
  71. data/include/rbs/serialize.h +39 -0
  72. data/lib/rbs/annotate/rdoc_annotator.rb +27 -31
  73. data/lib/rbs/ast/ruby/annotations.rb +42 -0
  74. data/lib/rbs/ast/ruby/comment_block.rb +6 -4
  75. data/lib/rbs/ast/ruby/declarations.rb +11 -1
  76. data/lib/rbs/ast/ruby/helpers/location_helper.rb +1 -1
  77. data/lib/rbs/ast/ruby/members.rb +40 -1
  78. data/lib/rbs/buffer.rb +48 -11
  79. data/lib/rbs/cli.rb +3 -5
  80. data/lib/rbs/collection/config/lockfile_generator.rb +14 -1
  81. data/lib/rbs/collection/sources/git.rb +6 -0
  82. data/lib/rbs/definition_builder/method_builder.rb +12 -6
  83. data/lib/rbs/environment.rb +10 -3
  84. data/lib/rbs/inline_parser.rb +54 -28
  85. data/lib/rbs/namespace.rb +47 -11
  86. data/lib/rbs/parser_aux.rb +4 -2
  87. data/lib/rbs/prototype/rbi.rb +193 -25
  88. data/lib/rbs/prototype/runtime.rb +2 -0
  89. data/lib/rbs/resolver/type_name_resolver.rb +12 -14
  90. data/lib/rbs/rewriter.rb +70 -0
  91. data/lib/rbs/test/type_check.rb +6 -1
  92. data/lib/rbs/type_name.rb +33 -13
  93. data/lib/rbs/unit_test/type_assertions.rb +9 -0
  94. data/lib/rbs/version.rb +1 -1
  95. data/lib/rbs/wasm/deserializer.rb +213 -0
  96. data/lib/rbs/wasm/location.rb +61 -0
  97. data/lib/rbs/wasm/parser.rb +137 -0
  98. data/lib/rbs/wasm/runtime.rb +196 -0
  99. data/lib/rbs/wasm/serialization_schema.rb +110 -0
  100. data/lib/rbs.rb +14 -2
  101. data/lib/rbs_jars.rb +39 -0
  102. data/lib/rdoc_plugin/parser.rb +5 -0
  103. data/rbs.gemspec +32 -2
  104. data/sig/annotate/rdoc_annotater.rbs +12 -9
  105. data/sig/ast/ruby/annotations.rbs +49 -0
  106. data/sig/ast/ruby/members.rbs +21 -1
  107. data/sig/buffer.rbs +19 -1
  108. data/sig/collection/config/lockfile_generator.rbs +2 -0
  109. data/sig/inline_parser.rbs +2 -0
  110. data/sig/manifest.yaml +0 -1
  111. data/sig/namespace.rbs +20 -0
  112. data/sig/parser.rbs +10 -0
  113. data/sig/prototype/rbi.rbs +33 -4
  114. data/sig/resolver/type_name_resolver.rbs +2 -4
  115. data/sig/rewriter.rbs +45 -0
  116. data/sig/typename.rbs +15 -0
  117. data/sig/unit_test/type_assertions.rbs +4 -0
  118. data/sig/wasm/deserializer.rbs +66 -0
  119. data/sig/wasm/serialization_schema.rbs +13 -0
  120. data/src/ast.c +186 -162
  121. data/src/lexer.c +220 -193
  122. data/src/lexer.re +2 -1
  123. data/src/lexstate.c +10 -1
  124. data/src/parser.c +61 -8
  125. data/src/serialize.c +958 -0
  126. data/src/util/rbs_allocator.c +14 -8
  127. data/stdlib/abbrev/0/array.rbs +1 -1
  128. data/stdlib/csv/0/csv.rbs +5 -5
  129. data/stdlib/delegate/0/delegator.rbs +2 -1
  130. data/stdlib/digest/0/digest.rbs +11 -5
  131. data/stdlib/erb/0/erb.rbs +1 -1
  132. data/stdlib/etc/0/etc.rbs +18 -4
  133. data/stdlib/fileutils/0/fileutils.rbs +21 -21
  134. data/stdlib/ipaddr/0/ipaddr.rbs +0 -5
  135. data/stdlib/json/0/json.rbs +6 -6
  136. data/stdlib/monitor/0/monitor.rbs +2 -2
  137. data/stdlib/openssl/0/openssl.rbs +46 -40
  138. data/stdlib/resolv/0/resolv.rbs +1 -1
  139. data/stdlib/shellwords/0/shellwords.rbs +1 -1
  140. data/stdlib/stringio/0/stringio.rbs +32 -10
  141. data/stdlib/strscan/0/string_scanner.rbs +74 -55
  142. data/stdlib/tempfile/0/manifest.yaml +3 -0
  143. data/stdlib/timeout/0/timeout.rbs +0 -5
  144. data/stdlib/tsort/0/cyclic.rbs +1 -1
  145. data/stdlib/tsort/0/interfaces.rbs +8 -8
  146. data/stdlib/tsort/0/tsort.rbs +9 -9
  147. data/stdlib/uri/0/generic.rbs +0 -5
  148. data/stdlib/zlib/0/gzip_reader.rbs +2 -2
  149. data/stdlib/zlib/0/zstream.rbs +0 -1
  150. data/wasm/README.md +60 -0
  151. data/wasm/rbs_wasm.c +423 -0
  152. metadata +29 -6
  153. data/.github/workflows/milestone.yml +0 -80
  154. data/.vscode/extensions.json +0 -5
  155. data/.vscode/settings.json +0 -19
data/core/struct.rbs CHANGED
@@ -104,8 +104,8 @@
104
104
  # * #inspect (aliased as #to_s): Returns a string representation of `self`.
105
105
  # * #to_h: Returns a hash of the member name/value pairs in `self`.
106
106
  #
107
- class Struct[Elem]
108
- include Enumerable[Elem]
107
+ class Struct[E]
108
+ include Enumerable[E]
109
109
 
110
110
  # The types that can be used when "indexing" into a `Struct` via `[]`, `[]=`, `dig`, and
111
111
  # `deconstruct_keys`.
@@ -362,7 +362,7 @@ class Struct[Elem]
362
362
  #
363
363
  # Related: #members.
364
364
  #
365
- def to_a: () -> Array[Elem]
365
+ def to_a: () -> Array[E]
366
366
 
367
367
  # <!--
368
368
  # rdoc-file=struct.c
@@ -385,8 +385,8 @@ class Struct[Elem]
385
385
  #
386
386
  # Raises ArgumentError if the block returns an inappropriate value.
387
387
  #
388
- def to_h: () -> Hash[Symbol, Elem]
389
- | [K, V] () { (Symbol key, Elem value) -> [K, V] } -> Hash[K, V]
388
+ def to_h: () -> Hash[Symbol, E]
389
+ | [K, V] () { (Symbol key, E value) -> [K, V] } -> Hash[K, V]
390
390
 
391
391
  # <!-- rdoc-file=struct.c -->
392
392
  # Returns the values in `self` as an array:
@@ -441,8 +441,8 @@ class Struct[Elem]
441
441
  #
442
442
  # Related: #each_pair.
443
443
  #
444
- def each: () -> Enumerator[Elem, self]
445
- | () { (Elem value) -> void } -> self
444
+ def each: () -> Enumerator[E, self]
445
+ | () { (E value) -> void } -> self
446
446
 
447
447
  # <!--
448
448
  # rdoc-file=struct.c
@@ -465,8 +465,8 @@ class Struct[Elem]
465
465
  #
466
466
  # Related: #each.
467
467
  #
468
- def each_pair: () -> Enumerator[[Symbol, Elem], self]
469
- | () { ([Symbol, Elem] key_value) -> void } -> self
468
+ def each_pair: () -> Enumerator[[Symbol, E], self]
469
+ | () { ([Symbol, E] key_value) -> void } -> self
470
470
 
471
471
  # <!--
472
472
  # rdoc-file=struct.c
@@ -492,7 +492,7 @@ class Struct[Elem]
492
492
  #
493
493
  # Raises IndexError if `n` is out of range.
494
494
  #
495
- def []: (index name_or_position) -> Elem
495
+ def []: (index name_or_position) -> E
496
496
 
497
497
  # <!--
498
498
  # rdoc-file=struct.c
@@ -521,7 +521,7 @@ class Struct[Elem]
521
521
  #
522
522
  # Raises IndexError if `n` is out of range.
523
523
  #
524
- def []=: (index name_or_position, Elem value) -> Elem
524
+ def []=: (index name_or_position, E value) -> E
525
525
 
526
526
  # <!--
527
527
  # rdoc-file=struct.c
@@ -540,8 +540,8 @@ class Struct[Elem]
540
540
  #
541
541
  # With no block given, returns an Enumerator.
542
542
  #
543
- def select: () -> Enumerator[Elem, Array[Elem]]
544
- | () { (Elem value) -> boolish } -> Array[Elem]
543
+ def select: () -> Enumerator[E, Array[E]]
544
+ | () { (E value) -> boolish } -> Array[E]
545
545
 
546
546
  # <!-- rdoc-file=struct.c -->
547
547
  # With a block given, returns an array of values from `self` for which the block
@@ -591,7 +591,7 @@ class Struct[Elem]
591
591
  # Raises RangeError if any element of the range is negative and out of range;
592
592
  # see Array@Array+Indexes.
593
593
  #
594
- def values_at: (*int | range[int?] positions) -> Array[Elem]
594
+ def values_at: (*int | range[int?] positions) -> Array[E]
595
595
 
596
596
  # <!--
597
597
  # rdoc-file=struct.c
@@ -634,7 +634,7 @@ class Struct[Elem]
634
634
  # f.dig(0, 0, :b, 0) # => 1
635
635
  # f.dig(:b, 0) # => nil
636
636
  #
637
- def dig: (index name_or_position) -> Elem
637
+ def dig: (index name_or_position) -> E
638
638
  | (index name_or_position, untyped, *untyped) -> untyped
639
639
 
640
640
  # <!-- rdoc-file=struct.c -->
@@ -664,5 +664,5 @@ class Struct[Elem]
664
664
  # h = joe.deconstruct_keys(nil)
665
665
  # h # => {:name=>"Joseph Smith, Jr.", :address=>"123 Maple, Anytown NC", :zip=>12345}
666
666
  #
667
- def deconstruct_keys: (Array[index & Hash::_Key]? indices) -> Hash[index & Hash::_Key, Elem]
667
+ def deconstruct_keys: (Array[index & Hash::_Key]? indices) -> Hash[index & Hash::_Key, E]
668
668
  end
data/core/thread.rbs CHANGED
@@ -1565,10 +1565,10 @@ class Thread::Mutex < Object
1565
1565
 
1566
1566
  # <!--
1567
1567
  # rdoc-file=thread_sync.rb
1568
- # - mutex.lock -> self
1568
+ # - mutex.unlock -> self
1569
1569
  # -->
1570
- # Attempts to grab the lock and waits if it isn't available. Raises
1571
- # `ThreadError` if `mutex` was locked by the current thread.
1570
+ # Releases the lock. Raises `ThreadError` if `mutex` wasn't locked by the
1571
+ # current thread.
1572
1572
  #
1573
1573
  def unlock: () -> self
1574
1574
  end
@@ -1604,7 +1604,7 @@ end
1604
1604
  #
1605
1605
  # consumer.join
1606
1606
  #
1607
- class Thread::Queue[Elem = untyped] < Object
1607
+ class Thread::Queue[E = untyped] < Object
1608
1608
  # <!-- rdoc-file=thread_sync.c -->
1609
1609
  # Pushes the given `object` to the queue.
1610
1610
  #
@@ -1719,7 +1719,7 @@ class Thread::Queue[Elem = untyped] < Object
1719
1719
  # If `timeout` seconds have passed and no data is available `nil` is returned.
1720
1720
  # If `timeout` is `0` it returns immediately.
1721
1721
  #
1722
- def pop: (?boolish non_block, ?timeout: _ToF?) -> Elem?
1722
+ def pop: (?boolish non_block, ?timeout: _ToF?) -> E?
1723
1723
 
1724
1724
  # <!--
1725
1725
  # rdoc-file=thread_sync.c
@@ -1729,7 +1729,7 @@ class Thread::Queue[Elem = untyped] < Object
1729
1729
  # -->
1730
1730
  # Pushes the given `object` to the queue.
1731
1731
  #
1732
- def push: (Elem obj) -> void
1732
+ def push: (E obj) -> void
1733
1733
 
1734
1734
  # <!--
1735
1735
  # rdoc-file=thread_sync.rb
@@ -1750,12 +1750,7 @@ end
1750
1750
  #
1751
1751
  # See Thread::Queue for an example of how a Thread::SizedQueue works.
1752
1752
  #
1753
- class Thread::SizedQueue[Elem = untyped] < Thread::Queue[Elem]
1754
- # <!--
1755
- # rdoc-file=thread_sync.rb
1756
- # - <<(object, non_block = false, timeout: nil)
1757
- # -->
1758
- #
1753
+ class Thread::SizedQueue[E = untyped] < Thread::Queue[E]
1759
1754
  alias << push
1760
1755
 
1761
1756
  # <!--
@@ -1813,8 +1808,8 @@ class Thread::SizedQueue[Elem = untyped] < Thread::Queue[Elem]
1813
1808
  # If `timeout` seconds have passed and no space is available `nil` is returned.
1814
1809
  # If `timeout` is `0` it returns immediately. Otherwise it returns `self`.
1815
1810
  #
1816
- def push: (Elem obj, ?boolish non_block) -> void
1817
- | (Elem obj, timeout: _ToF?) -> self?
1811
+ def push: (E obj, ?boolish non_block) -> void
1812
+ | (E obj, timeout: _ToF?) -> self?
1818
1813
  end
1819
1814
 
1820
1815
  class ConditionVariable = Thread::ConditionVariable
data/docs/CONTRIBUTING.md CHANGED
@@ -12,7 +12,8 @@
12
12
  The RBS repository contains the type definitions of Core API and Standard Libraries.
13
13
  There are some discussions whether if it is the best to have them in this repository, but we have them and continue updating the files meanwhile.
14
14
 
15
- The target version of the bundled type definitions is the latest _release_ of Ruby -- `3.1` as of January 2022.
15
+ The target version of the bundled type definitions is the [latest _release_ of Ruby](https://www.ruby-lang.org/en/downloads/branches/) -- `4.0` as of 2026.
16
+ Note, however, that the CI runs the tests on every non-EOL Ruby -- `3.3` and later as of 2026.
16
17
 
17
18
  **The core API** type definitions are in `core` directory.
18
19
  You will find the familiar class names in the directory, like `string.rbs` or `array.rbs`.
data/docs/inline.md CHANGED
@@ -123,10 +123,38 @@ end
123
123
 
124
124
  This creates the types `::API`, `::API::V1`, and `::API::V1::Resources`.
125
125
 
126
+ ### `module-self` constraint
127
+
128
+ The `module-self` constraint declares which classes or modules the module can be mixed into.
129
+
130
+ ```ruby
131
+ # @rbs module-self: _Each[String]
132
+ module Enumerable2
133
+ end
134
+ ```
135
+
136
+ This is equivalent to `module Enumerable2 : _Each[String]` in RBS, meaning `Enumerable2` can only be included in classes that satisfy the `_Each[String]` interface.
137
+
138
+ Multiple `module-self` constraints can be declared with separate annotations:
139
+
140
+ ```ruby
141
+ # @rbs module-self: _Each[String]
142
+ # @rbs module-self: Comparable
143
+ module StringCollection
144
+ end
145
+ ```
146
+
147
+ You can add a description after `--`:
148
+
149
+ ```ruby
150
+ # @rbs module-self: Minitest::Test -- depending on assertion methods
151
+ module TestHelper
152
+ end
153
+ ```
154
+
126
155
  ### Current Limitations
127
156
 
128
157
  - Generic module definitions are not supported
129
- - Module self-type constraints are not supported
130
158
 
131
159
  ## Method Definitions
132
160
 
@@ -135,6 +163,8 @@ Inline RBS supports methods defined using the `def` syntax in Ruby.
135
163
  ```ruby
136
164
  class Calculator
137
165
  def add(x, y) = x+y
166
+
167
+ def self.zero = 0
138
168
  end
139
169
  ```
140
170
 
@@ -205,6 +235,18 @@ The type of both methods is `(Integer, Integer) -> Integer | (Float, Float) -> F
205
235
  > The `@rbs METHOD-TYPE` syntax allows overloads with the `|` operator, just like in RBS files.
206
236
  > Multiple `: METHOD-TYPE` declarations are required for overloads.
207
237
 
238
+ The `#:` syntax can also be used as a trailing annotation to declare the return type of a method:
239
+
240
+ ```ruby
241
+ class Calculator
242
+ def add(x, y) #: Integer
243
+ x + y
244
+ end
245
+
246
+ def subtract(x, y) = x - y #: Integer
247
+ end
248
+ ```
249
+
208
250
  The `@rbs METHOD-TYPE` syntax allows having `...` at the last part.
209
251
 
210
252
  ```ruby
@@ -309,9 +351,10 @@ end
309
351
 
310
352
  ### Current Limitations
311
353
 
312
- - Class methods and singleton methods are not supported
313
- - Only positional and keyword parameters are supported. Splat parameters (`*x`, `**y`) and block parameter (`&block`) are not supported yet.
314
354
  - Method visibility declaration is not supported yet
355
+ - The `class << self` syntax is not supported
356
+ - Top-level method definitions (outside any class/module) are not supported
357
+ - Method definitions with a non-self receiver (e.g. `def obj.foo`) are not supported
315
358
 
316
359
  ## Attributes
317
360
 
@@ -389,6 +432,7 @@ The attribute definitions are ignored because the names are given by string lite
389
432
  ### Current Limitations
390
433
 
391
434
  - Attribute visibility is not supported yet. All attributes are _public_
435
+ - Top-level attribute definitions (outside any class/module) are not supported
392
436
 
393
437
  ## Mixin
394
438
 
@@ -487,7 +531,7 @@ end
487
531
  ```
488
532
 
489
533
  The `@rbs @VAR-NAME: TYPE` syntax enclosed in `class`/`module` syntax declares instance variables.
490
- You can add the documentation of the variable followed by two hyphones (`--`).
534
+ You can add the documentation of the variable followed by two hyphens (`--`).
491
535
 
492
536
  Instance variable declarations must be under the `class`/`module` syntax, and they are ignored if written inside method definitions.
493
537
 
@@ -519,14 +563,16 @@ The types of constants may be automatically inferred when the right-hand side co
519
563
  - **Floats**: `RATE = 3.14` → `Float`
520
564
  - **Booleans**: `ENABLED = true` → `bool`
521
565
  - **Strings**: `NAME = "test"` → `String`
522
- - **Symbols**: `STATUS = :ready` → `:ready`
566
+ - **Symbols**: `STATUS = :ready` → `Symbol`
567
+ - **Nil**: `EMPTY = nil` → `nil`
523
568
 
524
569
  ```ruby
525
570
  MAX_SIZE = 100 # Inferred as Integer
526
571
  PI = 3.14159 # Inferred as Float
527
572
  DEBUG = false # Inferred as bool
528
573
  APP_NAME = "MyApp" # Inferred as String
529
- DEFAULT_MODE = :strict # Inferred as :strict
574
+ DEFAULT_MODE = :strict # Inferred as Symbol
575
+ NONE = nil # Inferred as nil
530
576
  ```
531
577
 
532
578
  ### Explicit Type Annotations
@@ -567,10 +613,22 @@ MyKernel = Kernel #: module-alias
567
613
 
568
614
  This creates new type names that refer to the same class or module as the original.
569
615
 
570
- The annotations can have optional type name to specify the class/module name, for the case it cannot be infered through the right-hand-side of the constant declaration.
616
+ The annotations can have optional type name to specify the class/module name, for the case it cannot be inferred through the right-hand-side of the constant declaration.
571
617
 
572
618
  ```ruby
573
619
  MyObject = object #: class-alias Object
574
620
 
575
621
  MyKernel = kernel #: module-alias Kernel
576
622
  ```
623
+
624
+ ## Skip Annotation
625
+
626
+ The `@rbs skip` annotation makes inline RBS ignore the following declaration.
627
+
628
+ ```ruby
629
+ class Calculator
630
+ # @rbs skip
631
+ def debug_internal
632
+ end
633
+ end
634
+ ```
@@ -107,14 +107,14 @@ end
107
107
  For now, it's safe to ignore them, but they're included for completeness.
108
108
 
109
109
  ```rbs
110
- class Array[Elem]
110
+ class Array[E]
111
111
  def *: (String) -> String
112
- | (Integer) -> Array[Elem]
112
+ | (Integer) -> Array[E]
113
113
  end
114
114
  ```
115
115
 
116
116
  `Array`'s `*` method, when given a `String` returns a `String`. When given an
117
- `Integer`, it returns an `Array` of the same contained type `Elem` (in our example case, `Elem` corresponds to `Integer`).
117
+ `Integer`, it returns an `Array` of the same contained type `E` (in our example case, `E` corresponds to `Integer`).
118
118
 
119
119
  ### Union types
120
120
 
@@ -150,9 +150,9 @@ end
150
150
  ```
151
151
 
152
152
  ```rbs
153
- class Enumerable[Elem]
154
- def first: () -> Elem?
155
- | (Integer) -> Array[Elem]
153
+ class Enumerable[E]
154
+ def first: () -> E?
155
+ | (Integer) -> Array[E]
156
156
  end
157
157
  ```
158
158
 
@@ -160,12 +160,12 @@ end
160
160
 
161
161
  When called with no arguments, the return value will either be an instance of
162
162
  whatever type is contained in the enumerable, or `nil`. We represent that with
163
- the type variable `Elem`, and the `?` suffix nilable marker.
163
+ the type variable `E`, and the `?` suffix nilable marker.
164
164
 
165
165
  When called with an `Integer` positional argument, the return value will be an
166
166
  `Array` of whatever type is contained.
167
167
 
168
- The `?` syntax is a convenient shorthand for a union with nil. An equivalent union type would be `(Elem | nil)`.
168
+ The `?` syntax is a convenient shorthand for a union with nil. An equivalent union type would be `(E | nil)`.
169
169
 
170
170
  ### Keyword Arguments
171
171
 
@@ -222,9 +222,9 @@ end
222
222
  ```
223
223
 
224
224
  ```rbs
225
- class Array[Elem]
226
- def filter: () { (Elem) -> boolish } -> ::Array[Elem]
227
- | () -> ::Enumerator[Elem, ::Array[Elem]]
225
+ class Array[E]
226
+ def filter: () { (E) -> boolish } -> ::Array[E]
227
+ | () -> ::Enumerator[E, ::Array[E]]
228
228
  end
229
229
  ```
230
230
 
@@ -264,13 +264,13 @@ a.collect.with_index {|x, i| x * i}
264
264
  ```
265
265
 
266
266
  ```rbs
267
- class Array[Elem]
268
- def collect: [U] () { (Elem) -> U } -> Array[U]
269
- | () -> Enumerator[Elem, Array[untyped]]
267
+ class Array[E]
268
+ def collect: [U] () { (E) -> U } -> Array[U]
269
+ | () -> Enumerator[E, Array[untyped]]
270
270
  end
271
271
  ```
272
272
 
273
- Type variables can also be introduced in methods. Here, in `Array`'s `#collect` method, we introduce a type variable `U`. The block passed to `#collect` will receive a parameter of type `Elem`, and return a value of type `U`. Then `#collect` will return an `Array` of type `U`.
273
+ Type variables can also be introduced in methods. Here, in `Array`'s `#collect` method, we introduce a type variable `U`. The block passed to `#collect` will receive a parameter of type `E`, and return a value of type `U`. Then `#collect` will return an `Array` of type `U`.
274
274
 
275
275
  In this example, the method receives its signature from the inferred return type of the passed block. When then block is absent, as in when the method returns an `Enumerator`, we can't infer the type, and so the return value of the enumerator can only be described as `Array[untyped]`.
276
276
 
@@ -284,9 +284,9 @@ In this example, the method receives its signature from the inferred return type
284
284
  ```
285
285
 
286
286
  ```rbs
287
- class Enumerable[Elem]
288
- def partition: () { (Elem) -> boolish } -> [Array[Elem], Array[Elem]]
289
- | () -> ::Enumerator[Elem, [Array[Elem], Array[Elem] ]]
287
+ class Enumerable[E]
288
+ def partition: () { (E) -> boolish } -> [Array[E], Array[E]]
289
+ | () -> ::Enumerator[E, [Array[E], Array[E] ]]
290
290
  end
291
291
  ```
292
292
 
@@ -300,9 +300,9 @@ Tuples can be of any size, and they can have mixed types.
300
300
  ```
301
301
 
302
302
  ```rbs
303
- class Enumerable[Elem]
303
+ class Enumerable[E]
304
304
  def to_h: () -> ::Hash[untyped, untyped]
305
- | [T, U] () { (Elem) -> [T, U] } -> ::Hash[T, U]
305
+ | [T, U] () { (E) -> [T, U] } -> ::Hash[T, U]
306
306
  end
307
307
  ```
308
308
 
data/docs/release.md ADDED
@@ -0,0 +1,151 @@
1
+ # Releasing RBS
2
+
3
+ A release is a pull request, a tag, and one workflow run. Everything that leaves
4
+ the repository — both gems and the GitHub release — is produced by the `Release
5
+ gems` workflow, so nothing has to be built or pushed from a laptop.
6
+
7
+ Each release ships **two gems**:
8
+
9
+ | Gem | Platform | Parser |
10
+ | --- | --- | --- |
11
+ | `rbs-X.Y.Z.gem` | `ruby` (MRI) | C extension, compiled on install |
12
+ | `rbs-X.Y.Z-java.gem` | `java` (JRuby) | `rbs_parser.wasm`, built by the workflow |
13
+
14
+ The `-java` gem contains no native code — just `rbs_parser.wasm`. The Chicory/ASM
15
+ jars it needs are not shipped in the gem; they are declared as `jar-dependencies`
16
+ requirements and fetched from Maven when the gem is installed. So the gem can be
17
+ built once in any environment and runs on every JRuby.
18
+
19
+ There are three kinds of release, and they differ in what gets written up:
20
+
21
+ | Version | CHANGELOG section | GitHub release |
22
+ | --- | --- | --- |
23
+ | `X.Y.Z` | The whole cycle since the previous release proper, prereleases included | Published |
24
+ | `X.Y.Z.pre.N` | What changed since `X.Y.Z.pre.N-1` | Published, marked as a prerelease |
25
+ | `X.Y.Z.dev.N` | None | None |
26
+
27
+ `.dev.N` releases are cut from the development line for people who need a change
28
+ early, so they are gems and tags and nothing else.
29
+
30
+ ## Prerequisites
31
+
32
+ Push rights to the `rbs` gem on RubyGems are **not** needed: the workflow
33
+ authenticates through a trusted publisher registered for this repository and
34
+ `release-gems.yml`. What is needed is write access to the repository, since that
35
+ is what lets you dispatch the workflow.
36
+
37
+ ## Steps
38
+
39
+ ### 1. Prepare the release
40
+
41
+ Open a pull request that carries everything the release needs:
42
+
43
+ - `lib/rbs/version.rb` — set `RBS::VERSION` to the version being released.
44
+ - `Gemfile.lock` — run `bundle install` after the bump; the lockfile records the version too.
45
+ - `CHANGELOG.md` — add a section for the new version, directly under the `# CHANGELOG` heading.
46
+ Sections are newest first.
47
+
48
+ Label the pull request `skip-changelog`. It carries no change of its own, and without the label it
49
+ shows up in the next release's list — that is why 4.1.0's changelog contains a `Version 4.1.0`
50
+ entry.
51
+
52
+ `rake gem:changelog` lists the pull requests merged since the last release, already formatted:
53
+
54
+ ```console
55
+ $ bundle exec rake gem:changelog | pbcopy
56
+ ```
57
+
58
+ Where it starts follows `RBS::VERSION`, so bump the version first: a prerelease starts from the
59
+ latest tag, and a release proper skips the prerelease tags and starts from the previous release
60
+ proper. Pass a version to override it (`rake 'gem:changelog[4.1.0]'`). Only the list goes to
61
+ STDOUT, so it pipes cleanly. Pull requests labeled `skip-changelog` are left out and reported on
62
+ STDERR, and pull requests that only touch `rust/` are left out because the crates have their own
63
+ release cycle.
64
+
65
+ On a release proper, the `X.Y.Z.pre.N` sections above the previous release are replaced by the one
66
+ section being written — their pull requests are in it, and the notes they were published with stay
67
+ on their own GitHub releases.
68
+
69
+ Sort the list into the sections below. `rake gem:changelog:json` prints the same pull requests with
70
+ the changed files, labels, and body of each, which is what the sorting is based on.
71
+
72
+ ```markdown
73
+ ## X.Y.Z (YYYY-MM-DD)
74
+
75
+ ### Signature updates
76
+
77
+ ### Language updates
78
+
79
+ ### Library changes
80
+
81
+ #### rbs prototype
82
+
83
+ #### rbs collection
84
+
85
+ ### Miscellaneous
86
+ ```
87
+
88
+ The sections always appear in this order; delete the ones that end up empty, which is most of them
89
+ on a small release. Two things scale with the size of the release:
90
+
91
+ - **Summary paragraphs**, above the first section. A patch release usually has none, 4.1.0 has four
92
+ paragraphs, and 4.0.0 has nine.
93
+ - **A list of the types whose signatures changed**, as the first line of `### Signature updates`,
94
+ written as `**Updated classes/modules/methods:**` followed by the names in backticks. Used on
95
+ `X.Y.0` releases only.
96
+
97
+ The date is the day the gem is released, matching the `vX.Y.Z` tag — not the day this pull request
98
+ is opened. Fix it up before step 2 if the pull request sat for a few days.
99
+
100
+ ### 2. Tag the release
101
+
102
+ Once the pull request is merged, tag the merge commit and push the tag:
103
+
104
+ ```console
105
+ $ git switch master && git pull
106
+ $ git tag "v$(ruby -e 'load "lib/rbs/version.rb"; print RBS::VERSION')"
107
+ $ git push origin --tags
108
+ ```
109
+
110
+ The tag comes before anything is published, so that the gems and the release notes describe a
111
+ commit that is already immutable — and because a tag can be deleted, while a version pushed to
112
+ RubyGems can only be yanked.
113
+
114
+ ### 3. Run the `Release gems` workflow against the tag
115
+
116
+ Dispatch [`release-gems.yml`](../.github/workflows/release-gems.yml) from the Actions tab, picking
117
+ the `vX.Y.Z` tag — **not** a branch — in the ref selector. The trusted publisher has no branch
118
+ condition, so the ref you pick is what decides what gets published; the workflow refuses to run
119
+ unless the tag matches `RBS::VERSION`.
120
+
121
+ It then:
122
+
123
+ - builds `rbs-X.Y.Z.gem`,
124
+ - compiles `rbs_parser.wasm` and builds `rbs-X.Y.Z-java.gem`,
125
+ - checks both: platforms, the C extension on one and its absence on the other, and that the wasm
126
+ module made it into the `java` gem,
127
+ - installs the `java` gem on JRuby and parses with it, so the WebAssembly runtime is exercised
128
+ before anything is published,
129
+ - uploads both gems as an artifact,
130
+ - pushes both to RubyGems through trusted publishing,
131
+ - publishes the GitHub release with the notes from CHANGELOG.md, skipping this last step for
132
+ `.dev.N` versions.
133
+
134
+ Dispatching against a branch runs everything up to the artifact and stops, which is how the build
135
+ is exercised without releasing.
136
+
137
+ ### 4. Start the next development cycle
138
+
139
+ Open another pull request setting `RBS::VERSION` to the next prerelease (`4.1.1` → `4.1.2.pre`),
140
+ with `Gemfile.lock` regenerated, labeled `skip-changelog` like the release pull request itself.
141
+ Without it the version on `master` keeps claiming to be the released version for the whole
142
+ development period, and `rake gem:changelog` reads that version to decide where the next changelog
143
+ starts.
144
+
145
+ ## Notes
146
+
147
+ - Prereleases (`X.Y.Z.pre.N`) are only installed with `gem install rbs --pre`;
148
+ a plain `gem install rbs` is unaffected. On JRuby, `gem install rbs [--pre]`
149
+ resolves to the `-java` gem automatically.
150
+ - `Dockerfile.jruby` pins the WASI SDK / Chicory / ASM versions to match the
151
+ `wasm`, `jruby`, and `release-gems` workflows. Keep them in sync when bumping.
data/docs/syntax.md CHANGED
@@ -196,8 +196,8 @@ It is an alias of `top` type, and you can use `boolish` if we want to allow any
196
196
  We can see an example at the definition of `Enumerable#find`:
197
197
 
198
198
  ```rbs
199
- module Enumerable[Elem, Return]
200
- def find: () { (Elem) -> boolish } -> Elem?
199
+ module Enumerable[E, R]
200
+ def find: () { (E) -> boolish } -> E?
201
201
  end
202
202
  ```
203
203
 
@@ -0,0 +1,80 @@
1
+ # RBS AST binary serialization
2
+
3
+ This document describes the binary format used to move a parsed RBS AST out of
4
+ the parser and into Ruby objects without going through the Ruby C API. It exists
5
+ so that RBS can run on Ruby implementations that cannot load the C extension
6
+ (notably JRuby): the parser runs inside WebAssembly, serializes the result with
7
+ this format, and the host rebuilds `RBS::AST` objects in pure Ruby.
8
+
9
+ The encoder (`rbs_serialize_node`, `src/serialize.c`) and the schema that drives
10
+ the decoder (`RBS::WASM::SerializationSchema`, `lib/rbs/wasm/serialization_schema.rb`)
11
+ are both generated from `config.yml`, so they always agree. The decoder itself
12
+ is `RBS::WASM::Deserializer`.
13
+
14
+ ## Conventions
15
+
16
+ - All multi-byte integers are **little-endian**.
17
+ - `u8`, `u32` are unsigned; `i32` is signed.
18
+ - `str` is a `u32` byte length followed by that many raw bytes (no terminator).
19
+ - A value is reconstructed to mirror exactly what `ast_translation.c` produces,
20
+ including string encodings: string/integer literal nodes are UTF-8, while
21
+ comments, annotations and symbols use the source buffer's encoding.
22
+
23
+ ## Nodes
24
+
25
+ Every node begins with a `u8` **tag**:
26
+
27
+ - `0` — a NULL node (`nil` on the Ruby side).
28
+ - `1..N` — a node type, in the order they appear in `SerializationSchema::SCHEMA`.
29
+ - `SYMBOL_TAG` (`N + 1`) — an interned symbol, followed by `str` (the symbol's
30
+ bytes). Decoded with `String#to_sym`.
31
+
32
+ A few node types are encoded specially, matching their bespoke handling in
33
+ `ast_translation.c`:
34
+
35
+ | Node | Payload after tag | Decoded as |
36
+ | --- | --- | --- |
37
+ | `RBS::AST::Bool` | `u8` | `true` / `false` |
38
+ | `RBS::AST::Integer` | `str` | `String#to_i` |
39
+ | `RBS::AST::String` | `str` | the string (UTF-8) |
40
+ | `RBS::Types::Record::FieldType` | node, then `u8` | `[type, required]` |
41
+ | `RBS::Signature` | node-list, then node-list | `[directives, declarations]` |
42
+ | `RBS::Namespace` | node-list, then `u8` | `RBS::Namespace[path, absolute]` |
43
+ | `RBS::TypeName` | node, then node | `RBS::TypeName[namespace, name]` |
44
+
45
+ Every other node is encoded generically:
46
+
47
+ 1. If the node exposes a location, its **base location** is written (see below),
48
+ followed by one location range per declared child, in order.
49
+ 2. Each field is written in declaration order, encoded by its type (see below).
50
+
51
+ The decoder constructs `Klass.new(location:, **fields)` (omitting `location:`
52
+ for nodes that do not expose one). For `Class`, `Module`, `Interface`,
53
+ `TypeAlias` and `MethodType`, `RBS::AST::TypeParam.resolve_variables` is applied
54
+ to `type_params` first, exactly as the C translation does.
55
+
56
+ ## Fields
57
+
58
+ | Field type | Encoding |
59
+ | --- | --- |
60
+ | node (`rbs_node`, `rbs_type_name`, `rbs_ast_comment`, `rbs_ast_symbol`, ...) | a node (recursive; NULL allowed) |
61
+ | `rbs_node_list` | `u32` count, then that many nodes |
62
+ | `rbs_hash` | `u32` count, then count × (key node, value node) |
63
+ | `rbs_string` | `str` (source encoding) |
64
+ | `bool` | `u8` |
65
+ | enum | `u8` index into the enum's values (see `SCHEMA`) |
66
+ | `rbs_location_range` | a location range |
67
+ | `rbs_location_range_list` | `u32` count, then that many location ranges |
68
+ | `rbs_attr_ivar_name` | `u8` tag: `0` → `nil`, `1` → `false`, `2` → `str` → symbol |
69
+
70
+ ## Location ranges
71
+
72
+ A location range is a `u8` presence flag:
73
+
74
+ - `0` — null range (`nil`, or a node with no location).
75
+ - `1` — followed by `i32` start and `i32` end **character** positions.
76
+
77
+ The base location and child ranges together let the decoder rebuild an
78
+ `RBS::Location` (with its required/optional children) through the public
79
+ `RBS::Location` API, so the same decoder works whether `RBS::Location` is backed
80
+ by the C extension or a pure-Ruby implementation.