From cca9375c18eda238e695e01ccbba7ef072203103 Mon Sep 17 00:00:00 2001 From: Colby Haskell Date: Sat, 7 Oct 2023 17:16:14 -0400 Subject: [PATCH] Add more specific error for empty input --- docs/mkdocs/docs/home/exceptions.md | 24 ++++++++++++------------ include/nlohmann/detail/input/parser.hpp | 14 +++++++++++++- single_include/nlohmann/json.hpp | 15 ++++++++++++++- tests/src/unit-diagnostics.cpp | 2 +- tests/src/unit-regression1.cpp | 16 ++++++++-------- 5 files changed, 48 insertions(+), 23 deletions(-) diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index 0ecf8557ca..c2eea1571e 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -38,7 +38,7 @@ Note that [`JSON_THROW_USER`](../api/macros/json_throw_user.md) should leave the ```cpp #include - + #define JSON_TRY_USER if(true) #define JSON_CATCH_USER(exception) if(false) #define JSON_THROW_USER(exception) \ @@ -46,7 +46,7 @@ Note that [`JSON_THROW_USER`](../api/macros/json_throw_user.md) should leave the << " (function " << __FUNCTION__ << ") - " \ << (exception).what() << std::endl; \ std::abort();} - + #include ``` @@ -63,7 +63,7 @@ Exceptions in the library are thrown in the local context of the JSON value they ```cpp --8<-- "examples/diagnostics_standard.cpp" ``` - + Output: ``` @@ -81,7 +81,7 @@ As this global context comes at the price of storing one additional pointer per ```cpp --8<-- "examples/diagnostics_extended.cpp" ``` - + Output: ``` @@ -116,7 +116,7 @@ Exceptions have ids 1xx. ```cpp --8<-- "examples/parse_error.cpp" ``` - + Output: ``` @@ -139,7 +139,7 @@ This error indicates a syntax error while deserializing a JSON text. The error m No input: ``` - [json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal + [json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON ``` Control character was not escaped: @@ -368,7 +368,7 @@ Exceptions have ids 2xx. ```cpp --8<-- "examples/invalid_iterator.cpp" ``` - + Output: ``` @@ -532,7 +532,7 @@ Exceptions have ids 3xx. ```cpp --8<-- "examples/type_error.cpp" ``` - + Output: ``` @@ -726,7 +726,7 @@ The `dump()` function only works with UTF-8 encoded strings; that is, if you ass - Store the source file with UTF-8 encoding. - Pass an error handler as last parameter to the `dump()` function to avoid this exception: - - `json::error_handler_t::replace` will replace invalid bytes sequences with `U+FFFD` + - `json::error_handler_t::replace` will replace invalid bytes sequences with `U+FFFD` - `json::error_handler_t::ignore` will silently ignore invalid byte sequences ### json.exception.type_error.317 @@ -761,7 +761,7 @@ Exceptions have ids 4xx. ```cpp --8<-- "examples/out_of_range.cpp" ``` - + Output: ``` @@ -840,7 +840,7 @@ UBJSON and BSON only support integer numbers up to 9223372036854775807. !!! note - Since version 3.9.0, integer numbers beyond int64 are serialized as high-precision UBJSON numbers, and this exception does not further occur. + Since version 3.9.0, integer numbers beyond int64 are serialized as high-precision UBJSON numbers, and this exception does not further occur. ### json.exception.out_of_range.408 @@ -876,7 +876,7 @@ Exceptions have ids 5xx. ```cpp --8<-- "examples/other_error.cpp" ``` - + Output: ``` diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 8acbd4fcad..52154062d1 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -341,13 +341,25 @@ class parser m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); } + case token_type::end_of_input: + { + if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); + } + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr)); + } case token_type::uninitialized: case token_type::end_array: case token_type::end_object: case token_type::name_separator: case token_type::value_separator: - case token_type::end_of_input: case token_type::literal_or_value: default: // the last token was unexpected { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 14bc07d5fe..b54487ac6f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12441,13 +12441,26 @@ class parser m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); } + case token_type::end_of_input: + { + if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr)); + } case token_type::uninitialized: case token_type::end_array: case token_type::end_object: case token_type::name_separator: case token_type::value_separator: - case token_type::end_of_input: case token_type::literal_or_value: default: // the last token was unexpected { diff --git a/tests/src/unit-diagnostics.cpp b/tests/src/unit-diagnostics.cpp index 4f60a0f4d8..85866a2fc9 100644 --- a/tests/src/unit-diagnostics.cpp +++ b/tests/src/unit-diagnostics.cpp @@ -74,7 +74,7 @@ TEST_CASE("Better diagnostics") SECTION("Parse error") { json _; - CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error); + CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error); } SECTION("Wrong type in update()") diff --git a/tests/src/unit-regression1.cpp b/tests/src/unit-regression1.cpp index 284ebd014e..3d45a8d7d0 100644 --- a/tests/src/unit-regression1.cpp +++ b/tests/src/unit-regression1.cpp @@ -722,7 +722,7 @@ TEST_CASE("regression tests 1") { std::ifstream f("file_not_found.json"); json _; - CHECK_THROWS_WITH_AS(_ = json::parse(f), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::parse(f), "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("issue #367 - calling stream at EOF") @@ -736,7 +736,7 @@ TEST_CASE("regression tests 1") // 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_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("issue #367 - behavior of operator>> should more closely resemble that of built-in overloads") @@ -745,7 +745,7 @@ TEST_CASE("regression tests 1") { std::stringstream ss; json j; - CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("(whitespace)") @@ -765,7 +765,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == 111); - CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("one value + whitespace") @@ -788,7 +788,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == 333); - CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("three values") @@ -803,7 +803,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == 333); - CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("literals without whitespace") @@ -820,7 +820,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == ""); - CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("example from #529") @@ -833,7 +833,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == json({{"three", 3}})); - CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&); } SECTION("second example from #529")