From c085e3bac28076bf0dc047b9981d06555c1e709e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 1 Mar 2017 21:28:44 +0100 Subject: [PATCH] :hammer: started with user-defined exceptions #301 #244 Added class hierarchy for user-defined exceptions (#244). Integrated parse exceptions 101-103. Parse exceptions include the byte count of the last read character to locate the position of the error (#301). --- doc/Makefile | 1 + src/json.hpp | 209 +++++++++++++++- src/json.hpp.re2c | 210 +++++++++++++++- test/src/unit-class_lexer.cpp | 8 +- test/src/unit-class_parser.cpp | 392 ++++++++++++++++-------------- test/src/unit-deserialization.cpp | 58 +++-- test/src/unit-regression.cpp | 7 +- test/src/unit-testsuites.cpp | 6 +- test/src/unit-unicode.cpp | 11 +- 9 files changed, 650 insertions(+), 252 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 37a0e19f..cd23a9f7 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -58,6 +58,7 @@ doxygen: create_output create_links $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType JSONSerializer >@@g' html/*.html + $(SED) -i 's@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 upload: clean doxygen check_output cd html ; ../scripts/git-update-ghpages nlohmann/json diff --git a/src/json.hpp b/src/json.hpp index ff0ce5ad..d4e87e5e 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1088,6 +1088,178 @@ class basic_json /// @} + public: + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /*! + @brief general exception of the @ref basic_json class + + Extension of std::exception objects with a member @a id for exception ids. + + name / id | example massage | description + ------------------------------ | --------------- | ------------------------- + json.exception.[parse_error](@ref 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 @ref parse_error::byte indicates the error position. + json.exception.[parse_error](@ref 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](@ref 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](@ref 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 documentthat represents an array of objects. + json.exception.[parse_error](@ref parse_error).105 | "parse error: operation must have string member 'op'" | An operation of a JSON Patch document must contain Operation objects MUST have 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](@ref parse_error).106 | "parse error: array index 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](@ref parse_error).107 | "parse error: JSON pointer must be empty or begin with '/'" | 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](@ref 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](@ref parse_error).109 | "parse error: array index 'one' is not a number" | A JSON Pointer array index must be a number. + json.exception.[invalid_iterator](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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(). + json.exception.[type_error](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref type_error).309 | "cannot use insert() with" | The @ref insert() member functions can only be executed for certain JSON types. + json.exception.[type_error](@ref 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](@ref 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](@ref 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](@ref 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. + json.exception.[out_of_range](@ref 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](@ref 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. + json.exception.[out_of_range](@ref out_of_range).403 | "key 'foo' not found" | The provided key was not found in the JSON object. + json.exception.[out_of_range](@ref out_of_range).404 | "unresolved reference token 'foo'" | A reference token in a JSON Pointer could not be resolved. + json.exception.[out_of_range](@ref 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.other.500 | "unsuccessful" | A JSON Patch operation 'test' failed. + + @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. + + Exceptions have ids 1xx. + + @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. + */ + const size_t byte; + }; + + /*! + @brief exception indicating errors with iterators + + Exceptions have ids 2xx. + + @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. + + @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. + + @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 returns the allocator associated with the container */ @@ -6801,6 +6973,9 @@ 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""` + @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. @@ -9707,17 +9882,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; @@ -9740,7 +9915,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")); } } @@ -9774,7 +9949,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; @@ -10034,6 +10209,7 @@ basic_json_parser_6: goto basic_json_parser_6; } { + position += static_cast((m_cursor - m_start)); continue; } basic_json_parser_9: @@ -10889,6 +11065,7 @@ basic_json_parser_74: } + position += static_cast((m_cursor - m_start)); return last_token_type; } @@ -11044,7 +11221,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 + @throw parse_error.103 if to_unicode fails */ string_t get_string() const { @@ -11130,7 +11308,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 @@ -11143,7 +11321,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 { @@ -11398,6 +11576,11 @@ basic_json_parser_74: return false; } + constexpr size_t get_position() const + { + return position; + } + private: /// optional input stream std::istream* m_stream = nullptr; @@ -11417,6 +11600,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; }; /*! @@ -11678,7 +11863,7 @@ 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)); } } @@ -11690,7 +11875,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)); } } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index fc7cf965..1afc025a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1088,6 +1088,178 @@ class basic_json /// @} + public: + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /*! + @brief general exception of the @ref basic_json class + + Extension of std::exception objects with a member @a id for exception ids. + + name / id | example massage | description + ------------------------------ | --------------- | ------------------------- + json.exception.[parse_error](@ref 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 @ref parse_error::byte indicates the error position. + json.exception.[parse_error](@ref 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](@ref 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](@ref 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 documentthat represents an array of objects. + json.exception.[parse_error](@ref parse_error).105 | "parse error: operation must have string member 'op'" | An operation of a JSON Patch document must contain Operation objects MUST have 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](@ref parse_error).106 | "parse error: array index 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](@ref parse_error).107 | "parse error: JSON pointer must be empty or begin with '/'" | 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](@ref 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](@ref parse_error).109 | "parse error: array index 'one' is not a number" | A JSON Pointer array index must be a number. + json.exception.[invalid_iterator](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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(). + json.exception.[type_error](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref 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](@ref type_error).309 | "cannot use insert() with" | The @ref insert() member functions can only be executed for certain JSON types. + json.exception.[type_error](@ref 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](@ref 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](@ref 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](@ref 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. + json.exception.[out_of_range](@ref 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](@ref 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. + json.exception.[out_of_range](@ref out_of_range).403 | "key 'foo' not found" | The provided key was not found in the JSON object. + json.exception.[out_of_range](@ref out_of_range).404 | "unresolved reference token 'foo'" | A reference token in a JSON Pointer could not be resolved. + json.exception.[out_of_range](@ref 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.other.500 | "unsuccessful" | A JSON Patch operation 'test' failed. + + @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. + + Exceptions have ids 1xx. + + @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. + */ + const size_t byte; + }; + + /*! + @brief exception indicating errors with iterators + + Exceptions have ids 2xx. + + @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. + + @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. + + @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 returns the allocator associated with the container */ @@ -6801,6 +6973,9 @@ 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""` + @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. @@ -9707,17 +9882,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; @@ -9740,7 +9915,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")); } } @@ -9774,7 +9949,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; @@ -9869,7 +10044,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; } @@ -9923,6 +10098,7 @@ class basic_json */ } + position += static_cast((m_cursor - m_start)); return last_token_type; } @@ -10078,7 +10254,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 + @throw parse_error.103 if to_unicode fails */ string_t get_string() const { @@ -10164,7 +10341,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 @@ -10177,7 +10354,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 { @@ -10432,6 +10609,11 @@ class basic_json return false; } + constexpr size_t get_position() const + { + return position; + } + private: /// optional input stream std::istream* m_stream = nullptr; @@ -10451,6 +10633,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; }; /*! @@ -10712,7 +10896,7 @@ 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)); } } @@ -10724,7 +10908,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)); } } diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 0b019bfe..b9e602b7 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 e3ad3723..d5e7c99b 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") @@ -277,60 +281,61 @@ TEST_CASE("parser class") 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 +343,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 +518,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 +585,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-deserialization.cpp b/test/src/unit-deserialization.cpp index 9028fdfb..cacf6687 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-regression.cpp b/test/src/unit-regression.cpp index 7980371b..9dea8083 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -579,8 +579,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)") @@ -778,7 +779,7 @@ TEST_CASE("regression tests") 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") diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index e83d5b74..701348a8 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); } } @@ -757,7 +757,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); } } @@ -810,7 +810,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 c429b91c..f7cf0ada 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); } }