diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 962913610..960357dc5 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -175,6 +175,88 @@ template inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/) {} +#if JSON_DIAGNOSTIC_POSITIONS +/*! +@brief set the diagnostic positions of a value the DOM SAX parsers just stored + +Shared by json_sax_dom_parser and json_sax_dom_callback_parser. basic_json +befriends this struct, as the position members are private. +*/ +struct diagnostic_positions +{ + /*! + @param[in,out] v the value that was just parsed + @param[in] lexer the lexer that read it, or nullptr to leave @a v alone + */ + template + static void set_from_lexer(BasicJsonType& v, LexerType* lexer) + { + if (lexer) + { + // Lexer has read past the current field value, so set the end position to the current position. + // The start position will be set below based on the length of the string representation + // of the value. + v.end_position = lexer->get_position(); + + switch (v.type()) + { + case value_t::boolean: + { + // 4 and 5 are the string length of "true" and "false" + v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); + break; + } + + case value_t::null: + { + // 4 is the string length of "null" + v.start_position = v.end_position - 4; + break; + } + + case value_t::string: + { + // escape sequences make the token longer than the value it + // parses to, so the start position cannot be derived from + // the value; use the offset the lexer recorded instead + v.start_position = lexer->get_token_start_position(); + break; + } + + case value_t::discarded: + { + // an object or array the callback of + // json_sax_dom_callback_parser rejected has no position + v.end_position = std::string::npos; + v.start_position = v.end_position; + break; + } + + case value_t::binary: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + { + v.start_position = v.end_position - lexer->get_string().size(); + break; + } + + case value_t::object: + case value_t::array: + { + // object and array are handled in start_object() and start_array() handlers + // skip setting the values here. + break; + } + default: // LCOV_EXCL_LINE + // Handle all possible types discretely, default handler should never be reached. + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + } +}; +#endif + /*! @brief SAX implementation to create a JSON value from SAX events @@ -376,76 +458,6 @@ class json_sax_dom_parser private: -#if JSON_DIAGNOSTIC_POSITIONS - void handle_diagnostic_positions_for_json_value(BasicJsonType& v) - { - if (m_lexer_ref) - { - // Lexer has read past the current field value, so set the end position to the current position. - // The start position will be set below based on the length of the string representation - // of the value. - v.end_position = m_lexer_ref->get_position(); - - switch (v.type()) - { - case value_t::boolean: - { - // 4 and 5 are the string length of "true" and "false" - v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); - break; - } - - case value_t::null: - { - // 4 is the string length of "null" - v.start_position = v.end_position - 4; - break; - } - - case value_t::string: - { - // escape sequences make the token longer than the value it - // parses to, so the start position cannot be derived from - // the value; use the offset the lexer recorded instead - v.start_position = m_lexer_ref->get_token_start_position(); - break; - } - - // As we handle the start and end positions for values created during parsing, - // we do not expect the following value type to be called. Regardless, set the positions - // in case this is created manually or through a different constructor. Exclude from lcov - // since the exact condition of this switch is esoteric. - // LCOV_EXCL_START - case value_t::discarded: - { - v.end_position = std::string::npos; - v.start_position = v.end_position; - break; - } - // LCOV_EXCL_STOP - case value_t::binary: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - { - v.start_position = v.end_position - m_lexer_ref->get_string().size(); - break; - } - case value_t::object: - case value_t::array: - { - // object and array are handled in start_object() and start_array() handlers - // skip setting the values here. - break; - } - default: // LCOV_EXCL_LINE - // Handle all possible types discretely, default handler should never be reached. - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE - } - } - } -#endif - /*! @invariant If the ref stack is empty, then the passed value will be the new root. @@ -461,7 +473,7 @@ class json_sax_dom_parser root = BasicJsonType(std::forward(v)); #if JSON_DIAGNOSTIC_POSITIONS - handle_diagnostic_positions_for_json_value(root); + diagnostic_positions::set_from_lexer(root, m_lexer_ref); #endif return &root; @@ -474,7 +486,7 @@ class json_sax_dom_parser ref_stack.back()->m_data.m_value.array->emplace_back(std::forward(v)); #if JSON_DIAGNOSTIC_POSITIONS - handle_diagnostic_positions_for_json_value(ref_stack.back()->m_data.m_value.array->back()); + diagnostic_positions::set_from_lexer(ref_stack.back()->m_data.m_value.array->back(), m_lexer_ref); #endif return &(ref_stack.back()->m_data.m_value.array->back()); @@ -485,7 +497,7 @@ class json_sax_dom_parser *object_element = BasicJsonType(std::forward(v)); #if JSON_DIAGNOSTIC_POSITIONS - handle_diagnostic_positions_for_json_value(*object_element); + diagnostic_positions::set_from_lexer(*object_element, m_lexer_ref); #endif return object_element; @@ -662,7 +674,7 @@ class json_sax_dom_callback_parser #if JSON_DIAGNOSTIC_POSITIONS // Set start/end positions for discarded object. - handle_diagnostic_positions_for_json_value(*ref_stack.back()); + diagnostic_positions::set_from_lexer(*ref_stack.back(), m_lexer_ref); #endif } } @@ -778,7 +790,7 @@ class json_sax_dom_callback_parser #if JSON_DIAGNOSTIC_POSITIONS // Set start/end positions for discarded array. - handle_diagnostic_positions_for_json_value(*ref_stack.back()); + diagnostic_positions::set_from_lexer(*ref_stack.back(), m_lexer_ref); #endif } } @@ -831,72 +843,6 @@ class json_sax_dom_callback_parser private: -#if JSON_DIAGNOSTIC_POSITIONS - void handle_diagnostic_positions_for_json_value(BasicJsonType& v) - { - if (m_lexer_ref) - { - // Lexer has read past the current field value, so set the end position to the current position. - // The start position will be set below based on the length of the string representation - // of the value. - v.end_position = m_lexer_ref->get_position(); - - switch (v.type()) - { - case value_t::boolean: - { - // 4 and 5 are the string length of "true" and "false" - v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); - break; - } - - case value_t::null: - { - // 4 is the string length of "null" - v.start_position = v.end_position - 4; - break; - } - - case value_t::string: - { - // escape sequences make the token longer than the value it - // parses to, so the start position cannot be derived from - // the value; use the offset the lexer recorded instead - v.start_position = m_lexer_ref->get_token_start_position(); - break; - } - - case value_t::discarded: - { - v.end_position = std::string::npos; - v.start_position = v.end_position; - break; - } - - case value_t::binary: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - { - v.start_position = v.end_position - m_lexer_ref->get_string().size(); - break; - } - - case value_t::object: - case value_t::array: - { - // object and array are handled in start_object() and start_array() handlers - // skip setting the values here. - break; - } - default: // LCOV_EXCL_LINE - // Handle all possible types discretely, default handler should never be reached. - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE - } - } - } -#endif - /// if there is a pending duplicate-key stash entry for this exact slot, /// remove it from the stash; if restore_value is true, the stashed /// previous value is moved back into the slot first (use this when the @@ -1018,7 +964,7 @@ class json_sax_dom_callback_parser auto value = BasicJsonType(std::forward(v)); #if JSON_DIAGNOSTIC_POSITIONS - handle_diagnostic_positions_for_json_value(value); + diagnostic_positions::set_from_lexer(value, m_lexer_ref); #endif // check callback diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 500fcddf2..927035af4 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -149,6 +149,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec friend class ::nlohmann::detail::json_sax_dom_parser; template friend class ::nlohmann::detail::json_sax_dom_callback_parser; +#if JSON_DIAGNOSTIC_POSITIONS + friend struct ::nlohmann::detail::diagnostic_positions; +#endif friend class ::nlohmann::detail::exception; /// workaround type for MSVC diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 576498738..76925282a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -11589,6 +11589,88 @@ template inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/) {} +#if JSON_DIAGNOSTIC_POSITIONS +/*! +@brief set the diagnostic positions of a value the DOM SAX parsers just stored + +Shared by json_sax_dom_parser and json_sax_dom_callback_parser. basic_json +befriends this struct, as the position members are private. +*/ +struct diagnostic_positions +{ + /*! + @param[in,out] v the value that was just parsed + @param[in] lexer the lexer that read it, or nullptr to leave @a v alone + */ + template + static void set_from_lexer(BasicJsonType& v, LexerType* lexer) + { + if (lexer) + { + // Lexer has read past the current field value, so set the end position to the current position. + // The start position will be set below based on the length of the string representation + // of the value. + v.end_position = lexer->get_position(); + + switch (v.type()) + { + case value_t::boolean: + { + // 4 and 5 are the string length of "true" and "false" + v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); + break; + } + + case value_t::null: + { + // 4 is the string length of "null" + v.start_position = v.end_position - 4; + break; + } + + case value_t::string: + { + // escape sequences make the token longer than the value it + // parses to, so the start position cannot be derived from + // the value; use the offset the lexer recorded instead + v.start_position = lexer->get_token_start_position(); + break; + } + + case value_t::discarded: + { + // an object or array the callback of + // json_sax_dom_callback_parser rejected has no position + v.end_position = std::string::npos; + v.start_position = v.end_position; + break; + } + + case value_t::binary: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + { + v.start_position = v.end_position - lexer->get_string().size(); + break; + } + + case value_t::object: + case value_t::array: + { + // object and array are handled in start_object() and start_array() handlers + // skip setting the values here. + break; + } + default: // LCOV_EXCL_LINE + // Handle all possible types discretely, default handler should never be reached. + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + } +}; +#endif + /*! @brief SAX implementation to create a JSON value from SAX events @@ -11790,76 +11872,6 @@ class json_sax_dom_parser private: -#if JSON_DIAGNOSTIC_POSITIONS - void handle_diagnostic_positions_for_json_value(BasicJsonType& v) - { - if (m_lexer_ref) - { - // Lexer has read past the current field value, so set the end position to the current position. - // The start position will be set below based on the length of the string representation - // of the value. - v.end_position = m_lexer_ref->get_position(); - - switch (v.type()) - { - case value_t::boolean: - { - // 4 and 5 are the string length of "true" and "false" - v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); - break; - } - - case value_t::null: - { - // 4 is the string length of "null" - v.start_position = v.end_position - 4; - break; - } - - case value_t::string: - { - // escape sequences make the token longer than the value it - // parses to, so the start position cannot be derived from - // the value; use the offset the lexer recorded instead - v.start_position = m_lexer_ref->get_token_start_position(); - break; - } - - // As we handle the start and end positions for values created during parsing, - // we do not expect the following value type to be called. Regardless, set the positions - // in case this is created manually or through a different constructor. Exclude from lcov - // since the exact condition of this switch is esoteric. - // LCOV_EXCL_START - case value_t::discarded: - { - v.end_position = std::string::npos; - v.start_position = v.end_position; - break; - } - // LCOV_EXCL_STOP - case value_t::binary: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - { - v.start_position = v.end_position - m_lexer_ref->get_string().size(); - break; - } - case value_t::object: - case value_t::array: - { - // object and array are handled in start_object() and start_array() handlers - // skip setting the values here. - break; - } - default: // LCOV_EXCL_LINE - // Handle all possible types discretely, default handler should never be reached. - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE - } - } - } -#endif - /*! @invariant If the ref stack is empty, then the passed value will be the new root. @@ -11875,7 +11887,7 @@ class json_sax_dom_parser root = BasicJsonType(std::forward(v)); #if JSON_DIAGNOSTIC_POSITIONS - handle_diagnostic_positions_for_json_value(root); + diagnostic_positions::set_from_lexer(root, m_lexer_ref); #endif return &root; @@ -11888,7 +11900,7 @@ class json_sax_dom_parser ref_stack.back()->m_data.m_value.array->emplace_back(std::forward(v)); #if JSON_DIAGNOSTIC_POSITIONS - handle_diagnostic_positions_for_json_value(ref_stack.back()->m_data.m_value.array->back()); + diagnostic_positions::set_from_lexer(ref_stack.back()->m_data.m_value.array->back(), m_lexer_ref); #endif return &(ref_stack.back()->m_data.m_value.array->back()); @@ -11899,7 +11911,7 @@ class json_sax_dom_parser *object_element = BasicJsonType(std::forward(v)); #if JSON_DIAGNOSTIC_POSITIONS - handle_diagnostic_positions_for_json_value(*object_element); + diagnostic_positions::set_from_lexer(*object_element, m_lexer_ref); #endif return object_element; @@ -12076,7 +12088,7 @@ class json_sax_dom_callback_parser #if JSON_DIAGNOSTIC_POSITIONS // Set start/end positions for discarded object. - handle_diagnostic_positions_for_json_value(*ref_stack.back()); + diagnostic_positions::set_from_lexer(*ref_stack.back(), m_lexer_ref); #endif } } @@ -12192,7 +12204,7 @@ class json_sax_dom_callback_parser #if JSON_DIAGNOSTIC_POSITIONS // Set start/end positions for discarded array. - handle_diagnostic_positions_for_json_value(*ref_stack.back()); + diagnostic_positions::set_from_lexer(*ref_stack.back(), m_lexer_ref); #endif } } @@ -12245,72 +12257,6 @@ class json_sax_dom_callback_parser private: -#if JSON_DIAGNOSTIC_POSITIONS - void handle_diagnostic_positions_for_json_value(BasicJsonType& v) - { - if (m_lexer_ref) - { - // Lexer has read past the current field value, so set the end position to the current position. - // The start position will be set below based on the length of the string representation - // of the value. - v.end_position = m_lexer_ref->get_position(); - - switch (v.type()) - { - case value_t::boolean: - { - // 4 and 5 are the string length of "true" and "false" - v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5); - break; - } - - case value_t::null: - { - // 4 is the string length of "null" - v.start_position = v.end_position - 4; - break; - } - - case value_t::string: - { - // escape sequences make the token longer than the value it - // parses to, so the start position cannot be derived from - // the value; use the offset the lexer recorded instead - v.start_position = m_lexer_ref->get_token_start_position(); - break; - } - - case value_t::discarded: - { - v.end_position = std::string::npos; - v.start_position = v.end_position; - break; - } - - case value_t::binary: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - { - v.start_position = v.end_position - m_lexer_ref->get_string().size(); - break; - } - - case value_t::object: - case value_t::array: - { - // object and array are handled in start_object() and start_array() handlers - // skip setting the values here. - break; - } - default: // LCOV_EXCL_LINE - // Handle all possible types discretely, default handler should never be reached. - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE - } - } - } -#endif - /// if there is a pending duplicate-key stash entry for this exact slot, /// remove it from the stash; if restore_value is true, the stashed /// previous value is moved back into the slot first (use this when the @@ -12432,7 +12378,7 @@ class json_sax_dom_callback_parser auto value = BasicJsonType(std::forward(v)); #if JSON_DIAGNOSTIC_POSITIONS - handle_diagnostic_positions_for_json_value(value); + diagnostic_positions::set_from_lexer(value, m_lexer_ref); #endif // check callback @@ -26230,6 +26176,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec friend class ::nlohmann::detail::json_sax_dom_parser; template friend class ::nlohmann::detail::json_sax_dom_callback_parser; +#if JSON_DIAGNOSTIC_POSITIONS + friend struct ::nlohmann::detail::diagnostic_positions; +#endif friend class ::nlohmann::detail::exception; /// workaround type for MSVC