🔨 started with user-defined exceptions #301 #244

Added class hierarchy for user-defined exceptions (#244). Integrated parse exceptions 101-103. Parse exceptions include the byte count of the last read character to locate the position of the error (#301).
parent 7b8fd864
...@@ -58,6 +58,7 @@ doxygen: create_output create_links ...@@ -58,6 +58,7 @@ doxygen: create_output create_links
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
$(SED) -i 's@&lt;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberUnsignedType,&#160;NumberFloatType,&#160;AllocatorType&#160;JSONSerializer&#160;&gt;@@g' html/*.html $(SED) -i 's@&lt;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberUnsignedType,&#160;NumberFloatType,&#160;AllocatorType&#160;JSONSerializer&#160;&gt;@@g' html/*.html
$(SED) -i 's@template&lt;template&lt; typename U, typename V, typename... Args &gt; class ObjectType = std::map, template&lt; typename U, typename... Args &gt; class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, class NumberIntegerType = std::int64_t, class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, template&lt; typename U &gt; class AllocatorType = std::allocator, template&lt; typename T, typename SFINAE=void &gt; class JSONSerializer = adl_serializer&gt;@@g' html/*.html
upload: clean doxygen check_output upload: clean doxygen check_output
cd html ; ../scripts/git-update-ghpages nlohmann/json cd html ; ../scripts/git-update-ghpages nlohmann/json
......
...@@ -190,8 +190,10 @@ TEST_CASE("lexer class") ...@@ -190,8 +190,10 @@ TEST_CASE("lexer class")
SECTION("to_unicode") SECTION("to_unicode")
{ {
CHECK(json::lexer::to_unicode(0x1F4A9) == "💩"); // lexer to call to_unicode on
CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range); json::lexer dummy_lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(""), 0);
CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid"); CHECK(dummy_lexer.to_unicode(0x1F4A9) == "💩");
CHECK_THROWS_AS(dummy_lexer.to_unicode(0x200000), json::parse_error);
CHECK_THROWS_WITH(dummy_lexer.to_unicode(0x200000), "[json.exception.parse_error.103] parse error: code points above 0x10FFFF are invalid");
} }
} }
...@@ -90,15 +90,17 @@ TEST_CASE("deserialization") ...@@ -90,15 +90,17 @@ TEST_CASE("deserialization")
std::stringstream ss1, ss2; std::stringstream ss1, ss2;
ss1 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss1 << "[\"foo\",1,2,3,false,{\"one\":1}";
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss2 << "[\"foo\",1,2,3,false,{\"one\":1}";
CHECK_THROWS_AS(json::parse(ss1), std::invalid_argument); CHECK_THROWS_AS(json::parse(ss1), json::parse_error);
CHECK_THROWS_WITH(json::parse(ss2), "parse error - unexpected end of input; expected ']'"); CHECK_THROWS_WITH(json::parse(ss2),
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
} }
SECTION("string") SECTION("string")
{ {
json::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}"; json::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}";
CHECK_THROWS_AS(json::parse(s), std::invalid_argument); CHECK_THROWS_AS(json::parse(s), json::parse_error);
CHECK_THROWS_WITH(json::parse(s), "parse error - unexpected end of input; expected ']'"); CHECK_THROWS_WITH(json::parse(s),
"[json.exception.parse_error.101] parse error at 29: parse error - unexpected end of input; expected ']'");
} }
SECTION("operator<<") SECTION("operator<<")
...@@ -107,8 +109,9 @@ TEST_CASE("deserialization") ...@@ -107,8 +109,9 @@ TEST_CASE("deserialization")
ss1 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss1 << "[\"foo\",1,2,3,false,{\"one\":1}";
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss2 << "[\"foo\",1,2,3,false,{\"one\":1}";
json j; json j;
CHECK_THROWS_AS(j << ss1, std::invalid_argument); CHECK_THROWS_AS(j << ss1, json::parse_error);
CHECK_THROWS_WITH(j << ss2, "parse error - unexpected end of input; expected ']'"); CHECK_THROWS_WITH(j << ss2,
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
} }
SECTION("operator>>") SECTION("operator>>")
...@@ -117,15 +120,16 @@ TEST_CASE("deserialization") ...@@ -117,15 +120,16 @@ TEST_CASE("deserialization")
ss1 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss1 << "[\"foo\",1,2,3,false,{\"one\":1}";
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss2 << "[\"foo\",1,2,3,false,{\"one\":1}";
json j; json j;
CHECK_THROWS_AS(ss1 >> j, std::invalid_argument); CHECK_THROWS_AS(ss1 >> j, json::parse_error);
CHECK_THROWS_WITH(ss2 >> j, "parse error - unexpected end of input; expected ']'"); CHECK_THROWS_WITH(ss2 >> j,
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
} }
SECTION("user-defined string literal") SECTION("user-defined string literal")
{ {
CHECK_THROWS_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, std::invalid_argument); CHECK_THROWS_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, json::parse_error);
CHECK_THROWS_WITH("[\"foo\",1,2,3,false,{\"one\":1}"_json, CHECK_THROWS_WITH("[\"foo\",1,2,3,false,{\"one\":1}"_json,
"parse error - unexpected end of input; expected ']'"); "[json.exception.parse_error.101] parse error at 29: parse error - unexpected end of input; expected ']'");
} }
} }
...@@ -178,7 +182,7 @@ TEST_CASE("deserialization") ...@@ -178,7 +182,7 @@ TEST_CASE("deserialization")
SECTION("empty container") SECTION("empty container")
{ {
std::vector<uint8_t> v; std::vector<uint8_t> v;
CHECK_THROWS_AS(json::parse(v), std::invalid_argument); CHECK_THROWS_AS(json::parse(v), json::parse_error);
} }
} }
...@@ -223,7 +227,7 @@ TEST_CASE("deserialization") ...@@ -223,7 +227,7 @@ TEST_CASE("deserialization")
SECTION("with empty range") SECTION("with empty range")
{ {
std::vector<uint8_t> v; std::vector<uint8_t> v;
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
} }
...@@ -233,91 +237,91 @@ TEST_CASE("deserialization") ...@@ -233,91 +237,91 @@ TEST_CASE("deserialization")
SECTION("case 1") SECTION("case 1")
{ {
uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u'}; uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u'};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 2") SECTION("case 2")
{ {
uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1'}; uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1'};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 3") SECTION("case 3")
{ {
uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1', '1', '1', '1', '1', '1', '1', '1'}; uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1', '1', '1', '1', '1', '1', '1', '1'};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 4") SECTION("case 4")
{ {
uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', 'u', '1', '1', '1', '1', '1', '1', '1', '1', '\\'}; uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', 'u', '1', '1', '1', '1', '1', '1', '1', '1', '\\'};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 5") SECTION("case 5")
{ {
uint8_t v[] = {'\"', 0x7F, 0xC1}; uint8_t v[] = {'\"', 0x7F, 0xC1};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 6") SECTION("case 6")
{ {
uint8_t v[] = {'\"', 0x7F, 0xDF, 0x7F}; uint8_t v[] = {'\"', 0x7F, 0xDF, 0x7F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 7") SECTION("case 7")
{ {
uint8_t v[] = {'\"', 0x7F, 0xDF, 0xC0}; uint8_t v[] = {'\"', 0x7F, 0xDF, 0xC0};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 8") SECTION("case 8")
{ {
uint8_t v[] = {'\"', 0x7F, 0xE0, 0x9F}; uint8_t v[] = {'\"', 0x7F, 0xE0, 0x9F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 9") SECTION("case 9")
{ {
uint8_t v[] = {'\"', 0x7F, 0xEF, 0xC0}; uint8_t v[] = {'\"', 0x7F, 0xEF, 0xC0};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 10") SECTION("case 10")
{ {
uint8_t v[] = {'\"', 0x7F, 0xED, 0x7F}; uint8_t v[] = {'\"', 0x7F, 0xED, 0x7F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 11") SECTION("case 11")
{ {
uint8_t v[] = {'\"', 0x7F, 0xF0, 0x8F}; uint8_t v[] = {'\"', 0x7F, 0xF0, 0x8F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 12") SECTION("case 12")
{ {
uint8_t v[] = {'\"', 0x7F, 0xF0, 0xC0}; uint8_t v[] = {'\"', 0x7F, 0xF0, 0xC0};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 13") SECTION("case 13")
{ {
uint8_t v[] = {'\"', 0x7F, 0xF3, 0x7F}; uint8_t v[] = {'\"', 0x7F, 0xF3, 0x7F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 14") SECTION("case 14")
{ {
uint8_t v[] = {'\"', 0x7F, 0xF3, 0xC0}; uint8_t v[] = {'\"', 0x7F, 0xF3, 0xC0};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
SECTION("case 15") SECTION("case 15")
{ {
uint8_t v[] = {'\"', 0x7F, 0xF4, 0x7F}; uint8_t v[] = {'\"', 0x7F, 0xF4, 0x7F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
} }
} }
} }
......
...@@ -579,8 +579,9 @@ TEST_CASE("regression tests") ...@@ -579,8 +579,9 @@ TEST_CASE("regression tests")
// ss is not at EOF; this yielded an error before the fix // ss is not at EOF; this yielded an error before the fix
// (threw basic_string::append). No, it should just throw // (threw basic_string::append). No, it should just throw
// a parse error because of the EOF. // a parse error because of the EOF.
CHECK_THROWS_AS(j << ss, std::invalid_argument); CHECK_THROWS_AS(j << ss, json::parse_error);
CHECK_THROWS_WITH(j << ss, "parse error - unexpected end of input"); CHECK_THROWS_WITH(j << ss,
"[json.exception.parse_error.101] parse error at 1: parse error - unexpected end of input");
} }
SECTION("issue #389 - Integer-overflow (OSS-Fuzz issue 267)") SECTION("issue #389 - Integer-overflow (OSS-Fuzz issue 267)")
...@@ -778,7 +779,7 @@ TEST_CASE("regression tests") ...@@ -778,7 +779,7 @@ TEST_CASE("regression tests")
SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)") SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)")
{ {
std::vector<uint8_t> vec = {'-', '0', '1', '2', '2', '7', '4'}; std::vector<uint8_t> vec = {'-', '0', '1', '2', '2', '7', '4'};
CHECK_THROWS_AS(json::parse(vec), std::invalid_argument); CHECK_THROWS_AS(json::parse(vec), json::parse_error);
} }
SECTION("issue #454 - doubles are printed as integers") SECTION("issue #454 - doubles are printed as integers")
......
...@@ -79,7 +79,7 @@ TEST_CASE("compliance tests from json.org") ...@@ -79,7 +79,7 @@ TEST_CASE("compliance tests from json.org")
CAPTURE(filename); CAPTURE(filename);
json j; json j;
std::ifstream f(filename); std::ifstream f(filename);
CHECK_THROWS_AS(j << f, std::invalid_argument); CHECK_THROWS_AS(j << f, json::parse_error);
} }
} }
...@@ -757,7 +757,7 @@ TEST_CASE("nst's JSONTestSuite") ...@@ -757,7 +757,7 @@ TEST_CASE("nst's JSONTestSuite")
CAPTURE(filename); CAPTURE(filename);
std::ifstream f(filename); std::ifstream f(filename);
json j; json j;
CHECK_THROWS_AS(j << f, std::invalid_argument); CHECK_THROWS_AS(j << f, json::parse_error);
} }
} }
...@@ -810,7 +810,7 @@ TEST_CASE("nst's JSONTestSuite") ...@@ -810,7 +810,7 @@ TEST_CASE("nst's JSONTestSuite")
CAPTURE(filename); CAPTURE(filename);
std::ifstream f(filename); std::ifstream f(filename);
json j; json j;
CHECK_THROWS_AS(j << f, std::invalid_argument); CHECK_THROWS_AS(j << f, json::parse_error);
} }
} }
} }
......
...@@ -38,6 +38,9 @@ TEST_CASE("Unicode", "[hide]") ...@@ -38,6 +38,9 @@ TEST_CASE("Unicode", "[hide]")
{ {
SECTION("full enumeration of Unicode code points") SECTION("full enumeration of Unicode code points")
{ {
// lexer to call to_unicode on
json::lexer dummy_lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(""), 0);
// create an escaped string from a code point // create an escaped string from a code point
const auto codepoint_to_unicode = [](std::size_t cp) const auto codepoint_to_unicode = [](std::size_t cp)
{ {
...@@ -85,7 +88,7 @@ TEST_CASE("Unicode", "[hide]") ...@@ -85,7 +88,7 @@ TEST_CASE("Unicode", "[hide]")
// they are checked with codepoint_to_unicode. // they are checked with codepoint_to_unicode.
if (cp > 0x1f and cp != 0x22 and cp != 0x5c) if (cp > 0x1f and cp != 0x22 and cp != 0x5c)
{ {
unescaped_string = json::lexer::to_unicode(cp); unescaped_string = dummy_lexer.to_unicode(cp);
} }
} }
else else
...@@ -97,7 +100,7 @@ TEST_CASE("Unicode", "[hide]") ...@@ -97,7 +100,7 @@ TEST_CASE("Unicode", "[hide]")
const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu); const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
escaped_string = codepoint_to_unicode(codepoint1); escaped_string = codepoint_to_unicode(codepoint1);
escaped_string += codepoint_to_unicode(codepoint2); escaped_string += codepoint_to_unicode(codepoint2);
unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2); unescaped_string += dummy_lexer.to_unicode(codepoint1, codepoint2);
} }
// all other code points are valid and must not yield parse errors // all other code points are valid and must not yield parse errors
...@@ -170,7 +173,7 @@ TEST_CASE("Unicode", "[hide]") ...@@ -170,7 +173,7 @@ TEST_CASE("Unicode", "[hide]")
SECTION("error for incomplete/wrong BOM") SECTION("error for incomplete/wrong BOM")
{ {
CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument); CHECK_THROWS_AS(json::parse("\xef\xbb"), json::parse_error);
CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument); CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), json::parse_error);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment