rbs 4.1.0.pre.2 → 4.1.0

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +37 -0
  3. data/.github/workflows/bundle-update.yml +1 -1
  4. data/.github/workflows/c-check.yml +11 -6
  5. data/.github/workflows/comments.yml +2 -2
  6. data/.github/workflows/dependabot.yml +1 -1
  7. data/.github/workflows/jruby.yml +15 -3
  8. data/.github/workflows/milestone.yml +9 -1
  9. data/.github/workflows/ruby.yml +4 -4
  10. data/.github/workflows/rust.yml +10 -8
  11. data/.github/workflows/truffleruby.yml +1 -1
  12. data/.github/workflows/typecheck.yml +2 -2
  13. data/.github/workflows/wasm.yml +4 -2
  14. data/.github/workflows/windows.yml +2 -2
  15. data/.gitignore +3 -2
  16. data/CHANGELOG.md +88 -0
  17. data/Dockerfile.jruby +53 -0
  18. data/Rakefile +16 -29
  19. data/Steepfile +2 -0
  20. data/core/array.rbs +228 -165
  21. data/core/float.rbs +0 -24
  22. data/core/match_data.rbs +1 -1
  23. data/core/pathname.rbs +0 -10
  24. data/core/ractor.rbs +0 -10
  25. data/core/rubygems/errors.rbs +4 -1
  26. data/core/rubygems/requirement.rbs +0 -10
  27. data/core/rubygems/rubygems.rbs +4 -1
  28. data/core/rubygems/specification.rbs +8 -0
  29. data/core/rubygems/version.rbs +0 -160
  30. data/core/thread.rbs +3 -8
  31. data/docs/CONTRIBUTING.md +1 -1
  32. data/docs/release.md +69 -0
  33. data/ext/rbs_extension/ast_translation.c +2 -2
  34. data/ext/rbs_extension/legacy_location.c +11 -6
  35. data/lib/rbs/parser_aux.rb +4 -2
  36. data/lib/rbs/prototype/rbi.rb +193 -25
  37. data/lib/rbs/version.rb +1 -1
  38. data/lib/rbs/wasm/runtime.rb +7 -28
  39. data/lib/rbs_jars.rb +39 -0
  40. data/lib/rdoc_plugin/parser.rb +5 -0
  41. data/rbs.gemspec +16 -3
  42. data/sig/prototype/rbi.rbs +33 -4
  43. data/src/lexer.c +97 -93
  44. data/src/lexer.re +1 -1
  45. data/src/lexstate.c +6 -2
  46. data/src/util/rbs_allocator.c +13 -4
  47. data/stdlib/delegate/0/delegator.rbs +2 -1
  48. data/stdlib/digest/0/digest.rbs +10 -4
  49. data/stdlib/erb/0/erb.rbs +1 -1
  50. data/stdlib/ipaddr/0/ipaddr.rbs +0 -5
  51. data/stdlib/monitor/0/monitor.rbs +2 -2
  52. data/stdlib/openssl/0/openssl.rbs +39 -33
  53. data/stdlib/tempfile/0/manifest.yaml +3 -0
  54. data/stdlib/timeout/0/timeout.rbs +0 -5
  55. data/stdlib/uri/0/generic.rbs +0 -5
  56. data/stdlib/zlib/0/zstream.rbs +0 -1
  57. data/wasm/README.md +4 -3
  58. data/wasm/rbs_wasm.c +12 -0
  59. metadata +6 -1
data/core/float.rbs CHANGED
@@ -1222,8 +1222,6 @@ Float::EPSILON: Float
1222
1222
  #
1223
1223
  Float::INFINITY: Float
1224
1224
 
1225
- Float::Infinity: Float
1226
-
1227
1225
  # <!-- rdoc-file=numeric.c -->
1228
1226
  # The number of base digits for the `double` data type.
1229
1227
  #
@@ -1292,25 +1290,3 @@ Float::NAN: Float
1292
1290
  # decimal.
1293
1291
  #
1294
1292
  Float::RADIX: Integer
