From 84edc0516154b25d40b0c92c135549aa75d87aaa Mon Sep 17 00:00:00 2001 From: Takeshi Watanabe Date: Fri, 1 May 2026 11:45:25 +0900 Subject: [PATCH] Convert character offset to byte offset in parse_inline_*_annotation Parser.parse_inline_leading_annotation and parse_inline_trailing_annotation were passing character offsets directly to the C parser, which expects byte offsets since #2863. With ASCII-only input the two coincide so the bug was hidden, but multibyte content (e.g. a Japanese comment preceding the annotation) caused the C parser to start at an invalid byte position and emit a parsing error. Apply the existing byte_range helper to convert character offsets to byte offsets, matching the pattern used by parse_type, parse_method_type, and other parse_* methods. The existing tests for parse_inline_*_annotation used ASCII-only input which would not catch this class of bug, so multibyte regression tests are added. --- lib/rbs/parser_aux.rb | 6 ++++-- test/rbs/inline_annotation_parsing_test.rb | 24 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/rbs/parser_aux.rb b/lib/rbs/parser_aux.rb index 974c54e20..ff14f951d 100644 --- a/lib/rbs/parser_aux.rb +++ b/lib/rbs/parser_aux.rb @@ -121,12 +121,14 @@ def self.buffer(source) def self.parse_inline_leading_annotation(source, range, variables: []) buf = buffer(source) - _parse_inline_leading_annotation(buf, range.begin || 0, range.end || buf.last_position, variables) + byte_range = byte_range(range, buf.content) + _parse_inline_leading_annotation(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables) end def self.parse_inline_trailing_annotation(source, range, variables: []) buf = buffer(source) - _parse_inline_trailing_annotation(buf, range.begin || 0, range.end || buf.last_position, variables) + byte_range = byte_range(range, buf.content) + _parse_inline_trailing_annotation(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables) end def self.byte_range(char_range, content) diff --git a/test/rbs/inline_annotation_parsing_test.rb b/test/rbs/inline_annotation_parsing_test.rb index b76c8533c..266db36b8 100644 --- a/test/rbs/inline_annotation_parsing_test.rb +++ b/test/rbs/inline_annotation_parsing_test.rb @@ -559,4 +559,28 @@ def test_error__module_self Parser.parse_inline_leading_annotation("@rbs module-self: foo", 0...) end end + + def test_parse__trailing_assertion__multibyte_offset + buffer = Buffer.new(name: Pathname("test.rb"), content: "日本語\n: String") + + Parser.parse_inline_trailing_annotation(buffer, 4...12).tap do |annot| + assert_instance_of AST::Ruby::Annotations::NodeTypeAssertion, annot + assert_equal ": String", annot.location.source + assert_equal ":", annot.prefix_location.source + assert_equal "String", annot.type.location.source + end + end + + def test_parse__leading_annotation__multibyte_offset + buffer = Buffer.new(name: Pathname("test.rb"), content: "日本語のコメント\n@rbs return: String -- 戻り値の説明") + + Parser.parse_inline_leading_annotation(buffer, 9...).tap do |annot| + assert_instance_of AST::Ruby::Annotations::ReturnTypeAnnotation, annot + assert_equal "@rbs return: String -- 戻り値の説明", annot.location.source + assert_equal "return", annot.return_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.return_type.location.source + assert_equal "-- 戻り値の説明", annot.comment_location.source + end + end end