diff --git a/src/lexstate.c b/src/lexstate.c index 79f275717..eb4b4939c 100644 --- a/src/lexstate.c +++ b/src/lexstate.c @@ -120,7 +120,7 @@ unsigned int rbs_peek(rbs_lexer_t *lexer) { } bool rbs_next_char(rbs_lexer_t *lexer, unsigned int *codepoint, size_t *byte_len) { - if (RBS_UNLIKELY(lexer->current.byte_pos == lexer->end_pos)) { + if (RBS_UNLIKELY(lexer->current.byte_pos >= lexer->end_pos)) { return false; } diff --git a/test/rbs/type_parsing_test.rb b/test/rbs/type_parsing_test.rb index 9a95f146e..f58c42c68 100644 --- a/test/rbs/type_parsing_test.rb +++ b/test/rbs/type_parsing_test.rb @@ -1074,4 +1074,23 @@ def test_parse__byte_range_incorrect_in_euc_jp assert_equal RBS::TypeName.parse("Foo"), Parser.parse_type(euc, byte_range: 2...).name end + + def test_parse__byte_range_ending_mid_character + source = '"日本語" | Integer' + + # Character count 5 as a byte offset lands inside `本` (bytes 4...7). The + # lexer used to step over the boundary and read the whole string instead. + assert_raises RBS::ParsingError do + Parser.parse_type(source, byte_range: 0...'"日本語"'.size) + end + + Parser.parse_type(source, byte_range: 0...'"日本語"'.bytesize, require_eof: true).tap do |type| + assert_instance_of Types::Literal, type + assert_equal "日本語", type.literal + end + + Parser.parse_type("Integer", byte_range: 0...9999).tap do |type| + assert_instance_of Types::ClassInstance, type + end + end end