Skip to content

Commit 590dd54

Browse files
ydahbyroot
authored andcommitted
Fix parser error positions for newlines and multibyte characters
1 parent c484f8f commit 590dd54

4 files changed

Lines changed: 40 additions & 23 deletions

File tree

‎ext/json/ext/parser/parser.c‎

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -592,19 +592,15 @@ static void cursor_position(JSON_ParserState *state, long *line_out, long *colum
592592
}
593593

594594
const char *cursor = state->cursor;
595-
long column = 0;
596595
long line = 1;
597596

598-
while (cursor >= state->start) {
599-
if (*cursor-- == '\n') {
600-
line++;
601-
break;
602-
}
603-
column++;
597+
while (cursor > state->start && cursor[-1] != '\n') {
598+
cursor--;
604599
}
600+
long column = rb_enc_strlen(cursor, state->cursor, enc_utf8) + 1;
605601

606-
while (cursor >= state->start) {
607-
if (*cursor-- == '\n') {
602+
while (cursor > state->start) {
603+
if (*--cursor == '\n') {
608604
line++;
609605
}
610606
}

‎java/src/json/ext/Parser.java‎

Lines changed: 7 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎lib/json/common.rb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class ParserError < JSONError
138138
# Is +nil+ when raised by JSON::ResumableParser.
139139
attr_reader :line
140140

141-
# Column number where the parser encountered an error.
141+
# One-based column number where the parser encountered an error, counted in Unicode codepoints.
142142
# Is +nil+ when raised by JSON::ResumableParser.
143143
attr_reader :column
144144

‎test/json/json_parser_test.rb‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,33 @@ def test_parse_error_position
935935
assert_equal 17, error.column
936936
end
937937

938+
def test_parse_error_position_at_newline
939+
omit "JRuby string decoding errors don't contain positions" if RUBY_ENGINE == "jruby"
940+
941+
["[1,\n\"ab\ncd\"]", "[1,\n\"日本\ncd\"]"].each do |json|
942+
error = assert_raise(JSON::ParserError) { JSON.parse(json) }
943+
assert_equal 2, error.line
944+
assert_equal 4, error.column
945+
assert_include error.message, 'at line 2 column 4'
946+
end
947+
end
948+
949+
def test_parse_error_position_with_multibyte_characters
950+
{
951+
'["日本", @]' => [1, 8],
952+
"[\n\"é日😀\", @" => [2, 8],
953+
'{"日本":{"a":1,"a":2}}' => [1, 7],
954+
'["日本",' => [1, 7],
955+
"[1,\n" => [2, 1],
956+
'' => [1, 1],
957+
}.each do |json, (line, column)|
958+
error = assert_raise(JSON::ParserError) { JSON.parse(json) }
959+
assert_equal line, error.line
960+
assert_equal column, error.column
961+
assert_include error.message, "at line #{line} column #{column}"
962+
end
963+
end
964+
938965
def test_parse_error_json_path_on_load
939966
assert_parse_error_at "$" do
940967
JSON.load('{"a": {"b": {"c":', -> (obj) {

0 commit comments

Comments
 (0)