[ruby/json] parser.c: include line and column in error messages
https://github.com/ruby/json/commit/30e35b9ba5
This commit is contained in:
parent
8f008598c3
commit
50ef208369
Notes:
git
2025-05-13 05:12:37 +00:00
@ -230,7 +230,9 @@ module JSON
|
||||
class JSONError < StandardError; end
|
||||
|
||||
# This exception is raised if a parser error occurs.
|
||||
class ParserError < JSONError; end
|
||||
class ParserError < JSONError
|
||||
attr_reader :line, :column
|
||||
end
|
||||
|
||||
# This exception is raised if the nesting of parsed data structures is too
|
||||
# deep.
|
||||
|
@ -395,6 +395,23 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
|
||||
{
|
||||
unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 1];
|
||||
|
||||
const char *cursor = state->cursor;
|
||||
long column = 0;
|
||||
long line = 1;
|
||||
|
||||
while (cursor >= state->start) {
|
||||
if (*cursor-- == '\n') {
|
||||
break;
|
||||
}
|
||||
column++;
|
||||
}
|
||||
|
||||
while (cursor >= state->start) {
|
||||
if (*cursor-- == '\n') {
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
const char *ptr = state->cursor;
|
||||
size_t len = ptr ? strnlen(ptr, PARSE_ERROR_FRAGMENT_LEN) : 0;
|
||||
|
||||
@ -413,7 +430,14 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
|
||||
ptr = (const char *)buffer;
|
||||
}
|
||||
|
||||
rb_enc_raise(enc_utf8, rb_path2class("JSON::ParserError"), format, ptr);
|
||||
VALUE msg = rb_sprintf(format, ptr);
|
||||
VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column);
|
||||
RB_GC_GUARD(msg);
|
||||
|
||||
VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message);
|
||||
rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line));
|
||||
rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column));
|
||||
rb_exc_raise(exc);
|
||||
}
|
||||
|
||||
#ifdef RBIMPL_ATTR_NORETURN
|
||||
@ -508,11 +532,11 @@ json_eat_comments(JSON_ParserState *state)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
raise_parse_error("unexpected token at '%s'", state);
|
||||
raise_parse_error("unexpected token '%s'", state);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
raise_parse_error("unexpected token at '%s'", state);
|
||||
raise_parse_error("unexpected token '%s'", state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -870,7 +894,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
||||
return json_push_value(state, config, Qnil);
|
||||
}
|
||||
|
||||
raise_parse_error("unexpected token at '%s'", state);
|
||||
raise_parse_error("unexpected token '%s'", state);
|
||||
break;
|
||||
case 't':
|
||||
if ((state->end - state->cursor >= 4) && (memcmp(state->cursor, "true", 4) == 0)) {
|
||||
@ -878,7 +902,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
||||
return json_push_value(state, config, Qtrue);
|
||||
}
|
||||
|
||||
raise_parse_error("unexpected token at '%s'", state);
|
||||
raise_parse_error("unexpected token '%s'", state);
|
||||
break;
|
||||
case 'f':
|
||||
// Note: memcmp with a small power of two compile to an integer comparison
|
||||
@ -887,7 +911,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
||||
return json_push_value(state, config, Qfalse);
|
||||
}
|
||||
|
||||
raise_parse_error("unexpected token at '%s'", state);
|
||||
raise_parse_error("unexpected token '%s'", state);
|
||||
break;
|
||||
case 'N':
|
||||
// Note: memcmp with a small power of two compile to an integer comparison
|
||||
@ -896,7 +920,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
||||
return json_push_value(state, config, CNaN);
|
||||
}
|
||||
|
||||
raise_parse_error("unexpected token at '%s'", state);
|
||||
raise_parse_error("unexpected token '%s'", state);
|
||||
break;
|
||||
case 'I':
|
||||
if (config->allow_nan && (state->end - state->cursor >= 8) && (memcmp(state->cursor, "Infinity", 8) == 0)) {
|
||||
@ -904,7 +928,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
||||
return json_push_value(state, config, CInfinity);
|
||||
}
|
||||
|
||||
raise_parse_error("unexpected token at '%s'", state);
|
||||
raise_parse_error("unexpected token '%s'", state);
|
||||
break;
|
||||
case '-':
|
||||
// Note: memcmp with a small power of two compile to an integer comparison
|
||||
@ -913,7 +937,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
||||
state->cursor += 9;
|
||||
return json_push_value(state, config, CMinusInfinity);
|
||||
} else {
|
||||
raise_parse_error("unexpected token at '%s'", state);
|
||||
raise_parse_error("unexpected token '%s'", state);
|
||||
}
|
||||
}
|
||||
// Fallthrough
|
||||
|
@ -15,15 +15,19 @@ class JSONExtParserTest < Test::Unit::TestCase
|
||||
|
||||
def test_error_messages
|
||||
ex = assert_raise(ParserError) { parse('Infinity') }
|
||||
assert_equal "unexpected token at 'Infinity'", ex.message
|
||||
|
||||
unless RUBY_PLATFORM =~ /java/
|
||||
ex = assert_raise(ParserError) { parse('-Infinity') }
|
||||
assert_equal "unexpected token at '-Infinity'", ex.message
|
||||
assert_equal "unexpected token 'Infinity' at line 1 column 1", ex.message
|
||||
end
|
||||
|
||||
ex = assert_raise(ParserError) { parse('-Infinity') }
|
||||
unless RUBY_PLATFORM =~ /java/
|
||||
assert_equal "unexpected token '-Infinity' at line 1 column 1", ex.message
|
||||
end
|
||||
|
||||
ex = assert_raise(ParserError) { parse('NaN') }
|
||||
assert_equal "unexpected token at 'NaN'", ex.message
|
||||
unless RUBY_PLATFORM =~ /java/
|
||||
assert_equal "unexpected token 'NaN' at line 1 column 1", ex.message
|
||||
end
|
||||
end
|
||||
|
||||
if GC.respond_to?(:stress=)
|
||||
|
@ -638,7 +638,7 @@ class JSONParserTest < Test::Unit::TestCase
|
||||
error = assert_raise(JSON::ParserError) do
|
||||
JSON.parse('{"foo": ' + ('A' * 500) + '}')
|
||||
end
|
||||
assert_operator 60, :>, error.message.bytesize
|
||||
assert_operator 80, :>, error.message.bytesize
|
||||
end
|
||||
|
||||
def test_parse_error_incomplete_hash
|
||||
@ -646,7 +646,7 @@ class JSONParserTest < Test::Unit::TestCase
|
||||
JSON.parse('{"input":{"firstName":"Bob","lastName":"Mob","email":"bob@example.com"}')
|
||||
end
|
||||
if RUBY_ENGINE == "ruby"
|
||||
assert_equal %(expected ',' or '}' after object value, got: ''), error.message
|
||||
assert_equal %(expected ',' or '}' after object value, got: '' at line 1 column 72), error.message
|
||||
end
|
||||
end
|
||||
|
||||
@ -654,16 +654,16 @@ class JSONParserTest < Test::Unit::TestCase
|
||||
omit "C ext only test" unless RUBY_ENGINE == "ruby"
|
||||
|
||||
error = assert_raise(JSON::ParserError) { JSON.parse("あああああああああああああああああああああああ") }
|
||||
assert_equal "unexpected character: 'ああああああああああ'", error.message
|
||||
assert_equal "unexpected character: 'ああああああああああ' at line 1 column 1", error.message
|
||||
|
||||
error = assert_raise(JSON::ParserError) { JSON.parse("aあああああああああああああああああああああああ") }
|
||||
assert_equal "unexpected character: 'aああああああああああ'", error.message
|
||||
assert_equal "unexpected character: 'aああああああああああ' at line 1 column 1", error.message
|
||||
|
||||
error = assert_raise(JSON::ParserError) { JSON.parse("abあああああああああああああああああああああああ") }
|
||||
assert_equal "unexpected character: 'abあああああああああ'", error.message
|
||||
assert_equal "unexpected character: 'abあああああああああ' at line 1 column 1", error.message
|
||||
|
||||
error = assert_raise(JSON::ParserError) { JSON.parse("abcあああああああああああああああああああああああ") }
|
||||
assert_equal "unexpected character: 'abcあああああああああ'", error.message
|
||||
assert_equal "unexpected character: 'abcあああああああああ' at line 1 column 1", error.message
|
||||
end
|
||||
|
||||
def test_parse_leading_slash
|
||||
|
Loading…
x
Reference in New Issue
Block a user