[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
|
class JSONError < StandardError; end
|
||||||
|
|
||||||
# This exception is raised if a parser error occurs.
|
# 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
|
# This exception is raised if the nesting of parsed data structures is too
|
||||||
# deep.
|
# deep.
|
||||||
|
@ -395,6 +395,23 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
|
|||||||
{
|
{
|
||||||
unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 1];
|
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;
|
const char *ptr = state->cursor;
|
||||||
size_t len = ptr ? strnlen(ptr, PARSE_ERROR_FRAGMENT_LEN) : 0;
|
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;
|
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
|
#ifdef RBIMPL_ATTR_NORETURN
|
||||||
@ -508,11 +532,11 @@ json_eat_comments(JSON_ParserState *state)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
raise_parse_error("unexpected token at '%s'", state);
|
raise_parse_error("unexpected token '%s'", state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
return json_push_value(state, config, Qnil);
|
||||||
}
|
}
|
||||||
|
|
||||||
raise_parse_error("unexpected token at '%s'", state);
|
raise_parse_error("unexpected token '%s'", state);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
if ((state->end - state->cursor >= 4) && (memcmp(state->cursor, "true", 4) == 0)) {
|
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);
|
return json_push_value(state, config, Qtrue);
|
||||||
}
|
}
|
||||||
|
|
||||||
raise_parse_error("unexpected token at '%s'", state);
|
raise_parse_error("unexpected token '%s'", state);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
// Note: memcmp with a small power of two compile to an integer comparison
|
// 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);
|
return json_push_value(state, config, Qfalse);
|
||||||
}
|
}
|
||||||
|
|
||||||
raise_parse_error("unexpected token at '%s'", state);
|
raise_parse_error("unexpected token '%s'", state);
|
||||||
break;
|
break;
|
||||||
case 'N':
|
case 'N':
|
||||||
// Note: memcmp with a small power of two compile to an integer comparison
|
// 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);
|
return json_push_value(state, config, CNaN);
|
||||||
}
|
}
|
||||||
|
|
||||||
raise_parse_error("unexpected token at '%s'", state);
|
raise_parse_error("unexpected token '%s'", state);
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
if (config->allow_nan && (state->end - state->cursor >= 8) && (memcmp(state->cursor, "Infinity", 8) == 0)) {
|
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);
|
return json_push_value(state, config, CInfinity);
|
||||||
}
|
}
|
||||||
|
|
||||||
raise_parse_error("unexpected token at '%s'", state);
|
raise_parse_error("unexpected token '%s'", state);
|
||||||
break;
|
break;
|
||||||
case '-':
|
case '-':
|
||||||
// Note: memcmp with a small power of two compile to an integer comparison
|
// 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;
|
state->cursor += 9;
|
||||||
return json_push_value(state, config, CMinusInfinity);
|
return json_push_value(state, config, CMinusInfinity);
|
||||||
} else {
|
} else {
|
||||||
raise_parse_error("unexpected token at '%s'", state);
|
raise_parse_error("unexpected token '%s'", state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fallthrough
|
// Fallthrough
|
||||||
|
@ -15,15 +15,19 @@ class JSONExtParserTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_error_messages
|
def test_error_messages
|
||||||
ex = assert_raise(ParserError) { parse('Infinity') }
|
ex = assert_raise(ParserError) { parse('Infinity') }
|
||||||
assert_equal "unexpected token at 'Infinity'", ex.message
|
|
||||||
|
|
||||||
unless RUBY_PLATFORM =~ /java/
|
unless RUBY_PLATFORM =~ /java/
|
||||||
|
assert_equal "unexpected token 'Infinity' at line 1 column 1", ex.message
|
||||||
|
end
|
||||||
|
|
||||||
ex = assert_raise(ParserError) { parse('-Infinity') }
|
ex = assert_raise(ParserError) { parse('-Infinity') }
|
||||||
assert_equal "unexpected token at '-Infinity'", ex.message
|
unless RUBY_PLATFORM =~ /java/
|
||||||
|
assert_equal "unexpected token '-Infinity' at line 1 column 1", ex.message
|
||||||
end
|
end
|
||||||
|
|
||||||
ex = assert_raise(ParserError) { parse('NaN') }
|
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
|
end
|
||||||
|
|
||||||
if GC.respond_to?(:stress=)
|
if GC.respond_to?(:stress=)
|
||||||
|
@ -638,7 +638,7 @@ class JSONParserTest < Test::Unit::TestCase
|
|||||||
error = assert_raise(JSON::ParserError) do
|
error = assert_raise(JSON::ParserError) do
|
||||||
JSON.parse('{"foo": ' + ('A' * 500) + '}')
|
JSON.parse('{"foo": ' + ('A' * 500) + '}')
|
||||||
end
|
end
|
||||||
assert_operator 60, :>, error.message.bytesize
|
assert_operator 80, :>, error.message.bytesize
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_parse_error_incomplete_hash
|
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"}')
|
JSON.parse('{"input":{"firstName":"Bob","lastName":"Mob","email":"bob@example.com"}')
|
||||||
end
|
end
|
||||||
if RUBY_ENGINE == "ruby"
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -654,16 +654,16 @@ class JSONParserTest < Test::Unit::TestCase
|
|||||||
omit "C ext only test" unless RUBY_ENGINE == "ruby"
|
omit "C ext only test" unless RUBY_ENGINE == "ruby"
|
||||||
|
|
||||||
error = assert_raise(JSON::ParserError) { JSON.parse("あああああああああああああああああああああああ") }
|
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あああああああああああああああああああああああ") }
|
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あああああああああああああああああああああああ") }
|
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あああああああああああああああああああああああ") }
|
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
|
end
|
||||||
|
|
||||||
def test_parse_leading_slash
|
def test_parse_leading_slash
|
||||||
|
Loading…
x
Reference in New Issue
Block a user