1295
-
1296
- # Deprecated, do not use.
1297
- #
1298
- # Represents the rounding mode for floating point addition at the start time.
1299
- #
1300
- # Usually defaults to 1, rounding to the nearest number.
1301
- #
1302
- # Other modes include:
1303
- #
1304
- # -1
1305
- # : Indeterminable
1306
- # 0
1307
- # : Rounding towards zero
1308
- # 1
1309
- # : Rounding to the nearest number
1310
- # 2
1311
- # : Rounding towards positive infinity
1312
- # 3
1313
- # : Rounding towards negative infinity
1314
- #
1315
- #
1316
- Float::ROUNDS: Integer
data/core/match_data.rbs CHANGED
@@ -92,7 +92,7 @@ class MatchData
92
92
  # #<MatchData "hoge" foo:nil foo:"oge" foo:nil>
93
93
  # m[:foo] #=> "oge"
94
94
  #
95
- def []: (capture backref, ?nil) -> String?
95
+ def []: %a{implicitly-returns-nil} (capture backref, ?nil) -> String
96
96
  | (int start, int length) -> Array[String?]
97
97
  | (range[int?] range) -> Array[String?]
98
98
 
data/core/pathname.rbs CHANGED
@@ -234,11 +234,6 @@ class Pathname
234
234
  #
235
235
  def +: (Pathname | String | _ToStr other) -> Pathname
236
236
 
237
- # <!--
238
- # rdoc-file=pathname_builtin.rb
239
- # - /(other)
240
- # -->
241
- #
242
237
  alias / +
243
238
 
244
239
  # <!--
@@ -271,11 +266,6 @@ class Pathname
271
266
  #
272
267
  def ==: (untyped) -> bool
273
268
 
274
- # <!--
275
- # rdoc-file=pathname_builtin.rb
276
- # - ===(other)
277
- # -->
278
- #
279
269
  def ===: (untyped) -> bool
280
270
 
281
271
  # <!--
data/core/ractor.rbs CHANGED
@@ -470,11 +470,6 @@ class Ractor
470
470
  #
471
471
  def self.store_if_absent: [A] (Symbol) { (nil) -> A } -> A
472
472
 
473
- # <!--
474
- # rdoc-file=ractor.rb
475
- # - <<(...)
476
- # -->
477
- #
478
473
  alias << send
479
474
 
480
475
  # <!--
@@ -746,11 +741,6 @@ class Ractor
746
741
  # Port objects transmit messages between Ractors.
747
742
  #
748
743
  class Port[T = untyped]
749
- # <!--
750
- # rdoc-file=ractor.rb
751
- # - <<(obj, move: false)
752
- # -->
753
- #
754
744
  alias << send
755
745
 
756
746
  # <!--
@@ -17,7 +17,7 @@
17
17
  # Further RubyGems documentation can be found at:
18
18
  #
19
19
  # * [RubyGems Guides](https://guides.rubygems.org)
20
- # * [RubyGems API](https://www.rubydoc.info/github/ruby/rubygems) (also
20
+ # * [RubyGems API](https://guides.rubygems.org/rubygems-org-api/) (also
21
21
  # available from `gem server`)
22
22
  #
23
23
  # ## RubyGems Plugins
@@ -90,6 +90,9 @@
90
90
  #
91
91
  # -The RubyGems Team
92
92
  #
93
+ # <!-- rdoc-file=lib/rubygems/version.rb -->
94
+ # Workaround for directly loading Gem::Version in some cases
95
+ #
93
96
  module Gem
94
97
  # <!-- rdoc-file=lib/rubygems/errors.rb -->
95
98
  # Raised when RubyGems is unable to load or activate a gem. Contains the name
@@ -123,18 +123,8 @@ module Gem
123
123
  #
124
124
  def satisfied_by?: (Gem::Version version) -> bool
125
125
 
126
- # <!--
127
- # rdoc-file=lib/rubygems/requirement.rb
128
- # - ===(version)
129
- # -->
130
- #
131
126
  alias === satisfied_by?
132
127
 
133
- # <!--
134
- # rdoc-file=lib/rubygems/requirement.rb
135
- # - =~(version)
136
- # -->
137
- #
138
128
  alias =~ satisfied_by?
139
129
 
140
130
  # <!--
@@ -17,7 +17,7 @@
17
17
  # Further RubyGems documentation can be found at:
18
18
  #
19
19
  # * [RubyGems Guides](https://guides.rubygems.org)
20
- # * [RubyGems API](https://www.rubydoc.info/github/ruby/rubygems) (also
20
+ # * [RubyGems API](https://guides.rubygems.org/rubygems-org-api/) (also
21
21
  # available from `gem server`)
22
22
  #
23
23
  # ## RubyGems Plugins
@@ -90,6 +90,9 @@
90
90
  #
91
91
  # -The RubyGems Team
92
92
  #
