diff --git a/.travis.yml b/.travis.yml index c3cba69e6..dcf7b6a29 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,13 +78,13 @@ matrix: env: - COMPILER=g++-4.9 - SPECIAL=no_exceptions - - TEST_PATTERN=-e \"*\" addons: apt: sources: ['ubuntu-toolchain-r-test'] packages: [g++-4.9, cppcheck] - before_script: - - CPPFLAGS="-DJSON_NOEXCEPTION" make + after_success: + - make clean + - CPPFLAGS="-DJSON_NOEXCEPTION" make check TEST_PATTERN="-e \"*\"" # Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/) diff --git a/Makefile b/Makefile index 1b7cf8c17..f52511c60 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ doctest: # -Wno-documentation-unknown-command: code uses user-defined commands like @complexity # -Wno-exit-time-destructors: warning in Catch code # -Wno-keyword-macro: unit-tests use "#define private public" +# -Wno-weak-vtables: exception class is defined inline, but has virtual method # -Wno-range-loop-analysis: iterator_wrapper tests tests "for(const auto i...)" pedantic: $(MAKE) json_unit CXXFLAGS="\ @@ -58,6 +59,7 @@ pedantic: -Wno-documentation-unknown-command \ -Wno-exit-time-destructors \ -Wno-keyword-macro \ + -Wno-weak-vtables \ -Wno-range-loop-analysis" diff --git a/doc/Makefile b/doc/Makefile index 37a0e19f3..c900bdea3 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -58,6 +58,9 @@ 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@template<template< typename U, typename V, typename... Args > class ObjectType = std::map, template< typename U, typename... Args > 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< typename U > class AllocatorType = std::allocator, template< typename T, typename SFINAE=void > class JSONSerializer = adl_serializer>@@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 upload: clean doxygen check_output cd html ; ../scripts/git-update-ghpages nlohmann/json diff --git a/doc/examples/at__object_t_key_type.cpp b/doc/examples/at__object_t_key_type.cpp index 83646f155..7797418f4 100644 --- a/doc/examples/at__object_t_key_type.cpp +++ b/doc/examples/at__object_t_key_type.cpp @@ -21,13 +21,27 @@ int main() // output changed array std::cout << object << '\n'; - // try to write at a nonexisting key + + // exception type_error.304 try { + // use at() on a non-object type + json str = "I am a string"; + str.at("the good") = "Another string"; + } + catch (json::type_error& e) + { + std::cout << e.what() << '\n'; + } + + // exception out_of_range.401 + try + { + // try to write at a nonexisting key object.at("the fast") = "il rapido"; } - catch (std::out_of_range& e) + catch (json::out_of_range& e) { - std::cout << "out of range: " << e.what() << '\n'; + std::cout << e.what() << '\n'; } } diff --git a/doc/examples/at__object_t_key_type.link b/doc/examples/at__object_t_key_type.link index 4a050ecfd..8874783bd 100644 --- a/doc/examples/at__object_t_key_type.link +++ b/doc/examples/at__object_t_key_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at__object_t_key_type.output b/doc/examples/at__object_t_key_type.output index 79cff2d7f..b544b7299 100644 --- a/doc/examples/at__object_t_key_type.output +++ b/doc/examples/at__object_t_key_type.output @@ -1,3 +1,4 @@ "il brutto" {"the bad":"il cattivo","the good":"il buono","the ugly":"il brutto"} -out of range: key 'the fast' not found +[json.exception.type_error.304] cannot use at() with string +[json.exception.out_of_range.403] key 'the fast' not found diff --git a/doc/examples/at__object_t_key_type.test b/doc/examples/at__object_t_key_type.test new file mode 100644 index 000000000..654b9eb63 --- /dev/null +++ b/doc/examples/at__object_t_key_type.test @@ -0,0 +1,3 @@ +"il brutto" +{"the bad":"il cattivo","the good":"il buono","the ugly":"il brutto"} +[json.exception.out_of_range.403] key 'the fast' not found diff --git a/doc/examples/at__object_t_key_type_const.cpp b/doc/examples/at__object_t_key_type_const.cpp index 0e8d9d728..f60424041 100644 --- a/doc/examples/at__object_t_key_type_const.cpp +++ b/doc/examples/at__object_t_key_type_const.cpp @@ -15,12 +15,26 @@ int main() // output element with key "the ugly" std::cout << object.at("the ugly") << '\n'; - // try to read from a nonexisting key + + // exception type_error.304 try { + // use at() on a non-object type + const json str = "I am a string"; + std::cout << str.at("the good") << '\n'; + } + catch (json::type_error& e) + { + std::cout << e.what() << '\n'; + } + + // exception out_of_range.401 + try + { + // try to read from a nonexisting key std::cout << object.at("the fast") << '\n'; } - catch (std::out_of_range) + catch (json::out_of_range) { std::cout << "out of range" << '\n'; } diff --git a/doc/examples/at__object_t_key_type_const.link b/doc/examples/at__object_t_key_type_const.link index 1ad9c07d9..cd8594e66 100644 --- a/doc/examples/at__object_t_key_type_const.link +++ b/doc/examples/at__object_t_key_type_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at__object_t_key_type_const.output b/doc/examples/at__object_t_key_type_const.output index b3dd11d35..40ca3f09f 100644 --- a/doc/examples/at__object_t_key_type_const.output +++ b/doc/examples/at__object_t_key_type_const.output @@ -1,2 +1,3 @@ "il brutto" +[json.exception.type_error.304] cannot use at() with string out of range diff --git a/doc/examples/at__size_type.cpp b/doc/examples/at__size_type.cpp index e31d61d35..28def1dd2 100644 --- a/doc/examples/at__size_type.cpp +++ b/doc/examples/at__size_type.cpp @@ -16,13 +16,27 @@ int main() // output changed array std::cout << array << '\n'; - // try to write beyond the array limit + + // exception type_error.304 try { + // use at() on a non-array type + json str = "I am a string"; + str.at(0) = "Another string"; + } + catch (json::type_error& e) + { + std::cout << e.what() << '\n'; + } + + // exception out_of_range.401 + try + { + // try to write beyond the array limit array.at(5) = "sixth"; } - catch (std::out_of_range& e) + catch (json::out_of_range& e) { - std::cout << "out of range: " << e.what() << '\n'; + std::cout << e.what() << '\n'; } } diff --git a/doc/examples/at__size_type.link b/doc/examples/at__size_type.link index 00e42d300..cba0fa00f 100644 --- a/doc/examples/at__size_type.link +++ b/doc/examples/at__size_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at__size_type.output b/doc/examples/at__size_type.output index d1f68bdb8..54026436f 100644 --- a/doc/examples/at__size_type.output +++ b/doc/examples/at__size_type.output @@ -1,3 +1,4 @@ "third" ["first","second","third","fourth"] -out of range: array index 5 is out of range +[json.exception.type_error.304] cannot use at() with string +[json.exception.out_of_range.401] array index 5 is out of range diff --git a/doc/examples/at__size_type_const.cpp b/doc/examples/at__size_type_const.cpp index a8a43ed8f..213d22fd6 100644 --- a/doc/examples/at__size_type_const.cpp +++ b/doc/examples/at__size_type_const.cpp @@ -5,18 +5,32 @@ using json = nlohmann::json; int main() { // create JSON array - json array = {"first", "2nd", "third", "fourth"}; + const json array = {"first", "2nd", "third", "fourth"}; // output element at index 2 (third element) std::cout << array.at(2) << '\n'; - // try to read beyond the array limit + + // exception type_error.304 try { + // use at() on a non-array type + const json str = "I am a string"; + std::cout << str.at(0) << '\n'; + } + catch (json::type_error& e) + { + std::cout << e.what() << '\n'; + } + + // exception out_of_range.401 + try + { + // try to read beyond the array limit std::cout << array.at(5) << '\n'; } - catch (std::out_of_range) + catch (json::out_of_range& e) { - std::cout << "out of range" << '\n'; + std::cout << e.what() << '\n'; } } diff --git a/doc/examples/at__size_type_const.link b/doc/examples/at__size_type_const.link index 0fefb628c..ce3647aca 100644 --- a/doc/examples/at__size_type_const.link +++ b/doc/examples/at__size_type_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at__size_type_const.output b/doc/examples/at__size_type_const.output index d0b7a66a8..8135a27a5 100644 --- a/doc/examples/at__size_type_const.output +++ b/doc/examples/at__size_type_const.output @@ -1,2 +1,3 @@ "third" -out of range +[json.exception.type_error.304] cannot use at() with string +[json.exception.out_of_range.401] array index 5 is out of range diff --git a/doc/examples/at_json_pointer.cpp b/doc/examples/at_json_pointer.cpp index 0665e608c..3ef91282a 100644 --- a/doc/examples/at_json_pointer.cpp +++ b/doc/examples/at_json_pointer.cpp @@ -32,4 +32,60 @@ int main() j.at("/array/1"_json_pointer) = 21; // output the changed array std::cout << j["array"] << '\n'; + + + // out_of_range.106 + try + { + // try to use an array index with leading '0' + json::reference ref = j.at("/array/01"_json_pointer); + } + catch (json::parse_error& e) + { + std::cout << e.what() << '\n'; + } + + // out_of_range.109 + try + { + // try to use an array index that is not a number + json::reference ref = j.at("/array/one"_json_pointer); + } + catch (json::parse_error& e) + { + std::cout << e.what() << '\n'; + } + + // out_of_range.401 + try + { + // try to use a an invalid array index + json::reference ref = j.at("/array/4"_json_pointer); + } + catch (json::out_of_range& e) + { + std::cout << e.what() << '\n'; + } + + // out_of_range.402 + try + { + // try to use the array index '-' + json::reference ref = j.at("/array/-"_json_pointer); + } + catch (json::out_of_range& e) + { + std::cout << e.what() << '\n'; + } + + // out_of_range.404 + try + { + // try to use a JSON pointer that cannot be resolved + json::reference ref = j.at("/number/foo"_json_pointer); + } + catch (json::out_of_range& e) + { + std::cout << e.what() << '\n'; + } } diff --git a/doc/examples/at_json_pointer.link b/doc/examples/at_json_pointer.link index 7a7efa268..c8563ec20 100644 --- a/doc/examples/at_json_pointer.link +++ b/doc/examples/at_json_pointer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at_json_pointer.output b/doc/examples/at_json_pointer.output index 11913c723..505792f2a 100644 --- a/doc/examples/at_json_pointer.output +++ b/doc/examples/at_json_pointer.output @@ -4,3 +4,8 @@ 2 "bar" [1,21] +[json.exception.parse_error.106] parse error: array index '01' must not begin with '0' +[json.exception.parse_error.109] parse error: array index 'one' is not a number +[json.exception.out_of_range.401] array index 4 is out of range +[json.exception.out_of_range.402] array index '-' (2) is out of range +[json.exception.out_of_range.404] unresolved reference token 'foo' diff --git a/doc/examples/at_json_pointer_const.cpp b/doc/examples/at_json_pointer_const.cpp index e3cfc5154..a1d065f23 100644 --- a/doc/examples/at_json_pointer_const.cpp +++ b/doc/examples/at_json_pointer_const.cpp @@ -5,7 +5,7 @@ using json = nlohmann::json; int main() { // create a JSON value - json j = + const json j = { {"number", 1}, {"string", "foo"}, {"array", {1, 2}} }; @@ -20,4 +20,48 @@ int main() std::cout << j.at("/array"_json_pointer) << '\n'; // output element with JSON pointer "/array/1" std::cout << j.at("/array/1"_json_pointer) << '\n'; + + // out_of_range.109 + try + { + // try to use an array index that is not a number + json::const_reference ref = j.at("/array/one"_json_pointer); + } + catch (json::parse_error& e) + { + std::cout << e.what() << '\n'; + } + + // out_of_range.401 + try + { + // try to use a an invalid array index + json::const_reference ref = j.at("/array/4"_json_pointer); + } + catch (json::out_of_range& e) + { + std::cout << e.what() << '\n'; + } + + // out_of_range.402 + try + { + // try to use the array index '-' + json::const_reference ref = j.at("/array/-"_json_pointer); + } + catch (json::out_of_range& e) + { + std::cout << e.what() << '\n'; + } + + // out_of_range.404 + try + { + // try to use a JSON pointer that cannot be resolved + json::const_reference ref = j.at("/number/foo"_json_pointer); + } + catch (json::out_of_range& e) + { + std::cout << e.what() << '\n'; + } } diff --git a/doc/examples/at_json_pointer_const.link b/doc/examples/at_json_pointer_const.link index 9057e0b27..f421faf45 100644 --- a/doc/examples/at_json_pointer_const.link +++ b/doc/examples/at_json_pointer_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at_json_pointer_const.output b/doc/examples/at_json_pointer_const.output index 7b9306bbc..b3361f04b 100644 --- a/doc/examples/at_json_pointer_const.output +++ b/doc/examples/at_json_pointer_const.output @@ -2,3 +2,7 @@ "foo" [1,2] 2 +[json.exception.parse_error.109] parse error: array index 'one' is not a number +[json.exception.out_of_range.401] array index 4 is out of range +[json.exception.out_of_range.402] array index '-' (2) is out of range +[json.exception.out_of_range.404] unresolved reference token 'foo' diff --git a/doc/examples/back.cpp b/doc/examples/back.cpp index 70516e588..45f9483c7 100644 --- a/doc/examples/back.cpp +++ b/doc/examples/back.cpp @@ -5,7 +5,6 @@ using json = nlohmann::json; int main() { // create JSON values - json j_null; json j_boolean = true; json j_number_integer = 17; json j_number_float = 23.42; @@ -16,7 +15,6 @@ int main() json j_string = "Hello, world"; // call back() - //std::cout << j_null.back() << '\n'; // would throw std::cout << j_boolean.back() << '\n'; std::cout << j_number_integer.back() << '\n'; std::cout << j_number_float.back() << '\n'; @@ -25,4 +23,15 @@ int main() std::cout << j_array.back() << '\n'; //std::cout << j_array_empty.back() << '\n'; // undefined behavior std::cout << j_string.back() << '\n'; + + // back() called on a null value + try + { + json j_null; + j_null.back(); + } + catch (json::invalid_iterator& e) + { + std::cout << e.what() << '\n'; + } } diff --git a/doc/examples/back.link b/doc/examples/back.link index 0b0097805..15b1102ba 100644 --- a/doc/examples/back.link +++ b/doc/examples/back.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/back.output b/doc/examples/back.output index 159ba0fc5..2990dbf0e 100644 --- a/doc/examples/back.output +++ b/doc/examples/back.output @@ -4,3 +4,4 @@ true 2 16 "Hello, world" +[json.exception.invalid_iterator.214] cannot get value diff --git a/doc/examples/basic_json__InputIt_InputIt.cpp b/doc/examples/basic_json__InputIt_InputIt.cpp index 86a0faf8c..ce07e07bb 100644 --- a/doc/examples/basic_json__InputIt_InputIt.cpp +++ b/doc/examples/basic_json__InputIt_InputIt.cpp @@ -18,4 +18,14 @@ int main() std::cout << j_array_range << '\n'; std::cout << j_number_range << '\n'; std::cout << j_object_range << '\n'; + + // example for an exception + try + { + json j_invalid(j_number.begin() + 1, j_number.end()); + } + catch (json::invalid_iterator& e) + { + std::cout << e.what() << '\n'; + } } diff --git a/doc/examples/basic_json__InputIt_InputIt.link b/doc/examples/basic_json__InputIt_InputIt.link index ae46528cc..d360496a5 100644 --- a/doc/examples/basic_json__InputIt_InputIt.link +++ b/doc/examples/basic_json__InputIt_InputIt.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__InputIt_InputIt.output b/doc/examples/basic_json__InputIt_InputIt.output index 367142004..bfb017785 100644 --- a/doc/examples/basic_json__InputIt_InputIt.output +++ b/doc/examples/basic_json__InputIt_InputIt.output @@ -1,3 +1,4 @@ ["bravo","charly"] 42 {"one":"eins"} +[json.exception.invalid_iterator.204] iterators out of range diff --git a/doc/examples/get_ref.cpp b/doc/examples/get_ref.cpp index d0ddb2d27..a152d3471 100644 --- a/doc/examples/get_ref.cpp +++ b/doc/examples/get_ref.cpp @@ -19,7 +19,7 @@ int main() { auto r3 = value.get_ref(); } - catch (std::domain_error& ex) + catch (json::type_error& ex) { std::cout << ex.what() << '\n'; } diff --git a/doc/examples/get_ref.link b/doc/examples/get_ref.link index ef560c42c..c5bd9c03b 100644 --- a/doc/examples/get_ref.link +++ b/doc/examples/get_ref.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/get_ref.output b/doc/examples/get_ref.output index 50bc0df69..3811afa2f 100644 --- a/doc/examples/get_ref.output +++ b/doc/examples/get_ref.output @@ -1,2 +1,2 @@ 17 17 -incompatible ReferenceType for get_ref, actual type is number +[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number diff --git a/doc/examples/get_ref.test b/doc/examples/get_ref.test new file mode 100644 index 000000000..3811afa2f --- /dev/null +++ b/doc/examples/get_ref.test @@ -0,0 +1,2 @@ +17 17 +[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number diff --git a/doc/examples/json_pointer.cpp b/doc/examples/json_pointer.cpp index 140eac3b6..3b23dfb2f 100644 --- a/doc/examples/json_pointer.cpp +++ b/doc/examples/json_pointer.cpp @@ -19,9 +19,9 @@ int main() { json::json_pointer p9("foo"); } - catch (std::domain_error& e) + catch (json::parse_error& e) { - std::cout << "domain_error: " << e.what() << '\n'; + std::cout << e.what() << '\n'; } // error: JSON pointer uses escape symbol ~ not followed by 0 or 1 @@ -29,9 +29,9 @@ int main() { json::json_pointer p10("/foo/~"); } - catch (std::domain_error& e) + catch (json::parse_error& e) { - std::cout << "domain_error: " << e.what() << '\n'; + std::cout << e.what() << '\n'; } // error: JSON pointer uses escape symbol ~ not followed by 0 or 1 @@ -39,8 +39,8 @@ int main() { json::json_pointer p11("/foo/~3"); } - catch (std::domain_error& e) + catch (json::parse_error& e) { - std::cout << "domain_error: " << e.what() << '\n'; + std::cout << e.what() << '\n'; } } diff --git a/doc/examples/json_pointer.link b/doc/examples/json_pointer.link index 6602f0cf4..78881dd96 100644 --- a/doc/examples/json_pointer.link +++ b/doc/examples/json_pointer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer.output b/doc/examples/json_pointer.output index b81c8a201..33e2cc683 100644 --- a/doc/examples/json_pointer.output +++ b/doc/examples/json_pointer.output @@ -1,3 +1,3 @@ -domain_error: JSON pointer must be empty or begin with '/' -domain_error: escape error: '~' must be followed with '0' or '1' -domain_error: escape error: '~' must be followed with '0' or '1' +[json.exception.parse_error.107] parse error at 1: JSON pointer must be empty or begin with '/' - was: 'foo' +[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1' +[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1' diff --git a/doc/examples/json_pointer.test b/doc/examples/json_pointer.test new file mode 100644 index 000000000..33e2cc683 --- /dev/null +++ b/doc/examples/json_pointer.test @@ -0,0 +1,3 @@ +[json.exception.parse_error.107] parse error at 1: JSON pointer must be empty or begin with '/' - was: 'foo' +[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1' +[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1' diff --git a/doc/examples/object.cpp b/doc/examples/object.cpp index 54f3ca3b8..5b8ba5898 100644 --- a/doc/examples/object.cpp +++ b/doc/examples/object.cpp @@ -8,10 +8,20 @@ int main() json j_no_init_list = json::object(); json j_empty_init_list = json::object({}); json j_list_of_pairs = json::object({ {"one", 1}, {"two", 2} }); - //json j_invalid_list = json::object({ "one", 1 }); // would throw // serialize the JSON objects std::cout << j_no_init_list << '\n'; std::cout << j_empty_init_list << '\n'; std::cout << j_list_of_pairs << '\n'; + + // example for an exception + try + { + // can only create an object from a list of pairs + json j_invalid_object = json::object({{ "one", 1, 2 }}); + } + catch (json::type_error& e) + { + std::cout << e.what() << '\n'; + } } diff --git a/doc/examples/object.link b/doc/examples/object.link index fd0f424b9..d9e63e536 100644 --- a/doc/examples/object.link +++ b/doc/examples/object.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/object.output b/doc/examples/object.output index f6c27ee8f..1a1d8140e 100644 --- a/doc/examples/object.output +++ b/doc/examples/object.output @@ -1,3 +1,4 @@ {} {} {"one":1,"two":2} +[json.exception.type_error.301] cannot create object from initializer list diff --git a/doc/examples/operator__ValueType.cpp b/doc/examples/operator__ValueType.cpp index 9307e69bb..77af700fb 100644 --- a/doc/examples/operator__ValueType.cpp +++ b/doc/examples/operator__ValueType.cpp @@ -46,4 +46,14 @@ int main() { std::cout << i.first << ": " << i.second << '\n'; } + + // example for an exception + try + { + bool v1 = json_types["string"]; + } + catch (json::type_error& e) + { + std::cout << e.what() << '\n'; + } } diff --git a/doc/examples/operator__ValueType.link b/doc/examples/operator__ValueType.link index 1428a679c..9a689f828 100644 --- a/doc/examples/operator__ValueType.link +++ b/doc/examples/operator__ValueType.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__ValueType.output b/doc/examples/operator__ValueType.output index 5cd9cd3aa..a3bd9fff4 100644 --- a/doc/examples/operator__ValueType.output +++ b/doc/examples/operator__ValueType.output @@ -9,3 +9,4 @@ number: {"floating-point":17.23,"integer":42} null: null boolean: true array: [1,2,3,4,5] +[json.exception.type_error.302] type must be boolean, but is string diff --git a/src/json.hpp b/src/json.hpp index 1ed8cca26..3fedc045c 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -52,7 +52,6 @@ SOFTWARE. #include // addressof, allocator, allocator_traits, unique_ptr #include // accumulate #include // stringstream -#include // domain_error, invalid_argument, out_of_range #include // getline, stoi, string, to_string #include // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type #include // declval, forward, make_pair, move, pair, swap @@ -82,7 +81,7 @@ SOFTWARE. #endif // allow to disable exceptions -#if not defined(JSON_NOEXCEPTION) || defined(__EXCEPTIONS) +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && not defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception #define JSON_TRY try #define JSON_CATCH(exception) catch(exception) @@ -110,6 +109,214 @@ This namespace collects some functions that could not be defined inside the */ namespace detail { +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +Extension of std::exception objects with a member @a id for exception ids. + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// create exception with id an explanatory string + exception(int id_, const std::string& ename, const std::string& what_arg_) + : id(id_), + what_arg("[json.exception." + ename + "." + std::to_string(id_) + "] " + what_arg_) + {} + + /// returns the explanatory string + virtual const char* what() const noexcept + { + return what_arg.c_str(); + } + + /// the id of the exception + const int id; + + private: + /// the explanatory string + const std::string what_arg; +}; + +/*! +@brief exception indicating a parse error + +This excpetion is thrown by the library when a parse error occurs. Parse +errors can occur during the deserialization of JSON text as well as when +using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +@note For an input with n bytes, 1 is the index of the first character + and n+1 is the index of the terminating null byte or the end of + file. This also holds true when reading a byte vector (CBOR or + MessagePack). + +Exceptions have ids 1xx. + +name / id | example massage | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number wihtout a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.111 | parse error: bad input stream | Parsing CBOR or MessagePack from an input stream where the [`badbit` or `failbit`](http://en.cppreference.com/w/cpp/io/ios_base/iostate) is set. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xf8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | | While parsing a map key, a value that is not a string has been read. + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] byte_ the byte index where the error occured (or 0 if + the position cannot be determined) + @param[in] what_arg_ the explanatory string + */ + parse_error(int id_, size_t byte_, const std::string& what_arg_) + : exception(id_, "parse_error", "parse error" + + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + + ": " + what_arg_), + byte(byte_) + {} + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character + and n+1 is the index of the terminating null byte or the end of + file. This also holds true when reading a byte vector (CBOR or + MessagePack). + */ + const size_t byte; +}; + +/*! +@brief exception indicating errors with iterators + +Exceptions have ids 2xx. + +name / id | example massage | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compated, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + invalid_iterator(int id_, const std::string& what_arg_) + : exception(id_, "invalid_iterator", what_arg_) + {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +Exceptions have ids 3xx. + +name / id | example massage | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + type_error(int id_, const std::string& what_arg_) + : exception(id_, "type_error", what_arg_) + {} +}; + +/*! +@brief exception indicating access out of the defined range + +Exceptions have ids 4xx. + +name / id | example massage | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + out_of_range(int id_, const std::string& what_arg_) + : exception(id_, "out_of_range", what_arg_) + {} +}; + +/*! +@brief exception indicating other errors + +Exceptions have ids 5xx. + +name / id | example massage | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + other_error(int id_, const std::string& what_arg_) + : exception(id_, "other_error", what_arg_) + {} +}; + + + /////////////////////////// // JSON type enumeration // /////////////////////////// @@ -263,16 +470,8 @@ struct external_constructor template static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept { - // replace infinity and NAN by null - if (not std::isfinite(val)) - { - j = BasicJsonType{}; - } - else - { - j.m_type = value_t::number_float; - j.m_value = val; - } + j.m_type = value_t::number_float; + j.m_value = val; j.assert_invariant(); } }; @@ -636,8 +835,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW( - std::domain_error("type must be number, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be number, but is " + j.type_name())); } } } @@ -647,7 +845,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (not j.is_boolean()) { - JSON_THROW(std::domain_error("type must be boolean, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be boolean, but is " + j.type_name())); } b = *j.template get_ptr(); } @@ -657,7 +855,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (not j.is_string()) { - JSON_THROW(std::domain_error("type must be string, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be string, but is " + j.type_name())); } s = *j.template get_ptr(); } @@ -694,28 +892,21 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) { if (not j.is_array()) { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); } arr = *j.template get_ptr(); } // forward_list doesn't have an insert method -template +template::value, int> = 0> void from_json(const BasicJsonType& j, std::forward_list& l) { - // do not perform the check when user wants to retrieve jsons - // (except when it's null.. ?) - if (j.is_null()) + if (not j.is_array()) { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); - } - if (not std::is_same::value) - { - if (not j.is_array()) - { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); - } + JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); } + for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) { l.push_front(it->template get()); @@ -747,8 +938,8 @@ auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, prio using std::end; arr.reserve(j.size()); - std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType & i) + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType & i) { // get() returns *this, this won't call a from_json // method when value_type is BasicJsonType @@ -758,22 +949,15 @@ auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, prio template::value and + std::is_convertible::value and not std::is_same::value, int> = 0> void from_json(const BasicJsonType& j, CompatibleArrayType& arr) { - if (j.is_null()) + if (not j.is_array()) { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); } - // when T == BasicJsonType, do not check if value_t is correct - if (not std::is_same::value) - { - if (not j.is_array()) - { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); - } - } from_json_array_impl(j, arr, priority_tag<1> {}); } @@ -783,7 +967,7 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj) { if (not j.is_object()) { - JSON_THROW(std::domain_error("type must be object, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be object, but is " + j.type_name())); } auto inner_object = j.template get_ptr(); @@ -833,7 +1017,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW(std::domain_error("type must be number, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be number, but is " + j.type_name())); } } } @@ -1065,6 +1249,29 @@ class basic_json template using json_serializer = JSONSerializer; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; + + /// @} + + ///////////////////// // container types // ///////////////////// @@ -1755,7 +1962,7 @@ class basic_json { if (t == value_t::null) { - JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE + JSON_THROW(other_error(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE } break; } @@ -1913,9 +2120,6 @@ class basic_json @complexity Constant. - @throw std::bad_alloc if allocation for object, array, or string value - fails - @liveexample{The following code shows the constructor for different @ref value_t values,basic_json__value_t} @@ -2070,10 +2274,12 @@ class basic_json value_t::array and @ref value_t::object are valid); when @a type_deduction is set to `true`, this parameter has no effect - @throw std::domain_error if @a type_deduction is `false`, @a manual_type - is `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string; example: `"cannot create object from - initializer list"` + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(std::initializer_list) + for an example. @complexity Linear in the size of the initializer list @a init. @@ -2111,7 +2317,7 @@ class basic_json // if object is wanted but impossible, throw an exception if (manual_type == value_t::object and not is_an_object) { - JSON_THROW(std::domain_error("cannot create object from initializer list")); + JSON_THROW(type_error(301, "cannot create object from initializer list")); } } @@ -2187,16 +2393,17 @@ class basic_json related function @ref array(std::initializer_list), there are no cases which can only be expressed by this function. That is, any initializer list @a init can also be passed to the initializer list - constructor @ref basic_json(std::initializer_list, bool, - value_t). + constructor @ref basic_json(std::initializer_list, bool, value_t). @param[in] init initializer list to create an object from (optional) @return JSON object value - @throw std::domain_error if @a init is not a pair whose first elements are - strings; thrown by - @ref basic_json(std::initializer_list, bool, value_t) + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(std::initializer_list, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. @complexity Linear in the size of @a init. @@ -2248,10 +2455,10 @@ class basic_json The semantics depends on the different types a JSON value can have: - In case of primitive types (number, boolean, or string), @a first must be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, std::out_of_range is thrown. + copied. Otherwise, invalid_iterator.204 is thrown. - In case of structured types (array, object), the constructor behaves as similar versions for `std::vector`. - - In case of a null type, std::domain_error is thrown. + - In case of a null type, invalid_iterator.206 is thrown. @tparam InputIT an input iterator type (@ref iterator or @ref const_iterator) @@ -2262,14 +2469,19 @@ class basic_json @pre Iterators @a first and @a last must be initialized. **This precondition is enforced with an assertion.** - @throw std::domain_error if iterators are not compatible; that is, do not - belong to the same JSON value; example: `"iterators are not compatible"` - @throw std::out_of_range if iterators are for a primitive type (number, - boolean, or string) where an out of range error can be detected easily; - example: `"iterators out of range"` - @throw std::bad_alloc if allocation for object, array, or string fails - @throw std::domain_error if called with a null value; example: `"cannot - use construct with iterators from null"` + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. @complexity Linear in distance between @a first and @a last. @@ -2289,7 +2501,7 @@ class basic_json // make sure iterator fits the current value if (first.m_object != last.m_object) { - JSON_THROW(std::domain_error("iterators are not compatible")); + JSON_THROW(invalid_iterator(201, "iterators are not compatible")); } // copy type from first iterator @@ -2306,7 +2518,7 @@ class basic_json { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - JSON_THROW(std::out_of_range("iterators out of range")); + JSON_THROW(invalid_iterator(204, "iterators out of range")); } break; } @@ -2365,7 +2577,8 @@ class basic_json default: { - JSON_THROW(std::domain_error("cannot use construct with iterators from " + first.m_object->type_name())); + JSON_THROW(invalid_iterator(206, "cannot construct with iterators from " + + first.m_object->type_name())); } } @@ -2392,8 +2605,6 @@ class basic_json - The complexity is linear. - As postcondition, it holds: `other == basic_json(other)`. - @throw std::bad_alloc if allocation for object, array, or string fails. - @liveexample{The following code shows an example for the copy constructor.,basic_json__basic_json} @@ -3005,7 +3216,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(std::domain_error("type must be boolean, but is " + type_name())); + JSON_THROW(type_error(302, "type must be boolean, but is " + type_name())); } /// get a pointer to the value (object) @@ -3100,7 +3311,7 @@ class basic_json @tparam ThisType will be deduced as `basic_json` or `const basic_json` - @throw std::domain_error if ReferenceType does not match underlying value + @throw type_error.303 if ReferenceType does not match underlying value type of the current JSON */ template @@ -3117,8 +3328,7 @@ class basic_json return *ptr; } - JSON_THROW(std::domain_error("incompatible ReferenceType for get_ref, actual type is " + - obj.type_name())); + JSON_THROW(type_error(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); } public: @@ -3397,10 +3607,10 @@ class basic_json @return reference to the internally stored JSON value if the requested reference type @a ReferenceType fits to the JSON value; throws - std::domain_error otherwise + type_error.303 otherwise - @throw std::domain_error in case passed type @a ReferenceType is - incompatible with the stored JSON value + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below @complexity Constant. @@ -3443,8 +3653,9 @@ class basic_json @return copy of the JSON value, converted to type @a ValueType - @throw std::domain_error in case passed type @a ValueType is incompatible - to JSON, thrown by @ref get() const + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below @complexity Linear in the size of the JSON value. @@ -3491,17 +3702,21 @@ class basic_json @return reference to the element at index @a idx - @throw std::domain_error if the JSON value is not an array; example: - `"cannot use at() with string"` - @throw std::out_of_range if the index @a idx is out of range of the array; - that is, `idx >= size()`; example: `"array index 7 is out of range"` + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. @complexity Constant. - @liveexample{The example below shows how array elements can be read and - written using `at()`.,at__size_type} - @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} */ reference at(size_type idx) { @@ -3515,12 +3730,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(std::domain_error("cannot use at() with " + type_name())); + JSON_THROW(type_error(304, "cannot use at() with " + type_name())); } } @@ -3534,17 +3749,21 @@ class basic_json @return const reference to the element at index @a idx - @throw std::domain_error if the JSON value is not an array; example: - `"cannot use at() with string"` - @throw std::out_of_range if the index @a idx is out of range of the array; - that is, `idx >= size()`; example: `"array index 7 is out of range"` + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. @complexity Constant. - @liveexample{The example below shows how array elements can be read using - `at()`.,at__size_type_const} - @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} */ const_reference at(size_type idx) const { @@ -3558,12 +3777,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(std::domain_error("cannot use at() with " + type_name())); + JSON_THROW(type_error(304, "cannot use at() with " + type_name())); } } @@ -3577,21 +3796,25 @@ class basic_json @return reference to the element at key @a key - @throw std::domain_error if the JSON value is not an object; example: - `"cannot use at() with boolean"` - @throw std::out_of_range if the key @a key is is not stored in the object; - that is, `find(key) == end()`; example: `"key "the fast" not found"` + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. @complexity Logarithmic in the size of the container. - @liveexample{The example below shows how object elements can be read and - written using `at()`.,at__object_t_key_type} - @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} */ reference at(const typename object_t::key_type& key) { @@ -3605,12 +3828,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("key '" + key + "' not found")); + JSON_THROW(out_of_range(403, "key '" + key + "' not found")); } } else { - JSON_THROW(std::domain_error("cannot use at() with " + type_name())); + JSON_THROW(type_error(304, "cannot use at() with " + type_name())); } } @@ -3624,21 +3847,25 @@ class basic_json @return const reference to the element at key @a key - @throw std::domain_error if the JSON value is not an object; example: - `"cannot use at() with boolean"` - @throw std::out_of_range if the key @a key is is not stored in the object; - that is, `find(key) == end()`; example: `"key "the fast" not found"` + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. @complexity Logarithmic in the size of the container. - @liveexample{The example below shows how object elements can be read using - `at()`.,at__object_t_key_type_const} - @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} */ const_reference at(const typename object_t::key_type& key) const { @@ -3652,12 +3879,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("key '" + key + "' not found")); + JSON_THROW(out_of_range(403, "key '" + key + "' not found")); } } else { - JSON_THROW(std::domain_error("cannot use at() with " + type_name())); + JSON_THROW(type_error(304, "cannot use at() with " + type_name())); } } @@ -3674,8 +3901,8 @@ class basic_json @return reference to the element at index @a idx - @throw std::domain_error if JSON is not an array or null; example: - `"cannot use operator[] with string"` + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. @complexity Constant if @a idx is in the range of the array. Otherwise linear in `idx - size()`. @@ -3710,7 +3937,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3722,8 +3949,8 @@ class basic_json @return const reference to the element at index @a idx - @throw std::domain_error if JSON is not an array; example: `"cannot use - operator[] with null"` + @throw type_error.305 if the JSON value is not an array; in that cases, + using the [] operator with an index makes no sense. @complexity Constant. @@ -3740,7 +3967,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3756,8 +3983,8 @@ class basic_json @return reference to the element at key @a key - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3786,7 +4013,7 @@ class basic_json return m_value.object->operator[](key); } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3805,8 +4032,8 @@ class basic_json @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3828,7 +4055,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3844,8 +4071,8 @@ class basic_json @return reference to the element at key @a key - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3879,8 +4106,8 @@ class basic_json @return const reference to the element at key @a key - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3912,8 +4139,8 @@ class basic_json @return reference to the element at key @a key - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3943,7 +4170,7 @@ class basic_json return m_value.object->operator[](key); } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3962,8 +4189,8 @@ class basic_json @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3986,7 +4213,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3999,7 +4226,7 @@ class basic_json @code {.cpp} try { return at(key); - } catch(std::out_of_range) { + } catch(out_of_range) { return default_value; } @endcode @@ -4022,8 +4249,8 @@ class basic_json @return copy of the element at key @a key or @a default_value if @a key is not found - @throw std::domain_error if JSON is not an object; example: `"cannot use - value() with null"` + @throw type_error.306 if the JSON value is not an objec; in that cases, + using `value()` with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -4055,7 +4282,7 @@ class basic_json } else { - JSON_THROW(std::domain_error("cannot use value() with " + type_name())); + JSON_THROW(type_error(306, "cannot use value() with " + type_name())); } } @@ -4078,7 +4305,7 @@ class basic_json @code {.cpp} try { return at(ptr); - } catch(std::out_of_range) { + } catch(out_of_range) { return default_value; } @endcode @@ -4097,8 +4324,8 @@ class basic_json @return copy of the element at key @a key or @a default_value if @a key is not found - @throw std::domain_error if JSON is not an object; example: `"cannot use - value() with null"` + @throw type_error.306 if the JSON value is not an objec; in that cases, + using `value()` with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -4121,13 +4348,13 @@ class basic_json { return ptr.get_checked(this); } - JSON_CATCH (std::out_of_range&) + JSON_CATCH (out_of_range&) { return default_value; } } - JSON_THROW(std::domain_error("cannot use value() with " + type_name())); + JSON_THROW(type_error(306, "cannot use value() with " + type_name())); } /*! @@ -4156,7 +4383,7 @@ class basic_json assertions**). @post The JSON value remains unchanged. - @throw std::out_of_range when called on `null` value + @throw invalid_iterator.214 when called on `null` value @liveexample{The following code shows an example for `front()`.,front} @@ -4199,7 +4426,8 @@ class basic_json assertions**). @post The JSON value remains unchanged. - @throw std::out_of_range when called on `null` value. + @throw invalid_iterator.214 when called on a `null` value. See example + below. @liveexample{The following code shows an example for `back()`.,back} @@ -4243,11 +4471,12 @@ class basic_json @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. - @throw std::domain_error if called on a `null` value; example: `"cannot - use erase() with null"` - @throw std::domain_error if called on an iterator which does not belong to - the current JSON value; example: `"iterator does not fit current value"` - @throw std::out_of_range if called on a primitive type with invalid + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid iterator (i.e., any iterator which is not `begin()`); example: `"iterator out of range"` @@ -4278,7 +4507,7 @@ class basic_json // make sure iterator fits the current value if (this != pos.m_object) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } IteratorType result = end(); @@ -4293,7 +4522,7 @@ class basic_json { if (not pos.m_it.primitive_iterator.is_begin()) { - JSON_THROW(std::out_of_range("iterator out of range")); + JSON_THROW(invalid_iterator(205, "iterator out of range")); } if (is_string()) @@ -4323,7 +4552,7 @@ class basic_json default: { - JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); } } @@ -4350,11 +4579,11 @@ class basic_json @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. - @throw std::domain_error if called on a `null` value; example: `"cannot - use erase() with null"` - @throw std::domain_error if called on iterators which does not belong to - the current JSON value; example: `"iterators do not fit current value"` - @throw std::out_of_range if called on a primitive type with invalid + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid iterators (i.e., if `first != begin()` and `last != end()`); example: `"iterators out of range"` @@ -4385,7 +4614,7 @@ class basic_json // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) { - JSON_THROW(std::domain_error("iterators do not fit current value")); + JSON_THROW(invalid_iterator(203, "iterators do not fit current value")); } IteratorType result = end(); @@ -4400,7 +4629,7 @@ class basic_json { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - JSON_THROW(std::out_of_range("iterators out of range")); + JSON_THROW(invalid_iterator(204, "iterators out of range")); } if (is_string()) @@ -4432,7 +4661,7 @@ class basic_json default: { - JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); } } @@ -4453,7 +4682,7 @@ class basic_json @post References and iterators to the erased elements are invalidated. Other references and iterators are not affected. - @throw std::domain_error when called on a type other than JSON object; + @throw type_error.307 when called on a type other than JSON object; example: `"cannot use erase() with null"` @complexity `log(size()) + count(key)` @@ -4476,7 +4705,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); } /*! @@ -4486,9 +4715,9 @@ class basic_json @param[in] idx index of the element to remove - @throw std::domain_error when called on a type other than JSON array; + @throw type_error.307 when called on a type other than JSON object; example: `"cannot use erase() with null"` - @throw std::out_of_range when `idx >= size()`; example: `"array index 17 + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 is out of range"` @complexity Linear in distance between @a idx and the end of the container. @@ -4510,14 +4739,14 @@ class basic_json { if (idx >= size()) { - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); } } @@ -5219,7 +5448,7 @@ class basic_json @param[in] val the value to add to the JSON array - @throw std::domain_error when called on a type other than JSON array or + @throw type_error.308 when called on a type other than JSON array or null; example: `"cannot use push_back() with number"` @complexity Amortized constant. @@ -5235,7 +5464,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); + JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5271,7 +5500,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); + JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5305,7 +5534,7 @@ class basic_json @param[in] val the value to add to the JSON object - @throw std::domain_error when called on a type other than JSON object or + @throw type_error.308 when called on a type other than JSON object or null; example: `"cannot use push_back() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @@ -5321,7 +5550,7 @@ class basic_json // push_back only works for null objects or objects if (not(is_null() or is_object())) { - JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); + JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); } // transform null object into an object @@ -5404,7 +5633,7 @@ class basic_json @param[in] args arguments to forward to a constructor of @ref basic_json @tparam Args compatible types to create a @ref basic_json object - @throw std::domain_error when called on a type other than JSON array or + @throw type_error.311 when called on a type other than JSON array or null; example: `"cannot use emplace_back() with number"` @complexity Amortized constant. @@ -5421,7 +5650,7 @@ class basic_json // emplace_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(std::domain_error("cannot use emplace_back() with " + type_name())); + JSON_THROW(type_error(311, "cannot use emplace_back() with " + type_name())); } // transform null object into an array @@ -5451,7 +5680,7 @@ class basic_json already-existing element if no insertion happened, and a bool denoting whether the insertion took place. - @throw std::domain_error when called on a type other than JSON object or + @throw type_error.311 when called on a type other than JSON object or null; example: `"cannot use emplace() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @@ -5469,7 +5698,7 @@ class basic_json // emplace only works for null objects or arrays if (not(is_null() or is_object())) { - JSON_THROW(std::domain_error("cannot use emplace() with " + type_name())); + JSON_THROW(type_error(311, "cannot use emplace() with " + type_name())); } // transform null object into an object @@ -5500,10 +5729,10 @@ class basic_json @param[in] val element to insert @return iterator pointing to the inserted @a val. - @throw std::domain_error if called on JSON values other than arrays; + @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` @complexity Constant plus linear in the distance between @a pos and end of the container. @@ -5520,7 +5749,7 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5529,7 +5758,7 @@ class basic_json return result; } - JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); + JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); } /*! @@ -5553,10 +5782,10 @@ class basic_json @return iterator pointing to the first element inserted, or @a pos if `cnt==0` - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` @complexity Linear in @a cnt plus linear in the distance between @a pos and end of the container. @@ -5573,7 +5802,7 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5582,7 +5811,7 @@ class basic_json return result; } - JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); + JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); } /*! @@ -5595,13 +5824,13 @@ class basic_json @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - @throw std::domain_error if @a first and @a last do not belong to the same - JSON value; example: `"iterators do not fit"` - @throw std::domain_error if @a first or @a last are iterators into + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into container for which insert is called; example: `"passed iterators may not belong to container"` @@ -5620,24 +5849,24 @@ class basic_json // insert only works for arrays if (not is_array()) { - JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); + JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } // check if range iterators belong to the same JSON object if (first.m_object != last.m_object) { - JSON_THROW(std::domain_error("iterators do not fit")); + JSON_THROW(invalid_iterator(210, "iterators do not fit")); } if (first.m_object == this or last.m_object == this) { - JSON_THROW(std::domain_error("passed iterators may not belong to container")); + JSON_THROW(invalid_iterator(211, "passed iterators may not belong to container")); } // insert to array and return iterator @@ -5658,10 +5887,10 @@ class basic_json the end() iterator @param[in] ilist initializer list to insert the values from - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` @return iterator pointing to the first element inserted, or @a pos if `ilist` is empty @@ -5678,13 +5907,13 @@ class basic_json // insert only works for arrays if (not is_array()) { - JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); + JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5732,8 +5961,8 @@ class basic_json @param[in,out] other array to exchange the contents with - @throw std::domain_error when JSON value is not an array; example: - `"cannot use swap() with string"` + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` @complexity Constant. @@ -5751,7 +5980,7 @@ class basic_json } else { - JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); + JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); } } @@ -5765,7 +5994,7 @@ class basic_json @param[in,out] other object to exchange the contents with - @throw std::domain_error when JSON value is not an object; example: + @throw type_error.310 when JSON value is not an object; example: `"cannot use swap() with string"` @complexity Constant. @@ -5784,7 +6013,7 @@ class basic_json } else { - JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); + JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); } } @@ -5798,7 +6027,7 @@ class basic_json @param[in,out] other string to exchange the contents with - @throw std::domain_error when JSON value is not a string; example: `"cannot + @throw type_error.310 when JSON value is not a string; example: `"cannot use swap() with boolean"` @complexity Constant. @@ -5817,7 +6046,7 @@ class basic_json } else { - JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); + JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); } } @@ -6667,6 +6896,13 @@ class basic_json */ void dump_float(number_float_t x) { + // NaN / inf + if (not std::isfinite(x) or std::isnan(x)) + { + o.write("null", 4); + return; + } + // special case for 0.0 and -0.0 if (x == 0) { @@ -6824,6 +7060,11 @@ class basic_json @return result of the deserialization + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -6854,6 +7095,10 @@ class basic_json @return result of the deserialization + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -6890,6 +7135,11 @@ class basic_json @return result of the deserialization + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @throw parse_error.111 if input stream is in a bad state + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -6949,6 +7199,10 @@ class basic_json @return result of the deserialization + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -7019,6 +7273,10 @@ class basic_json @return result of the deserialization + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -7051,7 +7309,10 @@ class basic_json @param[in,out] i input stream to read a serialized JSON value from @param[in,out] j JSON value to write the deserialized input to - @throw std::invalid_argument in case of parse errors + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @throw parse_error.111 if input stream is in a bad state @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. @@ -7155,7 +7416,7 @@ class basic_json @tparam T the integral return type - @throw std::out_of_range if there are less than sizeof(T)+1 bytes in the + @throw parse_error.110 if there are less than sizeof(T)+1 bytes in the vector @a vec to read In the for loop, the bytes from the vector are copied in reverse order into @@ -7180,10 +7441,8 @@ class basic_json template static T get_from_vector(const std::vector& vec, const size_t current_index) { - if (current_index + sizeof(T) + 1 > vec.size()) - { - JSON_THROW(std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector")); - } + // check if we can read sizeof(T) bytes starting the next index + check_length(vec.size(), sizeof(T), current_index + 1); T result; auto* ptr = reinterpret_cast(&result); @@ -7235,25 +7494,25 @@ class basic_json // positive fixnum add_to_vector(v, 1, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 8 v.push_back(0xcc); add_to_vector(v, 1, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 16 v.push_back(0xcd); add_to_vector(v, 2, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 32 v.push_back(0xce); add_to_vector(v, 4, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 64 v.push_back(0xcf); @@ -7267,25 +7526,25 @@ class basic_json // negative fixnum add_to_vector(v, 1, j.m_value.number_integer); } - else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 8 v.push_back(0xd0); add_to_vector(v, 1, j.m_value.number_integer); } - else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 16 v.push_back(0xd1); add_to_vector(v, 2, j.m_value.number_integer); } - else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 32 v.push_back(0xd2); add_to_vector(v, 4, j.m_value.number_integer); } - else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 64 v.push_back(0xd3); @@ -7302,25 +7561,25 @@ class basic_json // positive fixnum add_to_vector(v, 1, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 8 v.push_back(0xcc); add_to_vector(v, 1, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 16 v.push_back(0xcd); add_to_vector(v, 2, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 32 v.push_back(0xce); add_to_vector(v, 4, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 64 v.push_back(0xcf); @@ -7477,19 +7736,19 @@ class basic_json { add_to_vector(v, 1, j.m_value.number_integer); } - else if (j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { v.push_back(0x18); // one-byte uint8_t add_to_vector(v, 1, j.m_value.number_integer); } - else if (j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { v.push_back(0x19); // two-byte uint16_t add_to_vector(v, 2, j.m_value.number_integer); } - else if (j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { v.push_back(0x1a); // four-byte uint32_t @@ -7511,19 +7770,19 @@ class basic_json { v.push_back(static_cast(0x20 + positive_number)); } - else if (positive_number <= std::numeric_limits::max()) + else if (positive_number <= (std::numeric_limits::max)()) { // int 8 v.push_back(0x38); add_to_vector(v, 1, positive_number); } - else if (positive_number <= std::numeric_limits::max()) + else if (positive_number <= (std::numeric_limits::max)()) { // int 16 v.push_back(0x39); add_to_vector(v, 2, positive_number); } - else if (positive_number <= std::numeric_limits::max()) + else if (positive_number <= (std::numeric_limits::max)()) { // int 32 v.push_back(0x3a); @@ -7732,22 +7991,83 @@ class basic_json // simple case: requested length is greater than the vector's length if (len > size or offset > size) { - JSON_THROW(std::out_of_range("len out of range")); + JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } // second case: adding offset would result in overflow - if ((size > (std::numeric_limits::max() - offset))) + if ((size > ((std::numeric_limits::max)() - offset))) { - JSON_THROW(std::out_of_range("len+offset out of range")); + JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } // last case: reading past the end of the vector if (len + offset > size) { - JSON_THROW(std::out_of_range("len+offset out of range")); + JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } } + /*! + @brief check if the next byte belongs to a string + + While parsing a map, the keys must be strings. This function checks if the + current byte is one of the start bytes for a string in MessagePack: + + - 0xa0 - 0xbf: fixstr + - 0xd9: str 8 + - 0xda: str 16 + - 0xdb: str 32 + + @param[in] v MessagePack serialization + @param[in] idx byte index in @a v to check for a string + + @throw parse_error.113 if `v[idx]` does not belong to a string + */ + static void msgpack_expect_string(const std::vector& v, size_t idx) + { + check_length(v.size(), 1, idx); + + const auto byte = v[idx]; + if ((byte >= 0xa0 and byte <= 0xbf) or (byte >= 0xd9 and byte <= 0xdb)) + { + return; + } + + std::stringstream ss; + ss << std::hex << static_cast(v[idx]); + JSON_THROW(parse_error(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str())); + } + + /*! + @brief check if the next byte belongs to a string + + While parsing a map, the keys must be strings. This function checks if the + current byte is one of the start bytes for a string in CBOR: + + - 0x60 - 0x77: fixed length + - 0x78 - 0x7b: variable length + - 0x7f: indefinity length + + @param[in] v CBOR serialization + @param[in] idx byte index in @a v to check for a string + + @throw parse_error.113 if `v[idx]` does not belong to a string + */ + static void cbor_expect_string(const std::vector& v, size_t idx) + { + check_length(v.size(), 1, idx); + + const auto byte = v[idx]; + if ((byte >= 0x60 and byte <= 0x7b) or byte == 0x7f) + { + return; + } + + std::stringstream ss; + ss << std::hex << static_cast(v[idx]); + JSON_THROW(parse_error(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str())); + } + /*! @brief create a JSON value from a given MessagePack vector @@ -7756,20 +8076,21 @@ class basic_json @return deserialized JSON value - @throw std::invalid_argument if unsupported features from MessagePack were + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from MessagePack were used in the given vector @a v or if the input is not valid MessagePack - @throw std::out_of_range if the given vector ends prematurely + @throw parse_error.113 if a string was expected as map key, but not found @sa https://github.com/msgpack/msgpack/blob/master/spec.md */ static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) { - // make sure reading 1 byte is safe - check_length(v.size(), 1, idx); - // store and increment index const size_t current_idx = idx++; + // make sure reading 1 byte is safe + check_length(v.size(), 1, current_idx); + if (v[current_idx] <= 0xbf) { if (v[current_idx] <= 0x7f) // positive fixint @@ -7782,6 +8103,7 @@ class basic_json const size_t len = v[current_idx] & 0x0f; for (size_t i = 0; i < len; ++i) { + msgpack_expect_string(v, idx); std::string key = from_msgpack_internal(v, idx); result[key] = from_msgpack_internal(v, idx); } @@ -7833,9 +8155,10 @@ class basic_json { // copy bytes in reverse order into the double variable float res; + check_length(v.size(), sizeof(float), current_idx + 1); for (size_t byte = 0; byte < sizeof(float); ++byte) { - reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; } idx += sizeof(float); // skip content bytes return res; @@ -7845,9 +8168,10 @@ class basic_json { // copy bytes in reverse order into the double variable double res; + check_length(v.size(), sizeof(double), current_idx + 1); for (size_t byte = 0; byte < sizeof(double); ++byte) { - reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; } idx += sizeof(double); // skip content bytes return res; @@ -7959,6 +8283,7 @@ class basic_json idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { + msgpack_expect_string(v, idx); std::string key = from_msgpack_internal(v, idx); result[key] = from_msgpack_internal(v, idx); } @@ -7972,6 +8297,7 @@ class basic_json idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { + msgpack_expect_string(v, idx); std::string key = from_msgpack_internal(v, idx); result[key] = from_msgpack_internal(v, idx); } @@ -7980,7 +8306,9 @@ class basic_json default: { - JSON_THROW(std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx])))); + std::stringstream ss; + ss << std::hex << static_cast(v[current_idx]); + JSON_THROW(parse_error(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str())); } } } @@ -7994,9 +8322,10 @@ class basic_json @return deserialized JSON value - @throw std::invalid_argument if unsupported features from CBOR were used in - the given vector @a v or if the input is not valid CBOR - @throw std::out_of_range if the given vector ends prematurely + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from CBOR were + used in the given vector @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found @sa https://tools.ietf.org/html/rfc7049 */ @@ -8005,7 +8334,10 @@ class basic_json // store and increment index const size_t current_idx = idx++; - switch (v.at(current_idx)) + // make sure reading 1 byte is safe + check_length(v.size(), 1, current_idx); + + switch (v[current_idx]) { // Integer 0x00..0x17 (0..23) case 0x00: @@ -8186,7 +8518,7 @@ class basic_json case 0x7f: // UTF-8 string (indefinite length) { std::string result; - while (v.at(idx) != 0xff) + while (check_length(v.size(), 1, idx), v[idx] != 0xff) { string_t s = from_cbor_internal(v, idx); result += s; @@ -8282,7 +8614,7 @@ class basic_json case 0x9f: // array (indefinite length) { basic_json result = value_t::array; - while (v.at(idx) != 0xff) + while (check_length(v.size(), 1, idx), v[idx] != 0xff) { result.push_back(from_cbor_internal(v, idx)); } @@ -8382,7 +8714,7 @@ class basic_json case 0xbf: // map (indefinite length) { basic_json result = value_t::object; - while (v.at(idx) != 0xff) + while (check_length(v.size(), 1, idx), v[idx] != 0xff) { std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); @@ -8418,7 +8750,8 @@ class basic_json // include at least decoding support for them even without such // support. An example of a small decoder for half-precision // floating-point numbers in the C language is shown in Fig. 3. - const int half = (v.at(current_idx + 1) << 8) + v.at(current_idx + 2); + check_length(v.size(), 2, current_idx + 1); + const int half = (v[current_idx + 1] << 8) + v[current_idx + 2]; const int exp = (half >> 10) & 0x1f; const int mant = half & 0x3ff; double val; @@ -8443,9 +8776,10 @@ class basic_json { // copy bytes in reverse order into the float variable float res; + check_length(v.size(), sizeof(float), current_idx + 1); for (size_t byte = 0; byte < sizeof(float); ++byte) { - reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; } idx += sizeof(float); // skip content bytes return res; @@ -8455,9 +8789,10 @@ class basic_json { // copy bytes in reverse order into the double variable double res; + check_length(v.size(), sizeof(double), current_idx + 1); for (size_t byte = 0; byte < sizeof(double); ++byte) { - reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; } idx += sizeof(double); // skip content bytes return res; @@ -8465,7 +8800,9 @@ class basic_json default: // anything else (0xFF is handled inside the other types) { - JSON_THROW(std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx])))); + std::stringstream ss; + ss << std::hex << static_cast(v[current_idx]); + JSON_THROW(parse_error(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str())); } } } @@ -8510,9 +8847,9 @@ class basic_json @param[in] start_index the index to start reading from @a v (0 by default) @return deserialized JSON value - @throw std::invalid_argument if unsupported features from MessagePack were + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from MessagePack were used in the given vector @a v or if the input is not valid MessagePack - @throw std::out_of_range if the given vector ends prematurely @complexity Linear in the size of the byte vector @a v. @@ -8573,9 +8910,9 @@ class basic_json @param[in] start_index the index to start reading from @a v (0 by default) @return deserialized JSON value - @throw std::invalid_argument if unsupported features from CBOR were used in - the given vector @a v or if the input is not valid MessagePack - @throw std::out_of_range if the given vector ends prematurely + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from CBOR were + used in the given vector @a v or if the input is not valid CBOR @complexity Linear in the size of the byte vector @a v. @@ -9133,7 +9470,7 @@ class basic_json case basic_json::value_t::null: { - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } default: @@ -9143,7 +9480,7 @@ class basic_json return *m_object; } - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } } } @@ -9177,7 +9514,7 @@ class basic_json return m_object; } - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } } } @@ -9277,7 +9614,7 @@ class basic_json // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(std::domain_error("cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -9319,7 +9656,7 @@ class basic_json // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(std::domain_error("cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -9328,7 +9665,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(std::domain_error("cannot compare order of object iterators")); + JSON_THROW(invalid_iterator(213, "cannot compare order of object iterators")); } case basic_json::value_t::array: @@ -9382,7 +9719,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(std::domain_error("cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -9444,7 +9781,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(std::domain_error("cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -9471,7 +9808,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(std::domain_error("cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator(208, "cannot use operator[] for object iterators")); } case basic_json::value_t::array: @@ -9481,7 +9818,7 @@ class basic_json case basic_json::value_t::null: { - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } default: @@ -9491,7 +9828,7 @@ class basic_json return *m_object; } - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } } } @@ -9509,7 +9846,7 @@ class basic_json return m_it.object_iterator->first; } - JSON_THROW(std::domain_error("cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator(207, "cannot use key() for non-object iterators")); } /*! @@ -9689,14 +10026,17 @@ class basic_json m_limit = m_content + len; } - /// a lexer from an input stream + /*! + @brief a lexer from an input stream + @throw parse_error.111 if input stream is in a bad state + */ explicit lexer(std::istream& s) : m_stream(&s), m_line_buffer() { // immediately abort if stream is erroneous if (s.fail()) { - JSON_THROW(std::invalid_argument("stream error")); + JSON_THROW(parse_error(111, 0, "bad input stream")); } // fill buffer @@ -9730,17 +10070,17 @@ class basic_json @return string representation of the code point; the length of the result string is between 1 and 4 characters. - @throw std::out_of_range if code point is > 0x10ffff; example: `"code - points above 0x10FFFF are invalid"` - @throw std::invalid_argument if the low surrogate is invalid; example: + @throw parse_error.102 if the low surrogate is invalid; example: `""missing or wrong low surrogate""` + @throw parse_error.103 if code point is > 0x10ffff; example: `"code + points above 0x10FFFF are invalid"` @complexity Constant. @see */ - static string_t to_unicode(const std::size_t codepoint1, - const std::size_t codepoint2 = 0) + string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) const { // calculate the code point from the given code points std::size_t codepoint = codepoint1; @@ -9763,7 +10103,7 @@ class basic_json } else { - JSON_THROW(std::invalid_argument("missing or wrong low surrogate")); + JSON_THROW(parse_error(102, get_position(), "missing or wrong low surrogate")); } } @@ -9797,7 +10137,7 @@ class basic_json } else { - JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid")); + JSON_THROW(parse_error(103, get_position(), "code points above 0x10FFFF are invalid")); } return result; @@ -10057,6 +10397,7 @@ basic_json_parser_6: goto basic_json_parser_6; } { + position += static_cast((m_cursor - m_start)); continue; } basic_json_parser_9: @@ -10912,6 +11253,7 @@ basic_json_parser_74: } + position += static_cast((m_cursor - m_start)); return last_token_type; } @@ -10990,7 +11332,7 @@ basic_json_parser_74: m_line_buffer_tmp.clear(); // check if stream is still good - if (not m_stream->good()) + if (m_stream->fail()) { JSON_THROW(std::invalid_argument("stream error")); } @@ -11074,7 +11416,8 @@ basic_json_parser_74: @return string value of current token without opening and closing quotes - @throw std::out_of_range if to_unicode fails + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails */ string_t get_string() const { @@ -11160,7 +11503,7 @@ basic_json_parser_74: // make sure there is a subsequent unicode if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') { - JSON_THROW(std::invalid_argument("missing low surrogate")); + JSON_THROW(parse_error(102, get_position(), "missing low surrogate")); } // get code yyyy from uxxxx\uyyyy @@ -11173,7 +11516,7 @@ basic_json_parser_74: else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) { // we found a lone low surrogate - JSON_THROW(std::invalid_argument("missing high surrogate")); + JSON_THROW(parse_error(102, get_position(), "missing high surrogate")); } else { @@ -11414,11 +11757,10 @@ basic_json_parser_74: result.m_type = value_t::number_float; result.m_value = val; - // replace infinity and NAN by null + // throw in case of infinity or NAN if (not std::isfinite(result.m_value.number_float)) { - result.m_type = value_t::null; - result.m_value = basic_json::json_value(); + JSON_THROW(out_of_range(406, "number overflow parsing '" + get_token_string() + "'")); } return true; @@ -11428,6 +11770,11 @@ basic_json_parser_74: return false; } + constexpr size_t get_position() const + { + return position; + } + private: /// optional input stream std::istream* m_stream = nullptr; @@ -11447,6 +11794,8 @@ basic_json_parser_74: const lexer_char_t* m_limit = nullptr; /// the last token type token_type last_token_type = token_type::end_of_input; + /// current position in the input (read bytes) + size_t position = 0; }; /*! @@ -11463,7 +11812,10 @@ basic_json_parser_74: m_lexer(reinterpret_cast(buff), std::strlen(buff)) {} - /// a parser reading from an input stream + /*! + @brief a parser reading from an input stream + @throw parse_error.111 if input stream is in a bad state + */ parser(std::istream& is, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(is) {} @@ -11479,7 +11831,12 @@ basic_json_parser_74: static_cast(std::distance(first, last))) {} - /// public parser interface + /*! + @brief public parser interface + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ basic_json parse() { // read first token @@ -11496,7 +11853,12 @@ basic_json_parser_74: } private: - /// the actual parser + /*! + @brief the actual parser + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ basic_json parse_internal(bool keep) { auto result = basic_json(value_t::discarded); @@ -11699,6 +12061,9 @@ basic_json_parser_74: return last_token; } + /*! + @throw parse_error.101 if expected token did not occur + */ void expect(typename lexer::token_type t) const { if (t != last_token) @@ -11708,10 +12073,13 @@ basic_json_parser_74: "'") : lexer::token_type_name(last_token)); error_msg += "; expected " + lexer::token_type_name(t); - JSON_THROW(std::invalid_argument(error_msg)); + JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg)); } } + /*! + @throw parse_error.101 if unexpected token occurred + */ void unexpect(typename lexer::token_type t) const { if (t == last_token) @@ -11720,7 +12088,7 @@ basic_json_parser_74: error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + "'") : lexer::token_type_name(last_token)); - JSON_THROW(std::invalid_argument(error_msg)); + JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg)); } } @@ -11763,12 +12131,12 @@ basic_json_parser_74: empty string is assumed which references the whole JSON value - @throw std::domain_error if reference token is nonempty and does not - begin with a slash (`/`); example: `"JSON pointer must be empty or - begin with /"` - @throw std::domain_error if a tilde (`~`) is not followed by `0` - (representing `~`) or `1` (representing `/`); example: `"escape error: - ~ must be followed with 0 or 1"` + @throw parse_error.107 if the given JSON pointer @a s is nonempty and + does not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s + is not followed by `0` (representing `~`) or `1` (representing `/`); + see example below @liveexample{The example shows the construction several valid JSON pointers as well as the exceptional behavior.,json_pointer} @@ -11811,12 +12179,15 @@ basic_json_parser_74: } private: - /// remove and return last reference pointer + /*! + @brief remove and return last reference pointer + @throw out_of_range.405 if JSON pointer has no parent + */ std::string pop_back() { if (is_root()) { - JSON_THROW(std::domain_error("JSON pointer has no parent")); + JSON_THROW(out_of_range(405, "JSON pointer has no parent")); } auto last = reference_tokens.back(); @@ -11834,7 +12205,7 @@ basic_json_parser_74: { if (is_root()) { - JSON_THROW(std::domain_error("JSON pointer has no parent")); + JSON_THROW(out_of_range(405, "JSON pointer has no parent")); } json_pointer result = *this; @@ -11846,6 +12217,9 @@ basic_json_parser_74: @brief create and return a reference to the pointed to value @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened */ reference get_and_create(reference j) const { @@ -11882,7 +12256,14 @@ basic_json_parser_74: case value_t::array: { // create an entry in the array - result = &result->operator[](static_cast(std::stoi(reference_token))); + JSON_TRY + { + result = &result->operator[](static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } break; } @@ -11895,7 +12276,7 @@ basic_json_parser_74: */ default: { - JSON_THROW(std::domain_error("invalid value to unflatten")); + JSON_THROW(type_error(313, "invalid value to unflatten")); } } } @@ -11918,9 +12299,9 @@ basic_json_parser_74: @complexity Linear in the length of the JSON pointer. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved */ reference get_unchecked(pointer ptr) const { @@ -11963,7 +12344,7 @@ basic_json_parser_74: // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(std::domain_error("array index must not begin with '0'")); + JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } if (reference_token == "-") @@ -11974,14 +12355,21 @@ basic_json_parser_74: else { // convert array index to number; unchecked access - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + JSON_TRY + { + ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } } break; } default: { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11989,6 +12377,12 @@ basic_json_parser_74: return *ptr; } + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ reference get_checked(pointer ptr) const { for (const auto& reference_token : reference_tokens) @@ -12007,25 +12401,32 @@ basic_json_parser_74: if (reference_token == "-") { // "-" always fails the range check - JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(std::domain_error("array index must not begin with '0'")); + JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check - ptr = &ptr->at(static_cast(std::stoi(reference_token))); + JSON_TRY + { + ptr = &ptr->at(static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } break; } default: { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -12040,6 +12441,11 @@ basic_json_parser_74: @return const reference to the JSON value pointed to by the JSON pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved */ const_reference get_unchecked(const_pointer ptr) const { @@ -12059,25 +12465,32 @@ basic_json_parser_74: if (reference_token == "-") { // "-" cannot be used for const access - JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(std::domain_error("array index must not begin with '0'")); + JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // use unchecked array access - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + JSON_TRY + { + ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } break; } default: { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -12085,6 +12498,12 @@ basic_json_parser_74: return *ptr; } + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ const_reference get_checked(const_pointer ptr) const { for (const auto& reference_token : reference_tokens) @@ -12103,25 +12522,32 @@ basic_json_parser_74: if (reference_token == "-") { // "-" always fails the range check - JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(std::domain_error("array index must not begin with '0'")); + JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check - ptr = &ptr->at(static_cast(std::stoi(reference_token))); + JSON_TRY + { + ptr = &ptr->at(static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } break; } default: { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -12129,7 +12555,15 @@ basic_json_parser_74: return *ptr; } - /// split the string input to reference tokens + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ static std::vector split(const std::string& reference_string) { std::vector result; @@ -12143,7 +12577,7 @@ basic_json_parser_74: // check if nonempty reference string begins with slash if (reference_string[0] != '/') { - JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'")); + JSON_THROW(parse_error(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); } // extract the reference tokens: @@ -12178,7 +12612,7 @@ basic_json_parser_74: (reference_token[pos + 1] != '0' and reference_token[pos + 1] != '1')) { - JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'")); + JSON_THROW(parse_error(108, 0, "escape character '~' must be followed with '0' or '1'")); } } @@ -12190,7 +12624,6 @@ basic_json_parser_74: return result; } - private: /*! @brief replace all occurrences of a substring by another string @@ -12199,7 +12632,8 @@ basic_json_parser_74: @param[in] f the substring to replace with @a t @param[in] t the string to replace @a f - @pre The search string @a f must not be empty. + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** @since version 2.0.0 */ @@ -12299,12 +12733,17 @@ basic_json_parser_74: @param[in] value flattened JSON @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened */ static basic_json unflatten(const basic_json& value) { if (not value.is_object()) { - JSON_THROW(std::domain_error("only objects can be unflattened")); + JSON_THROW(type_error(314, "only objects can be unflattened")); } basic_json result; @@ -12314,7 +12753,7 @@ basic_json_parser_74: { if (not element.second.is_primitive()) { - JSON_THROW(std::domain_error("values in object must be primitive")); + JSON_THROW(type_error(315, "values in object must be primitive")); } // assign value to reference pointed to by JSON pointer; Note @@ -12328,7 +12767,6 @@ basic_json_parser_74: return result; } - private: friend bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept { @@ -12377,9 +12815,9 @@ basic_json_parser_74: @complexity Constant. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved @liveexample{The behavior is shown in the example.,operatorjson_pointer} @@ -12404,9 +12842,10 @@ basic_json_parser_74: @complexity Constant. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} @@ -12427,15 +12866,30 @@ basic_json_parser_74: @return reference to the element pointed to by @a ptr + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + @complexity Constant. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @since version 2.0.0 @liveexample{The behavior is shown in the example.,at_json_pointer} - - @since version 2.0.0 */ reference at(const json_pointer& ptr) { @@ -12452,15 +12906,30 @@ basic_json_parser_74: @return reference to the element pointed to by @a ptr + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + @complexity Constant. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @since version 2.0.0 @liveexample{The behavior is shown in the example.,at_json_pointer_const} - - @since version 2.0.0 */ const_reference at(const json_pointer& ptr) const { @@ -12516,6 +12985,9 @@ basic_json_parser_74: @complexity Linear in the size the JSON value. + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitve + @liveexample{The following code shows how a flattened JSON object is unflattened into the original nested JSON object.,unflatten} @@ -12553,12 +13025,23 @@ basic_json_parser_74: any case, the original value is not changed: the patch is applied to a copy of the value. - @throw std::out_of_range if a JSON pointer inside the patch could not - be resolved successfully in the current JSON value; example: `"key baz - not found"` - @throw invalid_argument if the JSON patch is malformed (e.g., mandatory + @throw parse_error.104 if the JSON patch does not consist of an array of + objects + + @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory attributes are missing); example: `"operation add must have member path"` + @throw out_of_range.401 if an array index is out of range. + + @throw out_of_range.403 if a JSON pointer inside the patch could not be + resolved successfully in the current JSON value; example: `"key baz not + found"` + + @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", + "move") + + @throw other_error.501 if "test" operation was unsuccessful + @complexity Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is affected by the patch, the complexity can usually be neglected. @@ -12655,7 +13138,7 @@ basic_json_parser_74: if (static_cast(idx) > parent.size()) { // avoid undefined behavior - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); } else { @@ -12693,7 +13176,7 @@ basic_json_parser_74: } else { - JSON_THROW(std::out_of_range("key '" + last_path + "' not found")); + JSON_THROW(out_of_range(403, "key '" + last_path + "' not found")); } } else if (parent.is_array()) @@ -12703,11 +13186,10 @@ basic_json_parser_74: } }; - // type check + // type check: top level value must be an array if (not json_patch.is_array()) { - // a JSON patch must be an array of objects - JSON_THROW(std::invalid_argument("JSON patch must be an array of objects")); + JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects")); } // iterate and apply the operations @@ -12727,23 +13209,23 @@ basic_json_parser_74: // check if desired value is present if (it == val.m_value.object->end()) { - JSON_THROW(std::invalid_argument(error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error(105, 0, error_msg + " must have member '" + member + "'")); } // check if result is of type string if (string_type and not it->second.is_string()) { - JSON_THROW(std::invalid_argument(error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error(105, 0, error_msg + " must have string member '" + member + "'")); } // no error: return value return it->second; }; - // type check + // type check: every element of the array must be an object if (not val.is_object()) { - JSON_THROW(std::invalid_argument("JSON patch must be an array of objects")); + JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects")); } // collect mandatory members @@ -12808,7 +13290,7 @@ basic_json_parser_74: // the "path" location must exist - use at() success = (result.at(ptr) == get_value("test", "value", false)); } - JSON_CATCH (std::out_of_range&) + JSON_CATCH (out_of_range&) { // ignore out of range errors: success remains false } @@ -12816,7 +13298,7 @@ basic_json_parser_74: // throw an exception if test fails if (not success) { - JSON_THROW(std::domain_error("unsuccessful: " + val.dump())); + JSON_THROW(other_error(501, "unsuccessful: " + val.dump())); } break; @@ -12826,7 +13308,7 @@ basic_json_parser_74: { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(std::invalid_argument("operation value '" + op + "' is invalid")); + JSON_THROW(parse_error(105, 0, "operation value '" + op + "' is invalid")); } } } @@ -13055,6 +13537,22 @@ struct hash return h(j.dump()); } }; + +/// specialization for std::less +template <> +struct less<::nlohmann::detail::value_t> +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + } // namespace std /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index c58b29a87..7e7e16e30 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -52,7 +52,6 @@ SOFTWARE. #include // addressof, allocator, allocator_traits, unique_ptr #include // accumulate #include // stringstream -#include // domain_error, invalid_argument, out_of_range #include // getline, stoi, string, to_string #include // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type #include // declval, forward, make_pair, move, pair, swap @@ -82,7 +81,7 @@ SOFTWARE. #endif // allow to disable exceptions -#if not defined(JSON_NOEXCEPTION) || defined(__EXCEPTIONS) +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && not defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception #define JSON_TRY try #define JSON_CATCH(exception) catch(exception) @@ -110,6 +109,214 @@ This namespace collects some functions that could not be defined inside the */ namespace detail { +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +Extension of std::exception objects with a member @a id for exception ids. + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// create exception with id an explanatory string + exception(int id_, const std::string& ename, const std::string& what_arg_) + : id(id_), + what_arg("[json.exception." + ename + "." + std::to_string(id_) + "] " + what_arg_) + {} + + /// returns the explanatory string + virtual const char* what() const noexcept + { + return what_arg.c_str(); + } + + /// the id of the exception + const int id; + + private: + /// the explanatory string + const std::string what_arg; +}; + +/*! +@brief exception indicating a parse error + +This excpetion is thrown by the library when a parse error occurs. Parse +errors can occur during the deserialization of JSON text as well as when +using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +@note For an input with n bytes, 1 is the index of the first character + and n+1 is the index of the terminating null byte or the end of + file. This also holds true when reading a byte vector (CBOR or + MessagePack). + +Exceptions have ids 1xx. + +name / id | example massage | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number wihtout a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.111 | parse error: bad input stream | Parsing CBOR or MessagePack from an input stream where the [`badbit` or `failbit`](http://en.cppreference.com/w/cpp/io/ios_base/iostate) is set. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xf8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | | While parsing a map key, a value that is not a string has been read. + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] byte_ the byte index where the error occured (or 0 if + the position cannot be determined) + @param[in] what_arg_ the explanatory string + */ + parse_error(int id_, size_t byte_, const std::string& what_arg_) + : exception(id_, "parse_error", "parse error" + + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + + ": " + what_arg_), + byte(byte_) + {} + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character + and n+1 is the index of the terminating null byte or the end of + file. This also holds true when reading a byte vector (CBOR or + MessagePack). + */ + const size_t byte; +}; + +/*! +@brief exception indicating errors with iterators + +Exceptions have ids 2xx. + +name / id | example massage | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compated, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + invalid_iterator(int id_, const std::string& what_arg_) + : exception(id_, "invalid_iterator", what_arg_) + {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +Exceptions have ids 3xx. + +name / id | example massage | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + type_error(int id_, const std::string& what_arg_) + : exception(id_, "type_error", what_arg_) + {} +}; + +/*! +@brief exception indicating access out of the defined range + +Exceptions have ids 4xx. + +name / id | example massage | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + out_of_range(int id_, const std::string& what_arg_) + : exception(id_, "out_of_range", what_arg_) + {} +}; + +/*! +@brief exception indicating other errors + +Exceptions have ids 5xx. + +name / id | example massage | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + other_error(int id_, const std::string& what_arg_) + : exception(id_, "other_error", what_arg_) + {} +}; + + + /////////////////////////// // JSON type enumeration // /////////////////////////// @@ -263,16 +470,8 @@ struct external_constructor template static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept { - // replace infinity and NAN by null - if (not std::isfinite(val)) - { - j = BasicJsonType{}; - } - else - { - j.m_type = value_t::number_float; - j.m_value = val; - } + j.m_type = value_t::number_float; + j.m_value = val; j.assert_invariant(); } }; @@ -636,8 +835,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW( - std::domain_error("type must be number, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be number, but is " + j.type_name())); } } } @@ -647,7 +845,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (not j.is_boolean()) { - JSON_THROW(std::domain_error("type must be boolean, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be boolean, but is " + j.type_name())); } b = *j.template get_ptr(); } @@ -657,7 +855,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (not j.is_string()) { - JSON_THROW(std::domain_error("type must be string, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be string, but is " + j.type_name())); } s = *j.template get_ptr(); } @@ -694,28 +892,21 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) { if (not j.is_array()) { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); } arr = *j.template get_ptr(); } // forward_list doesn't have an insert method -template +template::value, int> = 0> void from_json(const BasicJsonType& j, std::forward_list& l) { - // do not perform the check when user wants to retrieve jsons - // (except when it's null.. ?) - if (j.is_null()) + if (not j.is_array()) { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); - } - if (not std::is_same::value) - { - if (not j.is_array()) - { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); - } + JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); } + for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) { l.push_front(it->template get()); @@ -747,8 +938,8 @@ auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, prio using std::end; arr.reserve(j.size()); - std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType & i) + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType & i) { // get() returns *this, this won't call a from_json // method when value_type is BasicJsonType @@ -758,22 +949,15 @@ auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, prio template::value and + std::is_convertible::value and not std::is_same::value, int> = 0> void from_json(const BasicJsonType& j, CompatibleArrayType& arr) { - if (j.is_null()) + if (not j.is_array()) { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); } - // when T == BasicJsonType, do not check if value_t is correct - if (not std::is_same::value) - { - if (not j.is_array()) - { - JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); - } - } from_json_array_impl(j, arr, priority_tag<1> {}); } @@ -783,7 +967,7 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj) { if (not j.is_object()) { - JSON_THROW(std::domain_error("type must be object, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be object, but is " + j.type_name())); } auto inner_object = j.template get_ptr(); @@ -833,7 +1017,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW(std::domain_error("type must be number, but is " + j.type_name())); + JSON_THROW(type_error(302, "type must be number, but is " + j.type_name())); } } } @@ -1065,6 +1249,29 @@ class basic_json template using json_serializer = JSONSerializer; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; + + /// @} + + ///////////////////// // container types // ///////////////////// @@ -1755,7 +1962,7 @@ class basic_json { if (t == value_t::null) { - JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE + JSON_THROW(other_error(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE } break; } @@ -1913,9 +2120,6 @@ class basic_json @complexity Constant. - @throw std::bad_alloc if allocation for object, array, or string value - fails - @liveexample{The following code shows the constructor for different @ref value_t values,basic_json__value_t} @@ -2070,10 +2274,12 @@ class basic_json value_t::array and @ref value_t::object are valid); when @a type_deduction is set to `true`, this parameter has no effect - @throw std::domain_error if @a type_deduction is `false`, @a manual_type - is `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string; example: `"cannot create object from - initializer list"` + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(std::initializer_list) + for an example. @complexity Linear in the size of the initializer list @a init. @@ -2111,7 +2317,7 @@ class basic_json // if object is wanted but impossible, throw an exception if (manual_type == value_t::object and not is_an_object) { - JSON_THROW(std::domain_error("cannot create object from initializer list")); + JSON_THROW(type_error(301, "cannot create object from initializer list")); } } @@ -2187,16 +2393,17 @@ class basic_json related function @ref array(std::initializer_list), there are no cases which can only be expressed by this function. That is, any initializer list @a init can also be passed to the initializer list - constructor @ref basic_json(std::initializer_list, bool, - value_t). + constructor @ref basic_json(std::initializer_list, bool, value_t). @param[in] init initializer list to create an object from (optional) @return JSON object value - @throw std::domain_error if @a init is not a pair whose first elements are - strings; thrown by - @ref basic_json(std::initializer_list, bool, value_t) + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(std::initializer_list, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. @complexity Linear in the size of @a init. @@ -2248,10 +2455,10 @@ class basic_json The semantics depends on the different types a JSON value can have: - In case of primitive types (number, boolean, or string), @a first must be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, std::out_of_range is thrown. + copied. Otherwise, invalid_iterator.204 is thrown. - In case of structured types (array, object), the constructor behaves as similar versions for `std::vector`. - - In case of a null type, std::domain_error is thrown. + - In case of a null type, invalid_iterator.206 is thrown. @tparam InputIT an input iterator type (@ref iterator or @ref const_iterator) @@ -2262,14 +2469,19 @@ class basic_json @pre Iterators @a first and @a last must be initialized. **This precondition is enforced with an assertion.** - @throw std::domain_error if iterators are not compatible; that is, do not - belong to the same JSON value; example: `"iterators are not compatible"` - @throw std::out_of_range if iterators are for a primitive type (number, - boolean, or string) where an out of range error can be detected easily; - example: `"iterators out of range"` - @throw std::bad_alloc if allocation for object, array, or string fails - @throw std::domain_error if called with a null value; example: `"cannot - use construct with iterators from null"` + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. @complexity Linear in distance between @a first and @a last. @@ -2289,7 +2501,7 @@ class basic_json // make sure iterator fits the current value if (first.m_object != last.m_object) { - JSON_THROW(std::domain_error("iterators are not compatible")); + JSON_THROW(invalid_iterator(201, "iterators are not compatible")); } // copy type from first iterator @@ -2306,7 +2518,7 @@ class basic_json { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - JSON_THROW(std::out_of_range("iterators out of range")); + JSON_THROW(invalid_iterator(204, "iterators out of range")); } break; } @@ -2365,7 +2577,8 @@ class basic_json default: { - JSON_THROW(std::domain_error("cannot use construct with iterators from " + first.m_object->type_name())); + JSON_THROW(invalid_iterator(206, "cannot construct with iterators from " + + first.m_object->type_name())); } } @@ -2392,8 +2605,6 @@ class basic_json - The complexity is linear. - As postcondition, it holds: `other == basic_json(other)`. - @throw std::bad_alloc if allocation for object, array, or string fails. - @liveexample{The following code shows an example for the copy constructor.,basic_json__basic_json} @@ -3005,7 +3216,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(std::domain_error("type must be boolean, but is " + type_name())); + JSON_THROW(type_error(302, "type must be boolean, but is " + type_name())); } /// get a pointer to the value (object) @@ -3100,7 +3311,7 @@ class basic_json @tparam ThisType will be deduced as `basic_json` or `const basic_json` - @throw std::domain_error if ReferenceType does not match underlying value + @throw type_error.303 if ReferenceType does not match underlying value type of the current JSON */ template @@ -3117,8 +3328,7 @@ class basic_json return *ptr; } - JSON_THROW(std::domain_error("incompatible ReferenceType for get_ref, actual type is " + - obj.type_name())); + JSON_THROW(type_error(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); } public: @@ -3397,10 +3607,10 @@ class basic_json @return reference to the internally stored JSON value if the requested reference type @a ReferenceType fits to the JSON value; throws - std::domain_error otherwise + type_error.303 otherwise - @throw std::domain_error in case passed type @a ReferenceType is - incompatible with the stored JSON value + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below @complexity Constant. @@ -3443,8 +3653,9 @@ class basic_json @return copy of the JSON value, converted to type @a ValueType - @throw std::domain_error in case passed type @a ValueType is incompatible - to JSON, thrown by @ref get() const + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below @complexity Linear in the size of the JSON value. @@ -3491,17 +3702,21 @@ class basic_json @return reference to the element at index @a idx - @throw std::domain_error if the JSON value is not an array; example: - `"cannot use at() with string"` - @throw std::out_of_range if the index @a idx is out of range of the array; - that is, `idx >= size()`; example: `"array index 7 is out of range"` + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. @complexity Constant. - @liveexample{The example below shows how array elements can be read and - written using `at()`.,at__size_type} - @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} */ reference at(size_type idx) { @@ -3515,12 +3730,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(std::domain_error("cannot use at() with " + type_name())); + JSON_THROW(type_error(304, "cannot use at() with " + type_name())); } } @@ -3534,17 +3749,21 @@ class basic_json @return const reference to the element at index @a idx - @throw std::domain_error if the JSON value is not an array; example: - `"cannot use at() with string"` - @throw std::out_of_range if the index @a idx is out of range of the array; - that is, `idx >= size()`; example: `"array index 7 is out of range"` + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. @complexity Constant. - @liveexample{The example below shows how array elements can be read using - `at()`.,at__size_type_const} - @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} */ const_reference at(size_type idx) const { @@ -3558,12 +3777,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(std::domain_error("cannot use at() with " + type_name())); + JSON_THROW(type_error(304, "cannot use at() with " + type_name())); } } @@ -3577,21 +3796,25 @@ class basic_json @return reference to the element at key @a key - @throw std::domain_error if the JSON value is not an object; example: - `"cannot use at() with boolean"` - @throw std::out_of_range if the key @a key is is not stored in the object; - that is, `find(key) == end()`; example: `"key "the fast" not found"` + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. @complexity Logarithmic in the size of the container. - @liveexample{The example below shows how object elements can be read and - written using `at()`.,at__object_t_key_type} - @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} */ reference at(const typename object_t::key_type& key) { @@ -3605,12 +3828,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("key '" + key + "' not found")); + JSON_THROW(out_of_range(403, "key '" + key + "' not found")); } } else { - JSON_THROW(std::domain_error("cannot use at() with " + type_name())); + JSON_THROW(type_error(304, "cannot use at() with " + type_name())); } } @@ -3624,21 +3847,25 @@ class basic_json @return const reference to the element at key @a key - @throw std::domain_error if the JSON value is not an object; example: - `"cannot use at() with boolean"` - @throw std::out_of_range if the key @a key is is not stored in the object; - that is, `find(key) == end()`; example: `"key "the fast" not found"` + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. @complexity Logarithmic in the size of the container. - @liveexample{The example below shows how object elements can be read using - `at()`.,at__object_t_key_type_const} - @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} */ const_reference at(const typename object_t::key_type& key) const { @@ -3652,12 +3879,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(std::out_of_range("key '" + key + "' not found")); + JSON_THROW(out_of_range(403, "key '" + key + "' not found")); } } else { - JSON_THROW(std::domain_error("cannot use at() with " + type_name())); + JSON_THROW(type_error(304, "cannot use at() with " + type_name())); } } @@ -3674,8 +3901,8 @@ class basic_json @return reference to the element at index @a idx - @throw std::domain_error if JSON is not an array or null; example: - `"cannot use operator[] with string"` + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. @complexity Constant if @a idx is in the range of the array. Otherwise linear in `idx - size()`. @@ -3710,7 +3937,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3722,8 +3949,8 @@ class basic_json @return const reference to the element at index @a idx - @throw std::domain_error if JSON is not an array; example: `"cannot use - operator[] with null"` + @throw type_error.305 if the JSON value is not an array; in that cases, + using the [] operator with an index makes no sense. @complexity Constant. @@ -3740,7 +3967,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3756,8 +3983,8 @@ class basic_json @return reference to the element at key @a key - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3786,7 +4013,7 @@ class basic_json return m_value.object->operator[](key); } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3805,8 +4032,8 @@ class basic_json @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3828,7 +4055,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3844,8 +4071,8 @@ class basic_json @return reference to the element at key @a key - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3879,8 +4106,8 @@ class basic_json @return const reference to the element at key @a key - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3912,8 +4139,8 @@ class basic_json @return reference to the element at key @a key - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with string"` + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3943,7 +4170,7 @@ class basic_json return m_value.object->operator[](key); } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3962,8 +4189,8 @@ class basic_json @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -3986,7 +4213,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); + JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); } /*! @@ -3999,7 +4226,7 @@ class basic_json @code {.cpp} try { return at(key); - } catch(std::out_of_range) { + } catch(out_of_range) { return default_value; } @endcode @@ -4022,8 +4249,8 @@ class basic_json @return copy of the element at key @a key or @a default_value if @a key is not found - @throw std::domain_error if JSON is not an object; example: `"cannot use - value() with null"` + @throw type_error.306 if the JSON value is not an objec; in that cases, + using `value()` with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -4055,7 +4282,7 @@ class basic_json } else { - JSON_THROW(std::domain_error("cannot use value() with " + type_name())); + JSON_THROW(type_error(306, "cannot use value() with " + type_name())); } } @@ -4078,7 +4305,7 @@ class basic_json @code {.cpp} try { return at(ptr); - } catch(std::out_of_range) { + } catch(out_of_range) { return default_value; } @endcode @@ -4097,8 +4324,8 @@ class basic_json @return copy of the element at key @a key or @a default_value if @a key is not found - @throw std::domain_error if JSON is not an object; example: `"cannot use - value() with null"` + @throw type_error.306 if the JSON value is not an objec; in that cases, + using `value()` with a key makes no sense. @complexity Logarithmic in the size of the container. @@ -4121,13 +4348,13 @@ class basic_json { return ptr.get_checked(this); } - JSON_CATCH (std::out_of_range&) + JSON_CATCH (out_of_range&) { return default_value; } } - JSON_THROW(std::domain_error("cannot use value() with " + type_name())); + JSON_THROW(type_error(306, "cannot use value() with " + type_name())); } /*! @@ -4156,7 +4383,7 @@ class basic_json assertions**). @post The JSON value remains unchanged. - @throw std::out_of_range when called on `null` value + @throw invalid_iterator.214 when called on `null` value @liveexample{The following code shows an example for `front()`.,front} @@ -4199,7 +4426,8 @@ class basic_json assertions**). @post The JSON value remains unchanged. - @throw std::out_of_range when called on `null` value. + @throw invalid_iterator.214 when called on a `null` value. See example + below. @liveexample{The following code shows an example for `back()`.,back} @@ -4243,11 +4471,12 @@ class basic_json @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. - @throw std::domain_error if called on a `null` value; example: `"cannot - use erase() with null"` - @throw std::domain_error if called on an iterator which does not belong to - the current JSON value; example: `"iterator does not fit current value"` - @throw std::out_of_range if called on a primitive type with invalid + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid iterator (i.e., any iterator which is not `begin()`); example: `"iterator out of range"` @@ -4278,7 +4507,7 @@ class basic_json // make sure iterator fits the current value if (this != pos.m_object) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } IteratorType result = end(); @@ -4293,7 +4522,7 @@ class basic_json { if (not pos.m_it.primitive_iterator.is_begin()) { - JSON_THROW(std::out_of_range("iterator out of range")); + JSON_THROW(invalid_iterator(205, "iterator out of range")); } if (is_string()) @@ -4323,7 +4552,7 @@ class basic_json default: { - JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); } } @@ -4350,11 +4579,11 @@ class basic_json @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. - @throw std::domain_error if called on a `null` value; example: `"cannot - use erase() with null"` - @throw std::domain_error if called on iterators which does not belong to - the current JSON value; example: `"iterators do not fit current value"` - @throw std::out_of_range if called on a primitive type with invalid + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid iterators (i.e., if `first != begin()` and `last != end()`); example: `"iterators out of range"` @@ -4385,7 +4614,7 @@ class basic_json // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) { - JSON_THROW(std::domain_error("iterators do not fit current value")); + JSON_THROW(invalid_iterator(203, "iterators do not fit current value")); } IteratorType result = end(); @@ -4400,7 +4629,7 @@ class basic_json { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - JSON_THROW(std::out_of_range("iterators out of range")); + JSON_THROW(invalid_iterator(204, "iterators out of range")); } if (is_string()) @@ -4432,7 +4661,7 @@ class basic_json default: { - JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); } } @@ -4453,7 +4682,7 @@ class basic_json @post References and iterators to the erased elements are invalidated. Other references and iterators are not affected. - @throw std::domain_error when called on a type other than JSON object; + @throw type_error.307 when called on a type other than JSON object; example: `"cannot use erase() with null"` @complexity `log(size()) + count(key)` @@ -4476,7 +4705,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); } /*! @@ -4486,9 +4715,9 @@ class basic_json @param[in] idx index of the element to remove - @throw std::domain_error when called on a type other than JSON array; + @throw type_error.307 when called on a type other than JSON object; example: `"cannot use erase() with null"` - @throw std::out_of_range when `idx >= size()`; example: `"array index 17 + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 is out of range"` @complexity Linear in distance between @a idx and the end of the container. @@ -4510,14 +4739,14 @@ class basic_json { if (idx >= size()) { - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); } } @@ -5219,7 +5448,7 @@ class basic_json @param[in] val the value to add to the JSON array - @throw std::domain_error when called on a type other than JSON array or + @throw type_error.308 when called on a type other than JSON array or null; example: `"cannot use push_back() with number"` @complexity Amortized constant. @@ -5235,7 +5464,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); + JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5271,7 +5500,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); + JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5305,7 +5534,7 @@ class basic_json @param[in] val the value to add to the JSON object - @throw std::domain_error when called on a type other than JSON object or + @throw type_error.308 when called on a type other than JSON object or null; example: `"cannot use push_back() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @@ -5321,7 +5550,7 @@ class basic_json // push_back only works for null objects or objects if (not(is_null() or is_object())) { - JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); + JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); } // transform null object into an object @@ -5404,7 +5633,7 @@ class basic_json @param[in] args arguments to forward to a constructor of @ref basic_json @tparam Args compatible types to create a @ref basic_json object - @throw std::domain_error when called on a type other than JSON array or + @throw type_error.311 when called on a type other than JSON array or null; example: `"cannot use emplace_back() with number"` @complexity Amortized constant. @@ -5421,7 +5650,7 @@ class basic_json // emplace_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(std::domain_error("cannot use emplace_back() with " + type_name())); + JSON_THROW(type_error(311, "cannot use emplace_back() with " + type_name())); } // transform null object into an array @@ -5451,7 +5680,7 @@ class basic_json already-existing element if no insertion happened, and a bool denoting whether the insertion took place. - @throw std::domain_error when called on a type other than JSON object or + @throw type_error.311 when called on a type other than JSON object or null; example: `"cannot use emplace() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @@ -5469,7 +5698,7 @@ class basic_json // emplace only works for null objects or arrays if (not(is_null() or is_object())) { - JSON_THROW(std::domain_error("cannot use emplace() with " + type_name())); + JSON_THROW(type_error(311, "cannot use emplace() with " + type_name())); } // transform null object into an object @@ -5500,10 +5729,10 @@ class basic_json @param[in] val element to insert @return iterator pointing to the inserted @a val. - @throw std::domain_error if called on JSON values other than arrays; + @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` @complexity Constant plus linear in the distance between @a pos and end of the container. @@ -5520,7 +5749,7 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5529,7 +5758,7 @@ class basic_json return result; } - JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); + JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); } /*! @@ -5553,10 +5782,10 @@ class basic_json @return iterator pointing to the first element inserted, or @a pos if `cnt==0` - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` @complexity Linear in @a cnt plus linear in the distance between @a pos and end of the container. @@ -5573,7 +5802,7 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5582,7 +5811,7 @@ class basic_json return result; } - JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); + JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); } /*! @@ -5595,13 +5824,13 @@ class basic_json @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - @throw std::domain_error if @a first and @a last do not belong to the same - JSON value; example: `"iterators do not fit"` - @throw std::domain_error if @a first or @a last are iterators into + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into container for which insert is called; example: `"passed iterators may not belong to container"` @@ -5620,24 +5849,24 @@ class basic_json // insert only works for arrays if (not is_array()) { - JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); + JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } // check if range iterators belong to the same JSON object if (first.m_object != last.m_object) { - JSON_THROW(std::domain_error("iterators do not fit")); + JSON_THROW(invalid_iterator(210, "iterators do not fit")); } if (first.m_object == this or last.m_object == this) { - JSON_THROW(std::domain_error("passed iterators may not belong to container")); + JSON_THROW(invalid_iterator(211, "passed iterators may not belong to container")); } // insert to array and return iterator @@ -5658,10 +5887,10 @@ class basic_json the end() iterator @param[in] ilist initializer list to insert the values from - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` @return iterator pointing to the first element inserted, or @a pos if `ilist` is empty @@ -5678,13 +5907,13 @@ class basic_json // insert only works for arrays if (not is_array()) { - JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); + JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(std::domain_error("iterator does not fit current value")); + JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5732,8 +5961,8 @@ class basic_json @param[in,out] other array to exchange the contents with - @throw std::domain_error when JSON value is not an array; example: - `"cannot use swap() with string"` + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` @complexity Constant. @@ -5751,7 +5980,7 @@ class basic_json } else { - JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); + JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); } } @@ -5765,7 +5994,7 @@ class basic_json @param[in,out] other object to exchange the contents with - @throw std::domain_error when JSON value is not an object; example: + @throw type_error.310 when JSON value is not an object; example: `"cannot use swap() with string"` @complexity Constant. @@ -5784,7 +6013,7 @@ class basic_json } else { - JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); + JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); } } @@ -5798,7 +6027,7 @@ class basic_json @param[in,out] other string to exchange the contents with - @throw std::domain_error when JSON value is not a string; example: `"cannot + @throw type_error.310 when JSON value is not a string; example: `"cannot use swap() with boolean"` @complexity Constant. @@ -5817,7 +6046,7 @@ class basic_json } else { - JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); + JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); } } @@ -6667,6 +6896,13 @@ class basic_json */ void dump_float(number_float_t x) { + // NaN / inf + if (not std::isfinite(x) or std::isnan(x)) + { + o.write("null", 4); + return; + } + // special case for 0.0 and -0.0 if (x == 0) { @@ -6824,6 +7060,11 @@ class basic_json @return result of the deserialization + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -6854,6 +7095,10 @@ class basic_json @return result of the deserialization + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -6890,6 +7135,11 @@ class basic_json @return result of the deserialization + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @throw parse_error.111 if input stream is in a bad state + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -6949,6 +7199,10 @@ class basic_json @return result of the deserialization + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -7019,6 +7273,10 @@ class basic_json @return result of the deserialization + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @@ -7051,7 +7309,10 @@ class basic_json @param[in,out] i input stream to read a serialized JSON value from @param[in,out] j JSON value to write the deserialized input to - @throw std::invalid_argument in case of parse errors + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + @throw parse_error.111 if input stream is in a bad state @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. @@ -7155,7 +7416,7 @@ class basic_json @tparam T the integral return type - @throw std::out_of_range if there are less than sizeof(T)+1 bytes in the + @throw parse_error.110 if there are less than sizeof(T)+1 bytes in the vector @a vec to read In the for loop, the bytes from the vector are copied in reverse order into @@ -7180,10 +7441,8 @@ class basic_json template static T get_from_vector(const std::vector& vec, const size_t current_index) { - if (current_index + sizeof(T) + 1 > vec.size()) - { - JSON_THROW(std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector")); - } + // check if we can read sizeof(T) bytes starting the next index + check_length(vec.size(), sizeof(T), current_index + 1); T result; auto* ptr = reinterpret_cast(&result); @@ -7235,25 +7494,25 @@ class basic_json // positive fixnum add_to_vector(v, 1, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 8 v.push_back(0xcc); add_to_vector(v, 1, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 16 v.push_back(0xcd); add_to_vector(v, 2, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 32 v.push_back(0xce); add_to_vector(v, 4, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 64 v.push_back(0xcf); @@ -7267,25 +7526,25 @@ class basic_json // negative fixnum add_to_vector(v, 1, j.m_value.number_integer); } - else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 8 v.push_back(0xd0); add_to_vector(v, 1, j.m_value.number_integer); } - else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 16 v.push_back(0xd1); add_to_vector(v, 2, j.m_value.number_integer); } - else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 32 v.push_back(0xd2); add_to_vector(v, 4, j.m_value.number_integer); } - else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 64 v.push_back(0xd3); @@ -7302,25 +7561,25 @@ class basic_json // positive fixnum add_to_vector(v, 1, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 8 v.push_back(0xcc); add_to_vector(v, 1, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 16 v.push_back(0xcd); add_to_vector(v, 2, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 32 v.push_back(0xce); add_to_vector(v, 4, j.m_value.number_unsigned); } - else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 64 v.push_back(0xcf); @@ -7477,19 +7736,19 @@ class basic_json { add_to_vector(v, 1, j.m_value.number_integer); } - else if (j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { v.push_back(0x18); // one-byte uint8_t add_to_vector(v, 1, j.m_value.number_integer); } - else if (j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { v.push_back(0x19); // two-byte uint16_t add_to_vector(v, 2, j.m_value.number_integer); } - else if (j.m_value.number_integer <= std::numeric_limits::max()) + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { v.push_back(0x1a); // four-byte uint32_t @@ -7511,19 +7770,19 @@ class basic_json { v.push_back(static_cast(0x20 + positive_number)); } - else if (positive_number <= std::numeric_limits::max()) + else if (positive_number <= (std::numeric_limits::max)()) { // int 8 v.push_back(0x38); add_to_vector(v, 1, positive_number); } - else if (positive_number <= std::numeric_limits::max()) + else if (positive_number <= (std::numeric_limits::max)()) { // int 16 v.push_back(0x39); add_to_vector(v, 2, positive_number); } - else if (positive_number <= std::numeric_limits::max()) + else if (positive_number <= (std::numeric_limits::max)()) { // int 32 v.push_back(0x3a); @@ -7732,22 +7991,83 @@ class basic_json // simple case: requested length is greater than the vector's length if (len > size or offset > size) { - JSON_THROW(std::out_of_range("len out of range")); + JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } // second case: adding offset would result in overflow - if ((size > (std::numeric_limits::max() - offset))) + if ((size > ((std::numeric_limits::max)() - offset))) { - JSON_THROW(std::out_of_range("len+offset out of range")); + JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } // last case: reading past the end of the vector if (len + offset > size) { - JSON_THROW(std::out_of_range("len+offset out of range")); + JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } } + /*! + @brief check if the next byte belongs to a string + + While parsing a map, the keys must be strings. This function checks if the + current byte is one of the start bytes for a string in MessagePack: + + - 0xa0 - 0xbf: fixstr + - 0xd9: str 8 + - 0xda: str 16 + - 0xdb: str 32 + + @param[in] v MessagePack serialization + @param[in] idx byte index in @a v to check for a string + + @throw parse_error.113 if `v[idx]` does not belong to a string + */ + static void msgpack_expect_string(const std::vector& v, size_t idx) + { + check_length(v.size(), 1, idx); + + const auto byte = v[idx]; + if ((byte >= 0xa0 and byte <= 0xbf) or (byte >= 0xd9 and byte <= 0xdb)) + { + return; + } + + std::stringstream ss; + ss << std::hex << static_cast(v[idx]); + JSON_THROW(parse_error(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str())); + } + + /*! + @brief check if the next byte belongs to a string + + While parsing a map, the keys must be strings. This function checks if the + current byte is one of the start bytes for a string in CBOR: + + - 0x60 - 0x77: fixed length + - 0x78 - 0x7b: variable length + - 0x7f: indefinity length + + @param[in] v CBOR serialization + @param[in] idx byte index in @a v to check for a string + + @throw parse_error.113 if `v[idx]` does not belong to a string + */ + static void cbor_expect_string(const std::vector& v, size_t idx) + { + check_length(v.size(), 1, idx); + + const auto byte = v[idx]; + if ((byte >= 0x60 and byte <= 0x7b) or byte == 0x7f) + { + return; + } + + std::stringstream ss; + ss << std::hex << static_cast(v[idx]); + JSON_THROW(parse_error(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str())); + } + /*! @brief create a JSON value from a given MessagePack vector @@ -7756,20 +8076,21 @@ class basic_json @return deserialized JSON value - @throw std::invalid_argument if unsupported features from MessagePack were + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from MessagePack were used in the given vector @a v or if the input is not valid MessagePack - @throw std::out_of_range if the given vector ends prematurely + @throw parse_error.113 if a string was expected as map key, but not found @sa https://github.com/msgpack/msgpack/blob/master/spec.md */ static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) { - // make sure reading 1 byte is safe - check_length(v.size(), 1, idx); - // store and increment index const size_t current_idx = idx++; + // make sure reading 1 byte is safe + check_length(v.size(), 1, current_idx); + if (v[current_idx] <= 0xbf) { if (v[current_idx] <= 0x7f) // positive fixint @@ -7782,6 +8103,7 @@ class basic_json const size_t len = v[current_idx] & 0x0f; for (size_t i = 0; i < len; ++i) { + msgpack_expect_string(v, idx); std::string key = from_msgpack_internal(v, idx); result[key] = from_msgpack_internal(v, idx); } @@ -7833,9 +8155,10 @@ class basic_json { // copy bytes in reverse order into the double variable float res; + check_length(v.size(), sizeof(float), current_idx + 1); for (size_t byte = 0; byte < sizeof(float); ++byte) { - reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; } idx += sizeof(float); // skip content bytes return res; @@ -7845,9 +8168,10 @@ class basic_json { // copy bytes in reverse order into the double variable double res; + check_length(v.size(), sizeof(double), current_idx + 1); for (size_t byte = 0; byte < sizeof(double); ++byte) { - reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; } idx += sizeof(double); // skip content bytes return res; @@ -7959,6 +8283,7 @@ class basic_json idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { + msgpack_expect_string(v, idx); std::string key = from_msgpack_internal(v, idx); result[key] = from_msgpack_internal(v, idx); } @@ -7972,6 +8297,7 @@ class basic_json idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { + msgpack_expect_string(v, idx); std::string key = from_msgpack_internal(v, idx); result[key] = from_msgpack_internal(v, idx); } @@ -7980,7 +8306,9 @@ class basic_json default: { - JSON_THROW(std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx])))); + std::stringstream ss; + ss << std::hex << static_cast(v[current_idx]); + JSON_THROW(parse_error(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str())); } } } @@ -7994,9 +8322,10 @@ class basic_json @return deserialized JSON value - @throw std::invalid_argument if unsupported features from CBOR were used in - the given vector @a v or if the input is not valid CBOR - @throw std::out_of_range if the given vector ends prematurely + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from CBOR were + used in the given vector @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found @sa https://tools.ietf.org/html/rfc7049 */ @@ -8005,7 +8334,10 @@ class basic_json // store and increment index const size_t current_idx = idx++; - switch (v.at(current_idx)) + // make sure reading 1 byte is safe + check_length(v.size(), 1, current_idx); + + switch (v[current_idx]) { // Integer 0x00..0x17 (0..23) case 0x00: @@ -8186,7 +8518,7 @@ class basic_json case 0x7f: // UTF-8 string (indefinite length) { std::string result; - while (v.at(idx) != 0xff) + while (check_length(v.size(), 1, idx), v[idx] != 0xff) { string_t s = from_cbor_internal(v, idx); result += s; @@ -8282,7 +8614,7 @@ class basic_json case 0x9f: // array (indefinite length) { basic_json result = value_t::array; - while (v.at(idx) != 0xff) + while (check_length(v.size(), 1, idx), v[idx] != 0xff) { result.push_back(from_cbor_internal(v, idx)); } @@ -8382,7 +8714,7 @@ class basic_json case 0xbf: // map (indefinite length) { basic_json result = value_t::object; - while (v.at(idx) != 0xff) + while (check_length(v.size(), 1, idx), v[idx] != 0xff) { std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); @@ -8418,7 +8750,8 @@ class basic_json // include at least decoding support for them even without such // support. An example of a small decoder for half-precision // floating-point numbers in the C language is shown in Fig. 3. - const int half = (v.at(current_idx + 1) << 8) + v.at(current_idx + 2); + check_length(v.size(), 2, current_idx + 1); + const int half = (v[current_idx + 1] << 8) + v[current_idx + 2]; const int exp = (half >> 10) & 0x1f; const int mant = half & 0x3ff; double val; @@ -8443,9 +8776,10 @@ class basic_json { // copy bytes in reverse order into the float variable float res; + check_length(v.size(), sizeof(float), current_idx + 1); for (size_t byte = 0; byte < sizeof(float); ++byte) { - reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; } idx += sizeof(float); // skip content bytes return res; @@ -8455,9 +8789,10 @@ class basic_json { // copy bytes in reverse order into the double variable double res; + check_length(v.size(), sizeof(double), current_idx + 1); for (size_t byte = 0; byte < sizeof(double); ++byte) { - reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; } idx += sizeof(double); // skip content bytes return res; @@ -8465,7 +8800,9 @@ class basic_json default: // anything else (0xFF is handled inside the other types) { - JSON_THROW(std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx])))); + std::stringstream ss; + ss << std::hex << static_cast(v[current_idx]); + JSON_THROW(parse_error(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str())); } } } @@ -8510,9 +8847,9 @@ class basic_json @param[in] start_index the index to start reading from @a v (0 by default) @return deserialized JSON value - @throw std::invalid_argument if unsupported features from MessagePack were + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from MessagePack were used in the given vector @a v or if the input is not valid MessagePack - @throw std::out_of_range if the given vector ends prematurely @complexity Linear in the size of the byte vector @a v. @@ -8573,9 +8910,9 @@ class basic_json @param[in] start_index the index to start reading from @a v (0 by default) @return deserialized JSON value - @throw std::invalid_argument if unsupported features from CBOR were used in - the given vector @a v or if the input is not valid MessagePack - @throw std::out_of_range if the given vector ends prematurely + @throw parse_error.110 if the given vector ends prematurely + @throw parse_error.112 if unsupported features from CBOR were + used in the given vector @a v or if the input is not valid CBOR @complexity Linear in the size of the byte vector @a v. @@ -9133,7 +9470,7 @@ class basic_json case basic_json::value_t::null: { - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } default: @@ -9143,7 +9480,7 @@ class basic_json return *m_object; } - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } } } @@ -9177,7 +9514,7 @@ class basic_json return m_object; } - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } } } @@ -9277,7 +9614,7 @@ class basic_json // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(std::domain_error("cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -9319,7 +9656,7 @@ class basic_json // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(std::domain_error("cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -9328,7 +9665,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(std::domain_error("cannot compare order of object iterators")); + JSON_THROW(invalid_iterator(213, "cannot compare order of object iterators")); } case basic_json::value_t::array: @@ -9382,7 +9719,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(std::domain_error("cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -9444,7 +9781,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(std::domain_error("cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -9471,7 +9808,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(std::domain_error("cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator(208, "cannot use operator[] for object iterators")); } case basic_json::value_t::array: @@ -9481,7 +9818,7 @@ class basic_json case basic_json::value_t::null: { - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } default: @@ -9491,7 +9828,7 @@ class basic_json return *m_object; } - JSON_THROW(std::out_of_range("cannot get value")); + JSON_THROW(invalid_iterator(214, "cannot get value")); } } } @@ -9509,7 +9846,7 @@ class basic_json return m_it.object_iterator->first; } - JSON_THROW(std::domain_error("cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator(207, "cannot use key() for non-object iterators")); } /*! @@ -9689,14 +10026,17 @@ class basic_json m_limit = m_content + len; } - /// a lexer from an input stream + /*! + @brief a lexer from an input stream + @throw parse_error.111 if input stream is in a bad state + */ explicit lexer(std::istream& s) : m_stream(&s), m_line_buffer() { // immediately abort if stream is erroneous if (s.fail()) { - JSON_THROW(std::invalid_argument("stream error")); + JSON_THROW(parse_error(111, 0, "bad input stream")); } // fill buffer @@ -9730,17 +10070,17 @@ class basic_json @return string representation of the code point; the length of the result string is between 1 and 4 characters. - @throw std::out_of_range if code point is > 0x10ffff; example: `"code - points above 0x10FFFF are invalid"` - @throw std::invalid_argument if the low surrogate is invalid; example: + @throw parse_error.102 if the low surrogate is invalid; example: `""missing or wrong low surrogate""` + @throw parse_error.103 if code point is > 0x10ffff; example: `"code + points above 0x10FFFF are invalid"` @complexity Constant. @see */ - static string_t to_unicode(const std::size_t codepoint1, - const std::size_t codepoint2 = 0) + string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) const { // calculate the code point from the given code points std::size_t codepoint = codepoint1; @@ -9763,7 +10103,7 @@ class basic_json } else { - JSON_THROW(std::invalid_argument("missing or wrong low surrogate")); + JSON_THROW(parse_error(102, get_position(), "missing or wrong low surrogate")); } } @@ -9797,7 +10137,7 @@ class basic_json } else { - JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid")); + JSON_THROW(parse_error(103, get_position(), "code points above 0x10FFFF are invalid")); } return result; @@ -9892,7 +10232,7 @@ class basic_json // ignore whitespace ws = [ \t\n\r]+; - ws { continue; } + ws { position += static_cast((m_cursor - m_start)); continue; } // structural characters "[" { last_token_type = token_type::begin_array; break; } @@ -9946,6 +10286,7 @@ class basic_json */ } + position += static_cast((m_cursor - m_start)); return last_token_type; } @@ -10024,7 +10365,7 @@ class basic_json m_line_buffer_tmp.clear(); // check if stream is still good - if (not m_stream->good()) + if (m_stream->fail()) { JSON_THROW(std::invalid_argument("stream error")); } @@ -10108,7 +10449,8 @@ class basic_json @return string value of current token without opening and closing quotes - @throw std::out_of_range if to_unicode fails + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails */ string_t get_string() const { @@ -10194,7 +10536,7 @@ class basic_json // make sure there is a subsequent unicode if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') { - JSON_THROW(std::invalid_argument("missing low surrogate")); + JSON_THROW(parse_error(102, get_position(), "missing low surrogate")); } // get code yyyy from uxxxx\uyyyy @@ -10207,7 +10549,7 @@ class basic_json else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) { // we found a lone low surrogate - JSON_THROW(std::invalid_argument("missing high surrogate")); + JSON_THROW(parse_error(102, get_position(), "missing high surrogate")); } else { @@ -10448,11 +10790,10 @@ class basic_json result.m_type = value_t::number_float; result.m_value = val; - // replace infinity and NAN by null + // throw in case of infinity or NAN if (not std::isfinite(result.m_value.number_float)) { - result.m_type = value_t::null; - result.m_value = basic_json::json_value(); + JSON_THROW(out_of_range(406, "number overflow parsing '" + get_token_string() + "'")); } return true; @@ -10462,6 +10803,11 @@ class basic_json return false; } + constexpr size_t get_position() const + { + return position; + } + private: /// optional input stream std::istream* m_stream = nullptr; @@ -10481,6 +10827,8 @@ class basic_json const lexer_char_t* m_limit = nullptr; /// the last token type token_type last_token_type = token_type::end_of_input; + /// current position in the input (read bytes) + size_t position = 0; }; /*! @@ -10497,7 +10845,10 @@ class basic_json m_lexer(reinterpret_cast(buff), std::strlen(buff)) {} - /// a parser reading from an input stream + /*! + @brief a parser reading from an input stream + @throw parse_error.111 if input stream is in a bad state + */ parser(std::istream& is, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(is) {} @@ -10513,7 +10864,12 @@ class basic_json static_cast(std::distance(first, last))) {} - /// public parser interface + /*! + @brief public parser interface + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ basic_json parse() { // read first token @@ -10530,7 +10886,12 @@ class basic_json } private: - /// the actual parser + /*! + @brief the actual parser + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ basic_json parse_internal(bool keep) { auto result = basic_json(value_t::discarded); @@ -10733,6 +11094,9 @@ class basic_json return last_token; } + /*! + @throw parse_error.101 if expected token did not occur + */ void expect(typename lexer::token_type t) const { if (t != last_token) @@ -10742,10 +11106,13 @@ class basic_json "'") : lexer::token_type_name(last_token)); error_msg += "; expected " + lexer::token_type_name(t); - JSON_THROW(std::invalid_argument(error_msg)); + JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg)); } } + /*! + @throw parse_error.101 if unexpected token occurred + */ void unexpect(typename lexer::token_type t) const { if (t == last_token) @@ -10754,7 +11121,7 @@ class basic_json error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + "'") : lexer::token_type_name(last_token)); - JSON_THROW(std::invalid_argument(error_msg)); + JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg)); } } @@ -10797,12 +11164,12 @@ class basic_json empty string is assumed which references the whole JSON value - @throw std::domain_error if reference token is nonempty and does not - begin with a slash (`/`); example: `"JSON pointer must be empty or - begin with /"` - @throw std::domain_error if a tilde (`~`) is not followed by `0` - (representing `~`) or `1` (representing `/`); example: `"escape error: - ~ must be followed with 0 or 1"` + @throw parse_error.107 if the given JSON pointer @a s is nonempty and + does not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s + is not followed by `0` (representing `~`) or `1` (representing `/`); + see example below @liveexample{The example shows the construction several valid JSON pointers as well as the exceptional behavior.,json_pointer} @@ -10845,12 +11212,15 @@ class basic_json } private: - /// remove and return last reference pointer + /*! + @brief remove and return last reference pointer + @throw out_of_range.405 if JSON pointer has no parent + */ std::string pop_back() { if (is_root()) { - JSON_THROW(std::domain_error("JSON pointer has no parent")); + JSON_THROW(out_of_range(405, "JSON pointer has no parent")); } auto last = reference_tokens.back(); @@ -10868,7 +11238,7 @@ class basic_json { if (is_root()) { - JSON_THROW(std::domain_error("JSON pointer has no parent")); + JSON_THROW(out_of_range(405, "JSON pointer has no parent")); } json_pointer result = *this; @@ -10880,6 +11250,9 @@ class basic_json @brief create and return a reference to the pointed to value @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened */ reference get_and_create(reference j) const { @@ -10916,7 +11289,14 @@ class basic_json case value_t::array: { // create an entry in the array - result = &result->operator[](static_cast(std::stoi(reference_token))); + JSON_TRY + { + result = &result->operator[](static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } break; } @@ -10929,7 +11309,7 @@ class basic_json */ default: { - JSON_THROW(std::domain_error("invalid value to unflatten")); + JSON_THROW(type_error(313, "invalid value to unflatten")); } } } @@ -10952,9 +11332,9 @@ class basic_json @complexity Linear in the length of the JSON pointer. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved */ reference get_unchecked(pointer ptr) const { @@ -10997,7 +11377,7 @@ class basic_json // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(std::domain_error("array index must not begin with '0'")); + JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } if (reference_token == "-") @@ -11008,14 +11388,21 @@ class basic_json else { // convert array index to number; unchecked access - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + JSON_TRY + { + ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } } break; } default: { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11023,6 +11410,12 @@ class basic_json return *ptr; } + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ reference get_checked(pointer ptr) const { for (const auto& reference_token : reference_tokens) @@ -11041,25 +11434,32 @@ class basic_json if (reference_token == "-") { // "-" always fails the range check - JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(std::domain_error("array index must not begin with '0'")); + JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check - ptr = &ptr->at(static_cast(std::stoi(reference_token))); + JSON_TRY + { + ptr = &ptr->at(static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } break; } default: { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11074,6 +11474,11 @@ class basic_json @return const reference to the JSON value pointed to by the JSON pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved */ const_reference get_unchecked(const_pointer ptr) const { @@ -11093,25 +11498,32 @@ class basic_json if (reference_token == "-") { // "-" cannot be used for const access - JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(std::domain_error("array index must not begin with '0'")); + JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // use unchecked array access - ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + JSON_TRY + { + ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } break; } default: { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11119,6 +11531,12 @@ class basic_json return *ptr; } + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ const_reference get_checked(const_pointer ptr) const { for (const auto& reference_token : reference_tokens) @@ -11137,25 +11555,32 @@ class basic_json if (reference_token == "-") { // "-" always fails the range check - JSON_THROW(std::out_of_range("array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(std::domain_error("array index must not begin with '0'")); + JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check - ptr = &ptr->at(static_cast(std::stoi(reference_token))); + JSON_TRY + { + ptr = &ptr->at(static_cast(std::stoi(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + } break; } default: { - JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11163,7 +11588,15 @@ class basic_json return *ptr; } - /// split the string input to reference tokens + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ static std::vector split(const std::string& reference_string) { std::vector result; @@ -11177,7 +11610,7 @@ class basic_json // check if nonempty reference string begins with slash if (reference_string[0] != '/') { - JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'")); + JSON_THROW(parse_error(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); } // extract the reference tokens: @@ -11212,7 +11645,7 @@ class basic_json (reference_token[pos + 1] != '0' and reference_token[pos + 1] != '1')) { - JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'")); + JSON_THROW(parse_error(108, 0, "escape character '~' must be followed with '0' or '1'")); } } @@ -11224,7 +11657,6 @@ class basic_json return result; } - private: /*! @brief replace all occurrences of a substring by another string @@ -11233,7 +11665,8 @@ class basic_json @param[in] f the substring to replace with @a t @param[in] t the string to replace @a f - @pre The search string @a f must not be empty. + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** @since version 2.0.0 */ @@ -11333,12 +11766,17 @@ class basic_json @param[in] value flattened JSON @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened */ static basic_json unflatten(const basic_json& value) { if (not value.is_object()) { - JSON_THROW(std::domain_error("only objects can be unflattened")); + JSON_THROW(type_error(314, "only objects can be unflattened")); } basic_json result; @@ -11348,7 +11786,7 @@ class basic_json { if (not element.second.is_primitive()) { - JSON_THROW(std::domain_error("values in object must be primitive")); + JSON_THROW(type_error(315, "values in object must be primitive")); } // assign value to reference pointed to by JSON pointer; Note @@ -11362,7 +11800,6 @@ class basic_json return result; } - private: friend bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept { @@ -11411,9 +11848,9 @@ class basic_json @complexity Constant. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved @liveexample{The behavior is shown in the example.,operatorjson_pointer} @@ -11438,9 +11875,10 @@ class basic_json @complexity Constant. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} @@ -11461,15 +11899,30 @@ class basic_json @return reference to the element pointed to by @a ptr + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + @complexity Constant. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @since version 2.0.0 @liveexample{The behavior is shown in the example.,at_json_pointer} - - @since version 2.0.0 */ reference at(const json_pointer& ptr) { @@ -11486,15 +11939,30 @@ class basic_json @return reference to the element pointed to by @a ptr + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + @complexity Constant. - @throw std::out_of_range if the JSON pointer can not be resolved - @throw std::domain_error if an array index begins with '0' - @throw std::invalid_argument if an array index was not a number + @since version 2.0.0 @liveexample{The behavior is shown in the example.,at_json_pointer_const} - - @since version 2.0.0 */ const_reference at(const json_pointer& ptr) const { @@ -11550,6 +12018,9 @@ class basic_json @complexity Linear in the size the JSON value. + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitve + @liveexample{The following code shows how a flattened JSON object is unflattened into the original nested JSON object.,unflatten} @@ -11587,12 +12058,23 @@ class basic_json any case, the original value is not changed: the patch is applied to a copy of the value. - @throw std::out_of_range if a JSON pointer inside the patch could not - be resolved successfully in the current JSON value; example: `"key baz - not found"` - @throw invalid_argument if the JSON patch is malformed (e.g., mandatory + @throw parse_error.104 if the JSON patch does not consist of an array of + objects + + @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory attributes are missing); example: `"operation add must have member path"` + @throw out_of_range.401 if an array index is out of range. + + @throw out_of_range.403 if a JSON pointer inside the patch could not be + resolved successfully in the current JSON value; example: `"key baz not + found"` + + @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", + "move") + + @throw other_error.501 if "test" operation was unsuccessful + @complexity Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is affected by the patch, the complexity can usually be neglected. @@ -11689,7 +12171,7 @@ class basic_json if (static_cast(idx) > parent.size()) { // avoid undefined behavior - JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); } else { @@ -11727,7 +12209,7 @@ class basic_json } else { - JSON_THROW(std::out_of_range("key '" + last_path + "' not found")); + JSON_THROW(out_of_range(403, "key '" + last_path + "' not found")); } } else if (parent.is_array()) @@ -11737,11 +12219,10 @@ class basic_json } }; - // type check + // type check: top level value must be an array if (not json_patch.is_array()) { - // a JSON patch must be an array of objects - JSON_THROW(std::invalid_argument("JSON patch must be an array of objects")); + JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects")); } // iterate and apply the operations @@ -11761,23 +12242,23 @@ class basic_json // check if desired value is present if (it == val.m_value.object->end()) { - JSON_THROW(std::invalid_argument(error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error(105, 0, error_msg + " must have member '" + member + "'")); } // check if result is of type string if (string_type and not it->second.is_string()) { - JSON_THROW(std::invalid_argument(error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error(105, 0, error_msg + " must have string member '" + member + "'")); } // no error: return value return it->second; }; - // type check + // type check: every element of the array must be an object if (not val.is_object()) { - JSON_THROW(std::invalid_argument("JSON patch must be an array of objects")); + JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects")); } // collect mandatory members @@ -11842,7 +12323,7 @@ class basic_json // the "path" location must exist - use at() success = (result.at(ptr) == get_value("test", "value", false)); } - JSON_CATCH (std::out_of_range&) + JSON_CATCH (out_of_range&) { // ignore out of range errors: success remains false } @@ -11850,7 +12331,7 @@ class basic_json // throw an exception if test fails if (not success) { - JSON_THROW(std::domain_error("unsuccessful: " + val.dump())); + JSON_THROW(other_error(501, "unsuccessful: " + val.dump())); } break; @@ -11860,7 +12341,7 @@ class basic_json { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(std::invalid_argument("operation value '" + op + "' is invalid")); + JSON_THROW(parse_error(105, 0, "operation value '" + op + "' is invalid")); } } } @@ -12089,6 +12570,22 @@ struct hash return h(j.dump()); } }; + +/// specialization for std::less +template <> +struct less<::nlohmann::detail::value_t> +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + } // namespace std /*! diff --git a/test/src/fuzzer-parse_cbor.cpp b/test/src/fuzzer-parse_cbor.cpp index 30fa69779..4bcfc7eff 100644 --- a/test/src/fuzzer-parse_cbor.cpp +++ b/test/src/fuzzer-parse_cbor.cpp @@ -41,26 +41,22 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) // parse serialization json j2 = json::from_cbor(vec2); - // deserializations must match - assert(j1 == j2); + // serializations must match + assert(json::to_cbor(j2) == vec2); } - catch (const std::invalid_argument&) + catch (const json::parse_error&) { // parsing a CBOR serialization must not fail assert(false); } } - catch (const std::invalid_argument&) + catch (const json::parse_error&) { // parse errors are ok, because input may be random bytes } - catch (const std::out_of_range&) + catch (const json::type_error&) { - // parse errors are ok, because input may be random bytes - } - catch (const std::domain_error&) - { - // parse errors are ok, because input may be random bytes + // type errors can occur during parsing, too } // return 0 - non-zero return values are reserved for future use diff --git a/test/src/fuzzer-parse_json.cpp b/test/src/fuzzer-parse_json.cpp index bd2e5e391..ff5850665 100644 --- a/test/src/fuzzer-parse_json.cpp +++ b/test/src/fuzzer-parse_json.cpp @@ -49,13 +49,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) // serializations must match assert(s1 == s2); } - catch (const std::invalid_argument&) + catch (const json::parse_error&) { // parsing a JSON serialization must not fail assert(false); } } - catch (const std::invalid_argument&) + catch (const json::parse_error&) { // parse errors are ok, because input may be random bytes } diff --git a/test/src/fuzzer-parse_msgpack.cpp b/test/src/fuzzer-parse_msgpack.cpp index bf2fcab07..420283a40 100644 --- a/test/src/fuzzer-parse_msgpack.cpp +++ b/test/src/fuzzer-parse_msgpack.cpp @@ -44,23 +44,19 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) // deserializations must match assert(j1 == j2); } - catch (const std::invalid_argument&) + catch (const json::parse_error&) { // parsing a MessagePack serialization must not fail assert(false); } } - catch (const std::invalid_argument&) + catch (const json::parse_error&) { // parse errors are ok, because input may be random bytes } - catch (const std::out_of_range&) + catch (const json::type_error&) { - // parse errors are ok, because input may be random bytes - } - catch (const std::domain_error&) - { - // parse errors are ok, because input may be random bytes + // type errors can occur during parsing, too } // return 0 - non-zero return values are reserved for future use diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp index 8386238a1..494ea83eb 100644 --- a/test/src/unit-algorithms.cpp +++ b/test/src/unit-algorithms.cpp @@ -240,8 +240,9 @@ TEST_CASE("algorithms") SECTION("sorting an object") { json j({{"one", 1}, {"two", 2}}); - CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), "cannot use offsets with object iterators"); + CHECK_THROWS_AS(std::sort(j.begin(), j.end()), json::invalid_iterator); + CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), + "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } } diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 84b280bc9..b4b539754 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -744,13 +744,17 @@ TEST_CASE("CBOR") SECTION("infinity") { json j = json::from_cbor(std::vector({0xf9, 0x7c, 0x00})); - CHECK(j == nullptr); + json::number_float_t d = j; + CHECK(not std::isfinite(d)); + CHECK(j.dump() == "null"); } SECTION("NaN") { json j = json::from_cbor(std::vector({0xf9, 0x7c, 0x01})); - CHECK(j == nullptr); + json::number_float_t d = j; + CHECK(std::isnan(d)); + CHECK(j.dump() == "null"); } } } @@ -1145,21 +1149,115 @@ TEST_CASE("CBOR") { SECTION("too short byte vector") { - CHECK_THROWS_AS(json::from_cbor(std::vector({0x18})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x19})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x19, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1a})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1a, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1a, 0x00, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1a, 0x00, 0x00, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), std::out_of_range); - CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x18})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x19})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x19, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1a})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1a, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1a, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1a, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error); + + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x18})), + "[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x19})), + "[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x19, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1a})), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1a, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1a, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1a, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1b})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1b, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1b, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + } + + SECTION("unsupported bytes") + { + SECTION("concrete examples") + { + CHECK_THROWS_AS(json::from_cbor(std::vector({0x1c})), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1c})), + "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0x1c"); + CHECK_THROWS_AS(json::from_cbor(std::vector({0xf8})), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0xf8})), + "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0xf8"); + } + + SECTION("all unsupported bytes") + { + for (auto byte : + { + // ? + 0x1c, 0x1d, 0x1e, 0x1f, + // ? + 0x3c, 0x3d, 0x3e, 0x3f, + // byte strings + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + // byte strings + 0x58, 0x59, 0x5a, 0x5b, + // ? + 0x5c, 0x5d, 0x5e, + // byte string + 0x5f, + // ? + 0x7c, 0x7d, 0x7e, + // ? + 0x9c, 0x9d, 0x9e, + // ? + 0xbc, 0xbd, 0xbe, + // date/time + 0xc0, 0xc1, + // bignum + 0xc2, 0xc3, + // fraction + 0xc4, + // bigfloat + 0xc5, + // tagged item + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, + // expected conversion + 0xd5, 0xd6, 0xd7, + // more tagged items + 0xd8, 0xd9, 0xda, 0xdb, + // ? + 0xdc, 0xdd, 0xde, 0xdf, + // (simple value) + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, + // undefined + 0xf7, + // simple value + 0xf8 + }) + { + CHECK_THROWS_AS(json::from_cbor(std::vector({static_cast(byte)})), json::parse_error); + } + } } } } @@ -1247,21 +1345,13 @@ TEST_CASE("CBOR regressions", "[!throws]") // deserializations must match CHECK(j1 == j2); } - catch (const std::invalid_argument&) + catch (const json::parse_error&) { // parsing a CBOR serialization must not fail CHECK(false); } } - catch (const std::invalid_argument&) - { - // parse errors are ok, because input may be random bytes - } - catch (const std::out_of_range&) - { - // parse errors are ok, because input may be random bytes - } - catch (const std::domain_error&) + catch (const json::parse_error&) { // parse errors are ok, because input may be random bytes } @@ -1271,7 +1361,7 @@ TEST_CASE("CBOR regressions", "[!throws]") SECTION("improve code coverage") { // exotic edge case - CHECK_THROWS_AS(json::check_length(0xffffffffffffffffull, 0xfffffffffffffff0ull, 0xff), std::out_of_range); + CHECK_THROWS_AS(json::check_length(0xffffffffffffffffull, 0xfffffffffffffff0ull, 0xff), json::parse_error); } } @@ -1344,7 +1434,7 @@ TEST_CASE("CBOR roundtrips", "[hide]") "test/data/nst_json_testsuite/test_parsing/y_number_after_space.json", "test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json", "test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json", - "test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json", + //"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json", "test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json", "test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json", "test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json", @@ -1356,9 +1446,9 @@ TEST_CASE("CBOR roundtrips", "[hide]") "test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json", - "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json", + //"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json", - "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json", + //"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json", "test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json", diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 840549a36..26b312659 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -147,8 +147,8 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); + CHECK_THROWS_AS(*it, json::invalid_iterator); + CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("number") @@ -157,8 +157,8 @@ TEST_CASE("const_iterator class") json::const_iterator it = j.cbegin(); CHECK(*it == json(17)); it = j.cend(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); + CHECK_THROWS_AS(*it, json::invalid_iterator); + CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("object") @@ -182,8 +182,8 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + CHECK_THROWS_AS(it->type_name(), json::invalid_iterator); + CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("number") @@ -192,8 +192,8 @@ TEST_CASE("const_iterator class") json::const_iterator it = j.cbegin(); CHECK(it->type_name() == "number"); it = j.cend(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + CHECK_THROWS_AS(it->type_name(), json::invalid_iterator); + CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("object") diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index 0238c3374..6ff3c369d 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -131,8 +131,8 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); + CHECK_THROWS_AS(*it, json::invalid_iterator); + CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("number") @@ -141,8 +141,8 @@ TEST_CASE("iterator class") json::iterator it = j.begin(); CHECK(*it == json(17)); it = j.end(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); + CHECK_THROWS_AS(*it, json::invalid_iterator); + CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("object") @@ -166,8 +166,8 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + CHECK_THROWS_AS(it->type_name(), json::invalid_iterator); + CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("number") @@ -176,8 +176,8 @@ TEST_CASE("iterator class") json::iterator it = j.begin(); CHECK(it->type_name() == "number"); it = j.end(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + CHECK_THROWS_AS(it->type_name(), json::invalid_iterator); + CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value"); } SECTION("object") diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 0b019bfef..b9e602b7b 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -190,8 +190,10 @@ TEST_CASE("lexer class") SECTION("to_unicode") { - CHECK(json::lexer::to_unicode(0x1F4A9) == "💩"); - CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range); - CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid"); + // lexer to call to_unicode on + json::lexer dummy_lexer(reinterpret_cast(""), 0); + 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"); } } diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index e3ad3723a..7f2c9a952 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -89,52 +89,56 @@ TEST_CASE("parser class") SECTION("errors") { // error: tab in string - CHECK_THROWS_AS(json::parser("\"\t\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_AS(json::parser("\"\t\"").parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); // error: newline in string - CHECK_THROWS_AS(json::parser("\"\n\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\r\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_AS(json::parser("\"\n\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\r\"").parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); // error: backspace in string - CHECK_THROWS_AS(json::parser("\"\b\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_AS(json::parser("\"\b\"").parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); // improve code coverage - CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[-4:1,]").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\uFF01").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("[-4:1,]").parse(), json::parse_error); // unescaped control characters - CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), json::parse_error); } SECTION("escaped") @@ -272,65 +276,69 @@ TEST_CASE("parser class") SECTION("overflow") { - CHECK(json::parser("1.18973e+4932").parse() == json()); + // overflows during parsing yield an exception + CHECK_THROWS_AS(json::parser("1.18973e+4932").parse() == json(), json::out_of_range); + CHECK_THROWS_WITH(json::parser("1.18973e+4932").parse() == json(), + "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'"); } SECTION("invalid numbers") { - CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("--1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E-").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1.E1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-1E").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E-#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.0:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.0Z").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E123:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0e0-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("01").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("--1").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1.").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1E").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1E-").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1.E1").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-1E").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0E#").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0E-#").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0#").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0.0:").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0.0Z").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0E123:").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0e0-:").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0e-:").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0f").parse(), json::parse_error); // numbers must not begin with "+" - CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("+1").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("+0").parse(), json::parse_error); CHECK_THROWS_WITH(json::parser("01").parse(), - "parse error - unexpected '01'"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected '01'"); CHECK_THROWS_WITH(json::parser("-01").parse(), - "parse error - unexpected '-01'"); - CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected '-01'"); + CHECK_THROWS_WITH(json::parser("--1").parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("1.").parse(), - "parse error - unexpected '.'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E").parse(), - "parse error - unexpected 'E'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E-").parse(), - "parse error - unexpected 'E'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1.E1").parse(), - "parse error - unexpected '.'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("-1E").parse(), - "parse error - unexpected 'E'; expected end of input"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0E#").parse(), - "parse error - unexpected 'E'; expected end of input"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0E-#").parse(), - "parse error - unexpected 'E'; expected end of input"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0#").parse(), - "parse error - unexpected '#'; expected end of input"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected '#'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0.0:").parse(), - "parse error - unexpected ':'; expected end of input"); + "[json.exception.parse_error.101] parse error at 5: parse error - unexpected ':'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0.0Z").parse(), - "parse error - unexpected 'Z'; expected end of input"); + "[json.exception.parse_error.101] parse error at 5: parse error - unexpected 'Z'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0E123:").parse(), - "parse error - unexpected ':'; expected end of input"); + "[json.exception.parse_error.101] parse error at 7: parse error - unexpected ':'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0e0-:").parse(), - "parse error - unexpected '-'; expected end of input"); + "[json.exception.parse_error.101] parse error at 5: parse error - unexpected '-'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0e-:").parse(), - "parse error - unexpected 'e'; expected end of input"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'e'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0f").parse(), - "parse error - unexpected 'f'; expected end of input"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected 'f'; expected end of input"); } } } @@ -338,147 +346,150 @@ TEST_CASE("parser class") SECTION("parse errors") { // unexpected end of number - CHECK_THROWS_AS(json::parser("0.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("--").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("0.:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("e.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e/").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("0.").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("--").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-0.").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-.").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("-:").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("0.:").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("e.").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1e.").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1e/").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1e:").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1E.").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1E/").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("1E:").parse(), json::parse_error); CHECK_THROWS_WITH(json::parser("0.").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-").parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("--").parse(), - "parse error - unexpected '-'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("-0.").parse(), - "parse error - unexpected '.'; expected end of input"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("-.").parse(), - "parse error - unexpected '-'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("-:").parse(), - "parse error - unexpected '-'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("0.:").parse(), - "parse error - unexpected '.'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("e.").parse(), - "parse error - unexpected 'e'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'e'"); CHECK_THROWS_WITH(json::parser("1e.").parse(), - "parse error - unexpected 'e'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'e'; expected end of input"); CHECK_THROWS_WITH(json::parser("1e/").parse(), - "parse error - unexpected 'e'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'e'; expected end of input"); CHECK_THROWS_WITH(json::parser("1e:").parse(), - "parse error - unexpected 'e'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'e'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E.").parse(), - "parse error - unexpected 'E'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E/").parse(), - "parse error - unexpected 'E'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E:").parse(), - "parse error - unexpected 'E'; expected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected 'E'; expected end of input"); // unexpected end of null - CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("nu").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("nul").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("n").parse(), "parse error - unexpected 'n'"); + CHECK_THROWS_AS(json::parser("n").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("nu").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("nul").parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser("n").parse(), "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'n'"); CHECK_THROWS_WITH(json::parser("nu").parse(), - "parse error - unexpected 'n'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'n'"); CHECK_THROWS_WITH(json::parser("nul").parse(), - "parse error - unexpected 'n'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'n'"); // unexpected end of true - CHECK_THROWS_AS(json::parser("t").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("tr").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("tru").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("t").parse(), "parse error - unexpected 't'"); + CHECK_THROWS_AS(json::parser("t").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("tr").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("tru").parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser("t").parse(), "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 't'"); CHECK_THROWS_WITH(json::parser("tr").parse(), - "parse error - unexpected 't'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 't'"); CHECK_THROWS_WITH(json::parser("tru").parse(), - "parse error - unexpected 't'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 't'"); // unexpected end of false - CHECK_THROWS_AS(json::parser("f").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fa").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fal").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fals").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("f").parse(), "parse error - unexpected 'f'"); + CHECK_THROWS_AS(json::parser("f").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("fa").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("fal").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("fals").parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser("f").parse(), "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'f'"); CHECK_THROWS_WITH(json::parser("fa").parse(), - "parse error - unexpected 'f'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'f'"); CHECK_THROWS_WITH(json::parser("fal").parse(), - "parse error - unexpected 'f'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'f'"); CHECK_THROWS_WITH(json::parser("fals").parse(), - "parse error - unexpected 'f'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected 'f'"); // missing/unexpected end of array - CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("[").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("[1").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("[1,").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("[1,]").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("]").parse(), json::parse_error); CHECK_THROWS_WITH(json::parser("[").parse(), - "parse error - unexpected end of input"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected end of input"); CHECK_THROWS_WITH(json::parser("[1").parse(), - "parse error - unexpected end of input; expected ']'"); + "[json.exception.parse_error.101] parse error at 3: parse error - unexpected end of input; expected ']'"); CHECK_THROWS_WITH(json::parser("[1,").parse(), - "parse error - unexpected end of input"); + "[json.exception.parse_error.101] parse error at 4: parse error - unexpected end of input"); CHECK_THROWS_WITH(json::parser("[1,]").parse(), - "parse error - unexpected ']'"); - CHECK_THROWS_WITH(json::parser("]").parse(), "parse error - unexpected ']'"); + "[json.exception.parse_error.101] parse error at 4: parse error - unexpected ']'"); + CHECK_THROWS_WITH(json::parser("]").parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected ']'"); // missing/unexpected end of object - CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("}").parse(), json::parse_error); CHECK_THROWS_WITH(json::parser("{").parse(), - "parse error - unexpected end of input; expected string literal"); + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected end of input; expected string literal"); CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(), - "parse error - unexpected end of input; expected ':'"); + "[json.exception.parse_error.101] parse error at 7: parse error - unexpected end of input; expected ':'"); CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(), - "parse error - unexpected end of input"); + "[json.exception.parse_error.101] parse error at 8: parse error - unexpected end of input"); CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(), - "parse error - unexpected '}'"); + "[json.exception.parse_error.101] parse error at 8: parse error - unexpected '}'"); CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(), - "parse error - unexpected '}'; expected string literal"); - CHECK_THROWS_WITH(json::parser("}").parse(), "parse error - unexpected '}'"); + "[json.exception.parse_error.101] parse error at 10: parse error - unexpected '}'; expected string literal"); + CHECK_THROWS_WITH(json::parser("}").parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '}'"); // missing/unexpected end of string - CHECK_THROWS_AS(json::parser("\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u0").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u01").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u012").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\u").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\u0").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\u01").parse(), json::parse_error); + CHECK_THROWS_AS(json::parser("\"\\u012").parse(), json::parse_error); CHECK_THROWS_WITH(json::parser("\"").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\\"").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u0").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u01").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u012").parse(), - "parse error - unexpected '\"'"); + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); // invalid escapes for (int c = 1; c < 128; ++c) @@ -510,8 +521,9 @@ TEST_CASE("parser class") // any other combination of backslash and character is invalid default: { - CHECK_THROWS_AS(json::parser(s.c_str()).parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser(s.c_str()).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_AS(json::parser(s.c_str()).parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser(s.c_str()).parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); break; } } @@ -576,40 +588,49 @@ TEST_CASE("parser class") } else { - CHECK_THROWS_AS(json::parser(s1.c_str()).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s2.c_str()).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s3.c_str()).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s4.c_str()).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s1.c_str()).parse(), json::parse_error); + CHECK_THROWS_AS(json::parser(s2.c_str()).parse(), json::parse_error); + CHECK_THROWS_AS(json::parser(s3.c_str()).parse(), json::parse_error); + CHECK_THROWS_AS(json::parser(s4.c_str()).parse(), json::parse_error); - CHECK_THROWS_WITH(json::parser(s1.c_str()).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s2.c_str()).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s3.c_str()).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s4.c_str()).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s1.c_str()).parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s2.c_str()).parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s3.c_str()).parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s4.c_str()).parse(), + "[json.exception.parse_error.101] parse error at 1: parse error - unexpected '\"'"); } } } // missing part of a surrogate pair - CHECK_THROWS_AS(json::parse("\"\\uD80C\""), std::invalid_argument); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), "missing low surrogate"); + CHECK_THROWS_AS(json::parse("\"\\uD80C\""), json::parse_error); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), + "[json.exception.parse_error.102] parse error at 8: missing low surrogate"); // invalid surrogate pair - CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), json::parse_error); + CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), json::parse_error); + CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), json::parse_error); CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""), - "missing or wrong low surrogate"); + "[json.exception.parse_error.102] parse error at 14: missing or wrong low surrogate"); CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""), - "missing or wrong low surrogate"); + "[json.exception.parse_error.102] parse error at 14: missing or wrong low surrogate"); CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""), - "missing or wrong low surrogate"); + "[json.exception.parse_error.102] parse error at 14: missing or wrong low surrogate"); } SECTION("tests found by mutate++") { // test case to make sure no comma preceeds the first key - CHECK_THROWS_AS(json::parser("{,\"key\": false}").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{,\"key\": false}").parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser("{,\"key\": false}").parse(), + "[json.exception.parse_error.101] parse error at 2: parse error - unexpected ','"); // test case to make sure an object is properly closed - CHECK_THROWS_AS(json::parser("[{\"key\": false true]").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("[{\"key\": false true]").parse(), json::parse_error); + CHECK_THROWS_WITH(json::parser("[{\"key\": false true]").parse(), + "[json.exception.parse_error.101] parse error at 19: parse error - unexpected true literal; expected '}'"); // test case to make sure the callback is properly evaluated after reading a key { diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 9363f0ba2..18c032e02 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -702,11 +702,17 @@ TEST_CASE("constructors") SECTION("infinity") { - // infinity is stored as null - // should change in the future: https://github.com/nlohmann/json/issues/388 + // infinity is stored properly, but serialized to null json::number_float_t n(std::numeric_limits::infinity()); json j(n); - CHECK(j.type() == json::value_t::null); + CHECK(j.type() == json::value_t::number_float); + + // check round trip of infinity + json::number_float_t d = j; + CHECK(d == n); + + // check that inf is serialized to null + CHECK(j.dump() == "null"); } } @@ -943,9 +949,9 @@ TEST_CASE("constructors") SECTION("object with error") { CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), - std::logic_error); + json::type_error); CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), - "cannot create object from initializer list"); + "[json.exception.type_error.301] cannot create object from initializer list"); } SECTION("empty array") @@ -1017,18 +1023,18 @@ TEST_CASE("constructors") { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error); - CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible"); + CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), json::invalid_iterator); + CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), json::invalid_iterator); + CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible"); + CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "[json.exception.invalid_iterator.201] iterators are not compatible"); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error); - CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible"); + CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), json::invalid_iterator); + CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible"); + CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible"); } } } @@ -1082,18 +1088,18 @@ TEST_CASE("constructors") { json jarray = {1, 2, 3, 4}; json jarray2 = {2, 3, 4, 5}; - CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error); - CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error); - CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible"); + CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), json::invalid_iterator); + CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), json::invalid_iterator); + CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible"); + CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "[json.exception.invalid_iterator.201] iterators are not compatible"); } { json jarray = {1, 2, 3, 4}; json jarray2 = {2, 3, 4, 5}; - CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error); - CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error); - CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible"); + CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), json::invalid_iterator); + CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible"); + CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible"); } } } @@ -1106,13 +1112,15 @@ TEST_CASE("constructors") { { json j; - CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null"); + CHECK_THROWS_AS(json(j.begin(), j.end()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.begin(), j.end()), + "[json.exception.invalid_iterator.206] cannot construct with iterators from null"); } { json j; - CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error); - CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null"); + CHECK_THROWS_AS(json(j.cbegin(), j.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), + "[json.exception.invalid_iterator.206] cannot construct with iterators from null"); } } @@ -1193,17 +1201,17 @@ TEST_CASE("constructors") { { json j = "foo"; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = "bar"; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } @@ -1211,17 +1219,17 @@ TEST_CASE("constructors") { { json j = false; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = true; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } @@ -1229,17 +1237,17 @@ TEST_CASE("constructors") { { json j = 17; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = 17; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } @@ -1247,17 +1255,17 @@ TEST_CASE("constructors") { { json j = 17u; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = 17u; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } @@ -1265,17 +1273,17 @@ TEST_CASE("constructors") { { json j = 23.42; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = 23.42; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } } diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index e4545fbe7..3a83aef3c 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -78,28 +78,28 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-object type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::null).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), json::type_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be object, but is null"); + "[json.exception.type_error.302] type must be object, but is null"); CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be object, but is array"); + "[json.exception.type_error.302] type must be object, but is array"); CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be object, but is string"); + "[json.exception.type_error.302] type must be object, but is string"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be object, but is boolean"); + "[json.exception.type_error.302] type must be object, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be object, but is number"); + "[json.exception.type_error.302] type must be object, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be object, but is number"); + "[json.exception.type_error.302] type must be object, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be object, but is number"); + "[json.exception.type_error.302] type must be object, but is number"); } } @@ -161,9 +161,9 @@ TEST_CASE("value conversion") std::forward_list a = j.get>(); CHECK(json(a) == j); - CHECK_THROWS_AS(json(json::value_t::null).get>(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::null).get>(), json::type_error); CHECK_THROWS_WITH(json(json::value_t::null).get>(), - "type must be array, but is null"); + "[json.exception.type_error.302] type must be array, but is null"); } SECTION("std::vector") @@ -171,16 +171,16 @@ TEST_CASE("value conversion") std::vector a = j.get>(); CHECK(json(a) == j); - CHECK_THROWS_AS(json(json::value_t::null).get>(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::null).get>(), json::type_error); CHECK_THROWS_WITH(json(json::value_t::null).get>(), - "type must be array, but is null"); + "[json.exception.type_error.302] type must be array, but is null"); #if not defined(JSON_NOEXCEPTION) SECTION("reserve is called on containers that supports it") { // making the call to from_json throw in order to check capacity std::vector v; - CHECK_THROWS_AS(nlohmann::from_json(j, v), std::logic_error); + CHECK_THROWS_AS(nlohmann::from_json(j, v), json::type_error); CHECK(v.capacity() == j.size()); // make sure all values are properly copied @@ -198,30 +198,30 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-array type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::null).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), json::type_error); CHECK_THROWS_WITH(json(json::value_t::object).get>(), - "type must be array, but is object"); + "[json.exception.type_error.302] type must be array, but is object"); CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be array, but is null"); + "[json.exception.type_error.302] type must be array, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be array, but is object"); + "[json.exception.type_error.302] type must be array, but is object"); CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be array, but is string"); + "[json.exception.type_error.302] type must be array, but is string"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be array, but is boolean"); + "[json.exception.type_error.302] type must be array, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be array, but is number"); + "[json.exception.type_error.302] type must be array, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be array, but is number"); + "[json.exception.type_error.302] type must be array, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be array, but is number"); + "[json.exception.type_error.302] type must be array, but is number"); } } @@ -280,28 +280,28 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-string type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::null).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), json::type_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be string, but is null"); + "[json.exception.type_error.302] type must be string, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be string, but is object"); + "[json.exception.type_error.302] type must be string, but is object"); CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be string, but is array"); + "[json.exception.type_error.302] type must be string, but is array"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be string, but is boolean"); + "[json.exception.type_error.302] type must be string, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be string, but is number"); + "[json.exception.type_error.302] type must be string, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be string, but is number"); + "[json.exception.type_error.302] type must be string, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be string, but is number"); + "[json.exception.type_error.302] type must be string, but is number"); } } @@ -342,28 +342,28 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-string type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::null).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), json::type_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be boolean, but is null"); + "[json.exception.type_error.302] type must be boolean, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be boolean, but is object"); + "[json.exception.type_error.302] type must be boolean, but is object"); CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be boolean, but is array"); + "[json.exception.type_error.302] type must be boolean, but is array"); CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be boolean, but is string"); + "[json.exception.type_error.302] type must be boolean, but is string"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be boolean, but is number"); + "[json.exception.type_error.302] type must be boolean, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be boolean, but is number"); + "[json.exception.type_error.302] type must be boolean, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be boolean, but is number"); + "[json.exception.type_error.302] type must be boolean, but is number"); } } @@ -598,22 +598,22 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-number type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::null).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), json::type_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be number, but is null"); + "[json.exception.type_error.302] type must be number, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be number, but is object"); + "[json.exception.type_error.302] type must be number, but is object"); CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be number, but is array"); + "[json.exception.type_error.302] type must be number, but is array"); CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be number, but is string"); + "[json.exception.type_error.302] type must be number, but is string"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be number, but is boolean"); + "[json.exception.type_error.302] type must be number, but is boolean"); CHECK_NOTHROW(json(json::value_t::number_float).get()); CHECK_NOTHROW(json(json::value_t::number_float).get()); @@ -857,22 +857,22 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-string type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::null).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), json::type_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), json::type_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be number, but is null"); + "[json.exception.type_error.302] type must be number, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be number, but is object"); + "[json.exception.type_error.302] type must be number, but is object"); CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be number, but is array"); + "[json.exception.type_error.302] type must be number, but is array"); CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be number, but is string"); + "[json.exception.type_error.302] type must be number, but is string"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be number, but is boolean"); + "[json.exception.type_error.302] type must be number, but is boolean"); CHECK_NOTHROW(json(json::value_t::number_integer).get()); CHECK_NOTHROW(json(json::value_t::number_unsigned).get()); @@ -954,8 +954,8 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-object type") { - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_WITH((json().get>()), "type must be object, but is null"); + CHECK_THROWS_AS((json().get>()), json::type_error); + CHECK_THROWS_WITH((json().get>()), "[json.exception.type_error.302] type must be object, but is null"); } } @@ -1023,17 +1023,17 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-object type") { - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_AS((json().get>()), json::type_error); + CHECK_THROWS_AS((json().get>()), json::type_error); + CHECK_THROWS_AS((json().get>()), json::type_error); + CHECK_THROWS_AS((json().get>()), json::type_error); // does type really must be an array? or it rather must not be null? // that's what I thought when other test like this one broke - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "[json.exception.type_error.302] type must be array, but is null"); } } } diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 9028fdfbb..cacf66870 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -90,15 +90,17 @@ TEST_CASE("deserialization") std::stringstream ss1, ss2; ss1 << "[\"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_WITH(json::parse(ss2), "parse error - unexpected end of input; expected ']'"); + CHECK_THROWS_AS(json::parse(ss1), json::parse_error); + CHECK_THROWS_WITH(json::parse(ss2), + "[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'"); } SECTION("string") { json::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}"; - CHECK_THROWS_AS(json::parse(s), std::invalid_argument); - CHECK_THROWS_WITH(json::parse(s), "parse error - unexpected end of input; expected ']'"); + CHECK_THROWS_AS(json::parse(s), json::parse_error); + CHECK_THROWS_WITH(json::parse(s), + "[json.exception.parse_error.101] parse error at 29: parse error - unexpected end of input; expected ']'"); } SECTION("operator<<") @@ -107,8 +109,9 @@ TEST_CASE("deserialization") ss1 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss2 << "[\"foo\",1,2,3,false,{\"one\":1}"; json j; - CHECK_THROWS_AS(j << ss1, std::invalid_argument); - CHECK_THROWS_WITH(j << ss2, "parse error - unexpected end of input; expected ']'"); + CHECK_THROWS_AS(j << ss1, json::parse_error); + CHECK_THROWS_WITH(j << ss2, + "[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'"); } SECTION("operator>>") @@ -117,15 +120,16 @@ TEST_CASE("deserialization") ss1 << "[\"foo\",1,2,3,false,{\"one\":1}"; ss2 << "[\"foo\",1,2,3,false,{\"one\":1}"; json j; - CHECK_THROWS_AS(ss1 >> j, std::invalid_argument); - CHECK_THROWS_WITH(ss2 >> j, "parse error - unexpected end of input; expected ']'"); + CHECK_THROWS_AS(ss1 >> j, json::parse_error); + 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") { - 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, - "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") SECTION("empty container") { std::vector 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") SECTION("with empty range") { std::vector 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") SECTION("case 1") { 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") { 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") { 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") { 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") { 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") { 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") { 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") { 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") { 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") { 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") { 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") { 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") { 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") { 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") { 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); } } } diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index f0763c5d4..b8a57af43 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -63,11 +63,13 @@ TEST_CASE("element access 1") SECTION("access outside bounds") { - CHECK_THROWS_AS(j.at(8), std::out_of_range); - CHECK_THROWS_AS(j_const.at(8), std::out_of_range); + CHECK_THROWS_AS(j.at(8), json::out_of_range); + CHECK_THROWS_AS(j_const.at(8), json::out_of_range); - CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); - CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); + CHECK_THROWS_WITH(j.at(8), + "[json.exception.out_of_range.401] array index 8 is out of range"); + CHECK_THROWS_WITH(j_const.at(8), + "[json.exception.out_of_range.401] array index 8 is out of range"); } SECTION("access on non-array type") @@ -76,77 +78,77 @@ TEST_CASE("element access 1") { json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray.at(0), json::type_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error); - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null"); + CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with null"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with null"); } SECTION("boolean") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray.at(0), json::type_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error); - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with boolean"); } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray.at(0), json::type_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error); - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string"); + CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with string"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with string"); } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray.at(0), json::type_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error); - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object"); + CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with object"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with object"); } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray.at(0), json::type_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error); - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number"); } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray.at(0), json::type_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error); - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number"); } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray.at(0), json::type_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error); - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number"); } } } @@ -191,8 +193,8 @@ TEST_CASE("element access 1") json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); CHECK_NOTHROW(j_nonarray[0]); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null"); + CHECK_THROWS_AS(j_nonarray_const[0], json::type_error); + CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with null"); } SECTION("implicit transformation to properly filled array") @@ -207,60 +209,60 @@ TEST_CASE("element access 1") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean"); + CHECK_THROWS_AS(j_nonarray[0], json::type_error); + CHECK_THROWS_AS(j_nonarray_const[0], json::type_error); + CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with boolean"); } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string"); + CHECK_THROWS_AS(j_nonarray[0], json::type_error); + CHECK_THROWS_AS(j_nonarray_const[0], json::type_error); + CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with string"); + CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with string"); } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object"); + CHECK_THROWS_AS(j_nonarray[0], json::type_error); + CHECK_THROWS_AS(j_nonarray_const[0], json::type_error); + CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with object"); + CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with object"); } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + CHECK_THROWS_AS(j_nonarray[0], json::type_error); + CHECK_THROWS_AS(j_nonarray_const[0], json::type_error); + CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with number"); } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + CHECK_THROWS_AS(j_nonarray[0], json::type_error); + CHECK_THROWS_AS(j_nonarray_const[0], json::type_error); + CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with number"); } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + CHECK_THROWS_AS(j_nonarray[0], json::type_error); + CHECK_THROWS_AS(j_nonarray_const[0], json::type_error); + CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with number"); } } } @@ -311,8 +313,9 @@ TEST_CASE("element access 1") } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); - CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range"); + CHECK_THROWS_AS(jarray.erase(8), json::out_of_range); + CHECK_THROWS_WITH(jarray.erase(8), + "[json.exception.out_of_range.401] array index 8 is out of range"); } } @@ -405,34 +408,36 @@ TEST_CASE("element access 1") { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.begin()), json::invalid_iterator); + CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), json::invalid_iterator); + CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), json::invalid_iterator); + CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), json::invalid_iterator); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), + "[json.exception.invalid_iterator.202] iterator does not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), json::invalid_iterator); + CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), json::invalid_iterator); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), json::invalid_iterator); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), json::invalid_iterator); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), + "[json.exception.invalid_iterator.202] iterator does not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); } } } @@ -442,50 +447,57 @@ TEST_CASE("element access 1") SECTION("null") { json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null"); + CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), + "[json.exception.type_error.307] cannot use erase() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean"); + CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), + "[json.exception.type_error.307] cannot use erase() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string"); + CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), + "[json.exception.type_error.307] cannot use erase() with string"); } SECTION("object") { json j_nonobject(json::value_t::object); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object"); + CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), + "[json.exception.type_error.307] cannot use erase() with object"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), + "[json.exception.type_error.307] cannot use erase() with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), + "[json.exception.type_error.307] cannot use erase() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), + "[json.exception.type_error.307] cannot use erase() with number"); } } } @@ -499,17 +511,17 @@ TEST_CASE("element access 1") { { json j; - CHECK_THROWS_AS(j.front(), std::out_of_range); - CHECK_THROWS_AS(j.back(), std::out_of_range); - CHECK_THROWS_WITH(j.front(), "cannot get value"); - CHECK_THROWS_WITH(j.back(), "cannot get value"); + CHECK_THROWS_AS(j.front(), json::invalid_iterator); + CHECK_THROWS_AS(j.back(), json::invalid_iterator); + CHECK_THROWS_WITH(j.front(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(j.back(), "[json.exception.invalid_iterator.214] cannot get value"); } { const json j{}; - CHECK_THROWS_AS(j.front(), std::out_of_range); - CHECK_THROWS_AS(j.back(), std::out_of_range); - CHECK_THROWS_WITH(j.front(), "cannot get value"); - CHECK_THROWS_WITH(j.back(), "cannot get value"); + CHECK_THROWS_AS(j.front(), json::invalid_iterator); + CHECK_THROWS_AS(j.back(), json::invalid_iterator); + CHECK_THROWS_WITH(j.front(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(j.back(), "[json.exception.invalid_iterator.214] cannot get value"); } } @@ -590,13 +602,15 @@ TEST_CASE("element access 1") { { json j; - CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); + CHECK_THROWS_AS(j.erase(j.begin()), json::type_error); + CHECK_THROWS_WITH(j.erase(j.begin()), + "[json.exception.type_error.307] cannot use erase() with null"); } { json j; - CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); + CHECK_THROWS_AS(j.erase(j.cbegin()), json::type_error); + CHECK_THROWS_WITH(j.erase(j.begin()), + "[json.exception.type_error.307] cannot use erase() with null"); } } @@ -687,13 +701,15 @@ TEST_CASE("element access 1") { { json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end()), + "[json.exception.invalid_iterator.205] iterator out of range"); } { json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend()), + "[json.exception.invalid_iterator.205] iterator out of range"); } } @@ -701,13 +717,15 @@ TEST_CASE("element access 1") { { json j = false; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end()), + "[json.exception.invalid_iterator.205] iterator out of range"); } { json j = true; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend()), + "[json.exception.invalid_iterator.205] iterator out of range"); } } @@ -715,13 +733,15 @@ TEST_CASE("element access 1") { { json j = 17; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end()), + "[json.exception.invalid_iterator.205] iterator out of range"); } { json j = 17; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend()), + "[json.exception.invalid_iterator.205] iterator out of range"); } } @@ -729,13 +749,15 @@ TEST_CASE("element access 1") { { json j = 17u; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end()), + "[json.exception.invalid_iterator.205] iterator out of range"); } { json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend()), + "[json.exception.invalid_iterator.205] iterator out of range"); } } @@ -743,13 +765,15 @@ TEST_CASE("element access 1") { { json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end()), + "[json.exception.invalid_iterator.205] iterator out of range"); } { json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend()), + "[json.exception.invalid_iterator.205] iterator out of range"); } } } @@ -760,13 +784,15 @@ TEST_CASE("element access 1") { { json j; - CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null"); + CHECK_THROWS_AS(j.erase(j.begin(), j.end()), json::type_error); + CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), + "[json.exception.type_error.307] cannot use erase() with null"); } { json j; - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null"); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), json::type_error); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), + "[json.exception.type_error.307] cannot use erase() with null"); } } @@ -857,17 +883,17 @@ TEST_CASE("element access 1") { { json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } @@ -875,17 +901,17 @@ TEST_CASE("element access 1") { { json j = false; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = true; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } @@ -893,17 +919,17 @@ TEST_CASE("element access 1") { { json j = 17; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = 17; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } @@ -911,17 +937,17 @@ TEST_CASE("element access 1") { { json j = 17u; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } @@ -929,17 +955,17 @@ TEST_CASE("element access 1") { { json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); } { json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); } } } diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 8e91e89d1..3533bdcd9 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -63,10 +63,12 @@ TEST_CASE("element access 2") SECTION("access outside bounds") { - CHECK_THROWS_AS(j.at("foo"), std::out_of_range); - CHECK_THROWS_AS(j_const.at("foo"), std::out_of_range); - CHECK_THROWS_WITH(j.at("foo"), "key 'foo' not found"); - CHECK_THROWS_WITH(j_const.at("foo"), "key 'foo' not found"); + CHECK_THROWS_AS(j.at("foo"), json::out_of_range); + CHECK_THROWS_AS(j_const.at("foo"), json::out_of_range); + CHECK_THROWS_WITH(j.at("foo"), + "[json.exception.out_of_range.403] key 'foo' not found"); + CHECK_THROWS_WITH(j_const.at("foo"), + "[json.exception.out_of_range.403] key 'foo' not found"); } SECTION("access on non-object type") @@ -75,70 +77,70 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with null"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with null"); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with null"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with boolean"); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with string"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with string"); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with string"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with string"); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with array"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with array"); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with array"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); } } } @@ -200,70 +202,84 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null"); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with null"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean"); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string"); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with string"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with string"); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array"); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with array"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), + "[json.exception.type_error.306] cannot use value() with number"); } } } @@ -304,75 +320,84 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null"); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with null"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean"); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with boolean"); CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with boolean"); + "[json.exception.type_error.306] cannot use value() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string"); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with string"); CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with string"); + "[json.exception.type_error.306] cannot use value() with string"); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array"); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with array"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); + "[json.exception.type_error.306] cannot use value() with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); + "[json.exception.type_error.306] cannot use value() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), + "[json.exception.type_error.306] cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); + "[json.exception.type_error.306] cannot use value() with number"); } } } @@ -447,106 +472,118 @@ TEST_CASE("element access 2") const json j_const_nonobject(j_nonobject); CHECK_NOTHROW(j_nonobject["foo"]); CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with null"); + CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "[json.exception.type_error.305] cannot use operator[] with null"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with null"); + "[json.exception.type_error.305] cannot use operator[] with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with boolean"); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_WITH(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with boolean"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with boolean"); + "[json.exception.type_error.305] cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with boolean"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with boolean"); + "[json.exception.type_error.305] cannot use operator[] with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with string"); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_WITH(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with string"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with string"); + "[json.exception.type_error.305] cannot use operator[] with string"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with string"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with string"); + "[json.exception.type_error.305] cannot use operator[] with string"); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with array"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with array"); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_WITH(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with array"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with array"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with array"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with array"); + "[json.exception.type_error.305] cannot use operator[] with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_WITH(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + "[json.exception.type_error.305] cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); + "[json.exception.type_error.305] cannot use operator[] with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_WITH(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + "[json.exception.type_error.305] cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); + "[json.exception.type_error.305] cannot use operator[] with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error); + CHECK_THROWS_WITH(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + "[json.exception.type_error.305] cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); + "[json.exception.type_error.305] cannot use operator[] with number"); } } } @@ -685,32 +722,34 @@ TEST_CASE("element access 2") { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), "iterator does not fit current value"); + CHECK_THROWS_AS(jobject.erase(jobject2.begin()), json::invalid_iterator); + CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), json::invalid_iterator); + CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), json::invalid_iterator); + CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), json::invalid_iterator); + CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), + "[json.exception.invalid_iterator.202] iterator does not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), "iterator does not fit current value"); + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), json::invalid_iterator); + CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), json::invalid_iterator); + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), json::invalid_iterator); + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), json::invalid_iterator); + CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), + "[json.exception.invalid_iterator.202] iterator does not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()), - "iterators do not fit current value"); + "[json.exception.invalid_iterator.203] iterators do not fit current value"); } } } @@ -720,43 +759,49 @@ TEST_CASE("element access 2") SECTION("null") { json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with null"); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), + "[json.exception.type_error.307] cannot use erase() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with boolean"); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), + "[json.exception.type_error.307] cannot use erase() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with string"); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), + "[json.exception.type_error.307] cannot use erase() with string"); } SECTION("array") { json j_nonobject(json::value_t::array); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with array"); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), + "[json.exception.type_error.307] cannot use erase() with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), + "[json.exception.type_error.307] cannot use erase() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), + "[json.exception.type_error.307] cannot use erase() with number"); } } } diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp index eebee1107..6605076ad 100644 --- a/test/src/unit-iterators1.cpp +++ b/test/src/unit-iterators1.cpp @@ -229,23 +229,23 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(it.key(), json::invalid_iterator); + CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(it.value() == json(true)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(cit.key(), json::invalid_iterator); + CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(cit.value() == json(true)); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); + CHECK_THROWS_AS(rit.key(), json::invalid_iterator); + CHECK_THROWS_AS(rit.value(), json::invalid_iterator); + CHECK_THROWS_AS(crit.key(), json::invalid_iterator); + CHECK_THROWS_AS(crit.value(), json::invalid_iterator); + CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); } } @@ -433,23 +433,23 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(it.key(), json::invalid_iterator); + CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(it.value() == json("hello world")); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(cit.key(), json::invalid_iterator); + CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(cit.value() == json("hello world")); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); + CHECK_THROWS_AS(rit.key(), json::invalid_iterator); + CHECK_THROWS_AS(rit.value(), json::invalid_iterator); + CHECK_THROWS_AS(crit.key(), json::invalid_iterator); + CHECK_THROWS_AS(crit.value(), json::invalid_iterator); + CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); } } @@ -630,11 +630,11 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(it.key(), json::invalid_iterator); + CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(it.value() == json(1)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(cit.key(), json::invalid_iterator); + CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(cit.value() == json(1)); } } @@ -1007,23 +1007,23 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(it.key(), json::invalid_iterator); + CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(it.value() == json(23)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(cit.key(), json::invalid_iterator); + CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(cit.value() == json(23)); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); + CHECK_THROWS_AS(rit.key(), json::invalid_iterator); + CHECK_THROWS_AS(rit.value(), json::invalid_iterator); + CHECK_THROWS_AS(crit.key(), json::invalid_iterator); + CHECK_THROWS_AS(crit.value(), json::invalid_iterator); + CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); } } @@ -1211,23 +1211,23 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(it.key(), json::invalid_iterator); + CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(it.value() == json(23)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(cit.key(), json::invalid_iterator); + CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(cit.value() == json(23)); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); + CHECK_THROWS_AS(rit.key(), json::invalid_iterator); + CHECK_THROWS_AS(rit.value(), json::invalid_iterator); + CHECK_THROWS_AS(crit.key(), json::invalid_iterator); + CHECK_THROWS_AS(crit.value(), json::invalid_iterator); + CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); } } @@ -1415,23 +1415,23 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(it.key(), json::invalid_iterator); + CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(it.value() == json(23.42)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_AS(cit.key(), json::invalid_iterator); + CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); CHECK(cit.value() == json(23.42)); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); + CHECK_THROWS_AS(rit.key(), json::invalid_iterator); + CHECK_THROWS_AS(rit.value(), json::invalid_iterator); + CHECK_THROWS_AS(crit.key(), json::invalid_iterator); + CHECK_THROWS_AS(crit.value(), json::invalid_iterator); + CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); } } @@ -1489,25 +1489,25 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_AS(it.value(), std::out_of_range); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_AS(cit.value(), std::out_of_range); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(it.value(), "cannot get value"); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(cit.value(), "cannot get value"); + CHECK_THROWS_AS(it.key(), json::invalid_iterator); + CHECK_THROWS_AS(it.value(), json::invalid_iterator); + CHECK_THROWS_AS(cit.key(), json::invalid_iterator); + CHECK_THROWS_AS(cit.value(), json::invalid_iterator); + CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(it.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(cit.value(), "[json.exception.invalid_iterator.214] cannot get value"); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); + CHECK_THROWS_AS(rit.key(), json::invalid_iterator); + CHECK_THROWS_AS(rit.value(), json::invalid_iterator); + CHECK_THROWS_AS(crit.key(), json::invalid_iterator); + CHECK_THROWS_AS(crit.value(), json::invalid_iterator); + CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); } } } diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index b2bc4a382..e20417a17 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -81,22 +81,22 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 < it1, std::domain_error); - CHECK_THROWS_AS(it1 < it2, std::domain_error); - CHECK_THROWS_AS(it2 < it3, std::domain_error); - CHECK_THROWS_AS(it1 < it3, std::domain_error); - CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_AS(it1 < it1, json::invalid_iterator); + CHECK_THROWS_AS(it1 < it2, json::invalid_iterator); + CHECK_THROWS_AS(it2 < it3, json::invalid_iterator); + CHECK_THROWS_AS(it1 < it3, json::invalid_iterator); + CHECK_THROWS_AS(it1_c < it1_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator); + CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator); + CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); } else { @@ -115,22 +115,22 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 <= it1, std::domain_error); - CHECK_THROWS_AS(it1 <= it2, std::domain_error); - CHECK_THROWS_AS(it2 <= it3, std::domain_error); - CHECK_THROWS_AS(it1 <= it3, std::domain_error); - CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_AS(it1 <= it1, json::invalid_iterator); + CHECK_THROWS_AS(it1 <= it2, json::invalid_iterator); + CHECK_THROWS_AS(it2 <= it3, json::invalid_iterator); + CHECK_THROWS_AS(it1 <= it3, json::invalid_iterator); + CHECK_THROWS_AS(it1_c <= it1_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator); + CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator); + CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); } else { @@ -150,22 +150,22 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 > it1, std::domain_error); - CHECK_THROWS_AS(it1 > it2, std::domain_error); - CHECK_THROWS_AS(it2 > it3, std::domain_error); - CHECK_THROWS_AS(it1 > it3, std::domain_error); - CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_AS(it1 > it1, json::invalid_iterator); + CHECK_THROWS_AS(it1 > it2, json::invalid_iterator); + CHECK_THROWS_AS(it2 > it3, json::invalid_iterator); + CHECK_THROWS_AS(it1 > it3, json::invalid_iterator); + CHECK_THROWS_AS(it1_c > it1_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator); + CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator); + CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); } else { @@ -185,22 +185,22 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 >= it1, std::domain_error); - CHECK_THROWS_AS(it1 >= it2, std::domain_error); - CHECK_THROWS_AS(it2 >= it3, std::domain_error); - CHECK_THROWS_AS(it1 >= it3, std::domain_error); - CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_AS(it1 >= it1, json::invalid_iterator); + CHECK_THROWS_AS(it1 >= it2, json::invalid_iterator); + CHECK_THROWS_AS(it2 >= it3, json::invalid_iterator); + CHECK_THROWS_AS(it1 >= it3, json::invalid_iterator); + CHECK_THROWS_AS(it1_c >= it1_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator); + CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator); + CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); } else { @@ -224,15 +224,15 @@ TEST_CASE("iterators 2") { if (j != k) { - CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); - CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); - CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_AS(j.begin() == k.begin(), json::invalid_iterator); + CHECK_THROWS_AS(j.cbegin() == k.cbegin(), json::invalid_iterator); + CHECK_THROWS_WITH(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); - CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); - CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_AS(j.begin() < k.begin(), json::invalid_iterator); + CHECK_THROWS_AS(j.cbegin() < k.cbegin(), json::invalid_iterator); + CHECK_THROWS_WITH(j.begin() < k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); } } } @@ -251,53 +251,53 @@ TEST_CASE("iterators 2") { { auto it = j_object.begin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it += 1, json::invalid_iterator); + CHECK_THROWS_WITH(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it += 1, json::invalid_iterator); + CHECK_THROWS_WITH(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.begin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it + 1, json::invalid_iterator); + CHECK_THROWS_WITH(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it + 1, json::invalid_iterator); + CHECK_THROWS_WITH(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.begin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it -= 1, json::invalid_iterator); + CHECK_THROWS_WITH(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it -= 1, json::invalid_iterator); + CHECK_THROWS_WITH(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.begin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it - 1, json::invalid_iterator); + CHECK_THROWS_WITH(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it - 1, json::invalid_iterator); + CHECK_THROWS_WITH(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.begin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it - it, json::invalid_iterator); + CHECK_THROWS_WITH(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it - it, json::invalid_iterator); + CHECK_THROWS_WITH(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } } @@ -380,17 +380,17 @@ TEST_CASE("iterators 2") { { auto it = j_object.begin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); + CHECK_THROWS_AS(it[0], json::invalid_iterator); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators"); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators"); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); + CHECK_THROWS_AS(it[0], json::invalid_iterator); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators"); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators"); } } @@ -420,17 +420,17 @@ TEST_CASE("iterators 2") { { auto it = j_null.begin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); + CHECK_THROWS_AS(it[0], json::invalid_iterator); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); } { auto it = j_null.cbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); + CHECK_THROWS_AS(it[0], json::invalid_iterator); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); } } @@ -439,14 +439,14 @@ TEST_CASE("iterators 2") { auto it = j_value.begin(); CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); } { auto it = j_value.cbegin(); CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); } } } @@ -500,22 +500,22 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 < it1, std::domain_error); - CHECK_THROWS_AS(it1 < it2, std::domain_error); - CHECK_THROWS_AS(it2 < it3, std::domain_error); - CHECK_THROWS_AS(it1 < it3, std::domain_error); - CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_AS(it1 < it1, json::invalid_iterator); + CHECK_THROWS_AS(it1 < it2, json::invalid_iterator); + CHECK_THROWS_AS(it2 < it3, json::invalid_iterator); + CHECK_THROWS_AS(it1 < it3, json::invalid_iterator); + CHECK_THROWS_AS(it1_c < it1_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator); + CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator); + CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); } else { @@ -534,22 +534,22 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 <= it1, std::domain_error); - CHECK_THROWS_AS(it1 <= it2, std::domain_error); - CHECK_THROWS_AS(it2 <= it3, std::domain_error); - CHECK_THROWS_AS(it1 <= it3, std::domain_error); - CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_AS(it1 <= it1, json::invalid_iterator); + CHECK_THROWS_AS(it1 <= it2, json::invalid_iterator); + CHECK_THROWS_AS(it2 <= it3, json::invalid_iterator); + CHECK_THROWS_AS(it1 <= it3, json::invalid_iterator); + CHECK_THROWS_AS(it1_c <= it1_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator); + CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator); + CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); } else { @@ -569,22 +569,22 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 > it1, std::domain_error); - CHECK_THROWS_AS(it1 > it2, std::domain_error); - CHECK_THROWS_AS(it2 > it3, std::domain_error); - CHECK_THROWS_AS(it1 > it3, std::domain_error); - CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_AS(it1 > it1, json::invalid_iterator); + CHECK_THROWS_AS(it1 > it2, json::invalid_iterator); + CHECK_THROWS_AS(it2 > it3, json::invalid_iterator); + CHECK_THROWS_AS(it1 > it3, json::invalid_iterator); + CHECK_THROWS_AS(it1_c > it1_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator); + CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator); + CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); } else { @@ -604,22 +604,22 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 >= it1, std::domain_error); - CHECK_THROWS_AS(it1 >= it2, std::domain_error); - CHECK_THROWS_AS(it2 >= it3, std::domain_error); - CHECK_THROWS_AS(it1 >= it3, std::domain_error); - CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_AS(it1 >= it1, json::invalid_iterator); + CHECK_THROWS_AS(it1 >= it2, json::invalid_iterator); + CHECK_THROWS_AS(it2 >= it3, json::invalid_iterator); + CHECK_THROWS_AS(it1 >= it3, json::invalid_iterator); + CHECK_THROWS_AS(it1_c >= it1_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator); + CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator); + CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator); + CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); } else { @@ -643,15 +643,15 @@ TEST_CASE("iterators 2") { if (j != k) { - CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error); - CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error); - CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_AS(j.rbegin() == k.rbegin(), json::invalid_iterator); + CHECK_THROWS_AS(j.crbegin() == k.crbegin(), json::invalid_iterator); + CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error); - CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error); - CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_AS(j.rbegin() < k.rbegin(), json::invalid_iterator); + CHECK_THROWS_AS(j.crbegin() < k.crbegin(), json::invalid_iterator); + CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); } } } @@ -670,53 +670,53 @@ TEST_CASE("iterators 2") { { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it += 1, json::invalid_iterator); + CHECK_THROWS_WITH(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it += 1, json::invalid_iterator); + CHECK_THROWS_WITH(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it + 1, json::invalid_iterator); + CHECK_THROWS_WITH(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it + 1, json::invalid_iterator); + CHECK_THROWS_WITH(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it -= 1, json::invalid_iterator); + CHECK_THROWS_WITH(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it -= 1, json::invalid_iterator); + CHECK_THROWS_WITH(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it - 1, json::invalid_iterator); + CHECK_THROWS_WITH(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it - 1, json::invalid_iterator); + CHECK_THROWS_WITH(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it - it, json::invalid_iterator); + CHECK_THROWS_WITH(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it - it, json::invalid_iterator); + CHECK_THROWS_WITH(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } } @@ -799,17 +799,17 @@ TEST_CASE("iterators 2") { { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it[0], json::invalid_iterator); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); + CHECK_THROWS_AS(it[0], json::invalid_iterator); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); } } @@ -839,17 +839,17 @@ TEST_CASE("iterators 2") { { auto it = j_null.rbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); + CHECK_THROWS_AS(it[0], json::invalid_iterator); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); } { auto it = j_null.crbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); + CHECK_THROWS_AS(it[0], json::invalid_iterator); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); } } @@ -858,14 +858,14 @@ TEST_CASE("iterators 2") { auto it = j_value.rbegin(); CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); } { auto it = j_value.crbegin(); CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); + CHECK_THROWS_AS(it[1], json::invalid_iterator); + CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); } } } diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 05ed7502f..9ab86ea42 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -75,8 +75,9 @@ TEST_CASE("JSON patch") json doc2 = R"({ "q": { "bar": 2 } })"_json; // because "a" does not exist. - CHECK_THROWS_AS(doc2.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(doc2.patch(patch), "key 'a' not found"); + CHECK_THROWS_AS(doc2.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(doc2.patch(patch), + "[json.exception.out_of_range.403] key 'a' not found"); } SECTION("4.2 remove") @@ -336,8 +337,8 @@ TEST_CASE("JSON patch") )"_json; // check that evaluation throws - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + CHECK_THROWS_AS(doc.patch(patch), json::other_error); + CHECK_THROWS_WITH(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); } SECTION("A.10. Adding a Nested Member Object") @@ -420,8 +421,9 @@ TEST_CASE("JSON patch") // references neither the root of the document, nor a member of // an existing object, nor a member of an existing array. - CHECK_THROWS_AS(doc.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(doc.patch(patch), "key 'baz' not found"); + CHECK_THROWS_AS(doc.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(doc.patch(patch), + "[json.exception.out_of_range.403] key 'baz' not found"); } // A.13. Invalid JSON Patch Document @@ -476,8 +478,8 @@ TEST_CASE("JSON patch") )"_json; // check that evaluation throws - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + CHECK_THROWS_AS(doc.patch(patch), json::other_error); + CHECK_THROWS_WITH(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); } SECTION("A.16. Adding an Array Value") @@ -666,40 +668,45 @@ TEST_CASE("JSON patch") { json j; json patch = {{"op", "add"}, {"path", ""}, {"value", 1}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects"); } SECTION("not an array of objects") { json j; json patch = {"op", "add", "path", "", "value", 1}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects"); } SECTION("missing 'op'") { json j; json patch = {{{"foo", "bar"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation must have member 'op'"); } SECTION("non-string 'op'") { json j; json patch = {{{"op", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation must have string member 'op'"); } SECTION("invalid operation") { json j; json patch = {{{"op", "foo"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation value 'foo' is invalid"); } } @@ -709,32 +716,36 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "add"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "add"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'"); } SECTION("missing 'value'") { json j; json patch = {{{"op", "add"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'"); } SECTION("invalid array index") { json j = {1, 2}; json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.401] array index 4 is out of range"); } } @@ -744,40 +755,45 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "remove"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "remove"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'"); } SECTION("nonexisting target location (array)") { json j = {1, 2, 3}; json patch = {{{"op", "remove"}, {"path", "/17"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.401] array index 17 is out of range"); } SECTION("nonexisting target location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "remove"}, {"path", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.403] key 'baz' not found"); } SECTION("root element as target location") { json j = "string"; json patch = {{{"op", "remove"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::domain_error); - CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.405] JSON pointer has no parent"); } } @@ -787,40 +803,45 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "replace"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "replace"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'"); } SECTION("missing 'value'") { json j; json patch = {{{"op", "replace"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'"); } SECTION("nonexisting target location (array)") { json j = {1, 2, 3}; json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.401] array index 17 is out of range"); } SECTION("nonexisting target location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.403] key 'baz' not found"); } } @@ -830,48 +851,54 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "move"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "move"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'"); } SECTION("missing 'from'") { json j; json patch = {{{"op", "move"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'"); } SECTION("non-string 'from'") { json j; json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'"); } SECTION("nonexisting from location (array)") { json j = {1, 2, 3}; json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.401] array index 5 is out of range"); } SECTION("nonexisting from location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.403] key 'baz' not found"); } } @@ -881,48 +908,54 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "copy"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "copy"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'"); } SECTION("missing 'from'") { json j; json patch = {{{"op", "copy"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'"); } SECTION("non-string 'from'") { json j; json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'"); } SECTION("nonexisting from location (array)") { json j = {1, 2, 3}; json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.401] array index 5 is out of range"); } SECTION("nonexisting from location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + CHECK_THROWS_AS(j.patch(patch), json::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.out_of_range.403] key 'baz' not found"); } } @@ -932,24 +965,27 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "test"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "test"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'"); } SECTION("missing 'value'") { json j; json patch = {{{"op", "test"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'"); + CHECK_THROWS_AS(j.patch(patch), json::parse_error); + CHECK_THROWS_WITH(j.patch(patch), + "[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'"); } } } @@ -1141,8 +1177,8 @@ TEST_CASE("JSON patch") )"_json; // the test will fail - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + CHECK_THROWS_AS(doc.patch(patch), json::other_error); + CHECK_THROWS_WITH(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); } } } diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 495d70668..d14261a0f 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -36,20 +36,25 @@ TEST_CASE("JSON pointers") { SECTION("errors") { - CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'"); + CHECK_THROWS_AS(json::json_pointer("foo"), json::parse_error); + CHECK_THROWS_WITH(json::json_pointer("foo"), + "[json.exception.parse_error.107] parse error at 1: JSON pointer must be empty or begin with '/' - was: 'foo'"); - CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'"); + CHECK_THROWS_AS(json::json_pointer("/~~"), json::parse_error); + CHECK_THROWS_WITH(json::json_pointer("/~~"), + "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'"); - CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'"); + CHECK_THROWS_AS(json::json_pointer("/~"), json::parse_error); + CHECK_THROWS_WITH(json::json_pointer("/~"), + "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'"); json::json_pointer p; - CHECK_THROWS_AS(p.top(), std::domain_error); - CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent"); - CHECK_THROWS_AS(p.pop_back(), std::domain_error); - CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent"); + CHECK_THROWS_AS(p.top(), json::out_of_range); + CHECK_THROWS_WITH(p.top(), + "[json.exception.out_of_range.405] JSON pointer has no parent"); + CHECK_THROWS_AS(p.pop_back(), json::out_of_range); + CHECK_THROWS_WITH(p.pop_back(), + "[json.exception.out_of_range.405] JSON pointer has no parent"); } SECTION("examples from RFC 6901") @@ -121,10 +126,12 @@ TEST_CASE("JSON pointers") // unresolved access json j_primitive = 1; - CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); - CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); + CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], json::out_of_range); + CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], + "[json.exception.out_of_range.404] unresolved reference token 'foo'"); + CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), json::out_of_range); + CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), + "[json.exception.out_of_range.404] unresolved reference token 'foo'"); } SECTION("const access") @@ -182,15 +189,18 @@ TEST_CASE("JSON pointers") CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); // unescaped access - CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), std::out_of_range); - CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), "key 'a' not found"); + CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), json::out_of_range); + CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), + "[json.exception.out_of_range.403] key 'a' not found"); // unresolved access const json j_primitive = 1; - CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); - CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); + CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], json::out_of_range); + CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], + "[json.exception.out_of_range.404] unresolved reference token 'foo'"); + CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), json::out_of_range); + CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), + "[json.exception.out_of_range.404] unresolved reference token 'foo'"); } SECTION("user-defined string literal") @@ -245,31 +255,54 @@ TEST_CASE("JSON pointers") CHECK(j == json({1, 13, 3, 33, nullptr, 55})); // error with leading 0 - CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error); - CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'"); - CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error); - CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'"); - CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error); - CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'"); - CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error); - CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'"); + CHECK_THROWS_AS(j["/01"_json_pointer], json::parse_error); + CHECK_THROWS_WITH(j["/01"_json_pointer], + "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'"); + CHECK_THROWS_AS(j_const["/01"_json_pointer], json::parse_error); + CHECK_THROWS_WITH(j_const["/01"_json_pointer], + "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'"); + CHECK_THROWS_AS(j.at("/01"_json_pointer), json::parse_error); + CHECK_THROWS_WITH(j.at("/01"_json_pointer), + "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'"); + CHECK_THROWS_AS(j_const.at("/01"_json_pointer), json::parse_error); + CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), + "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'"); // error with incorrect numbers - CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument); + CHECK_THROWS_AS(j["/one"_json_pointer] = 1, json::parse_error); + CHECK_THROWS_WITH(j["/one"_json_pointer] = 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + CHECK_THROWS_AS(j_const["/one"_json_pointer] == 1, json::parse_error); + CHECK_THROWS_WITH(j_const["/one"_json_pointer] == 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + + CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error); + CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + CHECK_THROWS_AS(j_const.at("/one"_json_pointer) == 1, json::parse_error); + CHECK_THROWS_WITH(j_const.at("/one"_json_pointer) == 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + + CHECK_THROWS_AS(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), json::parse_error); + CHECK_THROWS_WITH(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), + "[json.exception.parse_error.109] parse error: array index 'three' is not a number"); // assign to "-" j["/-"_json_pointer] = 99; CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99})); // error when using "-" in const object - CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range"); + CHECK_THROWS_AS(j_const["/-"_json_pointer], json::out_of_range); + CHECK_THROWS_WITH(j_const["/-"_json_pointer], + "[json.exception.out_of_range.402] array index '-' (3) is out of range"); // error when using "-" with at - CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range"); - CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range"); + CHECK_THROWS_AS(j.at("/-"_json_pointer), json::out_of_range); + CHECK_THROWS_WITH(j.at("/-"_json_pointer), + "[json.exception.out_of_range.402] array index '-' (7) is out of range"); + CHECK_THROWS_AS(j_const.at("/-"_json_pointer), json::out_of_range); + CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), + "[json.exception.out_of_range.402] array index '-' (3) is out of range"); } SECTION("const access") @@ -282,18 +315,22 @@ TEST_CASE("JSON pointers") CHECK(j["/2"_json_pointer] == j[2]); // assign to nonexisting index - CHECK_THROWS_AS(j.at("/3"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/3"_json_pointer), "array index 3 is out of range"); + CHECK_THROWS_AS(j.at("/3"_json_pointer), json::out_of_range); + CHECK_THROWS_WITH(j.at("/3"_json_pointer), + "[json.exception.out_of_range.401] array index 3 is out of range"); // assign to nonexisting index (with gap) - CHECK_THROWS_AS(j.at("/5"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range"); + CHECK_THROWS_AS(j.at("/5"_json_pointer), json::out_of_range); + CHECK_THROWS_WITH(j.at("/5"_json_pointer), + "[json.exception.out_of_range.401] array index 5 is out of range"); // assign to "-" - CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range"); - CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range"); + CHECK_THROWS_AS(j["/-"_json_pointer], json::out_of_range); + CHECK_THROWS_WITH(j["/-"_json_pointer], + "[json.exception.out_of_range.402] array index '-' (3) is out of range"); + CHECK_THROWS_AS(j.at("/-"_json_pointer), json::out_of_range); + CHECK_THROWS_WITH(j.at("/-"_json_pointer), + "[json.exception.out_of_range.402] array index '-' (3) is out of range"); } } @@ -349,17 +386,20 @@ TEST_CASE("JSON pointers") CHECK(j_flatten.unflatten() == j); // error for nonobjects - CHECK_THROWS_AS(json(1).unflatten(), std::domain_error); - CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened"); + CHECK_THROWS_AS(json(1).unflatten(), json::type_error); + CHECK_THROWS_WITH(json(1).unflatten(), + "[json.exception.type_error.314] only objects can be unflattened"); // error for nonprimitve values - CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error); - CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive"); + CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), json::type_error); + CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), + "[json.exception.type_error.315] values in object must be primitive"); // error for conflicting values json j_error = {{"", 42}, {"/foo", 17}}; - CHECK_THROWS_AS(j_error.unflatten(), std::domain_error); - CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten"); + CHECK_THROWS_AS(j_error.unflatten(), json::type_error); + CHECK_THROWS_WITH(j_error.unflatten(), + "[json.exception.type_error.313] invalid value to unflatten"); // explicit roundtrip check CHECK(j.flatten().unflatten() == j); diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 2b5fdd71c..0c3321110 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -152,8 +152,8 @@ TEST_CASE("modifiers") SECTION("other type") { json j = 1; - CHECK_THROWS_AS(j.push_back("Hello"), std::domain_error); - CHECK_THROWS_WITH(j.push_back("Hello"), "cannot use push_back() with number"); + CHECK_THROWS_AS(j.push_back("Hello"), json::type_error); + CHECK_THROWS_WITH(j.push_back("Hello"), "[json.exception.type_error.308] cannot use push_back() with number"); } } @@ -182,8 +182,8 @@ TEST_CASE("modifiers") { json j = 1; json k("Hello"); - CHECK_THROWS_AS(j.push_back(k), std::domain_error); - CHECK_THROWS_WITH(j.push_back(k), "cannot use push_back() with number"); + CHECK_THROWS_AS(j.push_back(k), json::type_error); + CHECK_THROWS_WITH(j.push_back(k), "[json.exception.type_error.308] cannot use push_back() with number"); } } } @@ -215,9 +215,9 @@ TEST_CASE("modifiers") { json j = 1; json k("Hello"); - CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), std::domain_error); + CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), json::type_error); CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})), - "cannot use push_back() with number"); + "[json.exception.type_error.308] cannot use push_back() with number"); } } @@ -252,8 +252,8 @@ TEST_CASE("modifiers") CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); json k = {{"key1", 1}}; - CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error); - CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object"); + CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), json::type_error); + CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object"); } } } @@ -291,8 +291,9 @@ TEST_CASE("modifiers") SECTION("other type") { json j = 1; - CHECK_THROWS_AS(j.emplace_back("Hello"), std::domain_error); - CHECK_THROWS_WITH(j.emplace_back("Hello"), "cannot use emplace_back() with number"); + CHECK_THROWS_AS(j.emplace_back("Hello"), json::type_error); + CHECK_THROWS_WITH(j.emplace_back("Hello"), + "[json.exception.type_error.311] cannot use emplace_back() with number"); } } @@ -350,8 +351,9 @@ TEST_CASE("modifiers") SECTION("other type") { json j = 1; - CHECK_THROWS_AS(j.emplace("foo", "bar"), std::domain_error); - CHECK_THROWS_WITH(j.emplace("foo", "bar"), "cannot use emplace() with number"); + CHECK_THROWS_AS(j.emplace("foo", "bar"), json::type_error); + CHECK_THROWS_WITH(j.emplace("foo", "bar"), + "[json.exception.type_error.311] cannot use emplace() with number"); } } @@ -381,8 +383,8 @@ TEST_CASE("modifiers") SECTION("other type") { json j = 1; - CHECK_THROWS_AS(j += "Hello", std::domain_error); - CHECK_THROWS_WITH(j += "Hello", "cannot use push_back() with number"); + CHECK_THROWS_AS(j += "Hello", json::type_error); + CHECK_THROWS_WITH(j += "Hello", "[json.exception.type_error.308] cannot use push_back() with number"); } } @@ -411,8 +413,8 @@ TEST_CASE("modifiers") { json j = 1; json k("Hello"); - CHECK_THROWS_AS(j += k, std::domain_error); - CHECK_THROWS_WITH(j += k, "cannot use push_back() with number"); + CHECK_THROWS_AS(j += k, json::type_error); + CHECK_THROWS_WITH(j += k, "[json.exception.type_error.308] cannot use push_back() with number"); } } } @@ -444,9 +446,9 @@ TEST_CASE("modifiers") { json j = 1; json k("Hello"); - CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), std::domain_error); + CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), json::type_error); CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}), - "cannot use push_back() with number"); + "[json.exception.type_error.308] cannot use push_back() with number"); } } @@ -481,8 +483,8 @@ TEST_CASE("modifiers") CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); json k = {{"key1", 1}}; - CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error); - CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object"); + CHECK_THROWS_AS((k += {1, 2, 3, 4}), json::type_error); + CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object"); } } } @@ -617,14 +619,15 @@ TEST_CASE("modifiers") { json j_other_array2 = {"first", "second"}; - CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), + json::invalid_iterator); CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), - std::domain_error); + json::invalid_iterator); CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), - "passed iterators may not belong to container"); + "[json.exception.invalid_iterator.211] passed iterators may not belong to container"); CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), - "iterators do not fit"); + "[json.exception.invalid_iterator.210] iterators do not fit"); } } @@ -663,22 +666,22 @@ TEST_CASE("modifiers") // pass iterator to a different array json j_another_array = {1, 2}; json j_yet_another_array = {"first", "second"}; - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), json::invalid_iterator); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), json::invalid_iterator); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), json::invalid_iterator); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), json::invalid_iterator); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), json::invalid_iterator); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), + "[json.exception.invalid_iterator.202] iterator does not fit current value"); CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value), - "iterator does not fit current value"); + "[json.exception.invalid_iterator.202] iterator does not fit current value"); CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11), - "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), "iterator does not fit current value"); + "[json.exception.invalid_iterator.202] iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), + "[json.exception.invalid_iterator.202] iterator does not fit current value"); CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), - "iterator does not fit current value"); + "[json.exception.invalid_iterator.202] iterator does not fit current value"); } SECTION("non-array type") @@ -686,20 +689,20 @@ TEST_CASE("modifiers") // call insert on a non-array type json j_nonarray = 3; json j_yet_another_array = {"first", "second"}; - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), json::type_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), json::type_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), json::type_error); CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), std::domain_error); + j_yet_another_array.end()), json::type_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), json::type_error); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "[json.exception.type_error.309] cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "[json.exception.type_error.309] cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "[json.exception.type_error.309] cannot use insert() with number"); CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), "cannot use insert() with number"); + j_yet_another_array.end()), "[json.exception.type_error.309] cannot use insert() with number"); CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), - "cannot use insert() with number"); + "[json.exception.type_error.309] cannot use insert() with number"); } } @@ -751,8 +754,8 @@ TEST_CASE("modifiers") json j = 17; json::array_t a = {"foo", "bar", "baz"}; - CHECK_THROWS_AS(j.swap(a), std::domain_error); - CHECK_THROWS_WITH(j.swap(a), "cannot use swap() with number"); + CHECK_THROWS_AS(j.swap(a), json::type_error); + CHECK_THROWS_WITH(j.swap(a), "[json.exception.type_error.310] cannot use swap() with number"); } } @@ -777,8 +780,8 @@ TEST_CASE("modifiers") json j = 17; json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; - CHECK_THROWS_AS(j.swap(o), std::domain_error); - CHECK_THROWS_WITH(j.swap(o), "cannot use swap() with number"); + CHECK_THROWS_AS(j.swap(o), json::type_error); + CHECK_THROWS_WITH(j.swap(o), "[json.exception.type_error.310] cannot use swap() with number"); } } @@ -803,8 +806,8 @@ TEST_CASE("modifiers") json j = 17; json::string_t s = "Hallo Welt"; - CHECK_THROWS_AS(j.swap(s), std::domain_error); - CHECK_THROWS_WITH(j.swap(s), "cannot use swap() with number"); + CHECK_THROWS_AS(j.swap(s), json::type_error); + CHECK_THROWS_WITH(j.swap(s), "[json.exception.type_error.310] cannot use swap() with number"); } } } diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 3a2ae5b15..1da33153b 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1016,6 +1016,90 @@ TEST_CASE("MessagePack") json j = json::from_msgpack(given); CHECK(j.get() == Approx(25.0000019073486)); } + + SECTION("errors") + { + SECTION("too short byte vector") + { + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcc})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcd})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcd, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xce})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xce, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xce, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xce, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcf})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcf, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcf, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error); + + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcc})), + "[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcd})), + "[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcd, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xce})), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xce, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xce, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xce, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcf})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcf, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcf, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); + } + + SECTION("unsupported bytes") + { + SECTION("concrete examples") + { + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xc1})), json::parse_error); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xc1})), + "[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xc1"); + CHECK_THROWS_AS(json::from_msgpack(std::vector({0xc6})), json::parse_error); + CHECK_THROWS_WITH(json::from_msgpack(std::vector({0xc6})), + "[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xc6"); + } + + SECTION("all unsupported bytes") + { + for (auto byte : + { + // never used + 0xc1, + // bin + 0xc4, 0xc5, 0xc6, + // ext + 0xc7, 0xc8, 0xc9, + // fixext + 0xd4, 0xd5, 0xd6, 0xd7, 0xd8 + }) + { + CHECK_THROWS_AS(json::from_msgpack(std::vector({static_cast(byte)})), json::parse_error); + } + } + } + } } @@ -1116,7 +1200,7 @@ TEST_CASE("MessagePack roundtrips", "[hide]") "test/data/nst_json_testsuite/test_parsing/y_number_after_space.json", "test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json", "test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json", - "test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json", + //"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json", "test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json", "test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json", "test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json", @@ -1128,9 +1212,9 @@ TEST_CASE("MessagePack roundtrips", "[hide]") "test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json", - "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json", + //"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json", - "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json", + //"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json", "test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json", diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 368c14701..ce984b0dc 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -33,6 +33,7 @@ SOFTWARE. using nlohmann::json; #include +#include TEST_CASE("regression tests") { @@ -49,6 +50,7 @@ TEST_CASE("regression tests") SECTION("issue #70 - Handle infinity and NaN cases") { + /* SECTION("NAN value") { CHECK(json(NAN) == json()); @@ -60,6 +62,36 @@ TEST_CASE("regression tests") CHECK(json(INFINITY) == json()); CHECK(json(json::number_float_t(INFINITY)) == json()); } + */ + + // With 3.0.0, the semantics of this changed: NAN and infinity are + // stored properly inside the JSON value (no exception or conversion + // to null), but are serialized as null. + SECTION("NAN value") + { + json j1 = NAN; + CHECK(j1.is_number_float()); + json::number_float_t f1 = j1; + CHECK(std::isnan(f1)); + + json j2 = json::number_float_t(NAN); + CHECK(j2.is_number_float()); + json::number_float_t f2 = j2; + CHECK(std::isnan(f2)); + } + + SECTION("infinity") + { + json j1 = INFINITY; + CHECK(j1.is_number_float()); + json::number_float_t f1 = j1; + CHECK(not std::isfinite(f1)); + + json j2 = json::number_float_t(INFINITY); + CHECK(j2.is_number_float()); + json::number_float_t f2 = j2; + CHECK(not std::isfinite(f2)); + } } SECTION("pull request #71 - handle enum type") @@ -429,16 +461,6 @@ TEST_CASE("regression tests") { setlocale(LC_NUMERIC, "de_DE.UTF-8"); - // Verify that snprintf uses special decimal and grouping characters. - // Disabled, because can't trigger locale-specific behavior in AppVeyor -#ifndef _MSC_VER - { - std::array buf; - std::snprintf(buf.data(), buf.size(), "%.2f", 12345.67); - CHECK(strcmp(buf.data(), "12345,67") == 0); - } -#endif - // verify that dumped correctly with '.' and no grouping const json j1 = 12345.67; CHECK(json(12345.67).dump() == "12345.67"); @@ -449,18 +471,6 @@ TEST_CASE("regression tests") { setlocale(LC_NUMERIC, "de_DE.UTF-8"); - // disabled, because locale-specific beharivor is not - // triggered in AppVeyor for some reason -#ifndef _MSC_VER - { - // verify that strtod now uses commas as decimal-separator - CHECK(std::strtod("3,14", nullptr) == 3.14); - - // verify that strtod does not understand dots as decimal separator - CHECK(std::strtod("3.14", nullptr) == 3); - } -#endif - // verify that parsed correctly despite using strtod internally CHECK(json::parse("3.14").get() == 3.14); @@ -559,14 +569,16 @@ TEST_CASE("regression tests") SECTION("issue #329 - serialized value not always can be parsed") { - json j = json::parse("22e2222"); - CHECK(j == json()); + CHECK_THROWS_AS(json::parse("22e2222"), json::out_of_range); + CHECK_THROWS_WITH(json::parse("22e2222"), + "[json.exception.out_of_range.406] number overflow parsing '22e2222'"); } SECTION("issue #366 - json::parse on failed stream gets stuck") { std::ifstream f("file_not_found.json"); - CHECK_THROWS_AS(json::parse(f), std::invalid_argument); + CHECK_THROWS_AS(json::parse(f), json::parse_error); + CHECK_THROWS_WITH(json::parse(f), "[json.exception.parse_error.111] parse error: bad input stream"); } SECTION("issue #367 - calling stream at EOF") @@ -580,8 +592,9 @@ TEST_CASE("regression tests") // ss is not at EOF; this yielded an error before the fix // (threw basic_string::append). No, it should just throw // a parse error because of the EOF. - CHECK_THROWS_AS(j << ss, std::invalid_argument); - CHECK_THROWS_WITH(j << ss, "parse error - unexpected end of input"); + CHECK_THROWS_AS(j << ss, json::parse_error); + 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)") @@ -612,37 +625,51 @@ TEST_CASE("regression tests") { // original test case std::vector vec {0x65, 0xf5, 0x0a, 0x48, 0x21}; - CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec), + "[json.exception.parse_error.110] parse error at 2: cannot read 5 bytes from vector"); } SECTION("issue #407 - Heap-buffer-overflow (OSS-Fuzz issue 343)") { // original test case: incomplete float64 std::vector vec1 {0xcb, 0x8f, 0x0a}; - CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range); + CHECK_THROWS_AS(json::from_msgpack(vec1), json::parse_error); + CHECK_THROWS_WITH(json::from_msgpack(vec1), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); // related test case: incomplete float32 std::vector vec2 {0xca, 0x8f, 0x0a}; - CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range); + CHECK_THROWS_AS(json::from_msgpack(vec2), json::parse_error); + CHECK_THROWS_WITH(json::from_msgpack(vec2), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); // related test case: incomplete Half-Precision Float (CBOR) std::vector vec3 {0xf9, 0x8f}; - CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec3), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec3), + "[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector"); // related test case: incomplete Single-Precision Float (CBOR) std::vector vec4 {0xfa, 0x8f, 0x0a}; - CHECK_THROWS_AS(json::from_cbor(vec4), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec4), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec4), + "[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector"); // related test case: incomplete Double-Precision Float (CBOR) std::vector vec5 {0xfb, 0x8f, 0x0a}; - CHECK_THROWS_AS(json::from_cbor(vec5), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec5), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec5), + "[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector"); } SECTION("issue #408 - Heap-buffer-overflow (OSS-Fuzz issue 344)") { // original test case std::vector vec1 {0x87}; - CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range); + CHECK_THROWS_AS(json::from_msgpack(vec1), json::parse_error); + CHECK_THROWS_WITH(json::from_msgpack(vec1), + "[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector"); // more test cases for MessagePack for (auto b : @@ -654,7 +681,7 @@ TEST_CASE("regression tests") }) { std::vector vec(1, static_cast(b)); - CHECK_THROWS_AS(json::from_msgpack(vec), std::out_of_range); + CHECK_THROWS_AS(json::from_msgpack(vec), json::parse_error); } // more test cases for CBOR @@ -669,28 +696,38 @@ TEST_CASE("regression tests") }) { std::vector vec(1, static_cast(b)); - CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec), json::parse_error); } // special case: empty input std::vector vec2; - CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); - CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec2), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec2), + "[json.exception.parse_error.110] parse error at 1: cannot read 1 bytes from vector"); + CHECK_THROWS_AS(json::from_msgpack(vec2), json::parse_error); + CHECK_THROWS_WITH(json::from_msgpack(vec2), + "[json.exception.parse_error.110] parse error at 1: cannot read 1 bytes from vector"); } SECTION("issue #411 - Heap-buffer-overflow (OSS-Fuzz issue 366)") { // original test case: empty UTF-8 string (indefinite length) std::vector vec1 {0x7f}; - CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec1), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec1), + "[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector"); // related test case: empty array (indefinite length) std::vector vec2 {0x9f}; - CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec2), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec2), + "[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector"); // related test case: empty map (indefinite length) std::vector vec3 {0xbf}; - CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec3), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec3), + "[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector"); } SECTION("issue #412 - Heap-buffer-overflow (OSS-Fuzz issue 367)") @@ -716,19 +753,27 @@ TEST_CASE("regression tests") 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60 }; - CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec), + "[json.exception.parse_error.110] parse error at 137: cannot read 1 bytes from vector"); // related test case: nonempty UTF-8 string (indefinite length) std::vector vec1 {0x7f, 0x61, 0x61}; - CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec1), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec1), + "[json.exception.parse_error.110] parse error at 4: cannot read 1 bytes from vector"); // related test case: nonempty array (indefinite length) std::vector vec2 {0x9f, 0x01}; - CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec2), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec2), + "[json.exception.parse_error.110] parse error at 3: cannot read 1 bytes from vector"); // related test case: nonempty map (indefinite length) std::vector vec3 {0xbf, 0x61, 0x61, 0x01}; - CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec3), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec3), + "[json.exception.parse_error.110] parse error at 5: cannot read 1 bytes from vector"); } SECTION("issue #414 - compare with literal 0)") @@ -761,7 +806,9 @@ TEST_CASE("regression tests") 0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfa }; - CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec1), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec1), + "[json.exception.parse_error.110] parse error at 49: cannot read 4 bytes from vector"); // related test case: double-precision std::vector vec2 @@ -773,13 +820,15 @@ TEST_CASE("regression tests") 0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfb }; - CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + CHECK_THROWS_AS(json::from_cbor(vec2), json::parse_error); + CHECK_THROWS_WITH(json::from_cbor(vec2), + "[json.exception.parse_error.110] parse error at 49: cannot read 8 bytes from vector"); } SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)") { std::vector 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") @@ -797,6 +846,62 @@ TEST_CASE("regression tests") CHECK(s1 == s2); } + SECTION("issue #473 - inconsistent behavior in conversion to array type") + { + json j_array = {1, 2, 3, 4}; + json j_number = 42; + json j_null = nullptr; + + SECTION("std::vector") + { + auto create = [](const json & j) + { + std::vector v = j; + }; + + CHECK_NOTHROW(create(j_array)); + CHECK_THROWS_AS(create(j_number), json::type_error); + CHECK_THROWS_WITH(create(j_number), "[json.exception.type_error.302] type must be array, but is number"); + CHECK_THROWS_AS(create(j_null), json::type_error); + CHECK_THROWS_WITH(create(j_null), "[json.exception.type_error.302] type must be array, but is null"); + } + + SECTION("std::list") + { + auto create = [](const json & j) + { + std::list v = j; + }; + + CHECK_NOTHROW(create(j_array)); + CHECK_THROWS_AS(create(j_number), json::type_error); + CHECK_THROWS_WITH(create(j_number), "[json.exception.type_error.302] type must be array, but is number"); + CHECK_THROWS_AS(create(j_null), json::type_error); + CHECK_THROWS_WITH(create(j_null), "[json.exception.type_error.302] type must be array, but is null"); + } + + SECTION("std::forward_list") + { + auto create = [](const json & j) + { + std::forward_list v = j; + }; + + CHECK_NOTHROW(create(j_array)); + CHECK_THROWS_AS(create(j_number), json::type_error); + CHECK_THROWS_WITH(create(j_number), "[json.exception.type_error.302] type must be array, but is number"); + CHECK_THROWS_AS(create(j_null), json::type_error); + CHECK_THROWS_WITH(create(j_null), "[json.exception.type_error.302] type must be array, but is null"); + } + } + + SECTION("issue #486 - json::value_t can't be a map's key type in VC++ 2015") + { + // the code below must compile with MSVC + std::map jsonTypes ; + jsonTypes[json::value_t::array] = "array"; + } + SECTION("issue #494 - conversion from vector to json fails to build") { std::vector boolVector = {false, true, false, false}; @@ -836,4 +941,24 @@ TEST_CASE("regression tests") CHECK_THROWS_AS(l.fill_line_buffer(), std::invalid_argument); } } + + SECTION("issue #504 - assertion error (OSS-Fuzz 856)") + { + std::vector vec1 = {0xf9, 0xff, 0xff, 0x4a, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x37, 0x02, 0x38}; + json j1 = json::from_cbor(vec1); + + // step 2: round trip + std::vector vec2 = json::to_cbor(j1); + + // parse serialization + json j2 = json::from_cbor(vec2); + + // NaN is dumped to "null" + CHECK(j2.is_number_float()); + CHECK(std::isnan(j2.get())); + CHECK(j2.dump() == "null"); + + // check if serializations match + CHECK(json::to_cbor(j2) == vec2); + } } diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index e83d5b740..8d6a81622 100644 --- a/test/src/unit-testsuites.cpp +++ b/test/src/unit-testsuites.cpp @@ -79,7 +79,7 @@ TEST_CASE("compliance tests from json.org") CAPTURE(filename); json j; std::ifstream f(filename); - CHECK_THROWS_AS(j << f, std::invalid_argument); + CHECK_THROWS_AS(j << f, json::parse_error); } } @@ -460,7 +460,6 @@ TEST_CASE("nst's JSONTestSuite") "test/data/nst_json_testsuite/test_parsing/y_number_after_space.json", "test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json", "test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json", - "test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json", "test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json", "test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json", "test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json", @@ -472,9 +471,7 @@ TEST_CASE("nst's JSONTestSuite") "test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json", - "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json", - "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json", "test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json", "test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json", @@ -757,7 +754,7 @@ TEST_CASE("nst's JSONTestSuite") CAPTURE(filename); std::ifstream f(filename); json j; - CHECK_THROWS_AS(j << f, std::invalid_argument); + CHECK_THROWS_AS(j << f, json::parse_error); } } @@ -765,9 +762,6 @@ TEST_CASE("nst's JSONTestSuite") { for (auto filename : { - // we currently do not limit exponents - "test/data/nst_json_testsuite/test_parsing/i_number_neg_int_huge_exp.json", - "test/data/nst_json_testsuite/test_parsing/i_number_pos_double_huge_exp.json", // we do not pose a limit on nesting "test/data/nst_json_testsuite/test_parsing/i_structure_500_nested_arrays.json", // we silently ignore BOMs @@ -787,6 +781,26 @@ TEST_CASE("nst's JSONTestSuite") } } + // numbers that overflow during parsing + SECTION("i/y -> n (out of range)") + { + for (auto filename : + { + "test/data/nst_json_testsuite/test_parsing/i_number_neg_int_huge_exp.json", + "test/data/nst_json_testsuite/test_parsing/i_number_pos_double_huge_exp.json", + "test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json", + "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json", + "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json" + } + ) + { + CAPTURE(filename); + std::ifstream f(filename); + json j; + CHECK_THROWS_AS(j << f, json::out_of_range); + } + } + SECTION("i -> n") { for (auto filename : @@ -810,7 +824,7 @@ TEST_CASE("nst's JSONTestSuite") CAPTURE(filename); std::ifstream f(filename); json j; - CHECK_THROWS_AS(j << f, std::invalid_argument); + CHECK_THROWS_AS(j << f, json::parse_error); } } } diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index c429b91cb..f7cf0ada3 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -38,6 +38,9 @@ TEST_CASE("Unicode", "[hide]") { SECTION("full enumeration of Unicode code points") { + // lexer to call to_unicode on + json::lexer dummy_lexer(reinterpret_cast(""), 0); + // create an escaped string from a code point const auto codepoint_to_unicode = [](std::size_t cp) { @@ -85,7 +88,7 @@ TEST_CASE("Unicode", "[hide]") // they are checked with codepoint_to_unicode. if (cp > 0x1f and cp != 0x22 and cp != 0x5c) { - unescaped_string = json::lexer::to_unicode(cp); + unescaped_string = dummy_lexer.to_unicode(cp); } } else @@ -97,7 +100,7 @@ TEST_CASE("Unicode", "[hide]") const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu); escaped_string = codepoint_to_unicode(codepoint1); 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 @@ -170,7 +173,7 @@ TEST_CASE("Unicode", "[hide]") SECTION("error for incomplete/wrong BOM") { - CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\xef\xbb"), json::parse_error); + CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), json::parse_error); } }