93
+ # <!-- rdoc-file=lib/rubygems/version.rb -->
94
+ # Workaround for directly loading Gem::Version in some cases
95
+ #
93
96
  module Gem
94
97
  interface _HashLike[K, V]
95
98
  def each_pair: () { ([ K, V ]) -> untyped } -> self
@@ -19,5 +19,13 @@
19
19
  # #metadata for restrictions on the format and size of metadata items you may
20
20
  # add to a specification.
21
21
  #
22
+ # Specifications must be deterministic, as in the example above. For instance,
23
+ # you cannot define attributes conditionally:
24
+ #
25
+ # # INVALID: do not do this.
26
+ # unless RUBY_ENGINE == "jruby"
27
+ # s.extensions << "ext/example/extconf.rb"
28
+ # end
29
+ #
22
30
  class Gem::Specification < Gem::BasicSpecification
23
31
  end
@@ -1,165 +1,5 @@
1
1
  %a{annotate:rdoc:skip}
2
2
  module Gem
3
- # <!-- rdoc-file=lib/rubygems/version.rb -->
4
- # The Version class processes string versions into comparable values. A version
5
- # string should normally be a series of numbers separated by periods. Each part
6
- # (digits separated by periods) is considered its own number, and these are used
7
- # for sorting. So for instance, 3.10 sorts higher than 3.2 because ten is
8
- # greater than two.
9
- #
10
- # If any part contains letters (currently only a-z are supported) then that
11
- # version is considered prerelease. Versions with a prerelease part in the Nth
12
- # part sort less than versions with N-1 parts. Prerelease parts are sorted
13
- # alphabetically using the normal Ruby string sorting rules. If a prerelease
14
- # part contains both letters and numbers, it will be broken into multiple parts
15
- # to provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is greater
16
- # than 1.0.a9).
17
- #
18
- # Prereleases sort between real releases (newest to oldest):
19
- #
20
- # 1. 1.0
21
- # 2. 1.0.b1
22
- # 3. 1.0.a.2
23
- # 4. 0.9
24
- #
25
- # If you want to specify a version restriction that includes both prereleases
26
- # and regular releases of the 1.x series this is the best way:
27
- #
28
- # s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'
29
- #
30
- # ## How Software Changes
31
- #
32
- # Users expect to be able to specify a version constraint that gives them some
33
- # reasonable expectation that new versions of a library will work with their
34
- # software if the version constraint is true, and not work with their software
35
- # if the version constraint is false. In other words, the perfect system will
36
- # accept all compatible versions of the library and reject all incompatible
37
- # versions.
38
- #
39
- # Libraries change in 3 ways (well, more than 3, but stay focused here!).
40
- #
41
- # 1. The change may be an implementation detail only and have no effect on the
42
- # client software.
43
- # 2. The change may add new features, but do so in a way that client software
44
- # written to an earlier version is still compatible.
45
- # 3. The change may change the public interface of the library in such a way
46
- # that old software is no longer compatible.
47
- #
48
- # Some examples are appropriate at this point. Suppose I have a Stack class
49
- # that supports a `push` and a `pop` method.
50
- #
51
- # ### Examples of Category 1 changes:
52
- #
53
- # * Switch from an array based implementation to a linked-list based
54
- # implementation.
55
- # * Provide an automatic (and transparent) backing store for large stacks.
56
- #
57
- # ### Examples of Category 2 changes might be:
58
- #
59
- # * Add a `depth` method to return the current depth of the stack.
60
- # * Add a `top` method that returns the current top of stack (without changing
61
- # the stack).
62
- # * Change `push` so that it returns the item pushed (previously it had no
63
- # usable return value).
64
- #
65
- # ### Examples of Category 3 changes might be:
66
- #
67
- # * Changes `pop` so that it no longer returns a value (you must use `top` to
68
- # get the top of the stack).
69
- # * Rename the methods to `push_item` and `pop_item`.
70
- #
71
- # ## RubyGems Rational Versioning
72
- #
73
- # * Versions shall be represented by three non-negative integers, separated by
74
- # periods (e.g. 3.1.4). The first integers is the "major" version number,
75
- # the second integer is the "minor" version number, and the third integer is
76
- # the "build" number.
77
- #
78
- # * A category 1 change (implementation detail) will increment the build
79
- # number.
80
- #
81
- # * A category 2 change (backwards compatible) will increment the minor
82
- # version number and reset the build number.
83
- #
84
- # * A category 3 change (incompatible) will increment the major build number
85
- # and reset the minor and build numbers.
86
- #
87
- # * Any "public" release of a gem should have a different version. Normally
88
- # that means incrementing the build number. This means a developer can
89
- # generate builds all day long, but as soon as they make a public release,
90
- # the version must be updated.
91
- #
92
- # ### Examples
93
- #
94
- # Let's work through a project lifecycle using our Stack example from above.
95
- #
96
- # Version 0.0.1
97
- # : The initial Stack class is release.
98
- #
99
- # Version 0.0.2
100
- # : Switched to a linked=list implementation because it is cooler.
101
- #
102
- # Version 0.1.0
103
- # : Added a `depth` method.
104
- #
105
- # Version 1.0.0
106
- # : Added `top` and made `pop` return nil (`pop` used to return the old top
107
- # item).
108
- #
109
- # Version 1.1.0
110
- # : `push` now returns the value pushed (it used it return nil).
111
- #
112
- # Version 1.1.1
113
- # : Fixed a bug in the linked list implementation.
114
- #
115
- # Version 1.1.2
116
- # : Fixed a bug introduced in the last fix.
117
- #
118
- #
119
- # Client A needs a stack with basic push/pop capability. They write to the
120
- # original interface (no `top`), so their version constraint looks like:
121
- #
122
- # gem 'stack', '>= 0.0'
123
- #
124
- # Essentially, any version is OK with Client A. An incompatible change to the
125
- # library will cause them grief, but they are willing to take the chance (we
126
- # call Client A optimistic).
127
- #
128
- # Client B is just like Client A except for two things: (1) They use the `depth`
129
- # method and (2) they are worried about future incompatibilities, so they write
130
- # their version constraint like this:
131
- #
132
- # gem 'stack', '~> 0.1'
133
- #
134
- # The `depth` method was introduced in version 0.1.0, so that version or
135
- # anything later is fine, as long as the version stays below version 1.0 where
136
- # incompatibilities are introduced. We call Client B pessimistic because they
137
- # are worried about incompatible future changes (it is OK to be pessimistic!).
138
- #
139
- # ## Preventing Version Catastrophe:
140
- #
141
- # From:
142
- # https://www.zenspider.com/ruby/2008/10/rubygems-how-to-preventing-catastrophe.
143
- # html
144
- #
145
- # Let's say you're depending on the fnord gem version 2.y.z. If you specify your
146
- # dependency as ">= 2.0.0" then, you're good, right? What happens if fnord 3.0
147
- # comes out and it isn't backwards compatible with 2.y.z? Your stuff will break
148
- # as a result of using ">=". The better route is to specify your dependency with
149
- # an "approximate" version specifier ("~>"). They're a tad confusing, so here is
150
- # how the dependency specifiers work:
151
- #
152
- # Specification From ... To (exclusive)
153
- # ">= 3.0" 3.0 ... &infin;
154
- # "~> 3.0" 3.0 ... 4.0
155
- # "~> 3.0.0" 3.0.0 ... 3.1
156
- # "~> 3.5" 3.5 ... 4.0
157
- # "~> 3.5.0" 3.5.0 ... 3.6
158
- # "~> 3" 3.0 ... 4.0
159
- #
160
- # For the last example, single-digit versions are automatically extended with a
161
- # zero to give a sensible result.
162
- #
163
3
  class Version
164
4
  include Comparable
165
5
 
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
@@ -1751,11 +1751,6 @@ end
1751
1751
  # See Thread::Queue for an example of how a Thread::SizedQueue works.
1752
1752
  #
1753
1753
  class Thread::SizedQueue[E = untyped] < Thread::Queue[E]
1754
- # <!--
1755
- # rdoc-file=thread_sync.rb
1756
- # - <<(object, non_block = false, timeout: nil)
1757
- # -->
1758
- #
1759
1754
  alias << push
1760
1755
 
1761
1756
  # <!--
data/docs/CONTRIBUTING.md CHANGED
@@ -13,7 +13,7 @@ The RBS repository contains the type definitions of Core API and Standard Librar
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
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 currently retains the tests on Ruby 3.2 as well.
16
+ Note, however, that the CI runs the tests on every non-EOL Ruby -- `3.3` and later as of 2026.
17
17
 
18
18
  **The core API** type definitions are in `core` directory.
19
19
  You will find the familiar class names in the directory, like `string.rbs` or `array.rbs`.
data/docs/release.md ADDED
@@ -0,0 +1,69 @@
1
+ # Releasing RBS
2
+
3
+ Each release ships **two gems**:
4
+
5
+ | Gem | Platform | Parser | How it is built |
6
+ | --- | --- | --- | --- |
7
+ | `rbs-X.Y.Z.gem` | `ruby` (MRI) | C extension | `rake release` (re-builds it) |
8
+ | `rbs-X.Y.Z-java.gem` | `java` (JRuby) | WebAssembly (`lib/rbs/wasm`) | Docker image, pushed manually |
9
+
10
+ The `-java` gem contains no native code — just `rbs_parser.wasm`. The Chicory/ASM
11
+ jars it needs are not shipped in the gem; they are declared as `jar-dependencies`
12
+ requirements and fetched from Maven when the gem is installed. So the gem can be
13
+ built once in any environment and runs on every JRuby.
14
+
15
+ ## Prerequisites
16
+
17
+ - Push rights to the `rbs` gem on RubyGems (`gem signin`). If your account has
18
+ MFA enabled, `gem push` / `rake release` will prompt for an OTP.
19
+ - Docker, for the `-java` gem. The WASI SDK is baked into the image, so there is
20
+ nothing to install on the host.
21
+
22
+ ## Steps
23
+
24
+ ### 1. Release the `ruby` gem
25
+
26
+ Once the version is bumped and committed, run on CRuby:
27
+
28
+ ```console
29
+ $ bundle exec rake release
30
+ ```
31
+
32
+ This re-builds the `ruby`-platform gem and then:
33
+
34
+ - creates the tag `vX.Y.Z`,
35
+ - pushes the current branch and the tag to `origin`,
36
+ - pushes the gem to RubyGems,
37
+ - runs `release:note`, which opens a GitHub **draft** release (with
38
+ `--prerelease` for `*.pre.*` versions) and prints the remaining manual steps.
39
+
40
+ ### 2. Build and push the `java` gem
41
+
42
+ The `java` gem is not built by `rake release`, so build and push it manually:
43
+
44
+ ```console
45
+ # Build from the committed state (the gemspec's file list comes from `git ls-files`).
46
+ $ docker build -f Dockerfile.jruby -t rbs-jruby .
47
+
48
+ # Build rbs_parser.wasm and the -java gem into ./pkg on the host. The Chicory/ASM
49
+ # jars are not bundled; they are fetched from Maven when the gem is installed.
50
+ $ docker run --rm -e RBS_PLATFORM=java -v "$PWD/pkg:/out" rbs-jruby \
51
+ gem build rbs.gemspec -o /out/rbs-X.Y.Z-java.gem
52
+
53
+ $ gem push pkg/rbs-X.Y.Z-java.gem
54
+ ```
55
+
56
+ Optionally confirm it installs and runs on JRuby before pushing:
57
+
58
+ ```console
59
+ $ docker run --rm -v "$PWD/pkg:/pkg" -w /tmp rbs-jruby bash -c \
60
+ 'gem install /pkg/rbs-X.Y.Z-java.gem && ruby -e "require %q{rbs}; puts [RUBY_ENGINE, RBS::VERSION].join(%q{ })"'
61
+ ```
62
+
63
+ ## Notes
64
+
65
+ - Prereleases (`X.Y.Z.pre.N`) are only installed with `gem install rbs --pre`;
66
+ a plain `gem install rbs` is unaffected. On JRuby, `gem install rbs [--pre]`
67
+ resolves to the `-java` gem automatically.
68
+ - The Dockerfile pins the WASI SDK / Chicory / ASM versions to match the
69
+ `wasm` and `jruby` CI workflows. Keep them in sync when bumping.
@@ -196,8 +196,8 @@ static VALUE rbs_intern_namespace(rbs_translation_context_t ctx, rbs_namespace_t
196
196
  return rb_funcallv(RBS_Namespace, intern_brackets(), 2, args);
197
197
  }
198
198
 
199
- static VALUE rbs_intern_type_name(VALUE namespace, VALUE name) {
200
- VALUE args[2] = { namespace, name };
199
+ static VALUE rbs_intern_type_name(VALUE type_namespace, VALUE name) {
200
+ VALUE args[2] = { type_namespace, name };
201
201
  return rb_funcallv(RBS_TypeName, intern_brackets(), 2, args);
202
202
  }
203
203
 
@@ -91,7 +91,7 @@ static rb_data_type_t location_type = {
91
91
  { rbs_loc_mark, (RUBY_DATA_FUNC) rbs_loc_free, rbs_loc_memsize },
92
92
  0,
93
93
  0,
94
- RUBY_TYPED_FREE_IMMEDIATELY
94
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
95
95
  };
96
96
 
97
97
  static VALUE location_s_allocate(VALUE klass) {
@@ -114,10 +114,11 @@ static VALUE location_initialize(VALUE self, VALUE buffer, VALUE start_pos, VALU
114
114
  int end = FIX2INT(end_pos);
115
115
 
116
116
  *loc = (rbs_loc) {
117
- .buffer = buffer,
117
+ .buffer = Qnil,
118
118
  .rg = (rbs_loc_range) { start, end },
119
119
  .children = NULL,
120
120
  };
121
+ RB_OBJ_WRITE(self, &loc->buffer, buffer);
121
122
 
122
123
  return Qnil;
123
124
  }
@@ -127,10 +128,11 @@ static VALUE location_initialize_copy(VALUE self, VALUE other) {
127
128
  rbs_loc *other_loc = rbs_check_location(other);
128
129
 
129
130
  *self_loc = (rbs_loc) {
130
- .buffer = other_loc->buffer,
131
+ .buffer = Qnil,
131
132
  .rg = other_loc->rg,
132
133
  .children = NULL,
133
134
  };
135
+ RB_OBJ_WRITE(self, &self_loc->buffer, other_loc->buffer);
134
136
 
135
137
  if (other_loc->children != NULL) {
136
138
  rbs_loc_legacy_alloc_children(self_loc, other_loc->children->cap);
@@ -193,7 +195,8 @@ VALUE rbs_new_location(VALUE buffer, rbs_range_t rg) {
193
195
  rbs_loc *loc;
194
196
  VALUE obj = TypedData_Make_Struct(RBS_Location, rbs_loc, &location_type, loc);
195
197
 
196
- rbs_loc_init(loc, buffer, (rbs_loc_range) { rg.start.char_pos, rg.end.char_pos });
198
+ rbs_loc_init(loc, Qnil, (rbs_loc_range) { rg.start.char_pos, rg.end.char_pos });
199
+ RB_OBJ_WRITE(obj, &loc->buffer, buffer);
197
200
 
198
201
  return obj;
199
202
  }
@@ -202,7 +205,8 @@ VALUE rbs_new_location2(VALUE buffer, int start_char, int end_char) {
202
205
  rbs_loc *loc;
203
206
  VALUE obj = TypedData_Make_Struct(RBS_Location, rbs_loc, &location_type, loc);
204
207
 
205
- rbs_loc_init(loc, buffer, (rbs_loc_range) { .start = start_char, .end = end_char });
208
+ rbs_loc_init(loc, Qnil, (rbs_loc_range) { .start = start_char, .end = end_char });
209
+ RB_OBJ_WRITE(obj, &loc->buffer, buffer);
206
210
 
207
211
  return obj;
208
212
  }
@@ -211,7 +215,8 @@ static VALUE rbs_new_location_from_loc_range(VALUE buffer, rbs_loc_range rg) {
211
215
  rbs_loc *loc;
212
216
  VALUE obj = TypedData_Make_Struct(RBS_Location, rbs_loc, &location_type, loc);
213
217
 
214
- rbs_loc_init(loc, buffer, rg);
218
+ rbs_loc_init(loc, Qnil, rg);
219
+ RB_OBJ_WRITE(obj, &loc->buffer, buffer);
215
220
 
216
221
  return obj;
217
222
  }
@@ -121,12 +121,14 @@ module RBS
121
121
 
122
122
  def self.parse_inline_leading_annotation(source, range, variables: [])
123
123
  buf = buffer(source)
124
- _parse_inline_leading_annotation(buf, range.begin || 0, range.end || buf.last_position, variables)
124
+ byte_range = byte_range(range, buf.content)
125
+ _parse_inline_leading_annotation(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables)
125
126
  end
126
127
 
127
128
  def self.parse_inline_trailing_annotation(source, range, variables: [])
128
129
  buf = buffer(source)
129
- _parse_inline_trailing_annotation(buf, range.begin || 0, range.end || buf.last_position, variables)
130
+ byte_range = byte_range(range, buf.content)
131
+ _parse_inline_trailing_annotation(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables)
130
132
  end
131
133
 
132
134
  def self.byte_range(char_range, content)