From a4d491e22d9d8e67aa47b7cf8011097d4ff673d9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 1 Jan 2021 17:23:10 +0100 Subject: [PATCH 01/38] :construction: better diagnostics --- include/nlohmann/json.hpp | 112 ++++++++++++++++++++++++++++++- single_include/nlohmann/json.hpp | 112 ++++++++++++++++++++++++++++++- 2 files changed, 222 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 8c9bef03df..a827b037b6 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1642,6 +1642,9 @@ class basic_json std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) { auto element = element_ref.moved_or_copied(); +#ifdef JSON_DIAGNOSTICS + (*element.m_value.array)[1].m_parent = this; +#endif m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); @@ -1652,6 +1655,12 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); +#ifdef JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif } assert_invariant(); @@ -2696,6 +2705,49 @@ class basic_json /// @} private: +#ifdef JSON_DIAGNOSTICS + std::string diagnostics() + { + std::string result; + for (basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (current->m_parent->m_value.array->operator[](i) == *current) + { + result = "/" + std::to_string(i) + result; + continue; + } + } + break; + } + + case value_t::object: + { + for (auto it : *current->m_parent->m_value.object) + { + if (it.second == *current) + { + result = "/" + it.first + result; + continue; + } + } + break; + } + + default: + break; + } + } + + return result; + } +#endif + ////////////////// // value access // ////////////////// @@ -3318,7 +3370,13 @@ class basic_json { JSON_TRY { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->at(idx); + result.m_parent = this; + return result; +#else return m_value.array->at(idx); +#endif } JSON_CATCH (std::out_of_range&) { @@ -3416,7 +3474,13 @@ class basic_json { JSON_TRY { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->at(key); + result.m_parent = this; + return result; +#else return m_value.object->at(key); +#endif } JSON_CATCH (std::out_of_range&) { @@ -3525,9 +3589,18 @@ class basic_json m_value.array->insert(m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif } +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->operator[](idx); + result.m_parent = this; + return result; +#else return m_value.array->operator[](idx); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); @@ -3603,7 +3676,13 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->operator[](key); + result.m_parent = this; + return result; +#else return m_value.object->operator[](key); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); @@ -3693,7 +3772,13 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->operator[](key); + result.m_parent = this; + return result; +#else return m_value.object->operator[](key); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); @@ -5249,6 +5334,9 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -5284,6 +5372,9 @@ class basic_json // add element to array m_value.array->push_back(val); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif } /*! @@ -5332,8 +5423,13 @@ class basic_json assert_invariant(); } - // add element to array + // add element to object +#ifdef JSON_DIAGNOSTICS + auto res = m_value.object->insert(val); + res.first->second.m_parent = this; +#else m_value.object->insert(val); +#endif } /*! @@ -5437,9 +5533,18 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->emplace_back(std::forward(args)...); + result.m_parent = this; + return result; +#else return m_value.array->emplace_back(std::forward(args)...); +#endif #else m_value.array->emplace_back(std::forward(args)...); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif return m_value.array->back(); #endif } @@ -6967,6 +7072,11 @@ class basic_json /// the value of the current element json_value m_value = {}; +#ifdef JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8b6344f921..f27a63ff21 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18266,6 +18266,9 @@ class basic_json std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) { auto element = element_ref.moved_or_copied(); +#ifdef JSON_DIAGNOSTICS + (*element.m_value.array)[1].m_parent = this; +#endif m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); @@ -18276,6 +18279,12 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); +#ifdef JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif } assert_invariant(); @@ -19320,6 +19329,49 @@ class basic_json /// @} private: +#ifdef JSON_DIAGNOSTICS + std::string diagnostics() + { + std::string result; + for (basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (current->m_parent->m_value.array->operator[](i) == *current) + { + result = "/" + std::to_string(i) + result; + continue; + } + } + break; + } + + case value_t::object: + { + for (auto it : *current->m_parent->m_value.object) + { + if (it.second == *current) + { + result = "/" + it.first + result; + continue; + } + } + break; + } + + default: + break; + } + } + + return result; + } +#endif + ////////////////// // value access // ////////////////// @@ -19942,7 +19994,13 @@ class basic_json { JSON_TRY { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->at(idx); + result.m_parent = this; + return result; +#else return m_value.array->at(idx); +#endif } JSON_CATCH (std::out_of_range&) { @@ -20040,7 +20098,13 @@ class basic_json { JSON_TRY { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->at(key); + result.m_parent = this; + return result; +#else return m_value.object->at(key); +#endif } JSON_CATCH (std::out_of_range&) { @@ -20149,9 +20213,18 @@ class basic_json m_value.array->insert(m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif } +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->operator[](idx); + result.m_parent = this; + return result; +#else return m_value.array->operator[](idx); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); @@ -20227,7 +20300,13 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->operator[](key); + result.m_parent = this; + return result; +#else return m_value.object->operator[](key); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); @@ -20317,7 +20396,13 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { +#ifdef JSON_DIAGNOSTICS + reference result = m_value.object->operator[](key); + result.m_parent = this; + return result; +#else return m_value.object->operator[](key); +#endif } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); @@ -21873,6 +21958,9 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -21908,6 +21996,9 @@ class basic_json // add element to array m_value.array->push_back(val); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif } /*! @@ -21956,8 +22047,13 @@ class basic_json assert_invariant(); } - // add element to array + // add element to object +#ifdef JSON_DIAGNOSTICS + auto res = m_value.object->insert(val); + res.first->second.m_parent = this; +#else m_value.object->insert(val); +#endif } /*! @@ -22061,9 +22157,18 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 +#ifdef JSON_DIAGNOSTICS + reference result = m_value.array->emplace_back(std::forward(args)...); + result.m_parent = this; + return result; +#else return m_value.array->emplace_back(std::forward(args)...); +#endif #else m_value.array->emplace_back(std::forward(args)...); +#ifdef JSON_DIAGNOSTICS + m_value.array->back().m_parent = this; +#endif return m_value.array->back(); #endif } @@ -23591,6 +23696,11 @@ class basic_json /// the value of the current element json_value m_value = {}; +#ifdef JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// From 7b047861b074b8e54d4aed1278a3fd7b3e10b30d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 2 Jan 2021 13:44:41 +0100 Subject: [PATCH 02/38] :construction: add diagnostics to exceptions --- .../nlohmann/detail/conversions/from_json.hpp | 30 +++--- include/nlohmann/detail/input/json_sax.hpp | 8 +- .../nlohmann/detail/iterators/iter_impl.hpp | 24 ++--- include/nlohmann/json.hpp | 36 ++++--- single_include/nlohmann/json.hpp | 98 +++++++++++-------- 5 files changed, 112 insertions(+), 84 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 438b84a2e1..0feea28330 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -27,7 +27,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { - JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be null, but is " + std::string(j.type_name()))); } n = nullptr; } @@ -58,7 +58,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name()))); } } @@ -67,7 +67,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be boolean, but is " + std::string(j.type_name()))); } b = *j.template get_ptr(); } @@ -77,7 +77,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name()))); } s = *j.template get_ptr(); } @@ -93,7 +93,7 @@ void from_json(const BasicJsonType& j, ConstructibleStringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name()))); } s = *j.template get_ptr(); @@ -133,7 +133,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); } l.clear(); std::transform(j.rbegin(), j.rend(), @@ -150,7 +150,7 @@ void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); } l.resize(j.size()); std::transform(j.begin(), j.end(), std::begin(l), @@ -241,7 +241,7 @@ void()) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); } @@ -253,7 +253,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be binary, but is " + std::string(j.type_name()))); } bin = *j.template get_ptr(); @@ -265,7 +265,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be object, but is " + std::string(j.type_name()))); } ConstructibleObjectType ret; @@ -319,7 +319,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name()))); } } @@ -348,14 +348,14 @@ void from_json(const BasicJsonType& j, std::map& { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name()))); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -368,14 +368,14 @@ void from_json(const BasicJsonType& j, std::unordered_map(), p.at(1).template get()); } diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 223acd60eb..316f517234 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -219,7 +219,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, + JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive object size: " + std::to_string(len))); } @@ -245,7 +245,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, + JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive array size: " + std::to_string(len))); } @@ -400,7 +400,7 @@ class json_sax_dom_callback_parser // check object limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive object size: " + std::to_string(len))); } return true; @@ -463,7 +463,7 @@ class json_sax_dom_callback_parser // check array limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive array size: " + std::to_string(len))); } return true; diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index 67134166e5..cceb8d05fc 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -257,7 +257,7 @@ class iter_impl } case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); default: { @@ -266,7 +266,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); } } } @@ -300,7 +300,7 @@ class iter_impl return m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); } } } @@ -401,7 +401,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers")); } JSON_ASSERT(m_object != nullptr); @@ -438,7 +438,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers")); } JSON_ASSERT(m_object != nullptr); @@ -446,7 +446,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + JSON_THROW(invalid_iterator::create(213, m_object->diagnostics() + "cannot compare order of object iterators")); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); @@ -494,7 +494,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators")); case value_t::array: { @@ -565,7 +565,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators")); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; @@ -586,13 +586,13 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator::create(208, m_object->diagnostics() + "cannot use operator[] for object iterators")); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); default: { @@ -601,7 +601,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); } } } @@ -619,7 +619,7 @@ class iter_impl return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator::create(207, m_object->diagnostics() + "cannot use key() for non-object iterators")); } /*! diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index a827b037b6..be3b1a3e17 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2155,6 +2155,9 @@ class basic_json basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) +#ifdef JSON_DIAGNOSTICS + , m_parent(other.m_parent) +#endif { // check that passed value is valid other.assert_invariant(); @@ -2704,12 +2707,11 @@ class basic_json /// @} - private: -#ifdef JSON_DIAGNOSTICS - std::string diagnostics() + std::string diagnostics() const { - std::string result; - for (basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) +#ifdef JSON_DIAGNOSTICS + std::vector tokens; + for (const basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) { switch (current->m_parent->type()) { @@ -2719,7 +2721,7 @@ class basic_json { if (current->m_parent->m_value.array->operator[](i) == *current) { - result = "/" + std::to_string(i) + result; + tokens.emplace_back(std::to_string(i)); continue; } } @@ -2728,11 +2730,11 @@ class basic_json case value_t::object: { - for (auto it : *current->m_parent->m_value.object) + for (const auto& element : *current->m_parent->m_value.object) { - if (it.second == *current) + if (element.second == *current) { - result = "/" + it.first + result; + tokens.emplace_back(element.first.c_str()); continue; } } @@ -2744,10 +2746,22 @@ class basic_json } } - return result; - } + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.begin(), tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + b; + }) + ") "; +#else + return ""; #endif + } + private: ////////////////// // value access // ////////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f27a63ff21..ee90b5fd18 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3513,7 +3513,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { - JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be null, but is " + std::string(j.type_name()))); } n = nullptr; } @@ -3544,7 +3544,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name()))); } } @@ -3553,7 +3553,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be boolean, but is " + std::string(j.type_name()))); } b = *j.template get_ptr(); } @@ -3563,7 +3563,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name()))); } s = *j.template get_ptr(); } @@ -3579,7 +3579,7 @@ void from_json(const BasicJsonType& j, ConstructibleStringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name()))); } s = *j.template get_ptr(); @@ -3619,7 +3619,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); } l.clear(); std::transform(j.rbegin(), j.rend(), @@ -3636,7 +3636,7 @@ void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); } l.resize(j.size()); std::transform(j.begin(), j.end(), std::begin(l), @@ -3727,7 +3727,7 @@ void()) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); } @@ -3739,7 +3739,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be binary, but is " + std::string(j.type_name()))); } bin = *j.template get_ptr(); @@ -3751,7 +3751,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be object, but is " + std::string(j.type_name()))); } ConstructibleObjectType ret; @@ -3805,7 +3805,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name()))); } } @@ -3834,14 +3834,14 @@ void from_json(const BasicJsonType& j, std::map& { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name()))); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -3854,14 +3854,14 @@ void from_json(const BasicJsonType& j, std::unordered_map(), p.at(1).template get()); } @@ -5511,7 +5511,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, + JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive object size: " + std::to_string(len))); } @@ -5537,7 +5537,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, + JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive array size: " + std::to_string(len))); } @@ -5692,7 +5692,7 @@ class json_sax_dom_callback_parser // check object limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive object size: " + std::to_string(len))); } return true; @@ -5755,7 +5755,7 @@ class json_sax_dom_callback_parser // check array limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive array size: " + std::to_string(len))); } return true; @@ -11144,7 +11144,7 @@ class iter_impl } case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); default: { @@ -11153,7 +11153,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); } } } @@ -11187,7 +11187,7 @@ class iter_impl return m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); } } } @@ -11288,7 +11288,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers")); } JSON_ASSERT(m_object != nullptr); @@ -11325,7 +11325,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers")); } JSON_ASSERT(m_object != nullptr); @@ -11333,7 +11333,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + JSON_THROW(invalid_iterator::create(213, m_object->diagnostics() + "cannot compare order of object iterators")); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); @@ -11381,7 +11381,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators")); case value_t::array: { @@ -11452,7 +11452,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators")); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; @@ -11473,13 +11473,13 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator::create(208, m_object->diagnostics() + "cannot use operator[] for object iterators")); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); default: { @@ -11488,7 +11488,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); } } } @@ -11506,7 +11506,7 @@ class iter_impl return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator::create(207, m_object->diagnostics() + "cannot use key() for non-object iterators")); } /*! @@ -18779,6 +18779,9 @@ class basic_json basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) +#ifdef JSON_DIAGNOSTICS + , m_parent(other.m_parent) +#endif { // check that passed value is valid other.assert_invariant(); @@ -19328,12 +19331,11 @@ class basic_json /// @} - private: -#ifdef JSON_DIAGNOSTICS - std::string diagnostics() + std::string diagnostics() const { - std::string result; - for (basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) +#ifdef JSON_DIAGNOSTICS + std::vector tokens; + for (const basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) { switch (current->m_parent->type()) { @@ -19343,7 +19345,7 @@ class basic_json { if (current->m_parent->m_value.array->operator[](i) == *current) { - result = "/" + std::to_string(i) + result; + tokens.emplace_back(std::to_string(i)); continue; } } @@ -19352,11 +19354,11 @@ class basic_json case value_t::object: { - for (auto it : *current->m_parent->m_value.object) + for (const auto& element : *current->m_parent->m_value.object) { - if (it.second == *current) + if (element.second == *current) { - result = "/" + it.first + result; + tokens.emplace_back(element.first.c_str()); continue; } } @@ -19368,10 +19370,22 @@ class basic_json } } - return result; - } + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.begin(), tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + b; + }) + ") "; +#else + return ""; #endif + } + private: ////////////////// // value access // ////////////////// From ecaab32ef05b440eb991b5526fb0b3bf9e521111 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 2 Jan 2021 13:45:00 +0100 Subject: [PATCH 03/38] :construction: add switch for diagnostics --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 44ede3e799..abd3a17c7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${M option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT}) option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF) option(JSON_ImplicitConversions "Enable implicit conversions." ON) +option(JSON_Diagnostics "Enable better diagnostic messages." OFF) ## ## CONFIGURATION @@ -79,6 +80,7 @@ target_compile_definitions( ${NLOHMANN_JSON_TARGET_NAME} INTERFACE JSON_USE_IMPLICIT_CONVERSIONS=$ + JSON_DIAGNOSTICS=$ ) target_include_directories( From c6e7fa21eddf614d530b1dba9acb81855f5d74d5 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 2 Jan 2021 13:58:05 +0100 Subject: [PATCH 04/38] :construction: fix preprocessor check --- include/nlohmann/json.hpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index be3b1a3e17..525939b6f4 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1642,7 +1642,7 @@ class basic_json std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) { auto element = element_ref.moved_or_copied(); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS (*element.m_value.array)[1].m_parent = this; #endif m_value.object->emplace( @@ -1655,7 +1655,7 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS for (auto& element : *m_value.array) { element.m_parent = this; @@ -2155,7 +2155,7 @@ class basic_json basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS , m_parent(other.m_parent) #endif { @@ -2709,7 +2709,7 @@ class basic_json std::string diagnostics() const { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS std::vector tokens; for (const basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) { @@ -3384,7 +3384,7 @@ class basic_json { JSON_TRY { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.array->at(idx); result.m_parent = this; return result; @@ -3488,7 +3488,7 @@ class basic_json { JSON_TRY { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.object->at(key); result.m_parent = this; return result; @@ -3603,12 +3603,12 @@ class basic_json m_value.array->insert(m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS m_value.array->back().m_parent = this; #endif } -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.array->operator[](idx); result.m_parent = this; return result; @@ -3690,7 +3690,7 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.object->operator[](key); result.m_parent = this; return result; @@ -3786,7 +3786,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.object->operator[](key); result.m_parent = this; return result; @@ -5348,7 +5348,7 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS m_value.array->back().m_parent = this; #endif // if val is moved from, basic_json move constructor marks it null so we do not call the destructor @@ -5386,7 +5386,7 @@ class basic_json // add element to array m_value.array->push_back(val); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS m_value.array->back().m_parent = this; #endif } @@ -5438,7 +5438,7 @@ class basic_json } // add element to object -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS auto res = m_value.object->insert(val); res.first->second.m_parent = this; #else @@ -5547,7 +5547,7 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.array->emplace_back(std::forward(args)...); result.m_parent = this; return result; @@ -5556,7 +5556,7 @@ class basic_json #endif #else m_value.array->emplace_back(std::forward(args)...); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS m_value.array->back().m_parent = this; #endif return m_value.array->back(); @@ -7086,7 +7086,7 @@ class basic_json /// the value of the current element json_value m_value = {}; -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS /// a pointer to a parent value (for debugging purposes) basic_json* m_parent = nullptr; #endif From 09cd4ed125c76f0214d66507f33fe4daa447ca6c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 2 Jan 2021 14:10:40 +0100 Subject: [PATCH 05/38] :construction: fix preprocessor check --- single_include/nlohmann/json.hpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ee90b5fd18..f97a95849a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18266,7 +18266,7 @@ class basic_json std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) { auto element = element_ref.moved_or_copied(); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS (*element.m_value.array)[1].m_parent = this; #endif m_value.object->emplace( @@ -18279,7 +18279,7 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS for (auto& element : *m_value.array) { element.m_parent = this; @@ -18779,7 +18779,7 @@ class basic_json basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS , m_parent(other.m_parent) #endif { @@ -19333,7 +19333,7 @@ class basic_json std::string diagnostics() const { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS std::vector tokens; for (const basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) { @@ -20008,7 +20008,7 @@ class basic_json { JSON_TRY { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.array->at(idx); result.m_parent = this; return result; @@ -20112,7 +20112,7 @@ class basic_json { JSON_TRY { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.object->at(key); result.m_parent = this; return result; @@ -20227,12 +20227,12 @@ class basic_json m_value.array->insert(m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS m_value.array->back().m_parent = this; #endif } -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.array->operator[](idx); result.m_parent = this; return result; @@ -20314,7 +20314,7 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.object->operator[](key); result.m_parent = this; return result; @@ -20410,7 +20410,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.object->operator[](key); result.m_parent = this; return result; @@ -21972,7 +21972,7 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS m_value.array->back().m_parent = this; #endif // if val is moved from, basic_json move constructor marks it null so we do not call the destructor @@ -22010,7 +22010,7 @@ class basic_json // add element to array m_value.array->push_back(val); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS m_value.array->back().m_parent = this; #endif } @@ -22062,7 +22062,7 @@ class basic_json } // add element to object -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS auto res = m_value.object->insert(val); res.first->second.m_parent = this; #else @@ -22171,7 +22171,7 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS reference result = m_value.array->emplace_back(std::forward(args)...); result.m_parent = this; return result; @@ -22180,7 +22180,7 @@ class basic_json #endif #else m_value.array->emplace_back(std::forward(args)...); -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS m_value.array->back().m_parent = this; #endif return m_value.array->back(); @@ -23710,7 +23710,7 @@ class basic_json /// the value of the current element json_value m_value = {}; -#ifdef JSON_DIAGNOSTICS +#if JSON_DIAGNOSTICS /// a pointer to a parent value (for debugging purposes) basic_json* m_parent = nullptr; #endif From 7323a8eb4e7405406e79c7e14fc9cf34c3a496fe Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 2 Jan 2021 16:13:04 +0100 Subject: [PATCH 06/38] :construction: add tests --- .../nlohmann/detail/output/binary_writer.hpp | 22 +-- include/nlohmann/json.hpp | 140 +++++++-------- single_include/nlohmann/json.hpp | 162 +++++++++--------- test/src/unit-bson.cpp | 8 + test/src/unit-iterators2.cpp | 88 ++++++++++ test/src/unit-regression1.cpp | 4 + 6 files changed, 262 insertions(+), 162 deletions(-) diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 0c6185e048..72ebbeda68 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -57,7 +57,7 @@ class binary_writer default: { - JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(317, j.diagnostics() + "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); } } } @@ -901,12 +901,12 @@ class binary_writer @return The size of a BSON document entry header, including the id marker and the entry name size (and its null-terminator). */ - static std::size_t calc_bson_entry_header_size(const string_t& name) + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) { const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, + JSON_THROW(out_of_range::create(409, j.diagnostics() + "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")")); } @@ -1017,21 +1017,21 @@ class binary_writer @brief Writes a BSON element with key @a name and unsigned @a value */ void write_bson_unsigned(const string_t& name, - const std::uint64_t value) + const BasicJsonType& j) { - if (value <= static_cast((std::numeric_limits::max)())) + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x10 /* int32 */); - write_number(static_cast(value)); + write_number(static_cast(j.m_value.number_unsigned)); } - else if (value <= static_cast((std::numeric_limits::max)())) + else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x12 /* int64 */); - write_number(static_cast(value)); + write_number(static_cast(j.m_value.number_unsigned)); } else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64")); + JSON_THROW(out_of_range::create(407, j.diagnostics() + "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64")); } } @@ -1108,7 +1108,7 @@ class binary_writer static std::size_t calc_bson_element_size(const string_t& name, const BasicJsonType& j) { - const auto header_size = calc_bson_entry_header_size(name); + const auto header_size = calc_bson_entry_header_size(name, j); switch (j.type()) { case value_t::object: @@ -1177,7 +1177,7 @@ class binary_writer return write_bson_integer(name, j.m_value.number_integer); case value_t::number_unsigned: - return write_bson_unsigned(name, j.m_value.number_unsigned); + return write_bson_unsigned(name, j); case value_t::string: return write_bson_string(name, *j.m_value.string); diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 525939b6f4..80bc871a2f 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1941,7 +1941,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + JSON_THROW(invalid_iterator::create(201, diagnostics() + "iterators are not compatible")); } // copy type from first iterator @@ -2774,7 +2774,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, diagnostics() + "type must be boolean, but is " + std::string(type_name()))); } /// get a pointer to the value (object) @@ -2895,7 +2895,7 @@ class basic_json return *ptr; } - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + JSON_THROW(type_error::create(303, obj.diagnostics() + "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); } public: @@ -3323,7 +3323,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, diagnostics() + "type must be binary, but is " + std::string(type_name()))); } return *get_ptr(); @@ -3334,7 +3334,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, diagnostics() + "type must be binary, but is " + std::string(type_name()))); } return *get_ptr(); @@ -3395,12 +3395,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); } } @@ -3442,12 +3442,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); } } @@ -3499,12 +3499,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + key + "' not found")); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); } } @@ -3550,12 +3550,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + key + "' not found")); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); } } @@ -3617,7 +3617,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); } /*! @@ -3647,7 +3647,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); } /*! @@ -3699,7 +3699,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); } /*! @@ -3741,7 +3741,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); } /*! @@ -3795,7 +3795,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); } /*! @@ -3839,7 +3839,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); } /*! @@ -3911,7 +3911,7 @@ class basic_json return default_value; } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, diagnostics() + "cannot use value() with " + std::string(type_name()))); } /*! @@ -3984,7 +3984,7 @@ class basic_json } } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, diagnostics() + "cannot use value() with " + std::string(type_name()))); } /*! @@ -4138,7 +4138,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } IteratorType result = end(); @@ -4190,7 +4190,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); } return result; @@ -4251,7 +4251,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + JSON_THROW(invalid_iterator::create(203, diagnostics() + "iterators do not fit current value")); } IteratorType result = end(); @@ -4268,7 +4268,7 @@ class basic_json if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, diagnostics() + "iterators out of range")); } if (is_string()) @@ -4306,7 +4306,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); } return result; @@ -4349,7 +4349,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); } /*! @@ -4383,14 +4383,14 @@ class basic_json { if (JSON_HEDLEY_UNLIKELY(idx >= size())) { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); } } @@ -5335,7 +5335,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an array @@ -5373,7 +5373,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an array @@ -5426,7 +5426,7 @@ class basic_json // push_back only works for null objects or objects if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an object @@ -5534,7 +5534,7 @@ class basic_json // emplace_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, diagnostics() + "cannot use emplace_back() with " + std::string(type_name()))); } // transform null object into an array @@ -5596,7 +5596,7 @@ class basic_json // emplace only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, diagnostics() + "cannot use emplace() with " + std::string(type_name()))); } // transform null object into an object @@ -5667,14 +5667,14 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } // insert to array and return iterator return insert_iterator(pos, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } /*! @@ -5718,14 +5718,14 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } // insert to array and return iterator return insert_iterator(pos, cnt, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } /*! @@ -5763,24 +5763,24 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); } if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + JSON_THROW(invalid_iterator::create(211, diagnostics() + "passed iterators may not belong to container")); } // insert to array and return iterator @@ -5816,13 +5816,13 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } // insert to array and return iterator @@ -5857,19 +5857,19 @@ class basic_json // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterators first and last must point to objects")); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); @@ -5906,11 +5906,11 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(type_name()))); } if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); + JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(j.type_name()))); } for (auto it = j.cbegin(); it != j.cend(); ++it) @@ -5957,20 +5957,20 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(type_name()))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() || !last.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterators first and last must point to objects")); } for (auto it = first; it != last; ++it) @@ -6065,7 +6065,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -6098,7 +6098,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -6131,7 +6131,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -6164,7 +6164,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -6178,7 +6178,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -8343,7 +8343,7 @@ class basic_json }; // wrapper for "add" operation; add value at ptr - const auto operation_add = [&result](json_pointer & ptr, basic_json val) + const auto operation_add = [this, &result](json_pointer & ptr, basic_json val) { // adding to the root of the target document means replacing it if (ptr.empty()) @@ -8387,7 +8387,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } // default case: insert add offset @@ -8403,7 +8403,7 @@ class basic_json }; // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [&result](json_pointer & ptr) + const auto operation_remove = [this, &result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); @@ -8421,7 +8421,7 @@ class basic_json } else { - JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); + JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + last_path + "' not found")); } } else if (parent.is_array()) @@ -8434,16 +8434,16 @@ class basic_json // type check: top level value must be an array if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, diagnostics() + "JSON patch must be an array of objects")); } // iterate and apply the operations for (const auto& val : json_patch) { // wrapper to get a value for an operation - const auto get_value = [&val](const std::string & op, - const std::string & member, - bool string_type) -> basic_json & + const auto get_value = [this, &val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & { // find value auto it = val.m_value.object->find(member); @@ -8454,13 +8454,13 @@ class basic_json // check if desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, diagnostics() + error_msg + " must have member '" + member + "'")); } // check if result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, diagnostics() + error_msg + " must have string member '" + member + "'")); } // no error: return value @@ -8470,7 +8470,7 @@ class basic_json // type check: every element of the array must be an object if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, diagnostics() + "JSON patch must be an array of objects")); } // collect mandatory members @@ -8548,7 +8548,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, diagnostics() + "unsuccessful: " + val.dump())); } break; @@ -8558,7 +8558,7 @@ class basic_json { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); + JSON_THROW(parse_error::create(105, 0, diagnostics() + "operation value '" + op + "' is invalid")); } } } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f97a95849a..16a6049de5 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12900,7 +12900,7 @@ class binary_writer default: { - JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(317, j.diagnostics() + "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); } } } @@ -13744,12 +13744,12 @@ class binary_writer @return The size of a BSON document entry header, including the id marker and the entry name size (and its null-terminator). */ - static std::size_t calc_bson_entry_header_size(const string_t& name) + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) { const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, + JSON_THROW(out_of_range::create(409, j.diagnostics() + "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")")); } @@ -13860,21 +13860,21 @@ class binary_writer @brief Writes a BSON element with key @a name and unsigned @a value */ void write_bson_unsigned(const string_t& name, - const std::uint64_t value) + const BasicJsonType& j) { - if (value <= static_cast((std::numeric_limits::max)())) + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x10 /* int32 */); - write_number(static_cast(value)); + write_number(static_cast(j.m_value.number_unsigned)); } - else if (value <= static_cast((std::numeric_limits::max)())) + else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x12 /* int64 */); - write_number(static_cast(value)); + write_number(static_cast(j.m_value.number_unsigned)); } else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64")); + JSON_THROW(out_of_range::create(407, j.diagnostics() + "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64")); } } @@ -13951,7 +13951,7 @@ class binary_writer static std::size_t calc_bson_element_size(const string_t& name, const BasicJsonType& j) { - const auto header_size = calc_bson_entry_header_size(name); + const auto header_size = calc_bson_entry_header_size(name, j); switch (j.type()) { case value_t::object: @@ -14020,7 +14020,7 @@ class binary_writer return write_bson_integer(name, j.m_value.number_integer); case value_t::number_unsigned: - return write_bson_unsigned(name, j.m_value.number_unsigned); + return write_bson_unsigned(name, j); case value_t::string: return write_bson_string(name, *j.m_value.string); @@ -18565,7 +18565,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + JSON_THROW(invalid_iterator::create(201, diagnostics() + "iterators are not compatible")); } // copy type from first iterator @@ -19398,7 +19398,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, diagnostics() + "type must be boolean, but is " + std::string(type_name()))); } /// get a pointer to the value (object) @@ -19519,7 +19519,7 @@ class basic_json return *ptr; } - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + JSON_THROW(type_error::create(303, obj.diagnostics() + "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); } public: @@ -19947,7 +19947,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, diagnostics() + "type must be binary, but is " + std::string(type_name()))); } return *get_ptr(); @@ -19958,7 +19958,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, diagnostics() + "type must be binary, but is " + std::string(type_name()))); } return *get_ptr(); @@ -20019,12 +20019,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); } } @@ -20066,12 +20066,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); } } @@ -20123,12 +20123,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + key + "' not found")); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); } } @@ -20174,12 +20174,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + key + "' not found")); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); } } @@ -20241,7 +20241,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); } /*! @@ -20271,7 +20271,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); } /*! @@ -20323,7 +20323,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); } /*! @@ -20365,7 +20365,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); } /*! @@ -20419,7 +20419,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); } /*! @@ -20463,7 +20463,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); } /*! @@ -20535,7 +20535,7 @@ class basic_json return default_value; } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, diagnostics() + "cannot use value() with " + std::string(type_name()))); } /*! @@ -20608,7 +20608,7 @@ class basic_json } } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, diagnostics() + "cannot use value() with " + std::string(type_name()))); } /*! @@ -20762,7 +20762,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } IteratorType result = end(); @@ -20814,7 +20814,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); } return result; @@ -20875,7 +20875,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + JSON_THROW(invalid_iterator::create(203, diagnostics() + "iterators do not fit current value")); } IteratorType result = end(); @@ -20892,7 +20892,7 @@ class basic_json if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, diagnostics() + "iterators out of range")); } if (is_string()) @@ -20930,7 +20930,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); } return result; @@ -20973,7 +20973,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); } /*! @@ -21007,14 +21007,14 @@ class basic_json { if (JSON_HEDLEY_UNLIKELY(idx >= size())) { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); } } @@ -21959,7 +21959,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an array @@ -21997,7 +21997,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an array @@ -22050,7 +22050,7 @@ class basic_json // push_back only works for null objects or objects if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an object @@ -22158,7 +22158,7 @@ class basic_json // emplace_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, diagnostics() + "cannot use emplace_back() with " + std::string(type_name()))); } // transform null object into an array @@ -22220,7 +22220,7 @@ class basic_json // emplace only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, diagnostics() + "cannot use emplace() with " + std::string(type_name()))); } // transform null object into an object @@ -22291,14 +22291,14 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } // insert to array and return iterator return insert_iterator(pos, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } /*! @@ -22342,14 +22342,14 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } // insert to array and return iterator return insert_iterator(pos, cnt, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } /*! @@ -22387,24 +22387,24 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); } if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + JSON_THROW(invalid_iterator::create(211, diagnostics() + "passed iterators may not belong to container")); } // insert to array and return iterator @@ -22440,13 +22440,13 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); } // insert to array and return iterator @@ -22481,19 +22481,19 @@ class basic_json // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterators first and last must point to objects")); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); @@ -22530,11 +22530,11 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(type_name()))); } if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); + JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(j.type_name()))); } for (auto it = j.cbegin(); it != j.cend(); ++it) @@ -22581,20 +22581,20 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(type_name()))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() || !last.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterators first and last must point to objects")); } for (auto it = first; it != last; ++it) @@ -22689,7 +22689,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -22722,7 +22722,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -22755,7 +22755,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -22788,7 +22788,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -22802,7 +22802,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); } } @@ -24967,7 +24967,7 @@ class basic_json }; // wrapper for "add" operation; add value at ptr - const auto operation_add = [&result](json_pointer & ptr, basic_json val) + const auto operation_add = [this, &result](json_pointer & ptr, basic_json val) { // adding to the root of the target document means replacing it if (ptr.empty()) @@ -25011,7 +25011,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } // default case: insert add offset @@ -25027,7 +25027,7 @@ class basic_json }; // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [&result](json_pointer & ptr) + const auto operation_remove = [this, &result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); @@ -25045,7 +25045,7 @@ class basic_json } else { - JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); + JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + last_path + "' not found")); } } else if (parent.is_array()) @@ -25058,16 +25058,16 @@ class basic_json // type check: top level value must be an array if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, diagnostics() + "JSON patch must be an array of objects")); } // iterate and apply the operations for (const auto& val : json_patch) { // wrapper to get a value for an operation - const auto get_value = [&val](const std::string & op, - const std::string & member, - bool string_type) -> basic_json & + const auto get_value = [this, &val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & { // find value auto it = val.m_value.object->find(member); @@ -25078,13 +25078,13 @@ class basic_json // check if desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, diagnostics() + error_msg + " must have member '" + member + "'")); } // check if result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, diagnostics() + error_msg + " must have string member '" + member + "'")); } // no error: return value @@ -25094,7 +25094,7 @@ class basic_json // type check: every element of the array must be an object if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, diagnostics() + "JSON patch must be an array of objects")); } // collect mandatory members @@ -25172,7 +25172,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, diagnostics() + "unsuccessful: " + val.dump())); } break; @@ -25182,7 +25182,7 @@ class basic_json { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); + JSON_THROW(parse_error::create(105, 0, diagnostics() + "operation value '" + op + "' is invalid")); } } } diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index 3be72c7d47..ef3c8d4088 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -101,7 +101,11 @@ TEST_CASE("BSON") { std::string("en\0try", 6), true } }; CHECK_THROWS_AS(json::to_bson(j), json::out_of_range&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.out_of_range.409] (/en) BSON key cannot contain code point U+0000 (at byte 2)"); +#else CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.out_of_range.409] BSON key cannot contain code point U+0000 (at byte 2)"); +#endif } SECTION("string length must be at least 1") @@ -1235,7 +1239,11 @@ TEST_CASE("BSON numerical data") }; CHECK_THROWS_AS(json::to_bson(j), json::out_of_range&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_STD_STR(json::to_bson(j), "[json.exception.out_of_range.407] (/entry) integer number " + std::to_string(i) + " cannot be represented by BSON as it does not fit int64"); +#else CHECK_THROWS_WITH_STD_STR(json::to_bson(j), "[json.exception.out_of_range.407] integer number " + std::to_string(i) + " cannot be represented by BSON as it does not fit int64"); +#endif } } diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index 85eb7beb7c..b9dcc22090 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -90,6 +90,16 @@ TEST_CASE("iterators 2") CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator&); CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator&); CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); +#else CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); @@ -98,6 +108,7 @@ TEST_CASE("iterators 2") CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); +#endif } else { @@ -124,6 +135,16 @@ TEST_CASE("iterators 2") CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator&); CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator&); CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); +#else CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); @@ -132,6 +153,7 @@ TEST_CASE("iterators 2") CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); +#endif } else { @@ -159,6 +181,16 @@ TEST_CASE("iterators 2") CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator&); CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator&); CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); +#else CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); @@ -167,6 +199,7 @@ TEST_CASE("iterators 2") CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); +#endif } else { @@ -194,6 +227,16 @@ TEST_CASE("iterators 2") CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator&); CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator&); CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); +#else CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); @@ -202,6 +245,7 @@ TEST_CASE("iterators 2") CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); +#endif } else { @@ -525,6 +569,16 @@ TEST_CASE("iterators 2") CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator&); CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator&); CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); +#else CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); @@ -533,6 +587,7 @@ TEST_CASE("iterators 2") CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); +#endif } else { @@ -559,6 +614,16 @@ TEST_CASE("iterators 2") CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator&); CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator&); CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); +#else CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); @@ -567,6 +632,7 @@ TEST_CASE("iterators 2") CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); +#endif } else { @@ -594,6 +660,16 @@ TEST_CASE("iterators 2") CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator&); CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator&); CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); +#else CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); @@ -602,6 +678,7 @@ TEST_CASE("iterators 2") CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); +#endif } else { @@ -629,6 +706,16 @@ TEST_CASE("iterators 2") CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator&); CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator&); CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); +#else CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); @@ -637,6 +724,7 @@ TEST_CASE("iterators 2") CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); +#endif } else { diff --git a/test/src/unit-regression1.cpp b/test/src/unit-regression1.cpp index df660ddb4b..bcb34ca87c 100644 --- a/test/src/unit-regression1.cpp +++ b/test/src/unit-regression1.cpp @@ -394,7 +394,11 @@ TEST_CASE("regression tests 1") // improve coverage o["int"] = 1; CHECK_THROWS_AS(s2 = o["int"], json::type_error); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] (/int) type must be string, but is number"); +#else CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number"); +#endif } #endif From ec0b1798bc7e45cd3aa6456e2cc829db08d1e69b Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 2 Jan 2021 21:36:11 +0100 Subject: [PATCH 07/38] :construction: implement more parent relations --- CMakeLists.txt | 4 + include/nlohmann/detail/input/json_sax.hpp | 14 ++- include/nlohmann/json.hpp | 86 +++++++++++++++++- single_include/nlohmann/json.hpp | 100 +++++++++++++++++++-- test/src/unit-iterators2.cpp | 18 ++-- test/src/unit-json_patch.cpp | 14 ++- 6 files changed, 219 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index abd3a17c7f..36f1cf7056 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,10 @@ if (NOT JSON_ImplicitConversions) message(STATUS "Implicit conversions are disabled") endif() +if (JSON_Diagnostics) + message(STATUS "Diagnostics enabled") +endif() + ## ## TARGET ## create target and add include path diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 316f517234..61266188b9 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -298,12 +298,18 @@ class json_sax_dom_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::forward(v)); +#if JSON_DIAGNOSTICS + ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); +#endif return &(ref_stack.back()->m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); +#if JSON_DIAGNOSTICS + object_element->m_parent = ref_stack.back(); +#endif return object_element; } @@ -574,7 +580,10 @@ class json_sax_dom_callback_parser // array if (ref_stack.back()->is_array()) { - ref_stack.back()->m_value.array->push_back(std::move(value)); + ref_stack.back()->m_value.array->emplace_back(std::move(value)); +#if JSON_DIAGNOSTICS + ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); +#endif return {true, &(ref_stack.back()->m_value.array->back())}; } @@ -592,6 +601,9 @@ class json_sax_dom_callback_parser JSON_ASSERT(object_element); *object_element = std::move(value); +#if JSON_DIAGNOSTICS + object_element->m_parent = ref_stack.back(); +#endif return {true, object_element}; } diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 80bc871a2f..2fc497bae6 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1872,6 +1872,12 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); +#if JSON_DIAGNOSTICS + for (auto& entry : *m_value.array) + { + entry.m_parent = this; + } +#endif assert_invariant(); } @@ -2004,6 +2010,12 @@ class basic_json { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } +#endif break; } @@ -2011,6 +2023,12 @@ class basic_json { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif break; } @@ -2074,12 +2092,24 @@ class basic_json case value_t::object: { m_value = *other.m_value.object; +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } +#endif break; } case value_t::array: { m_value = *other.m_value.array; +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif break; } @@ -2205,6 +2235,9 @@ class basic_json using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); +#if JSON_DIAGNOSTICS + m_parent = other.m_parent; +#endif assert_invariant(); return *this; @@ -2229,6 +2262,9 @@ class basic_json { assert_invariant(); m_value.destroy(m_type); +#if JSON_DIAGNOSTICS + m_parent = nullptr; +#endif } /// @} @@ -2751,7 +2787,7 @@ class basic_json return ""; } - return "(" + std::accumulate(tokens.begin(), tokens.end(), std::string{}, + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, [](const std::string & a, const std::string & b) { return a + "/" + b; @@ -3604,7 +3640,10 @@ class basic_json idx - m_value.array->size() + 1, basic_json()); #if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; + for (std::size_t i = idx + 1; i < m_value.array->size(); ++i) + { + m_value.array->operator[](i).m_parent = this; + } #endif } @@ -5609,6 +5648,11 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); + +#if JSON_DIAGNOSTICS + res.first->second.m_parent = this; +#endif + // create result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; @@ -5671,7 +5715,13 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, val); + result->m_parent = this; + return result; +#else return insert_iterator(pos, val); +#endif } JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); @@ -5722,7 +5772,16 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, cnt, val); + for (size_type i = 0; i < cnt; ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, cnt, val); +#endif } JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); @@ -5784,7 +5843,16 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + for (std::size_t i = 0; i < std::distance(first, last); ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); +#endif } /*! @@ -5826,7 +5894,17 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + const auto size = ilist.size(); + iterator result = insert_iterator(pos, ilist.begin(), ilist.end()); + for (std::size_t i = 0; i < size; ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, ilist.begin(), ilist.end()); +#endif } /*! @@ -8387,7 +8465,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, parent.diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } // default case: insert add offset @@ -8548,7 +8626,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, diagnostics() + "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, val.diagnostics() + "unsuccessful: " + val.dump())); } break; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 16a6049de5..f9bb662d1e 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5590,12 +5590,18 @@ class json_sax_dom_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::forward(v)); +#if JSON_DIAGNOSTICS + ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); +#endif return &(ref_stack.back()->m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); +#if JSON_DIAGNOSTICS + object_element->m_parent = ref_stack.back(); +#endif return object_element; } @@ -5866,7 +5872,10 @@ class json_sax_dom_callback_parser // array if (ref_stack.back()->is_array()) { - ref_stack.back()->m_value.array->push_back(std::move(value)); + ref_stack.back()->m_value.array->emplace_back(std::move(value)); +#if JSON_DIAGNOSTICS + ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); +#endif return {true, &(ref_stack.back()->m_value.array->back())}; } @@ -5884,6 +5893,9 @@ class json_sax_dom_callback_parser JSON_ASSERT(object_element); *object_element = std::move(value); +#if JSON_DIAGNOSTICS + object_element->m_parent = ref_stack.back(); +#endif return {true, object_element}; } @@ -18496,6 +18508,12 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); +#if JSON_DIAGNOSTICS + for (auto& entry : *m_value.array) + { + entry.m_parent = this; + } +#endif assert_invariant(); } @@ -18628,6 +18646,12 @@ class basic_json { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } +#endif break; } @@ -18635,6 +18659,12 @@ class basic_json { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif break; } @@ -18698,12 +18728,24 @@ class basic_json case value_t::object: { m_value = *other.m_value.object; +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } +#endif break; } case value_t::array: { m_value = *other.m_value.array; +#if JSON_DIAGNOSTICS + for (auto& element : *m_value.array) + { + element.m_parent = this; + } +#endif break; } @@ -18829,6 +18871,9 @@ class basic_json using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); +#if JSON_DIAGNOSTICS + m_parent = other.m_parent; +#endif assert_invariant(); return *this; @@ -18853,6 +18898,9 @@ class basic_json { assert_invariant(); m_value.destroy(m_type); +#if JSON_DIAGNOSTICS + m_parent = nullptr; +#endif } /// @} @@ -19375,7 +19423,7 @@ class basic_json return ""; } - return "(" + std::accumulate(tokens.begin(), tokens.end(), std::string{}, + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, [](const std::string & a, const std::string & b) { return a + "/" + b; @@ -20228,7 +20276,10 @@ class basic_json idx - m_value.array->size() + 1, basic_json()); #if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; + for (std::size_t i = idx + 1; i < m_value.array->size(); ++i) + { + m_value.array->operator[](i).m_parent = this; + } #endif } @@ -22233,6 +22284,11 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); + +#if JSON_DIAGNOSTICS + res.first->second.m_parent = this; +#endif + // create result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; @@ -22295,7 +22351,13 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, val); + result->m_parent = this; + return result; +#else return insert_iterator(pos, val); +#endif } JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); @@ -22346,7 +22408,16 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, cnt, val); + for (size_type i = 0; i < cnt; ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, cnt, val); +#endif } JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); @@ -22408,7 +22479,16 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + iterator result = insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + for (std::size_t i = 0; i < std::distance(first, last); ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); +#endif } /*! @@ -22450,7 +22530,17 @@ class basic_json } // insert to array and return iterator +#if JSON_DIAGNOSTICS + const auto size = ilist.size(); + iterator result = insert_iterator(pos, ilist.begin(), ilist.end()); + for (std::size_t i = 0; i < size; ++i) + { + (result + i)->m_parent = this; + } + return result; +#else return insert_iterator(pos, ilist.begin(), ilist.end()); +#endif } /*! @@ -25011,7 +25101,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, parent.diagnostics() + "array index " + std::to_string(idx) + " is out of range")); } // default case: insert add offset @@ -25172,7 +25262,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, diagnostics() + "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, val.diagnostics() + "unsuccessful: " + val.dump())); } break; diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index b9dcc22090..c17084c160 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -271,13 +271,16 @@ TEST_CASE("iterators 2") { CHECK_THROWS_AS(j.begin() == k.begin(), json::invalid_iterator&); CHECK_THROWS_AS(j.cbegin() == k.cbegin(), json::invalid_iterator&); - CHECK_THROWS_WITH(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_AS(j.begin() < k.begin(), json::invalid_iterator&); CHECK_THROWS_AS(j.cbegin() < k.cbegin(), json::invalid_iterator&); +#if JSON_DIAGNOSTICS + // the output differs in each loop, so we cannot fix a string for the expected exception +#else + CHECK_THROWS_WITH(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.begin() < k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); +#endif } } } @@ -750,13 +753,16 @@ TEST_CASE("iterators 2") { CHECK_THROWS_AS(j.rbegin() == k.rbegin(), json::invalid_iterator&); CHECK_THROWS_AS(j.crbegin() == k.crbegin(), json::invalid_iterator&); - CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_AS(j.rbegin() < k.rbegin(), json::invalid_iterator&); CHECK_THROWS_AS(j.crbegin() < k.crbegin(), json::invalid_iterator&); +#if JSON_DIAGNOSTICS + // the output differs in each loop, so we cannot fix a string for the expected exception +#else + CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); +#endif } } } diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 2ad7aadb82..cf11b5603b 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -343,7 +343,11 @@ TEST_CASE("JSON patch") // check that evaluation throws CHECK_THROWS_AS(doc.patch(patch), json::other_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); +#endif } SECTION("A.10. Adding a Nested Member Object") @@ -484,7 +488,11 @@ TEST_CASE("JSON patch") // check that evaluation throws CHECK_THROWS_AS(doc.patch(patch), json::other_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); +#endif } SECTION("A.16. Adding an Array Value") @@ -1183,7 +1191,11 @@ TEST_CASE("JSON patch") // the test will fail CHECK_THROWS_AS(doc.patch(patch), json::other_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump()); +#else CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump()); +#endif } } } @@ -1268,7 +1280,7 @@ TEST_CASE("JSON patch") std::ifstream f(filename); json suite = json::parse(f); - for (const auto& test : suite) + for (const auto test : suite) { INFO_WITH_TEMP(test.value("comment", "")); From 294fa343d54634856dfef4a645de5241bd2dcde1 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 3 Jan 2021 20:06:32 +0100 Subject: [PATCH 08/38] :bug: fix bug in move constructor --- include/nlohmann/json.hpp | 30 ++++++++++++++++++++++++++++-- single_include/nlohmann/json.hpp | 30 ++++++++++++++++++++++++++++-- test/src/unit-json_patch.cpp | 2 +- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 2fc497bae6..a8ae989574 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1873,9 +1873,9 @@ class basic_json { m_value.array = create(cnt, val); #if JSON_DIAGNOSTICS - for (auto& entry : *m_value.array) + for (auto& element : *m_value.array) { - entry.m_parent = this; + element.m_parent = this; } #endif assert_invariant(); @@ -2196,6 +2196,32 @@ class basic_json other.m_type = value_t::null; other.m_value = {}; +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + default: + break; + } +#endif + assert_invariant(); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f9bb662d1e..210f33141d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18509,9 +18509,9 @@ class basic_json { m_value.array = create(cnt, val); #if JSON_DIAGNOSTICS - for (auto& entry : *m_value.array) + for (auto& element : *m_value.array) { - entry.m_parent = this; + element.m_parent = this; } #endif assert_invariant(); @@ -18832,6 +18832,32 @@ class basic_json other.m_type = value_t::null; other.m_value = {}; +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + default: + break; + } +#endif + assert_invariant(); } diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index cf11b5603b..53570113b7 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -1280,7 +1280,7 @@ TEST_CASE("JSON patch") std::ifstream f(filename); json suite = json::parse(f); - for (const auto test : suite) + for (const auto& test : suite) { INFO_WITH_TEMP(test.value("comment", "")); From ddc3bb1992a9a2275f6d5e46c4daf33c8cc88e94 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 8 Jan 2021 11:09:58 +0100 Subject: [PATCH 09/38] :ok_hand: remove unnecessary assignment from destructor --- include/nlohmann/json.hpp | 3 --- single_include/nlohmann/json.hpp | 3 --- 2 files changed, 6 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 1eb65e2d7d..a911a62298 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2288,9 +2288,6 @@ class basic_json { assert_invariant(); m_value.destroy(m_type); -#if JSON_DIAGNOSTICS - m_parent = nullptr; -#endif } /// @} diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8987ead4f2..50e2581def 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18924,9 +18924,6 @@ class basic_json { assert_invariant(); m_value.destroy(m_type); -#if JSON_DIAGNOSTICS - m_parent = nullptr; -#endif } /// @} From 0617bd248d3f8e0c50aab4ddb5dd41e9132093dc Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 8 Jan 2021 11:10:24 +0100 Subject: [PATCH 10/38] :ok_hand: fix operator[] --- include/nlohmann/json.hpp | 13 +++++++++---- single_include/nlohmann/json.hpp | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index a911a62298..46e70d3e7e 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3659,11 +3659,16 @@ class basic_json // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) { - m_value.array->insert(m_value.array->end(), - idx - m_value.array->size() + 1, - basic_json()); #if JSON_DIAGNOSTICS - for (std::size_t i = idx + 1; i < m_value.array->size(); ++i) + // remember array size before resizing + const auto previous_size = m_value.array->size(); +#endif + + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + // set parent for values added above + for (auto i = previous_size; i <= idx; ++i) { m_value.array->operator[](i).m_parent = this; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 50e2581def..2f8c4a2cbc 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -20295,11 +20295,16 @@ class basic_json // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) { - m_value.array->insert(m_value.array->end(), - idx - m_value.array->size() + 1, - basic_json()); #if JSON_DIAGNOSTICS - for (std::size_t i = idx + 1; i < m_value.array->size(); ++i) + // remember array size before resizing + const auto previous_size = m_value.array->size(); +#endif + + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + // set parent for values added above + for (auto i = previous_size; i <= idx; ++i) { m_value.array->operator[](i).m_parent = this; } From 04a0a071592e813d5595afd38be3fa9eeef17e9b Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 8 Jan 2021 11:21:03 +0100 Subject: [PATCH 11/38] :ok_hand: fix move constructor and move assignment --- include/nlohmann/json.hpp | 13 ------------- single_include/nlohmann/json.hpp | 6 ------ 2 files changed, 19 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 46e70d3e7e..c1be2f4b1a 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2185,9 +2185,6 @@ class basic_json basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) -#if JSON_DIAGNOSTICS - , m_parent(other.m_parent) -#endif { // check that passed value is valid other.assert_invariant(); @@ -2261,9 +2258,6 @@ class basic_json using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); -#if JSON_DIAGNOSTICS - m_parent = other.m_parent; -#endif assert_invariant(); return *this; @@ -3659,13 +3653,6 @@ class basic_json // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) { -#if JSON_DIAGNOSTICS - // remember array size before resizing - const auto previous_size = m_value.array->size(); -#endif - - m_value.array->resize(idx + 1); - #if JSON_DIAGNOSTICS // set parent for values added above for (auto i = previous_size; i <= idx; ++i) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 2f8c4a2cbc..c17457dc2f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18821,9 +18821,6 @@ class basic_json basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) -#if JSON_DIAGNOSTICS - , m_parent(other.m_parent) -#endif { // check that passed value is valid other.assert_invariant(); @@ -18897,9 +18894,6 @@ class basic_json using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); -#if JSON_DIAGNOSTICS - m_parent = other.m_parent; -#endif assert_invariant(); return *this; From e4af1ddb189d75ef07b218f3f353a0b5e23f00f4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 8 Jan 2021 11:21:41 +0100 Subject: [PATCH 12/38] :ok_hand: fix operator[] --- include/nlohmann/json.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index c1be2f4b1a..8318013c78 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3653,6 +3653,13 @@ class basic_json // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) { +#if JSON_DIAGNOSTICS + // remember array size before resizing + const auto previous_size = m_value.array->size(); +#endif + + m_value.array->resize(idx + 1); + #if JSON_DIAGNOSTICS // set parent for values added above for (auto i = previous_size; i <= idx; ++i) From d4a91b7445e88d81730ca75a5905e514f58e9d05 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 8 Jan 2021 11:29:28 +0100 Subject: [PATCH 13/38] :ok_hand: clean operator[] --- include/nlohmann/json.hpp | 6 ------ single_include/nlohmann/json.hpp | 6 ------ 2 files changed, 12 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 8318013c78..38bab38067 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3669,13 +3669,7 @@ class basic_json #endif } -#if JSON_DIAGNOSTICS - reference result = m_value.array->operator[](idx); - result.m_parent = this; - return result; -#else return m_value.array->operator[](idx); -#endif } JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index c17457dc2f..2e9768f771 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -20305,13 +20305,7 @@ class basic_json #endif } -#if JSON_DIAGNOSTICS - reference result = m_value.array->operator[](idx); - result.m_parent = this; - return result; -#else return m_value.array->operator[](idx); -#endif } JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); From 43cd5c8a4d3c1239902ea4e106acdcf39647a47b Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 8 Jan 2021 18:00:23 +0100 Subject: [PATCH 14/38] :ok_hand: fix constructor --- include/nlohmann/json.hpp | 15 +++++++++------ single_include/nlohmann/json.hpp | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 38bab38067..13685dd10e 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1639,16 +1639,19 @@ class basic_json m_type = value_t::object; m_value = value_t::object; - std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) + for (auto& element_ref : init) { auto element = element_ref.moved_or_copied(); + auto res = m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + #if JSON_DIAGNOSTICS - (*element.m_value.array)[1].m_parent = this; + res.first->second.m_parent = this; +#else + static_cast(res); // unused variable - fix warning #endif - m_value.object->emplace( - std::move(*((*element.m_value.array)[0].m_value.string)), - std::move((*element.m_value.array)[1])); - }); + } } else { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 2e9768f771..59a829b5fb 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18275,16 +18275,19 @@ class basic_json m_type = value_t::object; m_value = value_t::object; - std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) + for (auto& element_ref : init) { auto element = element_ref.moved_or_copied(); + auto res = m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + #if JSON_DIAGNOSTICS - (*element.m_value.array)[1].m_parent = this; + res.first->second.m_parent = this; +#else + static_cast(res); // unused variable - fix warning #endif - m_value.object->emplace( - std::move(*((*element.m_value.array)[0].m_value.string)), - std::move((*element.m_value.array)[1])); - }); + } } else { From e160749003dd351805cc058b655d50b3b606becd Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 9 Jan 2021 19:21:18 +0100 Subject: [PATCH 15/38] :recycle: move diagnostic code in header --- .../nlohmann/detail/conversions/from_json.hpp | 32 +- include/nlohmann/detail/diagnostics_t.hpp | 85 +++ include/nlohmann/detail/exceptions.hpp | 31 +- .../nlohmann/detail/input/binary_reader.hpp | 38 +- include/nlohmann/detail/input/json_sax.hpp | 13 +- include/nlohmann/detail/input/parser.hpp | 26 +- .../nlohmann/detail/iterators/iter_impl.hpp | 26 +- include/nlohmann/detail/json_pointer.hpp | 49 +- .../nlohmann/detail/output/binary_writer.hpp | 8 +- include/nlohmann/detail/output/serializer.hpp | 5 +- include/nlohmann/json.hpp | 201 ++--- single_include/nlohmann/json.hpp | 687 ++++++++++-------- test/src/unit-json_patch.cpp | 95 ++- 13 files changed, 748 insertions(+), 548 deletions(-) create mode 100644 include/nlohmann/detail/diagnostics_t.hpp diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 0feea28330..2a8d7543e2 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -13,6 +13,7 @@ #include // valarray #include +#include #include #include #include @@ -27,7 +28,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be null, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } n = nullptr; } @@ -58,7 +59,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } } @@ -67,7 +68,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be boolean, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } b = *j.template get_ptr(); } @@ -77,7 +78,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } s = *j.template get_ptr(); } @@ -93,7 +94,7 @@ void from_json(const BasicJsonType& j, ConstructibleStringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } s = *j.template get_ptr(); @@ -133,7 +134,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } l.clear(); std::transform(j.rbegin(), j.rend(), @@ -150,7 +151,7 @@ void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } l.resize(j.size()); std::transform(j.begin(), j.end(), std::begin(l), @@ -241,8 +242,7 @@ void()) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + - std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } from_json_array_impl(j, arr, priority_tag<3> {}); @@ -253,7 +253,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be binary, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } bin = *j.template get_ptr(); @@ -265,7 +265,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } ConstructibleObjectType ret; @@ -319,7 +319,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } } @@ -348,14 +348,14 @@ void from_json(const BasicJsonType& j, std::map& { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), detail::diagnostics_t(j))); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -368,14 +368,14 @@ void from_json(const BasicJsonType& j, std::unordered_map(j))); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), detail::diagnostics_t(j))); } m.emplace(p.at(0).template get(), p.at(1).template get()); } diff --git a/include/nlohmann/detail/diagnostics_t.hpp b/include/nlohmann/detail/diagnostics_t.hpp new file mode 100644 index 0000000000..727e82350f --- /dev/null +++ b/include/nlohmann/detail/diagnostics_t.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ + +template +class diagnostics_t +{ + public: + diagnostics_t() noexcept = default; + diagnostics_t(const BasicJsonType& j) noexcept + : m_j(&j) + {} + + std::string diagnostics() const + { +#if JSON_DIAGNOSTICS + if (m_j == nullptr) + { + return ""; + } + + std::vector tokens; + for (const auto* current = m_j; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (current->m_parent->m_value.array->operator[](i) == *current) + { + tokens.emplace_back(std::to_string(i)); + continue; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (element.second == *current) + { + tokens.emplace_back(element.first.c_str()); + continue; + } + } + break; + } + + default: + break; + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + b; + }) + ") "; +#else + return ""; +#endif + } + + private: + const BasicJsonType* m_j = static_cast(nullptr); +}; + +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/detail/exceptions.hpp b/include/nlohmann/detail/exceptions.hpp index dd92897d5a..56a582f794 100644 --- a/include/nlohmann/detail/exceptions.hpp +++ b/include/nlohmann/detail/exceptions.hpp @@ -4,6 +4,7 @@ #include // runtime_error #include // to_string +#include #include #include @@ -127,18 +128,20 @@ class parse_error : public exception @param[in] what_arg the explanatory string @return parse_error object */ - static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + what_arg; + position_string(pos) + ": " + diagnostics.diagnostics() + what_arg; return parse_error(id_, pos.chars_read_total, w.c_str()); } - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { std::string w = exception::name("parse_error", id_) + "parse error" + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + what_arg; + ": " + diagnostics.diagnostics() + what_arg; return parse_error(id_, byte_, w.c_str()); } @@ -204,9 +207,10 @@ caught.,invalid_iterator} class invalid_iterator : public exception { public: - static invalid_iterator create(int id_, const std::string& what_arg) + template + static invalid_iterator create(int id_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { - std::string w = exception::name("invalid_iterator", id_) + what_arg; + std::string w = exception::name("invalid_iterator", id_) + diagnostics.diagnostics() + what_arg; return invalid_iterator(id_, w.c_str()); } @@ -258,9 +262,10 @@ caught.,type_error} class type_error : public exception { public: - static type_error create(int id_, const std::string& what_arg) + template + static type_error create(int id_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { - std::string w = exception::name("type_error", id_) + what_arg; + std::string w = exception::name("type_error", id_) + diagnostics.diagnostics() + what_arg; return type_error(id_, w.c_str()); } @@ -305,9 +310,10 @@ caught.,out_of_range} class out_of_range : public exception { public: - static out_of_range create(int id_, const std::string& what_arg) + template + static out_of_range create(int id_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { - std::string w = exception::name("out_of_range", id_) + what_arg; + std::string w = exception::name("out_of_range", id_) + diagnostics.diagnostics() + what_arg; return out_of_range(id_, w.c_str()); } @@ -343,9 +349,10 @@ caught.,other_error} class other_error : public exception { public: - static other_error create(int id_, const std::string& what_arg) + template + static other_error create(int id_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { - std::string w = exception::name("other_error", id_) + what_arg; + std::string w = exception::name("other_error", id_) + diagnostics.diagnostics() + what_arg; return other_error(id_, w.c_str()); } diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 806e360306..a896f15586 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -13,6 +13,7 @@ #include // make_pair, move #include // vector +#include #include #include #include @@ -64,6 +65,7 @@ class binary_reader using json_sax_t = SAX; using char_type = typename InputAdapterType::char_type; using char_int_type = typename std::char_traits::int_type; + using diagnostics_t = detail::diagnostics_t; public: /*! @@ -137,7 +139,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { return sax->parse_error(chars_read, get_token_string(), - parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), diagnostics_t())); } } @@ -213,7 +215,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 1)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), diagnostics_t())); } return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); @@ -234,7 +236,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 0)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), diagnostics_t())); } // All BSON binary values have a subtype @@ -316,7 +318,7 @@ class binary_reader { std::array cr{{}}; (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); - return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()))); + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), diagnostics_t())); } } } @@ -716,7 +718,7 @@ class binary_reader case cbor_tag_handler_t::error: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), diagnostics_t())); } case cbor_tag_handler_t::ignore: @@ -831,7 +833,7 @@ class binary_reader default: // anything else (0xFF is handled inside the other types) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), diagnostics_t())); } } } @@ -926,7 +928,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), diagnostics_t())); } } } @@ -1025,7 +1027,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), diagnostics_t())); } } } @@ -1492,7 +1494,7 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), diagnostics_t())); } } } @@ -1574,7 +1576,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), diagnostics_t())); } } } @@ -1824,7 +1826,7 @@ class binary_reader default: auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), diagnostics_t())); } } @@ -1894,7 +1896,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), diagnostics_t())); } } } @@ -1932,7 +1934,7 @@ class binary_reader return false; } auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), diagnostics_t())); } return get_ubjson_size_value(result.first); @@ -2022,7 +2024,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current > 127)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), diagnostics_t())); } string_t s(1, static_cast(current)); return sax->string(s); @@ -2043,7 +2045,7 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), diagnostics_t())); } } } @@ -2221,7 +2223,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), diagnostics_t())); } switch (result_number) @@ -2233,7 +2235,7 @@ class binary_reader case token_type::value_float: return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); default: - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), diagnostics_t())); } } @@ -2389,7 +2391,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { return sax->parse_error(chars_read, "", - parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), diagnostics_t())); } return true; } diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 61266188b9..f29aa1a682 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -5,6 +5,7 @@ #include // move #include // vector +#include #include #include @@ -154,6 +155,7 @@ class json_sax_dom_parser using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; + using diagnostics_t = detail::diagnostics_t; /*! @param[in, out] r reference to a JSON value that is manipulated while @@ -219,8 +221,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + - "excessive object size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), diagnostics_t(*ref_stack.back()))); } return true; @@ -245,8 +246,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + - "excessive array size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), diagnostics_t(*ref_stack.back()))); } return true; @@ -336,6 +336,7 @@ class json_sax_dom_callback_parser using binary_t = typename BasicJsonType::binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; + using diagnostics_t = detail::diagnostics_t; json_sax_dom_callback_parser(BasicJsonType& r, const parser_callback_t cb, @@ -406,7 +407,7 @@ class json_sax_dom_callback_parser // check object limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive object size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), diagnostics_t(*ref_stack.back()))); } return true; @@ -469,7 +470,7 @@ class json_sax_dom_callback_parser // check array limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive array size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), diagnostics_t(*ref_stack.back()))); } return true; diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index ffe483aa1e..40ef371eb9 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -8,6 +8,7 @@ #include // vector #include +#include #include #include #include @@ -57,6 +58,7 @@ class parser using string_t = typename BasicJsonType::string_t; using lexer_t = lexer; using token_type = typename lexer_t::token_type; + using diagnostics_t = detail::diagnostics_t; public: /// a parser reading from an input adapter @@ -96,7 +98,7 @@ class parser sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"))); + exception_message(token_type::end_of_input, "value"), diagnostics_t())); } // in case of an error, return discarded value @@ -125,7 +127,7 @@ class parser sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"))); + exception_message(token_type::end_of_input, "value"), diagnostics_t())); } // in case of an error, return discarded value @@ -162,7 +164,7 @@ class parser 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::end_of_input, "value"))); + exception_message(token_type::end_of_input, "value"), diagnostics_t())); } return result; @@ -209,7 +211,7 @@ class parser 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::value_string, "object key"))); + exception_message(token_type::value_string, "object key"), diagnostics_t())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { @@ -222,7 +224,7 @@ class parser 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::name_separator, "object separator"))); + exception_message(token_type::name_separator, "object separator"), diagnostics_t())); } // remember we are now inside an object @@ -265,7 +267,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", diagnostics_t())); } if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) @@ -336,7 +338,7 @@ class parser 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::uninitialized, "value"))); + exception_message(token_type::uninitialized, "value"), diagnostics_t())); } default: // the last token was unexpected @@ -344,7 +346,7 @@ class parser 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"))); + exception_message(token_type::literal_or_value, "value"), diagnostics_t())); } } } @@ -391,7 +393,7 @@ class parser 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::end_array, "array"))); + exception_message(token_type::end_array, "array"), diagnostics_t())); } else // object { @@ -404,7 +406,7 @@ class parser 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::value_string, "object key"))); + exception_message(token_type::value_string, "object key"), diagnostics_t())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) @@ -418,7 +420,7 @@ class parser 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::name_separator, "object separator"))); + exception_message(token_type::name_separator, "object separator"), diagnostics_t())); } // parse values @@ -447,7 +449,7 @@ class parser 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::end_object, "object"))); + exception_message(token_type::end_object, "object"), diagnostics_t())); } } } diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index cceb8d05fc..565ac64d1a 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -3,6 +3,7 @@ #include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include // conditional, is_const, remove_const +#include #include #include #include @@ -51,6 +52,7 @@ class iter_impl // make sure BasicJsonType is basic_json or const basic_json static_assert(is_basic_json::type>::value, "iter_impl only accepts (const) basic_json"); + using diagnostics_t = detail::diagnostics_t; public: @@ -257,7 +259,7 @@ class iter_impl } case value_t::null: - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); default: { @@ -266,7 +268,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); } } } @@ -300,7 +302,7 @@ class iter_impl return m_object; } - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); } } } @@ -401,7 +403,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", diagnostics_t(*m_object))); } JSON_ASSERT(m_object != nullptr); @@ -438,7 +440,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", diagnostics_t(*m_object))); } JSON_ASSERT(m_object != nullptr); @@ -446,7 +448,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(213, m_object->diagnostics() + "cannot compare order of object iterators")); + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", diagnostics_t(*m_object))); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); @@ -494,7 +496,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", diagnostics_t(*m_object))); case value_t::array: { @@ -565,7 +567,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", diagnostics_t(*m_object))); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; @@ -586,13 +588,13 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(208, m_object->diagnostics() + "cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", diagnostics_t(*m_object))); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); default: { @@ -601,7 +603,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); } } } @@ -619,7 +621,7 @@ class iter_impl return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create(207, m_object->diagnostics() + "cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", diagnostics_t(*m_object))); } /*! diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 865376cf18..f3a70bdcee 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -8,6 +8,7 @@ #include // move #include // vector +#include #include #include #include @@ -21,6 +22,8 @@ class json_pointer NLOHMANN_BASIC_JSON_TPL_DECLARATION friend class basic_json; + using diagnostics_t = detail::diagnostics_t; + public: /*! @brief create JSON pointer @@ -247,7 +250,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", diagnostics_t())); } reference_tokens.pop_back(); @@ -271,7 +274,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", diagnostics_t())); } return reference_tokens.back(); @@ -337,15 +340,13 @@ class json_pointer // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + s + - "' must not begin with '0'")); + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", diagnostics_t())); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", diagnostics_t())); } std::size_t processed_chars = 0; @@ -356,20 +357,20 @@ class json_pointer } JSON_CATCH(std::out_of_range&) { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", diagnostics_t())); } // check if the string was completely read if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", diagnostics_t())); } // only triggered on special platforms (like 32bit), see also // https://github.com/nlohmann/json/pull/2203 if (res >= static_cast((std::numeric_limits::max)())) { - JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", diagnostics_t())); // LCOV_EXCL_LINE } return static_cast(res); @@ -380,7 +381,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", diagnostics_t())); } json_pointer result = *this; @@ -443,7 +444,7 @@ class json_pointer single value; that is, with an empty list of reference tokens. */ default: - JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", diagnostics_t(j))); } } @@ -515,7 +516,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", diagnostics_t(*ptr))); } } @@ -548,7 +549,7 @@ class json_pointer // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + ") is out of range", diagnostics_t(*ptr))); } // note: at performs range check @@ -557,7 +558,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", diagnostics_t(*ptr))); } } @@ -595,9 +596,7 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", diagnostics_t(*ptr))); } // use unchecked array access @@ -606,7 +605,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", diagnostics_t(*ptr))); } } @@ -639,7 +638,7 @@ class json_pointer // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + ") is out of range", diagnostics_t(*ptr))); } // note: at performs range check @@ -648,7 +647,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", diagnostics_t(*ptr))); } } @@ -752,9 +751,7 @@ class json_pointer // check if nonempty reference string begins with slash if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) { - JSON_THROW(detail::parse_error::create(107, 1, - "JSON pointer must be empty or begin with '/' - was: '" + - reference_string + "'")); + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", diagnostics_t())); } // extract the reference tokens: @@ -789,7 +786,7 @@ class json_pointer (reference_token[pos + 1] != '0' && reference_token[pos + 1] != '1'))) { - JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", diagnostics_t())); } } @@ -916,7 +913,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!value.is_object())) { - JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", diagnostics_t(value))); } BasicJsonType result; @@ -926,7 +923,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", diagnostics_t(element))); } // assign value to reference pointed to by JSON pointer; Note that if diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 72ebbeda68..10bc34011b 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -8,6 +8,7 @@ #include // string #include // isnan, isinf +#include #include #include #include @@ -57,7 +58,7 @@ class binary_writer default: { - JSON_THROW(type_error::create(317, j.diagnostics() + "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), detail::diagnostics_t(j)));; } } } @@ -906,8 +907,7 @@ class binary_writer const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, j.diagnostics() + - "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")")); + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", detail::diagnostics_t(j))); } return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; @@ -1031,7 +1031,7 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, j.diagnostics() + "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64")); + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", detail::diagnostics_t(j))); } } diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index 0a34c8011e..3384141805 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -13,6 +13,7 @@ #include // move #include +#include #include #include #include @@ -499,7 +500,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn)); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, detail::diagnostics_t())); } case error_handler_t::ignore: @@ -593,7 +594,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn)); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, detail::diagnostics_t())); } case error_handler_t::ignore: diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 13685dd10e..086c0f5572 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -51,6 +51,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -189,10 +190,15 @@ class basic_json friend class ::nlohmann::detail::json_sax_dom_parser; template friend class ::nlohmann::detail::json_sax_dom_callback_parser; + template + friend class ::nlohmann::detail::diagnostics_t; /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + /// shortcut + using diagnostics_t = ::nlohmann::detail::diagnostics_t; + JSON_PRIVATE_UNLESS_TESTED: // convenience aliases for types residing in namespace detail; using lexer = ::nlohmann::detail::lexer_base; @@ -1060,7 +1066,7 @@ class basic_json object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1")); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1", diagnostics_t())); // LCOV_EXCL_LINE } break; } @@ -1629,7 +1635,7 @@ class basic_json // if object is wanted but impossible, throw an exception if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { - JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + JSON_THROW(type_error::create(301, "cannot create object from initializer list", diagnostics_t())); } } @@ -1950,7 +1956,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(201, diagnostics() + "iterators are not compatible")); + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", diagnostics_t())); } // copy type from first iterator @@ -1968,7 +1974,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", diagnostics_t())); } break; } @@ -2042,8 +2048,7 @@ class basic_json } default: - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + - std::string(first.m_object->type_name()))); + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), diagnostics_t())); } assert_invariant(); @@ -2763,60 +2768,6 @@ class basic_json /// @} - std::string diagnostics() const - { -#if JSON_DIAGNOSTICS - std::vector tokens; - for (const basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) - { - switch (current->m_parent->type()) - { - case value_t::array: - { - for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) - { - if (current->m_parent->m_value.array->operator[](i) == *current) - { - tokens.emplace_back(std::to_string(i)); - continue; - } - } - break; - } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_value.object) - { - if (element.second == *current) - { - tokens.emplace_back(element.first.c_str()); - continue; - } - } - break; - } - - default: - break; - } - } - - if (tokens.empty()) - { - return ""; - } - - return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + b; - }) + ") "; -#else - return ""; -#endif - } - private: ////////////////// // value access // @@ -2830,7 +2781,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(type_error::create(302, diagnostics() + "type must be boolean, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), diagnostics_t(*this))); } /// get a pointer to the value (object) @@ -2951,7 +2902,7 @@ class basic_json return *ptr; } - JSON_THROW(type_error::create(303, obj.diagnostics() + "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), diagnostics_t(obj))); } public: @@ -3379,7 +3330,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, diagnostics() + "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), diagnostics_t(*this))); } return *get_ptr(); @@ -3390,7 +3341,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, diagnostics() + "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), diagnostics_t(*this))); } return *get_ptr(); @@ -3451,12 +3402,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", diagnostics_t(*this))); } } else { - JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -3498,12 +3449,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", diagnostics_t(*this))); } } else { - JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -3555,12 +3506,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", diagnostics_t(*this))); } } else { - JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -3606,12 +3557,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", diagnostics_t(*this))); } } else { - JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -3675,7 +3626,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -3705,7 +3656,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -3757,7 +3708,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -3799,7 +3750,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -3853,7 +3804,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -3897,7 +3848,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -3969,7 +3920,7 @@ class basic_json return default_value; } - JSON_THROW(type_error::create(306, diagnostics() + "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -4042,7 +3993,7 @@ class basic_json } } - JSON_THROW(type_error::create(306, diagnostics() + "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -4196,7 +4147,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } IteratorType result = end(); @@ -4212,7 +4163,7 @@ class basic_json { if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) { - JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + JSON_THROW(invalid_iterator::create(205, "iterator out of range", diagnostics_t(*this))); } if (is_string()) @@ -4248,7 +4199,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), diagnostics_t(*this))); } return result; @@ -4309,7 +4260,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { - JSON_THROW(invalid_iterator::create(203, diagnostics() + "iterators do not fit current value")); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", diagnostics_t(*this))); } IteratorType result = end(); @@ -4326,7 +4277,7 @@ class basic_json if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, diagnostics() + "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", diagnostics_t(*this))); } if (is_string()) @@ -4364,7 +4315,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), diagnostics_t(*this))); } return result; @@ -4407,7 +4358,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -4441,14 +4392,14 @@ class basic_json { if (JSON_HEDLEY_UNLIKELY(idx >= size())) { - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", diagnostics_t(*this))); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -5393,7 +5344,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an array @@ -5431,7 +5382,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an array @@ -5484,7 +5435,7 @@ class basic_json // push_back only works for null objects or objects if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an object @@ -5592,7 +5543,7 @@ class basic_json // emplace_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(311, diagnostics() + "cannot use emplace_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an array @@ -5654,7 +5605,7 @@ class basic_json // emplace only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(311, diagnostics() + "cannot use emplace() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an object @@ -5730,7 +5681,7 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } // insert to array and return iterator @@ -5743,7 +5694,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -5787,7 +5738,7 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } // insert to array and return iterator @@ -5803,7 +5754,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -5841,24 +5792,24 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", diagnostics_t(*this))); } if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - JSON_THROW(invalid_iterator::create(211, diagnostics() + "passed iterators may not belong to container")); + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", diagnostics_t(*this))); } // insert to array and return iterator @@ -5903,13 +5854,13 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } // insert to array and return iterator @@ -5954,19 +5905,19 @@ class basic_json // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", diagnostics_t(*this))); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", diagnostics_t(*this))); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); @@ -6003,11 +5954,11 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), diagnostics_t(*this))); } if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(j.type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), diagnostics_t(*this))); } for (auto it = j.cbegin(); it != j.cend(); ++it) @@ -6054,20 +6005,20 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), diagnostics_t(*this))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", diagnostics_t(*this))); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() || !last.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", diagnostics_t(*this))); } for (auto it = first; it != last; ++it) @@ -6162,7 +6113,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -6195,7 +6146,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -6228,7 +6179,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -6261,7 +6212,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -6275,7 +6226,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -8484,7 +8435,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, parent.diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", diagnostics_t(parent))); } // default case: insert add offset @@ -8518,7 +8469,7 @@ class basic_json } else { - JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + last_path + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", diagnostics_t(*this))); } } else if (parent.is_array()) @@ -8531,7 +8482,7 @@ class basic_json // type check: top level value must be an array if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, diagnostics() + "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", diagnostics_t(json_patch))); } // iterate and apply the operations @@ -8551,13 +8502,13 @@ class basic_json // check if desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { - JSON_THROW(parse_error::create(105, 0, diagnostics() + error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", diagnostics_t(val))); } // check if result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { - JSON_THROW(parse_error::create(105, 0, diagnostics() + error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", diagnostics_t(val))); } // no error: return value @@ -8567,7 +8518,7 @@ class basic_json // type check: every element of the array must be an object if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { - JSON_THROW(parse_error::create(104, 0, diagnostics() + "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", diagnostics_t(val))); } // collect mandatory members @@ -8645,7 +8596,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, val.diagnostics() + "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), diagnostics_t(val))); } break; @@ -8655,7 +8606,7 @@ class basic_json { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error::create(105, 0, diagnostics() + "operation value '" + op + "' is invalid")); + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", diagnostics_t(val))); } } } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 59a829b5fb..bd79bd6fd3 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -73,6 +73,175 @@ SOFTWARE. #include // runtime_error #include // to_string +// #include + + +#include +#include +// #include + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ + +template +class diagnostics_t +{ + public: + diagnostics_t() noexcept = default; + diagnostics_t(const BasicJsonType& j) noexcept + : m_j(&j) + {} + + std::string diagnostics() const + { +#if JSON_DIAGNOSTICS + if (m_j == nullptr) + { + return ""; + } + + std::vector tokens; + for (const auto* current = m_j; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (current->m_parent->m_value.array->operator[](i) == *current) + { + tokens.emplace_back(std::to_string(i)); + continue; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (element.second == *current) + { + tokens.emplace_back(element.first.c_str()); + continue; + } + } + break; + } + + default: + break; + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + b; + }) + ") "; +#else + return ""; +#endif + } + + private: + const BasicJsonType* m_j = static_cast(nullptr); +}; + +} // namespace detail +} // namespace nlohmann + // #include @@ -2500,18 +2669,20 @@ class parse_error : public exception @param[in] what_arg the explanatory string @return parse_error object */ - static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + what_arg; + position_string(pos) + ": " + diagnostics.diagnostics() + what_arg; return parse_error(id_, pos.chars_read_total, w.c_str()); } - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { std::string w = exception::name("parse_error", id_) + "parse error" + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + what_arg; + ": " + diagnostics.diagnostics() + what_arg; return parse_error(id_, byte_, w.c_str()); } @@ -2577,9 +2748,10 @@ caught.,invalid_iterator} class invalid_iterator : public exception { public: - static invalid_iterator create(int id_, const std::string& what_arg) + template + static invalid_iterator create(int id_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { - std::string w = exception::name("invalid_iterator", id_) + what_arg; + std::string w = exception::name("invalid_iterator", id_) + diagnostics.diagnostics() + what_arg; return invalid_iterator(id_, w.c_str()); } @@ -2631,9 +2803,10 @@ caught.,type_error} class type_error : public exception { public: - static type_error create(int id_, const std::string& what_arg) + template + static type_error create(int id_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { - std::string w = exception::name("type_error", id_) + what_arg; + std::string w = exception::name("type_error", id_) + diagnostics.diagnostics() + what_arg; return type_error(id_, w.c_str()); } @@ -2678,9 +2851,10 @@ caught.,out_of_range} class out_of_range : public exception { public: - static out_of_range create(int id_, const std::string& what_arg) + template + static out_of_range create(int id_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { - std::string w = exception::name("out_of_range", id_) + what_arg; + std::string w = exception::name("out_of_range", id_) + diagnostics.diagnostics() + what_arg; return out_of_range(id_, w.c_str()); } @@ -2716,9 +2890,10 @@ caught.,other_error} class other_error : public exception { public: - static other_error create(int id_, const std::string& what_arg) + template + static other_error create(int id_, const std::string& what_arg, const detail::diagnostics_t& diagnostics) { - std::string w = exception::name("other_error", id_) + what_arg; + std::string w = exception::name("other_error", id_) + diagnostics.diagnostics() + what_arg; return other_error(id_, w.c_str()); } @@ -2729,6 +2904,8 @@ class other_error : public exception } // namespace detail } // namespace nlohmann +// #include + // #include // #include @@ -3423,87 +3600,6 @@ struct is_constructible_tuple> : conjunction -#include // array -#include // size_t -#include // uint8_t -#include // string - -namespace nlohmann -{ -namespace detail -{ -/////////////////////////// -// JSON type enumeration // -/////////////////////////// - -/*! -@brief the JSON type enumeration - -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. - -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. - -@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type - -@since version 1.0.0 -*/ -enum class value_t : std::uint8_t -{ - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function -}; - -/*! -@brief comparison operator for JSON types - -Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string < binary -- furthermore, each type is not smaller than itself -- discarded values are not comparable -- binary is represented as a b"" string in python and directly comparable to a - string; however, making a binary array directly comparable with a string would - be surprising behavior in a JSON file. - -@since version 1.0.0 -*/ -inline bool operator<(const value_t lhs, const value_t rhs) noexcept -{ - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; - - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; -} -} // namespace detail -} // namespace nlohmann - - namespace nlohmann { namespace detail @@ -3513,7 +3609,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be null, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } n = nullptr; } @@ -3544,7 +3640,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } } @@ -3553,7 +3649,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be boolean, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } b = *j.template get_ptr(); } @@ -3563,7 +3659,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } s = *j.template get_ptr(); } @@ -3579,7 +3675,7 @@ void from_json(const BasicJsonType& j, ConstructibleStringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } s = *j.template get_ptr(); @@ -3619,7 +3715,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } l.clear(); std::transform(j.rbegin(), j.rend(), @@ -3636,7 +3732,7 @@ void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } l.resize(j.size()); std::transform(j.begin(), j.end(), std::begin(l), @@ -3727,8 +3823,7 @@ void()) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + - std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } from_json_array_impl(j, arr, priority_tag<3> {}); @@ -3739,7 +3834,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be binary, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } bin = *j.template get_ptr(); @@ -3751,7 +3846,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } ConstructibleObjectType ret; @@ -3805,7 +3900,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } } @@ -3834,14 +3929,14 @@ void from_json(const BasicJsonType& j, std::map& { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), detail::diagnostics_t(j))); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), detail::diagnostics_t(j))); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -3854,14 +3949,14 @@ void from_json(const BasicJsonType& j, std::unordered_map(j))); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), detail::diagnostics_t(j))); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -4666,6 +4761,8 @@ class byte_container_with_subtype : public BinaryType // #include +// #include + // #include @@ -4805,6 +4902,8 @@ std::size_t hash(const BasicJsonType& j) #include // make_pair, move #include // vector +// #include + // #include // #include @@ -5295,6 +5394,8 @@ class span_input_adapter #include // move #include // vector +// #include + // #include // #include @@ -5446,6 +5547,7 @@ class json_sax_dom_parser using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; + using diagnostics_t = detail::diagnostics_t; /*! @param[in, out] r reference to a JSON value that is manipulated while @@ -5511,8 +5613,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + - "excessive object size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), diagnostics_t(*ref_stack.back()))); } return true; @@ -5537,8 +5638,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + - "excessive array size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), diagnostics_t(*ref_stack.back()))); } return true; @@ -5628,6 +5728,7 @@ class json_sax_dom_callback_parser using binary_t = typename BasicJsonType::binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; + using diagnostics_t = detail::diagnostics_t; json_sax_dom_callback_parser(BasicJsonType& r, const parser_callback_t cb, @@ -5698,7 +5799,7 @@ class json_sax_dom_callback_parser // check object limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive object size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), diagnostics_t(*ref_stack.back()))); } return true; @@ -5761,7 +5862,7 @@ class json_sax_dom_callback_parser // check array limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive array size: " + std::to_string(len))); + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), diagnostics_t(*ref_stack.back()))); } return true; @@ -7827,6 +7928,7 @@ class binary_reader using json_sax_t = SAX; using char_type = typename InputAdapterType::char_type; using char_int_type = typename std::char_traits::int_type; + using diagnostics_t = detail::diagnostics_t; public: /*! @@ -7900,7 +8002,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { return sax->parse_error(chars_read, get_token_string(), - parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), diagnostics_t())); } } @@ -7976,7 +8078,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 1)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), diagnostics_t())); } return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); @@ -7997,7 +8099,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 0)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), diagnostics_t())); } // All BSON binary values have a subtype @@ -8079,7 +8181,7 @@ class binary_reader { std::array cr{{}}; (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); - return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()))); + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), diagnostics_t())); } } } @@ -8479,7 +8581,7 @@ class binary_reader case cbor_tag_handler_t::error: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), diagnostics_t())); } case cbor_tag_handler_t::ignore: @@ -8594,7 +8696,7 @@ class binary_reader default: // anything else (0xFF is handled inside the other types) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), diagnostics_t())); } } } @@ -8689,7 +8791,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), diagnostics_t())); } } } @@ -8788,7 +8890,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), diagnostics_t())); } } } @@ -9255,7 +9357,7 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), diagnostics_t())); } } } @@ -9337,7 +9439,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), diagnostics_t())); } } } @@ -9587,7 +9689,7 @@ class binary_reader default: auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), diagnostics_t())); } } @@ -9657,7 +9759,7 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), diagnostics_t())); } } } @@ -9695,7 +9797,7 @@ class binary_reader return false; } auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), diagnostics_t())); } return get_ubjson_size_value(result.first); @@ -9785,7 +9887,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current > 127)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), diagnostics_t())); } string_t s(1, static_cast(current)); return sax->string(s); @@ -9806,7 +9908,7 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"))); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), diagnostics_t())); } } } @@ -9984,7 +10086,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), diagnostics_t())); } switch (result_number) @@ -9996,7 +10098,7 @@ class binary_reader case token_type::value_float: return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); default: - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"))); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), diagnostics_t())); } } @@ -10152,7 +10254,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { return sax->parse_error(chars_read, "", - parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), diagnostics_t())); } return true; } @@ -10239,6 +10341,8 @@ class binary_reader // #include +// #include + // #include // #include @@ -10294,6 +10398,7 @@ class parser using string_t = typename BasicJsonType::string_t; using lexer_t = lexer; using token_type = typename lexer_t::token_type; + using diagnostics_t = detail::diagnostics_t; public: /// a parser reading from an input adapter @@ -10333,7 +10438,7 @@ class parser sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"))); + exception_message(token_type::end_of_input, "value"), diagnostics_t())); } // in case of an error, return discarded value @@ -10362,7 +10467,7 @@ class parser sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"))); + exception_message(token_type::end_of_input, "value"), diagnostics_t())); } // in case of an error, return discarded value @@ -10399,7 +10504,7 @@ class parser 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::end_of_input, "value"))); + exception_message(token_type::end_of_input, "value"), diagnostics_t())); } return result; @@ -10446,7 +10551,7 @@ class parser 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::value_string, "object key"))); + exception_message(token_type::value_string, "object key"), diagnostics_t())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { @@ -10459,7 +10564,7 @@ class parser 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::name_separator, "object separator"))); + exception_message(token_type::name_separator, "object separator"), diagnostics_t())); } // remember we are now inside an object @@ -10502,7 +10607,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", diagnostics_t())); } if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) @@ -10573,7 +10678,7 @@ class parser 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::uninitialized, "value"))); + exception_message(token_type::uninitialized, "value"), diagnostics_t())); } default: // the last token was unexpected @@ -10581,7 +10686,7 @@ class parser 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"))); + exception_message(token_type::literal_or_value, "value"), diagnostics_t())); } } } @@ -10628,7 +10733,7 @@ class parser 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::end_array, "array"))); + exception_message(token_type::end_array, "array"), diagnostics_t())); } else // object { @@ -10641,7 +10746,7 @@ class parser 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::value_string, "object key"))); + exception_message(token_type::value_string, "object key"), diagnostics_t())); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) @@ -10655,7 +10760,7 @@ class parser 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::name_separator, "object separator"))); + exception_message(token_type::name_separator, "object separator"), diagnostics_t())); } // parse values @@ -10684,7 +10789,7 @@ class parser 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::end_object, "object"))); + exception_message(token_type::end_object, "object"), diagnostics_t())); } } } @@ -10895,6 +11000,8 @@ template struct internal_iterator #include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include // conditional, is_const, remove_const +// #include + // #include // #include @@ -10950,6 +11057,7 @@ class iter_impl // make sure BasicJsonType is basic_json or const basic_json static_assert(is_basic_json::type>::value, "iter_impl only accepts (const) basic_json"); + using diagnostics_t = detail::diagnostics_t; public: @@ -11156,7 +11264,7 @@ class iter_impl } case value_t::null: - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); default: { @@ -11165,7 +11273,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); } } } @@ -11199,7 +11307,7 @@ class iter_impl return m_object; } - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); } } } @@ -11300,7 +11408,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", diagnostics_t(*m_object))); } JSON_ASSERT(m_object != nullptr); @@ -11337,7 +11445,7 @@ class iter_impl // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", diagnostics_t(*m_object))); } JSON_ASSERT(m_object != nullptr); @@ -11345,7 +11453,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(213, m_object->diagnostics() + "cannot compare order of object iterators")); + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", diagnostics_t(*m_object))); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); @@ -11393,7 +11501,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", diagnostics_t(*m_object))); case value_t::array: { @@ -11464,7 +11572,7 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", diagnostics_t(*m_object))); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; @@ -11485,13 +11593,13 @@ class iter_impl switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(208, m_object->diagnostics() + "cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", diagnostics_t(*m_object))); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); default: { @@ -11500,7 +11608,7 @@ class iter_impl return *m_object; } - JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value", diagnostics_t(*m_object))); } } } @@ -11518,7 +11626,7 @@ class iter_impl return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create(207, m_object->diagnostics() + "cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", diagnostics_t(*m_object))); } /*! @@ -11675,6 +11783,8 @@ class json_reverse_iterator : public std::reverse_iterator #include // move #include // vector +// #include + // #include // #include @@ -11691,6 +11801,8 @@ class json_pointer NLOHMANN_BASIC_JSON_TPL_DECLARATION friend class basic_json; + using diagnostics_t = detail::diagnostics_t; + public: /*! @brief create JSON pointer @@ -11917,7 +12029,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", diagnostics_t())); } reference_tokens.pop_back(); @@ -11941,7 +12053,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", diagnostics_t())); } return reference_tokens.back(); @@ -12007,15 +12119,13 @@ class json_pointer // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) { - JSON_THROW(detail::parse_error::create(106, 0, - "array index '" + s + - "' must not begin with '0'")); + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", diagnostics_t())); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", diagnostics_t())); } std::size_t processed_chars = 0; @@ -12026,20 +12136,20 @@ class json_pointer } JSON_CATCH(std::out_of_range&) { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", diagnostics_t())); } // check if the string was completely read if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", diagnostics_t())); } // only triggered on special platforms (like 32bit), see also // https://github.com/nlohmann/json/pull/2203 if (res >= static_cast((std::numeric_limits::max)())) { - JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", diagnostics_t())); // LCOV_EXCL_LINE } return static_cast(res); @@ -12050,7 +12160,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", diagnostics_t())); } json_pointer result = *this; @@ -12113,7 +12223,7 @@ class json_pointer single value; that is, with an empty list of reference tokens. */ default: - JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", diagnostics_t(j))); } } @@ -12185,7 +12295,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", diagnostics_t(*ptr))); } } @@ -12218,7 +12328,7 @@ class json_pointer // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + ") is out of range", diagnostics_t(*ptr))); } // note: at performs range check @@ -12227,7 +12337,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", diagnostics_t(*ptr))); } } @@ -12265,9 +12375,7 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", diagnostics_t(*ptr))); } // use unchecked array access @@ -12276,7 +12384,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", diagnostics_t(*ptr))); } } @@ -12309,7 +12417,7 @@ class json_pointer // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + ") is out of range", diagnostics_t(*ptr))); } // note: at performs range check @@ -12318,7 +12426,7 @@ class json_pointer } default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", diagnostics_t(*ptr))); } } @@ -12422,9 +12530,7 @@ class json_pointer // check if nonempty reference string begins with slash if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) { - JSON_THROW(detail::parse_error::create(107, 1, - "JSON pointer must be empty or begin with '/' - was: '" + - reference_string + "'")); + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", diagnostics_t())); } // extract the reference tokens: @@ -12459,7 +12565,7 @@ class json_pointer (reference_token[pos + 1] != '0' && reference_token[pos + 1] != '1'))) { - JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", diagnostics_t())); } } @@ -12586,7 +12692,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!value.is_object())) { - JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", diagnostics_t(value))); } BasicJsonType result; @@ -12596,7 +12702,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", diagnostics_t(element))); } // assign value to reference pointed to by JSON pointer; Note that if @@ -12736,6 +12842,8 @@ class json_ref #include // string #include // isnan, isinf +// #include + // #include // #include @@ -12912,7 +13020,7 @@ class binary_writer default: { - JSON_THROW(type_error::create(317, j.diagnostics() + "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), detail::diagnostics_t(j)));; } } } @@ -13761,8 +13869,7 @@ class binary_writer const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, j.diagnostics() + - "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")")); + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", detail::diagnostics_t(j))); } return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; @@ -13886,7 +13993,7 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, j.diagnostics() + "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64")); + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", detail::diagnostics_t(j))); } } @@ -15574,6 +15681,8 @@ char* to_chars(char* first, const char* last, FloatType value) } // namespace detail } // namespace nlohmann +// #include + // #include // #include @@ -16066,7 +16175,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn)); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, detail::diagnostics_t())); } case error_handler_t::ignore: @@ -16160,7 +16269,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn)); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, detail::diagnostics_t())); } case error_handler_t::ignore: @@ -16825,10 +16934,15 @@ class basic_json friend class ::nlohmann::detail::json_sax_dom_parser; template friend class ::nlohmann::detail::json_sax_dom_callback_parser; + template + friend class ::nlohmann::detail::diagnostics_t; /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + /// shortcut + using diagnostics_t = ::nlohmann::detail::diagnostics_t; + JSON_PRIVATE_UNLESS_TESTED: // convenience aliases for types residing in namespace detail; using lexer = ::nlohmann::detail::lexer_base; @@ -17696,7 +17810,7 @@ class basic_json object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1")); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1", diagnostics_t())); // LCOV_EXCL_LINE } break; } @@ -18265,7 +18379,7 @@ class basic_json // if object is wanted but impossible, throw an exception if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { - JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + JSON_THROW(type_error::create(301, "cannot create object from initializer list", diagnostics_t())); } } @@ -18586,7 +18700,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(201, diagnostics() + "iterators are not compatible")); + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", diagnostics_t())); } // copy type from first iterator @@ -18604,7 +18718,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", diagnostics_t())); } break; } @@ -18678,8 +18792,7 @@ class basic_json } default: - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + - std::string(first.m_object->type_name()))); + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), diagnostics_t())); } assert_invariant(); @@ -19399,60 +19512,6 @@ class basic_json /// @} - std::string diagnostics() const - { -#if JSON_DIAGNOSTICS - std::vector tokens; - for (const basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) - { - switch (current->m_parent->type()) - { - case value_t::array: - { - for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) - { - if (current->m_parent->m_value.array->operator[](i) == *current) - { - tokens.emplace_back(std::to_string(i)); - continue; - } - } - break; - } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_value.object) - { - if (element.second == *current) - { - tokens.emplace_back(element.first.c_str()); - continue; - } - } - break; - } - - default: - break; - } - } - - if (tokens.empty()) - { - return ""; - } - - return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + b; - }) + ") "; -#else - return ""; -#endif - } - private: ////////////////// // value access // @@ -19466,7 +19525,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(type_error::create(302, diagnostics() + "type must be boolean, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), diagnostics_t(*this))); } /// get a pointer to the value (object) @@ -19587,7 +19646,7 @@ class basic_json return *ptr; } - JSON_THROW(type_error::create(303, obj.diagnostics() + "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), diagnostics_t(obj))); } public: @@ -20015,7 +20074,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, diagnostics() + "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), diagnostics_t(*this))); } return *get_ptr(); @@ -20026,7 +20085,7 @@ class basic_json { if (!is_binary()) { - JSON_THROW(type_error::create(302, diagnostics() + "type must be binary, but is " + std::string(type_name()))); + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), diagnostics_t(*this))); } return *get_ptr(); @@ -20087,12 +20146,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", diagnostics_t(*this))); } } else { - JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -20134,12 +20193,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", diagnostics_t(*this))); } } else { - JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -20191,12 +20250,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", diagnostics_t(*this))); } } else { - JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -20242,12 +20301,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", diagnostics_t(*this))); } } else { - JSON_THROW(type_error::create(304, diagnostics() + "cannot use at() with " + std::string(type_name()))); + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -20311,7 +20370,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -20341,7 +20400,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -20393,7 +20452,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -20435,7 +20494,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -20489,7 +20548,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -20533,7 +20592,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, diagnostics() + "cannot use operator[] with a string argument with " + std::string(type_name()))); + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -20605,7 +20664,7 @@ class basic_json return default_value; } - JSON_THROW(type_error::create(306, diagnostics() + "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -20678,7 +20737,7 @@ class basic_json } } - JSON_THROW(type_error::create(306, diagnostics() + "cannot use value() with " + std::string(type_name()))); + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -20832,7 +20891,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } IteratorType result = end(); @@ -20848,7 +20907,7 @@ class basic_json { if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) { - JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + JSON_THROW(invalid_iterator::create(205, "iterator out of range", diagnostics_t(*this))); } if (is_string()) @@ -20884,7 +20943,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), diagnostics_t(*this))); } return result; @@ -20945,7 +21004,7 @@ class basic_json // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { - JSON_THROW(invalid_iterator::create(203, diagnostics() + "iterators do not fit current value")); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", diagnostics_t(*this))); } IteratorType result = end(); @@ -20962,7 +21021,7 @@ class basic_json if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, diagnostics() + "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", diagnostics_t(*this))); } if (is_string()) @@ -21000,7 +21059,7 @@ class basic_json } default: - JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), diagnostics_t(*this))); } return result; @@ -21043,7 +21102,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -21077,14 +21136,14 @@ class basic_json { if (JSON_HEDLEY_UNLIKELY(idx >= size())) { - JSON_THROW(out_of_range::create(401, diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", diagnostics_t(*this))); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error::create(307, diagnostics() + "cannot use erase() with " + std::string(type_name()))); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -22029,7 +22088,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an array @@ -22067,7 +22126,7 @@ class basic_json // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an array @@ -22120,7 +22179,7 @@ class basic_json // push_back only works for null objects or objects if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(308, diagnostics() + "cannot use push_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an object @@ -22228,7 +22287,7 @@ class basic_json // emplace_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(311, diagnostics() + "cannot use emplace_back() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an array @@ -22290,7 +22349,7 @@ class basic_json // emplace only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(311, diagnostics() + "cannot use emplace() with " + std::string(type_name()))); + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), diagnostics_t(*this))); } // transform null object into an object @@ -22366,7 +22425,7 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } // insert to array and return iterator @@ -22379,7 +22438,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -22423,7 +22482,7 @@ class basic_json // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } // insert to array and return iterator @@ -22439,7 +22498,7 @@ class basic_json #endif } - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } /*! @@ -22477,24 +22536,24 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", diagnostics_t(*this))); } if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - JSON_THROW(invalid_iterator::create(211, diagnostics() + "passed iterators may not belong to container")); + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", diagnostics_t(*this))); } // insert to array and return iterator @@ -22539,13 +22598,13 @@ class basic_json // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", diagnostics_t(*this))); } // insert to array and return iterator @@ -22590,19 +22649,19 @@ class basic_json // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(309, diagnostics() + "cannot use insert() with " + std::string(type_name()))); + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", diagnostics_t(*this))); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", diagnostics_t(*this))); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); @@ -22639,11 +22698,11 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), diagnostics_t(*this))); } if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(j.type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), diagnostics_t(*this))); } for (auto it = j.cbegin(); it != j.cend(); ++it) @@ -22690,20 +22749,20 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, diagnostics() + "cannot use update() with " + std::string(type_name()))); + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), diagnostics_t(*this))); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, diagnostics() + "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", diagnostics_t(*this))); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() || !last.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, diagnostics() + "iterators first and last must point to objects")); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", diagnostics_t(*this))); } for (auto it = first; it != last; ++it) @@ -22798,7 +22857,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -22831,7 +22890,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -22864,7 +22923,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -22897,7 +22956,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -22911,7 +22970,7 @@ class basic_json } else { - JSON_THROW(type_error::create(310, diagnostics() + "cannot use swap() with " + std::string(type_name()))); + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), diagnostics_t(*this))); } } @@ -25120,7 +25179,7 @@ class basic_json if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior - JSON_THROW(out_of_range::create(401, parent.diagnostics() + "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", diagnostics_t(parent))); } // default case: insert add offset @@ -25154,7 +25213,7 @@ class basic_json } else { - JSON_THROW(out_of_range::create(403, diagnostics() + "key '" + last_path + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", diagnostics_t(*this))); } } else if (parent.is_array()) @@ -25167,7 +25226,7 @@ class basic_json // type check: top level value must be an array if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, diagnostics() + "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", diagnostics_t(json_patch))); } // iterate and apply the operations @@ -25187,13 +25246,13 @@ class basic_json // check if desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { - JSON_THROW(parse_error::create(105, 0, diagnostics() + error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", diagnostics_t(val))); } // check if result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { - JSON_THROW(parse_error::create(105, 0, diagnostics() + error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", diagnostics_t(val))); } // no error: return value @@ -25203,7 +25262,7 @@ class basic_json // type check: every element of the array must be an object if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { - JSON_THROW(parse_error::create(104, 0, diagnostics() + "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", diagnostics_t(val))); } // collect mandatory members @@ -25281,7 +25340,7 @@ class basic_json // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, val.diagnostics() + "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), diagnostics_t(val))); } break; @@ -25291,7 +25350,7 @@ class basic_json { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error::create(105, 0, diagnostics() + "operation value '" + op + "' is invalid")); + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", diagnostics_t(val))); } } } diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 53570113b7..9d1966f0ad 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -692,7 +692,11 @@ TEST_CASE("JSON patch") json patch = {"op", "add", "path", "", "value", 1}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.104] parse error: (/0) JSON patch must be an array of objects"); +#else "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects"); +#endif } SECTION("missing 'op'") @@ -701,7 +705,11 @@ TEST_CASE("JSON patch") json patch = {{{"foo", "bar"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation must have member 'op'"); +#else "[json.exception.parse_error.105] parse error: operation must have member 'op'"); +#endif } SECTION("non-string 'op'") @@ -710,7 +718,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation must have string member 'op'"); +#else "[json.exception.parse_error.105] parse error: operation must have string member 'op'"); +#endif } SECTION("invalid operation") @@ -719,7 +731,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "foo"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation value 'foo' is invalid"); +#else "[json.exception.parse_error.105] parse error: operation value 'foo' is invalid"); +#endif } } @@ -731,7 +747,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "add"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'"); +#endif } SECTION("non-string 'path'") @@ -740,7 +760,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "add"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have string member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'"); +#endif } SECTION("missing 'value'") @@ -749,7 +773,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "add"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'value'"); +#else "[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'"); +#endif } SECTION("invalid array index") @@ -770,7 +798,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "remove"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'"); +#endif } SECTION("non-string 'path'") @@ -779,7 +811,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "remove"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have string member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'"); +#endif } SECTION("nonexisting target location (array)") @@ -818,7 +854,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "replace"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'"); +#endif } SECTION("non-string 'path'") @@ -827,7 +867,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "replace"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have string member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'"); +#endif } SECTION("missing 'value'") @@ -836,7 +880,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "replace"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'value'"); +#else "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'"); +#endif } SECTION("nonexisting target location (array)") @@ -866,7 +914,12 @@ TEST_CASE("JSON patch") json patch = {{{"op", "move"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'"); +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'path'" +#else + "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'" +#endif + ); } SECTION("non-string 'path'") @@ -875,7 +928,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "move"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'"); +#endif } SECTION("missing 'from'") @@ -884,7 +941,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "move"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'from'"); +#else "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'"); +#endif } SECTION("non-string 'from'") @@ -893,7 +954,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'from'"); +#else "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'"); +#endif } SECTION("nonexisting from location (array)") @@ -923,7 +988,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "copy"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'"); +#endif } SECTION("non-string 'path'") @@ -932,7 +1001,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "copy"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'"); +#endif } SECTION("missing 'from'") @@ -941,7 +1014,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "copy"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'from'"); +#else "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'"); +#endif } SECTION("non-string 'from'") @@ -950,7 +1027,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'from'"); +#else "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'"); +#endif } SECTION("nonexisting from location (array)") @@ -980,7 +1061,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "test"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'"); +#endif } SECTION("non-string 'path'") @@ -989,7 +1074,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "test"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have string member 'path'"); +#else "[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'"); +#endif } SECTION("missing 'value'") @@ -998,7 +1087,11 @@ TEST_CASE("JSON patch") json patch = {{{"op", "test"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); CHECK_THROWS_WITH(j.patch(patch), +#if JSON_DIAGNOSTICS + "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'value'"); +#else "[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'"); +#endif } } } From a83404525e3b810c790bdaac97f0a8ca773097b7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 10 Jan 2021 13:39:36 +0100 Subject: [PATCH 16/38] :rotating_light: fix warnings --- include/nlohmann/detail/value_t.hpp | 2 +- include/nlohmann/json.hpp | 14 ++-- single_include/nlohmann/json.hpp | 16 ++-- test/src/unit-json_patch.cpp | 116 +++++++++++----------------- 4 files changed, 62 insertions(+), 86 deletions(-) diff --git a/include/nlohmann/detail/value_t.hpp b/include/nlohmann/detail/value_t.hpp index 0383df06f8..a98c4355a0 100644 --- a/include/nlohmann/detail/value_t.hpp +++ b/include/nlohmann/detail/value_t.hpp @@ -32,7 +32,7 @@ number_float), because the library distinguishes these three types for numbers: @ref basic_json::number_float_t is used for floating-point numbers or to approximate integers which do not fit in the limits of their respective type. -@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON value with the default value for a given type @since version 1.0.0 diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 086c0f5572..5a913c9643 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -5746,7 +5746,7 @@ class basic_json iterator result = insert_iterator(pos, cnt, val); for (size_type i = 0; i < cnt; ++i) { - (result + i)->m_parent = this; + (result + static_cast(i))->m_parent = this; } return result; #else @@ -5815,7 +5815,7 @@ class basic_json // insert to array and return iterator #if JSON_DIAGNOSTICS iterator result = insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); - for (std::size_t i = 0; i < std::distance(first, last); ++i) + for (typename iterator::difference_type i = 0; i < std::distance(first, last); ++i) { (result + i)->m_parent = this; } @@ -5869,7 +5869,7 @@ class basic_json iterator result = insert_iterator(pos, ilist.begin(), ilist.end()); for (std::size_t i = 0; i < size; ++i) { - (result + i)->m_parent = this; + (result + static_cast(i))->m_parent = this; } return result; #else @@ -8391,7 +8391,7 @@ class basic_json }; // wrapper for "add" operation; add value at ptr - const auto operation_add = [this, &result](json_pointer & ptr, basic_json val) + const auto operation_add = [&result](json_pointer & ptr, basic_json val) { // adding to the root of the target document means replacing it if (ptr.empty()) @@ -8489,9 +8489,9 @@ class basic_json for (const auto& val : json_patch) { // wrapper to get a value for an operation - const auto get_value = [this, &val](const std::string & op, - const std::string & member, - bool string_type) -> basic_json & + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & { // find value auto it = val.m_value.object->find(member); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index bd79bd6fd3..c8a4116f93 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -113,7 +113,7 @@ number_float), because the library distinguishes these three types for numbers: @ref basic_json::number_float_t is used for floating-point numbers or to approximate integers which do not fit in the limits of their respective type. -@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON value with the default value for a given type @since version 1.0.0 @@ -22490,7 +22490,7 @@ class basic_json iterator result = insert_iterator(pos, cnt, val); for (size_type i = 0; i < cnt; ++i) { - (result + i)->m_parent = this; + (result + static_cast(i))->m_parent = this; } return result; #else @@ -22559,7 +22559,7 @@ class basic_json // insert to array and return iterator #if JSON_DIAGNOSTICS iterator result = insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); - for (std::size_t i = 0; i < std::distance(first, last); ++i) + for (typename iterator::difference_type i = 0; i < std::distance(first, last); ++i) { (result + i)->m_parent = this; } @@ -22613,7 +22613,7 @@ class basic_json iterator result = insert_iterator(pos, ilist.begin(), ilist.end()); for (std::size_t i = 0; i < size; ++i) { - (result + i)->m_parent = this; + (result + static_cast(i))->m_parent = this; } return result; #else @@ -25135,7 +25135,7 @@ class basic_json }; // wrapper for "add" operation; add value at ptr - const auto operation_add = [this, &result](json_pointer & ptr, basic_json val) + const auto operation_add = [&result](json_pointer & ptr, basic_json val) { // adding to the root of the target document means replacing it if (ptr.empty()) @@ -25233,9 +25233,9 @@ class basic_json for (const auto& val : json_patch) { // wrapper to get a value for an operation - const auto get_value = [this, &val](const std::string & op, - const std::string & member, - bool string_type) -> basic_json & + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & { // find value auto it = val.m_value.object->find(member); diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 9d1966f0ad..af44e42326 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -691,11 +691,10 @@ TEST_CASE("JSON patch") json j; json patch = {"op", "add", "path", "", "value", 1}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.104] parse error: (/0) JSON patch must be an array of objects"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.104] parse error: (/0) JSON patch must be an array of objects"); #else - "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects"); #endif } @@ -704,11 +703,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"foo", "bar"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation must have member 'op'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have member 'op'"); #else - "[json.exception.parse_error.105] parse error: operation must have member 'op'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have member 'op'"); #endif } @@ -717,11 +715,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation must have string member 'op'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have string member 'op'"); #else - "[json.exception.parse_error.105] parse error: operation must have string member 'op'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have string member 'op'"); #endif } @@ -730,11 +727,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "foo"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation value 'foo' is invalid"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation value 'foo' is invalid"); #else - "[json.exception.parse_error.105] parse error: operation value 'foo' is invalid"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation value 'foo' is invalid"); #endif } } @@ -746,11 +742,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "add"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'"); #endif } @@ -759,11 +754,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "add"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have string member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'"); #endif } @@ -772,11 +766,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "add"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'value'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'value'"); #else - "[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'"); #endif } @@ -797,11 +790,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "remove"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'"); #endif } @@ -810,11 +802,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "remove"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have string member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'"); #endif } @@ -853,11 +844,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "replace"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'"); #endif } @@ -866,11 +856,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "replace"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have string member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'"); #endif } @@ -879,11 +868,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "replace"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'value'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'value'"); #else - "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'"); #endif } @@ -913,13 +901,11 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "move"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'path'" + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'" + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'"); #endif - ); } SECTION("non-string 'path'") @@ -927,11 +913,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "move"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'"); #endif } @@ -940,11 +925,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "move"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'from'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'from'"); #else - "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'"); #endif } @@ -953,11 +937,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'from'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'from'"); #else - "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'"); #endif } @@ -987,11 +970,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "copy"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'"); #endif } @@ -1000,11 +982,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "copy"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'"); #endif } @@ -1013,11 +994,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "copy"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'from'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'from'"); #else - "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'"); #endif } @@ -1026,11 +1006,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'from'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'from'"); #else - "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'"); #endif } @@ -1060,11 +1039,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "test"}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'"); #endif } @@ -1073,11 +1051,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "test"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have string member 'path'"); #else - "[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'"); #endif } @@ -1086,11 +1063,10 @@ TEST_CASE("JSON patch") json j; json patch = {{{"op", "test"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), #if JSON_DIAGNOSTICS - "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'value'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'value'"); #else - "[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'"); + CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'"); #endif } } From 1d6ba22f158dae41726636d05cf001b08a371c59 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 10 Jan 2021 14:10:59 +0100 Subject: [PATCH 17/38] :recycle: simplify code --- include/nlohmann/detail/output/binary_writer.hpp | 7 ++++--- include/nlohmann/detail/output/serializer.hpp | 5 +++-- single_include/nlohmann/json.hpp | 12 +++++++----- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 10bc34011b..b8314402cc 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -30,6 +30,7 @@ class binary_writer using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; using number_float_t = typename BasicJsonType::number_float_t; + using diagnostics_t = detail::diagnostics_t; public: /*! @@ -58,7 +59,7 @@ class binary_writer default: { - JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), detail::diagnostics_t(j)));; + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), diagnostics_t(j)));; } } } @@ -907,7 +908,7 @@ class binary_writer const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", detail::diagnostics_t(j))); + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", diagnostics_t(j))); } return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; @@ -1031,7 +1032,7 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", detail::diagnostics_t(j))); + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", diagnostics_t(j))); } } diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index 3384141805..7c570f8cb7 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -45,6 +45,7 @@ class serializer using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using binary_char_t = typename BasicJsonType::binary_t::value_type; + using diagnostics_t = detail::diagnostics_t; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; @@ -500,7 +501,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, detail::diagnostics_t())); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, diagnostics_t())); } case error_handler_t::ignore: @@ -594,7 +595,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, detail::diagnostics_t())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, diagnostics_t())); } case error_handler_t::ignore: diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index c8a4116f93..0aab317974 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12992,6 +12992,7 @@ class binary_writer using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; using number_float_t = typename BasicJsonType::number_float_t; + using diagnostics_t = detail::diagnostics_t; public: /*! @@ -13020,7 +13021,7 @@ class binary_writer default: { - JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), detail::diagnostics_t(j)));; + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), diagnostics_t(j)));; } } } @@ -13869,7 +13870,7 @@ class binary_writer const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", detail::diagnostics_t(j))); + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", diagnostics_t(j))); } return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; @@ -13993,7 +13994,7 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", detail::diagnostics_t(j))); + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", diagnostics_t(j))); } } @@ -15720,6 +15721,7 @@ class serializer using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using binary_char_t = typename BasicJsonType::binary_t::value_type; + using diagnostics_t = detail::diagnostics_t; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; @@ -16175,7 +16177,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, detail::diagnostics_t())); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, diagnostics_t())); } case error_handler_t::ignore: @@ -16269,7 +16271,7 @@ class serializer { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, detail::diagnostics_t())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, diagnostics_t())); } case error_handler_t::ignore: From 9d0150c234caa1e6b0ea24f4735153d5f43030e6 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 10 Jan 2021 15:04:14 +0100 Subject: [PATCH 18/38] :recycle: simplify code --- include/nlohmann/json.hpp | 181 ++++++++++--------------------- single_include/nlohmann/json.hpp | 181 ++++++++++--------------------- 2 files changed, 118 insertions(+), 244 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 5a913c9643..cbfde69280 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1242,6 +1242,47 @@ class basic_json JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); } + reference set_parent(reference j, bool recursive) + { +#if JSON_DIAGNOSTICS + if (recursive) + { + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + default: + break; + } + } + else + { + j.m_parent = this; + } +#else + static_cast(j); + static_cast(recursive); +#endif + + return j; + } + public: ////////////////////////// // JSON parser callback // @@ -1651,12 +1692,7 @@ class basic_json auto res = m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); - -#if JSON_DIAGNOSTICS - res.first->second.m_parent = this; -#else - static_cast(res); // unused variable - fix warning -#endif + set_parent(res.first->second, false); } } else @@ -1664,12 +1700,7 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.array) - { - element.m_parent = this; - } -#endif + set_parent(*this, true); } assert_invariant(); @@ -1881,12 +1912,7 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.array) - { - element.m_parent = this; - } -#endif + set_parent(*this, true); assert_invariant(); } @@ -2019,12 +2045,7 @@ class basic_json { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.object) - { - element.second.m_parent = this; - } -#endif + set_parent(*this, true); break; } @@ -2032,12 +2053,7 @@ class basic_json { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.array) - { - element.m_parent = this; - } -#endif + set_parent(*this, true); break; } @@ -2100,24 +2116,14 @@ class basic_json case value_t::object: { m_value = *other.m_value.object; -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.object) - { - element.second.m_parent = this; - } -#endif + set_parent(*this, true); break; } case value_t::array: { m_value = *other.m_value.array; -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.array) - { - element.m_parent = this; - } -#endif + set_parent(*this, true); break; } @@ -2201,32 +2207,7 @@ class basic_json other.m_type = value_t::null; other.m_value = {}; -#if JSON_DIAGNOSTICS - switch (m_type) - { - case value_t::array: - { - for (auto& element : *m_value.array) - { - element.m_parent = this; - } - break; - } - - case value_t::object: - { - for (auto& element : *m_value.object) - { - element.second.m_parent = this; - } - break; - } - - default: - break; - } -#endif - + set_parent(*this, true); assert_invariant(); } @@ -3391,13 +3372,7 @@ class basic_json { JSON_TRY { -#if JSON_DIAGNOSTICS - reference result = m_value.array->at(idx); - result.m_parent = this; - return result; -#else - return m_value.array->at(idx); -#endif + return set_parent(m_value.array->at(idx), false); } JSON_CATCH (std::out_of_range&) { @@ -3495,13 +3470,7 @@ class basic_json { JSON_TRY { -#if JSON_DIAGNOSTICS - reference result = m_value.object->at(key); - result.m_parent = this; - return result; -#else - return m_value.object->at(key); -#endif + return set_parent(m_value.object->at(key), false); } JSON_CATCH (std::out_of_range&) { @@ -3699,13 +3668,7 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { -#if JSON_DIAGNOSTICS - reference result = m_value.object->operator[](key); - result.m_parent = this; - return result; -#else - return m_value.object->operator[](key); -#endif + return set_parent(m_value.object->operator[](key), false); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); @@ -3795,13 +3758,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { -#if JSON_DIAGNOSTICS - reference result = m_value.object->operator[](key); - result.m_parent = this; - return result; -#else - return m_value.object->operator[](key); -#endif + return set_parent(m_value.object->operator[](key), false); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); @@ -5357,9 +5314,7 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); -#if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; -#endif + set_parent(m_value.array->back(), false); // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -5395,9 +5350,7 @@ class basic_json // add element to array m_value.array->push_back(val); -#if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; -#endif + set_parent(m_value.array->back(), false); } /*! @@ -5447,12 +5400,8 @@ class basic_json } // add element to object -#if JSON_DIAGNOSTICS auto res = m_value.object->insert(val); - res.first->second.m_parent = this; -#else - m_value.object->insert(val); -#endif + set_parent(res.first->second, false); } /*! @@ -5556,19 +5505,10 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 -#if JSON_DIAGNOSTICS - reference result = m_value.array->emplace_back(std::forward(args)...); - result.m_parent = this; - return result; -#else - return m_value.array->emplace_back(std::forward(args)...); -#endif + return set_parent(m_value.array->emplace_back(std::forward(args)...), false); #else m_value.array->emplace_back(std::forward(args)...); -#if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; -#endif - return m_value.array->back(); + return set_parent(m_value.array->back(), false); #endif } @@ -5618,10 +5558,7 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); - -#if JSON_DIAGNOSTICS - res.first->second.m_parent = this; -#endif + set_parent(res.first->second, false); // create result iterator and set iterator to the result of emplace auto it = begin(); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 0aab317974..4d079db097 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17988,6 +17988,47 @@ class basic_json JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); } + reference set_parent(reference j, bool recursive) + { +#if JSON_DIAGNOSTICS + if (recursive) + { + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + default: + break; + } + } + else + { + j.m_parent = this; + } +#else + static_cast(j); + static_cast(recursive); +#endif + + return j; + } + public: ////////////////////////// // JSON parser callback // @@ -18397,12 +18438,7 @@ class basic_json auto res = m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); - -#if JSON_DIAGNOSTICS - res.first->second.m_parent = this; -#else - static_cast(res); // unused variable - fix warning -#endif + set_parent(res.first->second, false); } } else @@ -18410,12 +18446,7 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.array) - { - element.m_parent = this; - } -#endif + set_parent(*this, true); } assert_invariant(); @@ -18627,12 +18658,7 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.array) - { - element.m_parent = this; - } -#endif + set_parent(*this, true); assert_invariant(); } @@ -18765,12 +18791,7 @@ class basic_json { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.object) - { - element.second.m_parent = this; - } -#endif + set_parent(*this, true); break; } @@ -18778,12 +18799,7 @@ class basic_json { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.array) - { - element.m_parent = this; - } -#endif + set_parent(*this, true); break; } @@ -18846,24 +18862,14 @@ class basic_json case value_t::object: { m_value = *other.m_value.object; -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.object) - { - element.second.m_parent = this; - } -#endif + set_parent(*this, true); break; } case value_t::array: { m_value = *other.m_value.array; -#if JSON_DIAGNOSTICS - for (auto& element : *m_value.array) - { - element.m_parent = this; - } -#endif + set_parent(*this, true); break; } @@ -18947,32 +18953,7 @@ class basic_json other.m_type = value_t::null; other.m_value = {}; -#if JSON_DIAGNOSTICS - switch (m_type) - { - case value_t::array: - { - for (auto& element : *m_value.array) - { - element.m_parent = this; - } - break; - } - - case value_t::object: - { - for (auto& element : *m_value.object) - { - element.second.m_parent = this; - } - break; - } - - default: - break; - } -#endif - + set_parent(*this, true); assert_invariant(); } @@ -20137,13 +20118,7 @@ class basic_json { JSON_TRY { -#if JSON_DIAGNOSTICS - reference result = m_value.array->at(idx); - result.m_parent = this; - return result; -#else - return m_value.array->at(idx); -#endif + return set_parent(m_value.array->at(idx), false); } JSON_CATCH (std::out_of_range&) { @@ -20241,13 +20216,7 @@ class basic_json { JSON_TRY { -#if JSON_DIAGNOSTICS - reference result = m_value.object->at(key); - result.m_parent = this; - return result; -#else - return m_value.object->at(key); -#endif + return set_parent(m_value.object->at(key), false); } JSON_CATCH (std::out_of_range&) { @@ -20445,13 +20414,7 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { -#if JSON_DIAGNOSTICS - reference result = m_value.object->operator[](key); - result.m_parent = this; - return result; -#else - return m_value.object->operator[](key); -#endif + return set_parent(m_value.object->operator[](key), false); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); @@ -20541,13 +20504,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { -#if JSON_DIAGNOSTICS - reference result = m_value.object->operator[](key); - result.m_parent = this; - return result; -#else - return m_value.object->operator[](key); -#endif + return set_parent(m_value.object->operator[](key), false); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); @@ -22103,9 +22060,7 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); -#if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; -#endif + set_parent(m_value.array->back(), false); // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -22141,9 +22096,7 @@ class basic_json // add element to array m_value.array->push_back(val); -#if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; -#endif + set_parent(m_value.array->back(), false); } /*! @@ -22193,12 +22146,8 @@ class basic_json } // add element to object -#if JSON_DIAGNOSTICS auto res = m_value.object->insert(val); - res.first->second.m_parent = this; -#else - m_value.object->insert(val); -#endif + set_parent(res.first->second, false); } /*! @@ -22302,19 +22251,10 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 -#if JSON_DIAGNOSTICS - reference result = m_value.array->emplace_back(std::forward(args)...); - result.m_parent = this; - return result; -#else - return m_value.array->emplace_back(std::forward(args)...); -#endif + return set_parent(m_value.array->emplace_back(std::forward(args)...), false); #else m_value.array->emplace_back(std::forward(args)...); -#if JSON_DIAGNOSTICS - m_value.array->back().m_parent = this; -#endif - return m_value.array->back(); + return set_parent(m_value.array->back(), false); #endif } @@ -22364,10 +22304,7 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); - -#if JSON_DIAGNOSTICS - res.first->second.m_parent = this; -#endif + set_parent(res.first->second, false); // create result iterator and set iterator to the result of emplace auto it = begin(); From ff57bdcc8bc55bd1db425573bce3b8b899f4025f Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 10 Jan 2021 22:40:50 +0100 Subject: [PATCH 19/38] :bug: fix invariants --- .../nlohmann/detail/conversions/to_json.hpp | 10 +++ include/nlohmann/detail/input/json_sax.hpp | 33 +++---- include/nlohmann/detail/input/parser.hpp | 4 +- include/nlohmann/json.hpp | 42 ++++++--- single_include/nlohmann/json.hpp | 89 ++++++++++++------- 5 files changed, 118 insertions(+), 60 deletions(-) diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index b45004fd42..eeb7865231 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -132,6 +132,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = arr; + j.set_parent(j, true); j.assert_invariant(); } @@ -140,6 +141,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = std::move(arr); + j.set_parent(j, true); j.assert_invariant(); } @@ -152,6 +154,7 @@ struct external_constructor using std::end; j.m_type = value_t::array; j.m_value.array = j.template create(begin(arr), end(arr)); + j.set_parent(j, true); j.assert_invariant(); } @@ -164,6 +167,9 @@ struct external_constructor for (const bool x : arr) { j.m_value.array->push_back(x); +#if JSON_DIAGNOSTICS + j.m_value.array->back().m_parent = &j; +#endif } j.assert_invariant(); } @@ -179,6 +185,7 @@ struct external_constructor { std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); } + j.set_parent(j, true); j.assert_invariant(); } }; @@ -191,6 +198,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = obj; + j.set_parent(j, true); j.assert_invariant(); } @@ -199,6 +207,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = std::move(obj); + j.set_parent(j, true); j.assert_invariant(); } @@ -211,6 +220,7 @@ struct external_constructor j.m_type = value_t::object; j.m_value.object = j.template create(begin(obj), end(obj)); + j.set_parent(j, true); j.assert_invariant(); } }; diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index f29aa1a682..3088cbbcd8 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -236,6 +236,7 @@ class json_sax_dom_parser bool end_object() { + ref_stack.back()->set_parent(*ref_stack.back(), true); ref_stack.pop_back(); return true; } @@ -254,6 +255,7 @@ class json_sax_dom_parser bool end_array() { + ref_stack.back()->set_parent(*ref_stack.back(), true); ref_stack.pop_back(); return true; } @@ -298,18 +300,12 @@ class json_sax_dom_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::forward(v)); -#if JSON_DIAGNOSTICS - ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); -#endif return &(ref_stack.back()->m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); -#if JSON_DIAGNOSTICS - object_element->m_parent = ref_stack.back(); -#endif return object_element; } @@ -432,10 +428,17 @@ class json_sax_dom_callback_parser bool end_object() { - if (ref_stack.back() && !callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + if (ref_stack.back()) { - // discard object - *ref_stack.back() = discarded; + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parent(*ref_stack.back(), true); + } } JSON_ASSERT(!ref_stack.empty()); @@ -483,7 +486,11 @@ class json_sax_dom_callback_parser if (ref_stack.back()) { keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (!keep) + if (keep) + { + ref_stack.back()->set_parent(*ref_stack.back(), true); + } + else { // discard array *ref_stack.back() = discarded; @@ -582,9 +589,6 @@ class json_sax_dom_callback_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::move(value)); -#if JSON_DIAGNOSTICS - ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); -#endif return {true, &(ref_stack.back()->m_value.array->back())}; } @@ -602,9 +606,6 @@ class json_sax_dom_callback_parser JSON_ASSERT(object_element); *object_element = std::move(value); -#if JSON_DIAGNOSTICS - object_element->m_parent = ref_stack.back(); -#endif return {true, object_element}; } diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 40ef371eb9..d9ac2b684c 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -90,7 +90,6 @@ class parser { json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); sax_parse_internal(&sdp); - result.assert_invariant(); // in strict mode, input must be completely read if (strict && (get_token() != token_type::end_of_input)) @@ -119,7 +118,6 @@ class parser { json_sax_dom_parser sdp(result, allow_exceptions); sax_parse_internal(&sdp); - result.assert_invariant(); // in strict mode, input must be completely read if (strict && (get_token() != token_type::end_of_input)) @@ -137,6 +135,8 @@ class parser return; } } + + result.assert_invariant(); } /*! diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index cbfde69280..10fff0baeb 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1233,13 +1233,29 @@ class basic_json invariant. Furthermore, it has to be called each time the type of a JSON value is changed, because the invariant expresses a relationship between @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to true + during destruction of objects when the invariant does not + need to hold. */ - void assert_invariant() const noexcept + void assert_invariant(bool check_parents = true) const noexcept { JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); +#endif } reference set_parent(reference j, bool recursive) @@ -1497,6 +1513,7 @@ class basic_json std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); + set_parent(*this, true); assert_invariant(); } @@ -1575,6 +1592,7 @@ class basic_json default: // LCOV_EXCL_LINE JSON_ASSERT(false); // LCOV_EXCL_LINE } + set_parent(*this, true); assert_invariant(); } @@ -1689,10 +1707,9 @@ class basic_json for (auto& element_ref : init) { auto element = element_ref.moved_or_copied(); - auto res = m_value.object->emplace( - std::move(*((*element.m_value.array)[0].m_value.string)), - std::move((*element.m_value.array)[1])); - set_parent(res.first->second, false); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); } } else @@ -1700,9 +1717,9 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); - set_parent(*this, true); } + set_parent(*this, true); assert_invariant(); } @@ -2045,7 +2062,6 @@ class basic_json { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); - set_parent(*this, true); break; } @@ -2053,7 +2069,6 @@ class basic_json { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); - set_parent(*this, true); break; } @@ -2067,6 +2082,7 @@ class basic_json JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), diagnostics_t())); } + set_parent(*this, true); assert_invariant(); } @@ -2116,14 +2132,12 @@ class basic_json case value_t::object: { m_value = *other.m_value.object; - set_parent(*this, true); break; } case value_t::array: { m_value = *other.m_value.array; - set_parent(*this, true); break; } @@ -2167,6 +2181,7 @@ class basic_json break; } + set_parent(*this, true); assert_invariant(); } @@ -2201,7 +2216,7 @@ class basic_json m_value(std::move(other.m_value)) { // check that passed value is valid - other.assert_invariant(); + other.assert_invariant(false); // invalidate payload other.m_type = value_t::null; @@ -2248,6 +2263,7 @@ class basic_json swap(m_type, other.m_type); swap(m_value, other.m_value); + set_parent(*this, true); assert_invariant(); return *this; } @@ -2269,7 +2285,7 @@ class basic_json */ ~basic_json() noexcept { - assert_invariant(); + assert_invariant(false); m_value.destroy(m_type); } @@ -5990,6 +6006,8 @@ class basic_json { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); + + set_parent(*this, true); assert_invariant(); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4d079db097..2d98dce466 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4302,6 +4302,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = arr; + j.set_parent(j, true); j.assert_invariant(); } @@ -4310,6 +4311,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = std::move(arr); + j.set_parent(j, true); j.assert_invariant(); } @@ -4322,6 +4324,7 @@ struct external_constructor using std::end; j.m_type = value_t::array; j.m_value.array = j.template create(begin(arr), end(arr)); + j.set_parent(j, true); j.assert_invariant(); } @@ -4334,6 +4337,9 @@ struct external_constructor for (const bool x : arr) { j.m_value.array->push_back(x); +#if JSON_DIAGNOSTICS + j.m_value.array->back().m_parent = &j; +#endif } j.assert_invariant(); } @@ -4349,6 +4355,7 @@ struct external_constructor { std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); } + j.set_parent(j, true); j.assert_invariant(); } }; @@ -4361,6 +4368,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = obj; + j.set_parent(j, true); j.assert_invariant(); } @@ -4369,6 +4377,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = std::move(obj); + j.set_parent(j, true); j.assert_invariant(); } @@ -4381,6 +4390,7 @@ struct external_constructor j.m_type = value_t::object; j.m_value.object = j.template create(begin(obj), end(obj)); + j.set_parent(j, true); j.assert_invariant(); } }; @@ -5628,6 +5638,7 @@ class json_sax_dom_parser bool end_object() { + ref_stack.back()->set_parent(*ref_stack.back(), true); ref_stack.pop_back(); return true; } @@ -5646,6 +5657,7 @@ class json_sax_dom_parser bool end_array() { + ref_stack.back()->set_parent(*ref_stack.back(), true); ref_stack.pop_back(); return true; } @@ -5690,18 +5702,12 @@ class json_sax_dom_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::forward(v)); -#if JSON_DIAGNOSTICS - ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); -#endif return &(ref_stack.back()->m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); -#if JSON_DIAGNOSTICS - object_element->m_parent = ref_stack.back(); -#endif return object_element; } @@ -5824,10 +5830,17 @@ class json_sax_dom_callback_parser bool end_object() { - if (ref_stack.back() && !callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + if (ref_stack.back()) { - // discard object - *ref_stack.back() = discarded; + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parent(*ref_stack.back(), true); + } } JSON_ASSERT(!ref_stack.empty()); @@ -5875,7 +5888,11 @@ class json_sax_dom_callback_parser if (ref_stack.back()) { keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (!keep) + if (keep) + { + ref_stack.back()->set_parent(*ref_stack.back(), true); + } + else { // discard array *ref_stack.back() = discarded; @@ -5974,9 +5991,6 @@ class json_sax_dom_callback_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_value.array->emplace_back(std::move(value)); -#if JSON_DIAGNOSTICS - ref_stack.back()->m_value.array->back().m_parent = ref_stack.back(); -#endif return {true, &(ref_stack.back()->m_value.array->back())}; } @@ -5994,9 +6008,6 @@ class json_sax_dom_callback_parser JSON_ASSERT(object_element); *object_element = std::move(value); -#if JSON_DIAGNOSTICS - object_element->m_parent = ref_stack.back(); -#endif return {true, object_element}; } @@ -10430,7 +10441,6 @@ class parser { json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); sax_parse_internal(&sdp); - result.assert_invariant(); // in strict mode, input must be completely read if (strict && (get_token() != token_type::end_of_input)) @@ -10459,7 +10469,6 @@ class parser { json_sax_dom_parser sdp(result, allow_exceptions); sax_parse_internal(&sdp); - result.assert_invariant(); // in strict mode, input must be completely read if (strict && (get_token() != token_type::end_of_input)) @@ -10477,6 +10486,8 @@ class parser return; } } + + result.assert_invariant(); } /*! @@ -17979,13 +17990,29 @@ class basic_json invariant. Furthermore, it has to be called each time the type of a JSON value is changed, because the invariant expresses a relationship between @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to true + during destruction of objects when the invariant does not + need to hold. */ - void assert_invariant() const noexcept + void assert_invariant(bool check_parents = true) const noexcept { JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); +#endif } reference set_parent(reference j, bool recursive) @@ -18243,6 +18270,7 @@ class basic_json std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); + set_parent(*this, true); assert_invariant(); } @@ -18321,6 +18349,7 @@ class basic_json default: // LCOV_EXCL_LINE JSON_ASSERT(false); // LCOV_EXCL_LINE } + set_parent(*this, true); assert_invariant(); } @@ -18435,10 +18464,9 @@ class basic_json for (auto& element_ref : init) { auto element = element_ref.moved_or_copied(); - auto res = m_value.object->emplace( - std::move(*((*element.m_value.array)[0].m_value.string)), - std::move((*element.m_value.array)[1])); - set_parent(res.first->second, false); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); } } else @@ -18446,9 +18474,9 @@ class basic_json // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); - set_parent(*this, true); } + set_parent(*this, true); assert_invariant(); } @@ -18791,7 +18819,6 @@ class basic_json { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); - set_parent(*this, true); break; } @@ -18799,7 +18826,6 @@ class basic_json { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); - set_parent(*this, true); break; } @@ -18813,6 +18839,7 @@ class basic_json JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), diagnostics_t())); } + set_parent(*this, true); assert_invariant(); } @@ -18862,14 +18889,12 @@ class basic_json case value_t::object: { m_value = *other.m_value.object; - set_parent(*this, true); break; } case value_t::array: { m_value = *other.m_value.array; - set_parent(*this, true); break; } @@ -18913,6 +18938,7 @@ class basic_json break; } + set_parent(*this, true); assert_invariant(); } @@ -18947,7 +18973,7 @@ class basic_json m_value(std::move(other.m_value)) { // check that passed value is valid - other.assert_invariant(); + other.assert_invariant(false); // invalidate payload other.m_type = value_t::null; @@ -18994,6 +19020,7 @@ class basic_json swap(m_type, other.m_type); swap(m_value, other.m_value); + set_parent(*this, true); assert_invariant(); return *this; } @@ -19015,7 +19042,7 @@ class basic_json */ ~basic_json() noexcept { - assert_invariant(); + assert_invariant(false); m_value.destroy(m_type); } @@ -22736,6 +22763,8 @@ class basic_json { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); + + set_parent(*this, true); assert_invariant(); } From b9d3aa40670a57cedeb71e52c192a7678f0e23c2 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 14 Jan 2021 21:55:49 +0100 Subject: [PATCH 20/38] :recycle: split set_parent function --- .../nlohmann/detail/conversions/to_json.hpp | 14 +-- include/nlohmann/detail/input/json_sax.hpp | 8 +- include/nlohmann/json.hpp | 84 +++++++------- single_include/nlohmann/json.hpp | 106 +++++++++--------- 4 files changed, 104 insertions(+), 108 deletions(-) diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index eeb7865231..f42735e834 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -132,7 +132,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = arr; - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -141,7 +141,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = std::move(arr); - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -154,7 +154,7 @@ struct external_constructor using std::end; j.m_type = value_t::array; j.m_value.array = j.template create(begin(arr), end(arr)); - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -185,7 +185,7 @@ struct external_constructor { std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); } - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } }; @@ -198,7 +198,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = obj; - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -207,7 +207,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = std::move(obj); - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -220,7 +220,7 @@ struct external_constructor j.m_type = value_t::object; j.m_value.object = j.template create(begin(obj), end(obj)); - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } }; diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 3088cbbcd8..4bd64c80e3 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -236,7 +236,7 @@ class json_sax_dom_parser bool end_object() { - ref_stack.back()->set_parent(*ref_stack.back(), true); + ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; } @@ -255,7 +255,7 @@ class json_sax_dom_parser bool end_array() { - ref_stack.back()->set_parent(*ref_stack.back(), true); + ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; } @@ -437,7 +437,7 @@ class json_sax_dom_callback_parser } else { - ref_stack.back()->set_parent(*ref_stack.back(), true); + ref_stack.back()->set_parents(); } } @@ -488,7 +488,7 @@ class json_sax_dom_callback_parser keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); if (keep) { - ref_stack.back()->set_parent(*ref_stack.back(), true); + ref_stack.back()->set_parents(); } else { diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 10fff0baeb..6eba29e95f 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1258,44 +1258,42 @@ class basic_json #endif } - reference set_parent(reference j, bool recursive) + void set_parents() { #if JSON_DIAGNOSTICS - if (recursive) + switch (m_type) { - switch (m_type) + case value_t::array: { - case value_t::array: + for (auto& element : *m_value.array) { - for (auto& element : *m_value.array) - { - element.m_parent = this; - } - break; + element.m_parent = this; } + break; + } - case value_t::object: + case value_t::object: + { + for (auto& element : *m_value.object) { - for (auto& element : *m_value.object) - { - element.second.m_parent = this; - } - break; + element.second.m_parent = this; } - - default: - break; + break; } + + default: + break; } - else - { - j.m_parent = this; - } +#endif + } + + reference set_parent(reference j) + { +#if JSON_DIAGNOSTICS + j.m_parent = this; #else static_cast(j); - static_cast(recursive); #endif - return j; } @@ -1513,7 +1511,7 @@ class basic_json std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -1592,7 +1590,7 @@ class basic_json default: // LCOV_EXCL_LINE JSON_ASSERT(false); // LCOV_EXCL_LINE } - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -1719,7 +1717,7 @@ class basic_json m_value.array = create(init.begin(), init.end()); } - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -1929,7 +1927,7 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -2082,7 +2080,7 @@ class basic_json JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), diagnostics_t())); } - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -2181,7 +2179,7 @@ class basic_json break; } - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -2222,7 +2220,7 @@ class basic_json other.m_type = value_t::null; other.m_value = {}; - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -2263,7 +2261,7 @@ class basic_json swap(m_type, other.m_type); swap(m_value, other.m_value); - set_parent(*this, true); + set_parents(); assert_invariant(); return *this; } @@ -3388,7 +3386,7 @@ class basic_json { JSON_TRY { - return set_parent(m_value.array->at(idx), false); + return set_parent(m_value.array->at(idx)); } JSON_CATCH (std::out_of_range&) { @@ -3486,7 +3484,7 @@ class basic_json { JSON_TRY { - return set_parent(m_value.object->at(key), false); + return set_parent(m_value.object->at(key)); } JSON_CATCH (std::out_of_range&) { @@ -3684,7 +3682,7 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return set_parent(m_value.object->operator[](key), false); + return set_parent(m_value.object->operator[](key)); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); @@ -3774,7 +3772,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return set_parent(m_value.object->operator[](key), false); + return set_parent(m_value.object->operator[](key)); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); @@ -5330,7 +5328,7 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); - set_parent(m_value.array->back(), false); + set_parent(m_value.array->back()); // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -5366,7 +5364,7 @@ class basic_json // add element to array m_value.array->push_back(val); - set_parent(m_value.array->back(), false); + set_parent(m_value.array->back()); } /*! @@ -5417,7 +5415,7 @@ class basic_json // add element to object auto res = m_value.object->insert(val); - set_parent(res.first->second, false); + set_parent(res.first->second); } /*! @@ -5521,10 +5519,10 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 - return set_parent(m_value.array->emplace_back(std::forward(args)...), false); + return set_parent(m_value.array->emplace_back(std::forward(args)...)); #else m_value.array->emplace_back(std::forward(args)...); - return set_parent(m_value.array->back(), false); + return set_parent(m_value.array->back()); #endif } @@ -5574,7 +5572,7 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); - set_parent(res.first->second, false); + set_parent(res.first->second); // create result iterator and set iterator to the result of emplace auto it = begin(); @@ -6007,7 +6005,7 @@ class basic_json std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); - set_parent(*this, true); + set_parents(); assert_invariant(); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 2d98dce466..98b26f039f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4302,7 +4302,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = arr; - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -4311,7 +4311,7 @@ struct external_constructor { j.m_type = value_t::array; j.m_value = std::move(arr); - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -4324,7 +4324,7 @@ struct external_constructor using std::end; j.m_type = value_t::array; j.m_value.array = j.template create(begin(arr), end(arr)); - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -4355,7 +4355,7 @@ struct external_constructor { std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); } - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } }; @@ -4368,7 +4368,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = obj; - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -4377,7 +4377,7 @@ struct external_constructor { j.m_type = value_t::object; j.m_value = std::move(obj); - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } @@ -4390,7 +4390,7 @@ struct external_constructor j.m_type = value_t::object; j.m_value.object = j.template create(begin(obj), end(obj)); - j.set_parent(j, true); + j.set_parents(); j.assert_invariant(); } }; @@ -5638,7 +5638,7 @@ class json_sax_dom_parser bool end_object() { - ref_stack.back()->set_parent(*ref_stack.back(), true); + ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; } @@ -5657,7 +5657,7 @@ class json_sax_dom_parser bool end_array() { - ref_stack.back()->set_parent(*ref_stack.back(), true); + ref_stack.back()->set_parents(); ref_stack.pop_back(); return true; } @@ -5839,7 +5839,7 @@ class json_sax_dom_callback_parser } else { - ref_stack.back()->set_parent(*ref_stack.back(), true); + ref_stack.back()->set_parents(); } } @@ -5890,7 +5890,7 @@ class json_sax_dom_callback_parser keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); if (keep) { - ref_stack.back()->set_parent(*ref_stack.back(), true); + ref_stack.back()->set_parents(); } else { @@ -18015,44 +18015,42 @@ class basic_json #endif } - reference set_parent(reference j, bool recursive) + void set_parents() { #if JSON_DIAGNOSTICS - if (recursive) + switch (m_type) { - switch (m_type) + case value_t::array: { - case value_t::array: + for (auto& element : *m_value.array) { - for (auto& element : *m_value.array) - { - element.m_parent = this; - } - break; + element.m_parent = this; } + break; + } - case value_t::object: + case value_t::object: + { + for (auto& element : *m_value.object) { - for (auto& element : *m_value.object) - { - element.second.m_parent = this; - } - break; + element.second.m_parent = this; } - - default: - break; + break; } + + default: + break; } - else - { - j.m_parent = this; - } +#endif + } + + reference set_parent(reference j) + { +#if JSON_DIAGNOSTICS + j.m_parent = this; #else static_cast(j); - static_cast(recursive); #endif - return j; } @@ -18270,7 +18268,7 @@ class basic_json std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -18349,7 +18347,7 @@ class basic_json default: // LCOV_EXCL_LINE JSON_ASSERT(false); // LCOV_EXCL_LINE } - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -18476,7 +18474,7 @@ class basic_json m_value.array = create(init.begin(), init.end()); } - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -18686,7 +18684,7 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -18839,7 +18837,7 @@ class basic_json JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), diagnostics_t())); } - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -18938,7 +18936,7 @@ class basic_json break; } - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -18979,7 +18977,7 @@ class basic_json other.m_type = value_t::null; other.m_value = {}; - set_parent(*this, true); + set_parents(); assert_invariant(); } @@ -19020,7 +19018,7 @@ class basic_json swap(m_type, other.m_type); swap(m_value, other.m_value); - set_parent(*this, true); + set_parents(); assert_invariant(); return *this; } @@ -20145,7 +20143,7 @@ class basic_json { JSON_TRY { - return set_parent(m_value.array->at(idx), false); + return set_parent(m_value.array->at(idx)); } JSON_CATCH (std::out_of_range&) { @@ -20243,7 +20241,7 @@ class basic_json { JSON_TRY { - return set_parent(m_value.object->at(key), false); + return set_parent(m_value.object->at(key)); } JSON_CATCH (std::out_of_range&) { @@ -20441,7 +20439,7 @@ class basic_json // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return set_parent(m_value.object->operator[](key), false); + return set_parent(m_value.object->operator[](key)); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); @@ -20531,7 +20529,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return set_parent(m_value.object->operator[](key), false); + return set_parent(m_value.object->operator[](key)); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), diagnostics_t(*this))); @@ -22087,7 +22085,7 @@ class basic_json // add element to array (move semantics) m_value.array->push_back(std::move(val)); - set_parent(m_value.array->back(), false); + set_parent(m_value.array->back()); // if val is moved from, basic_json move constructor marks it null so we do not call the destructor } @@ -22123,7 +22121,7 @@ class basic_json // add element to array m_value.array->push_back(val); - set_parent(m_value.array->back(), false); + set_parent(m_value.array->back()); } /*! @@ -22174,7 +22172,7 @@ class basic_json // add element to object auto res = m_value.object->insert(val); - set_parent(res.first->second, false); + set_parent(res.first->second); } /*! @@ -22278,10 +22276,10 @@ class basic_json // add element to array (perfect forwarding) #ifdef JSON_HAS_CPP_17 - return set_parent(m_value.array->emplace_back(std::forward(args)...), false); + return set_parent(m_value.array->emplace_back(std::forward(args)...)); #else m_value.array->emplace_back(std::forward(args)...); - return set_parent(m_value.array->back(), false); + return set_parent(m_value.array->back()); #endif } @@ -22331,7 +22329,7 @@ class basic_json // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); - set_parent(res.first->second, false); + set_parent(res.first->second); // create result iterator and set iterator to the result of emplace auto it = begin(); @@ -22764,7 +22762,7 @@ class basic_json std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); - set_parent(*this, true); + set_parents(); assert_invariant(); } From a77621687a826bfb020a3ceeda656f7b45479b44 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 14 Jan 2021 21:56:19 +0100 Subject: [PATCH 21/38] :memo: fix comment --- include/nlohmann/json.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 6eba29e95f..9bbb83b9a4 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1239,7 +1239,7 @@ class basic_json container's elements must have the current value as parent. @param[in] check_parents whether the parent relation should be checked. - The value is true by default and should only be set to true + The value is true by default and should only be set to false during destruction of objects when the invariant does not need to hold. */ diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 98b26f039f..78375c5c0c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17996,7 +17996,7 @@ class basic_json container's elements must have the current value as parent. @param[in] check_parents whether the parent relation should be checked. - The value is true by default and should only be set to true + The value is true by default and should only be set to false during destruction of objects when the invariant does not need to hold. */ From 0d1fb383b7f4572a154d7a26a1318e2c617b4de8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 14 Jan 2021 22:05:08 +0100 Subject: [PATCH 22/38] :ok_hand: address comment --- include/nlohmann/detail/conversions/to_json.hpp | 4 +--- single_include/nlohmann/json.hpp | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index f42735e834..228e81879e 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -167,9 +167,7 @@ struct external_constructor for (const bool x : arr) { j.m_value.array->push_back(x); -#if JSON_DIAGNOSTICS - j.m_value.array->back().m_parent = &j; -#endif + j.set_parent(j.m_value.array->back()); } j.assert_invariant(); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 78375c5c0c..4ed9994e22 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4337,9 +4337,7 @@ struct external_constructor for (const bool x : arr) { j.m_value.array->push_back(x); -#if JSON_DIAGNOSTICS - j.m_value.array->back().m_parent = &j; -#endif + j.set_parent(j.m_value.array->back()); } j.assert_invariant(); } From f8037660d06a0fbb6b1cde944d436032d04564ae Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 14 Jan 2021 22:43:52 +0100 Subject: [PATCH 23/38] :recycle: add iterator set_parent function --- include/nlohmann/json.hpp | 57 ++++++++++---------------------- single_include/nlohmann/json.hpp | 57 ++++++++++---------------------- 2 files changed, 36 insertions(+), 78 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 9bbb83b9a4..81587ef263 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1287,6 +1287,19 @@ class basic_json #endif } + iterator set_parents(iterator it, typename iterator::difference_type count) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count); +#endif + return it; + } + reference set_parent(reference j) { #if JSON_DIAGNOSTICS @@ -3601,7 +3614,7 @@ class basic_json // set parent for values added above for (auto i = previous_size; i <= idx; ++i) { - m_value.array->operator[](i).m_parent = this; + set_parent(m_value.array->operator[](i)); } #endif } @@ -5636,13 +5649,7 @@ class basic_json } // insert to array and return iterator -#if JSON_DIAGNOSTICS - iterator result = insert_iterator(pos, val); - result->m_parent = this; - return result; -#else - return insert_iterator(pos, val); -#endif + return set_parents(insert_iterator(pos, val), static_cast(1)); } JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); @@ -5693,16 +5700,7 @@ class basic_json } // insert to array and return iterator -#if JSON_DIAGNOSTICS - iterator result = insert_iterator(pos, cnt, val); - for (size_type i = 0; i < cnt; ++i) - { - (result + static_cast(i))->m_parent = this; - } - return result; -#else - return insert_iterator(pos, cnt, val); -#endif + return set_parents(insert_iterator(pos, cnt, val), static_cast(cnt)); } JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); @@ -5764,16 +5762,7 @@ class basic_json } // insert to array and return iterator -#if JSON_DIAGNOSTICS - iterator result = insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); - for (typename iterator::difference_type i = 0; i < std::distance(first, last); ++i) - { - (result + i)->m_parent = this; - } - return result; -#else - return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); -#endif + return set_parents(insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator), std::distance(first, last)); } /*! @@ -5815,17 +5804,7 @@ class basic_json } // insert to array and return iterator -#if JSON_DIAGNOSTICS - const auto size = ilist.size(); - iterator result = insert_iterator(pos, ilist.begin(), ilist.end()); - for (std::size_t i = 0; i < size; ++i) - { - (result + static_cast(i))->m_parent = this; - } - return result; -#else - return insert_iterator(pos, ilist.begin(), ilist.end()); -#endif + return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast(ilist.size())); } /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4ed9994e22..f388b0d02e 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18042,6 +18042,19 @@ class basic_json #endif } + iterator set_parents(iterator it, typename iterator::difference_type count) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count); +#endif + return it; + } + reference set_parent(reference j) { #if JSON_DIAGNOSTICS @@ -20356,7 +20369,7 @@ class basic_json // set parent for values added above for (auto i = previous_size; i <= idx; ++i) { - m_value.array->operator[](i).m_parent = this; + set_parent(m_value.array->operator[](i)); } #endif } @@ -22391,13 +22404,7 @@ class basic_json } // insert to array and return iterator -#if JSON_DIAGNOSTICS - iterator result = insert_iterator(pos, val); - result->m_parent = this; - return result; -#else - return insert_iterator(pos, val); -#endif + return set_parents(insert_iterator(pos, val), static_cast(1)); } JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); @@ -22448,16 +22455,7 @@ class basic_json } // insert to array and return iterator -#if JSON_DIAGNOSTICS - iterator result = insert_iterator(pos, cnt, val); - for (size_type i = 0; i < cnt; ++i) - { - (result + static_cast(i))->m_parent = this; - } - return result; -#else - return insert_iterator(pos, cnt, val); -#endif + return set_parents(insert_iterator(pos, cnt, val), static_cast(cnt)); } JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), diagnostics_t(*this))); @@ -22519,16 +22517,7 @@ class basic_json } // insert to array and return iterator -#if JSON_DIAGNOSTICS - iterator result = insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); - for (typename iterator::difference_type i = 0; i < std::distance(first, last); ++i) - { - (result + i)->m_parent = this; - } - return result; -#else - return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); -#endif + return set_parents(insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator), std::distance(first, last)); } /*! @@ -22570,17 +22559,7 @@ class basic_json } // insert to array and return iterator -#if JSON_DIAGNOSTICS - const auto size = ilist.size(); - iterator result = insert_iterator(pos, ilist.begin(), ilist.end()); - for (std::size_t i = 0; i < size; ++i) - { - (result + static_cast(i))->m_parent = this; - } - return result; -#else - return insert_iterator(pos, ilist.begin(), ilist.end()); -#endif + return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast(ilist.size())); } /*! From b0d8628c498d1a2be3391a1949886843746b9878 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 15 Jan 2021 16:54:00 +0100 Subject: [PATCH 24/38] :ok_hand: address comments --- include/nlohmann/json.hpp | 9 ++------- single_include/nlohmann/json.hpp | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 81587ef263..16bf6af005 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3607,16 +3607,10 @@ class basic_json // remember array size before resizing const auto previous_size = m_value.array->size(); #endif - m_value.array->resize(idx + 1); -#if JSON_DIAGNOSTICS // set parent for values added above - for (auto i = previous_size; i <= idx; ++i) - { - set_parent(m_value.array->operator[](i)); - } -#endif + set_parents(begin() + previous_size, idx + 1 - previous_size); } return m_value.array->operator[](idx); @@ -5985,6 +5979,7 @@ class basic_json std::swap(m_value, other.m_value); set_parents(); + other.set_parents(); assert_invariant(); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f388b0d02e..7617b8edaa 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -20362,16 +20362,10 @@ class basic_json // remember array size before resizing const auto previous_size = m_value.array->size(); #endif - m_value.array->resize(idx + 1); -#if JSON_DIAGNOSTICS // set parent for values added above - for (auto i = previous_size; i <= idx; ++i) - { - set_parent(m_value.array->operator[](i)); - } -#endif + set_parents(begin() + previous_size, idx + 1 - previous_size); } return m_value.array->operator[](idx); @@ -22740,6 +22734,7 @@ class basic_json std::swap(m_value, other.m_value); set_parents(); + other.set_parents(); assert_invariant(); } From 7633a21e6c1f2330743a8dc3a824e3144be76e1d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 15 Jan 2021 16:58:05 +0100 Subject: [PATCH 25/38] :green_heart: fix build --- include/nlohmann/json.hpp | 2 ++ single_include/nlohmann/json.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 16bf6af005..af54f1b163 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3609,8 +3609,10 @@ class basic_json #endif m_value.array->resize(idx + 1); +#if JSON_DIAGNOSTICS // set parent for values added above set_parents(begin() + previous_size, idx + 1 - previous_size); +#endif } return m_value.array->operator[](idx); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 7617b8edaa..ab3d857c27 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -20364,8 +20364,10 @@ class basic_json #endif m_value.array->resize(idx + 1); +#if JSON_DIAGNOSTICS // set parent for values added above set_parents(begin() + previous_size, idx + 1 - previous_size); +#endif } return m_value.array->operator[](idx); From e9d641130d8e50793c763f1dee978fd8dbdb2708 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 16 Jan 2021 15:33:05 +0100 Subject: [PATCH 26/38] :bug: proper JSON Pointer escape in diagnostic messages --- include/nlohmann/detail/diagnostics_t.hpp | 3 +- include/nlohmann/detail/json_pointer.hpp | 47 +-- include/nlohmann/detail/string_escape.hpp | 63 ++++ include/nlohmann/json.hpp | 4 +- single_include/nlohmann/json.hpp | 334 ++++++++++++---------- 5 files changed, 252 insertions(+), 199 deletions(-) create mode 100644 include/nlohmann/detail/string_escape.hpp diff --git a/include/nlohmann/detail/diagnostics_t.hpp b/include/nlohmann/detail/diagnostics_t.hpp index 727e82350f..a2f7f23a06 100644 --- a/include/nlohmann/detail/diagnostics_t.hpp +++ b/include/nlohmann/detail/diagnostics_t.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace nlohmann { @@ -70,7 +71,7 @@ class diagnostics_t return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, [](const std::string & a, const std::string & b) { - return a + "/" + b; + return a + "/" + detail::escape(b); }) + ") "; #else return ""; diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index f3a70bdcee..52cf171345 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace nlohmann @@ -70,7 +71,7 @@ class json_pointer std::string{}, [](const std::string & a, const std::string & b) { - return a + "/" + escape(b); + return a + "/" + detail::escape(b); }); } @@ -791,53 +792,13 @@ class json_pointer } // finally, store the reference token - unescape(reference_token); + detail::unescape(reference_token); result.push_back(reference_token); } return result; } - /*! - @brief replace all occurrences of a substring by another string - - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f - - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** - - @since version 2.0.0 - */ - static void replace_substring(std::string& s, const std::string& f, - const std::string& t) - { - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} - } - - JSON_PRIVATE_UNLESS_TESTED: - /// escape "~" to "~0" and "/" to "~1" - static std::string escape(std::string s) - { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } - - /// unescape "~1" to tilde and "~0" to slash (order is important!) - static void unescape(std::string& s) - { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); - } - private: /*! @param[in] reference_string the reference string to the current value @@ -883,7 +844,7 @@ class json_pointer // iterate object and use keys as reference string for (const auto& element : *value.m_value.object) { - flatten(reference_string + "/" + escape(element.first), element.second, result); + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); } } break; diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp new file mode 100644 index 0000000000..84f7da52e0 --- /dev/null +++ b/include/nlohmann/detail/string_escape.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index af54f1b163..833d8d4d27 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -8651,7 +8651,7 @@ class basic_json for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch - const auto key = json_pointer::escape(it.key()); + const auto key = detail::escape(it.key()); if (target.find(it.key()) != target.end()) { @@ -8675,7 +8675,7 @@ class basic_json if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it - const auto key = json_pointer::escape(it.key()); + const auto key = detail::escape(it.key()); result.push_back( { {"op", "add"}, {"path", path + "/" + key}, diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ab3d857c27..5f23622f55 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -161,116 +161,10 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept } // namespace detail } // namespace nlohmann +// #include -namespace nlohmann -{ -namespace detail -{ - -template -class diagnostics_t -{ - public: - diagnostics_t() noexcept = default; - diagnostics_t(const BasicJsonType& j) noexcept - : m_j(&j) - {} - - std::string diagnostics() const - { -#if JSON_DIAGNOSTICS - if (m_j == nullptr) - { - return ""; - } - - std::vector tokens; - for (const auto* current = m_j; current->m_parent != nullptr; current = current->m_parent) - { - switch (current->m_parent->type()) - { - case value_t::array: - { - for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) - { - if (current->m_parent->m_value.array->operator[](i) == *current) - { - tokens.emplace_back(std::to_string(i)); - continue; - } - } - break; - } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_value.object) - { - if (element.second == *current) - { - tokens.emplace_back(element.first.c_str()); - continue; - } - } - break; - } - - default: - break; - } - } - - if (tokens.empty()) - { - return ""; - } - - return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + b; - }) + ") "; -#else - return ""; -#endif - } - - private: - const BasicJsonType* m_j = static_cast(nullptr); -}; - -} // namespace detail -} // namespace nlohmann - -// #include - - -#include // size_t - -namespace nlohmann -{ -namespace detail -{ -/// struct to capture the start position of the current token -struct position_t -{ - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; - - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const - { - return chars_read_total; - } -}; - -} // namespace detail -} // namespace nlohmann +#include // #include @@ -2549,6 +2443,178 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ + +template +class diagnostics_t +{ + public: + diagnostics_t() noexcept = default; + diagnostics_t(const BasicJsonType& j) noexcept + : m_j(&j) + {} + + std::string diagnostics() const + { +#if JSON_DIAGNOSTICS + if (m_j == nullptr) + { + return ""; + } + + std::vector tokens; + for (const auto* current = m_j; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (current->m_parent->m_value.array->operator[](i) == *current) + { + tokens.emplace_back(std::to_string(i)); + continue; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (element.second == *current) + { + tokens.emplace_back(element.first.c_str()); + continue; + } + } + break; + } + + default: + break; + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + return ""; +#endif + } + + private: + const BasicJsonType* m_j = static_cast(nullptr); +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + namespace nlohmann { namespace detail @@ -11798,6 +11864,8 @@ class json_reverse_iterator : public std::reverse_iterator // #include +// #include + // #include @@ -11858,7 +11926,7 @@ class json_pointer std::string{}, [](const std::string & a, const std::string & b) { - return a + "/" + escape(b); + return a + "/" + detail::escape(b); }); } @@ -12579,53 +12647,13 @@ class json_pointer } // finally, store the reference token - unescape(reference_token); + detail::unescape(reference_token); result.push_back(reference_token); } return result; } - /*! - @brief replace all occurrences of a substring by another string - - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f - - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** - - @since version 2.0.0 - */ - static void replace_substring(std::string& s, const std::string& f, - const std::string& t) - { - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} - } - - JSON_PRIVATE_UNLESS_TESTED: - /// escape "~" to "~0" and "/" to "~1" - static std::string escape(std::string s) - { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } - - /// unescape "~1" to tilde and "~0" to slash (order is important!) - static void unescape(std::string& s) - { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); - } - private: /*! @param[in] reference_string the reference string to the current value @@ -12671,7 +12699,7 @@ class json_pointer // iterate object and use keys as reference string for (const auto& element : *value.m_value.object) { - flatten(reference_string + "/" + escape(element.first), element.second, result); + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); } } break; @@ -25406,7 +25434,7 @@ class basic_json for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch - const auto key = json_pointer::escape(it.key()); + const auto key = detail::escape(it.key()); if (target.find(it.key()) != target.end()) { @@ -25430,7 +25458,7 @@ class basic_json if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it - const auto key = json_pointer::escape(it.key()); + const auto key = detail::escape(it.key()); result.push_back( { {"op", "add"}, {"path", path + "/" + key}, From aeecc09ba141e4a91a0005cecad90490921a6bb9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 16 Jan 2021 15:33:19 +0100 Subject: [PATCH 27/38] :white_check_mark: add tests for diagnostics --- test/CMakeLists.txt | 1 + test/src/unit-diagnostics.cpp | 81 +++++++++++++++++++++++++++++++++++ test/src/unit-unicode.cpp | 4 +- 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 test/src/unit-diagnostics.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3d8bceb70c..e5484fc731 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -110,6 +110,7 @@ set(files src/unit-convenience.cpp src/unit-conversions.cpp src/unit-deserialization.cpp + src/unit-diagnostics.cpp src/unit-element_access1.cpp src/unit-element_access2.cpp src/unit-hash.cpp diff --git a/test/src/unit-diagnostics.cpp b/test/src/unit-diagnostics.cpp new file mode 100644 index 0000000000..d7c8e772b6 --- /dev/null +++ b/test/src/unit-diagnostics.cpp @@ -0,0 +1,81 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.9.1 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "doctest_compatibility.h" + +#define JSON_DIAGNOSTICS 1 +#include +using nlohmann::json; + +TEST_CASE("Better diagnostics") +{ + SECTION("invalid type") + { + json j; + j["a"]["b"]["c"] = 1; + std::string s; + CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"], "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error); + } + + SECTION("missing key") + { + json j; + j["object"]["object"] = true; + CHECK_THROWS_WITH_AS(j["object"].at("not_found"), "[json.exception.out_of_range.403] (/object) key 'not_found' not found", json::out_of_range); + } + + SECTION("array index out of range") + { + json j; + j["array"][4] = true; + CHECK_THROWS_WITH_AS(j["array"].at(5), "[json.exception.out_of_range.401] (/array) array index 5 is out of range", json::out_of_range); + } + + SECTION("array index at wrong type") + { + json j; + j["array"][4] = true; + CHECK_THROWS_WITH_AS(j["array"][4][5], "[json.exception.type_error.305] (/array/4) cannot use operator[] with a numeric argument with boolean", json::type_error); + } + + SECTION("wrong iterator") + { + json j; + j["array"] = json::array(); + CHECK_THROWS_WITH_AS(j["array"].erase(j.begin()), "[json.exception.invalid_iterator.202] (/array) iterator does not fit current value", json::invalid_iterator); + } + + SECTION("JSON Pointer escaping") + { + json j; + j["a/b"]["m~n"] = 1; + std::string s; + CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"], "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error); + } +} diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index acaca2888d..654c48c2a3 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -1181,8 +1181,8 @@ TEST_CASE("Unicode" * doctest::skip()) CHECK_NOTHROW(json::json_pointer("/" + ptr)); // check escape/unescape roundtrip - auto escaped = json::json_pointer::escape(ptr); - json::json_pointer::unescape(escaped); + auto escaped = nlohmann::detail::escape(ptr); + nlohmann::detail::unescape(escaped); CHECK(escaped == ptr); } } From e23af7434dad09d966c63459b53cb257d5ce1115 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 17 Jan 2021 13:32:26 +0100 Subject: [PATCH 28/38] :rotating_light: fix warnings --- include/nlohmann/json.hpp | 4 +++- single_include/nlohmann/json.hpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 833d8d4d27..b7fcc466a0 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1255,6 +1255,8 @@ class basic_json { return j.m_parent == this; })); +#else + static_cast(check_parents); #endif } @@ -3611,7 +3613,7 @@ class basic_json #if JSON_DIAGNOSTICS // set parent for values added above - set_parents(begin() + previous_size, idx + 1 - previous_size); + set_parents(begin() + static_cast(previous_size), static_cast(idx + 1 - previous_size)); #endif } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 5f23622f55..8d55debbf4 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18038,6 +18038,8 @@ class basic_json { return j.m_parent == this; })); +#else + static_cast(check_parents); #endif } @@ -20394,7 +20396,7 @@ class basic_json #if JSON_DIAGNOSTICS // set parent for values added above - set_parents(begin() + previous_size, idx + 1 - previous_size); + set_parents(begin() + static_cast(previous_size), static_cast(idx + 1 - previous_size)); #endif } From 65107f7c9d61e60a1e26bbbeec59a82954dcc559 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 17 Jan 2021 13:32:36 +0100 Subject: [PATCH 29/38] :green_heart: fix build --- test/src/unit-diagnostics.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/src/unit-diagnostics.cpp b/test/src/unit-diagnostics.cpp index d7c8e772b6..bdae902261 100644 --- a/test/src/unit-diagnostics.cpp +++ b/test/src/unit-diagnostics.cpp @@ -29,7 +29,12 @@ SOFTWARE. #include "doctest_compatibility.h" +#ifdef JSON_DIAGNOSTICS + #undef JSON_DIAGNOSTICS +#endif + #define JSON_DIAGNOSTICS 1 + #include using nlohmann::json; @@ -40,7 +45,7 @@ TEST_CASE("Better diagnostics") json j; j["a"]["b"]["c"] = 1; std::string s; - CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"], "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error); + CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"].get(), "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error); } SECTION("missing key") From 5ec098051443a0aee9d1abe7c8f018ec1e1ab063 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 17 Jan 2021 16:51:14 +0100 Subject: [PATCH 30/38] :green_heart: fix build --- test/src/unit-diagnostics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/unit-diagnostics.cpp b/test/src/unit-diagnostics.cpp index bdae902261..c77030b669 100644 --- a/test/src/unit-diagnostics.cpp +++ b/test/src/unit-diagnostics.cpp @@ -81,6 +81,6 @@ TEST_CASE("Better diagnostics") json j; j["a/b"]["m~n"] = 1; std::string s; - CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"], "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error); + CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"].get(), "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error); } } From 33379684b4d7cdce6eaf243512419e97872bd127 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 17 Jan 2021 22:52:40 +0100 Subject: [PATCH 31/38] :white_check_mark: improve coverage --- include/nlohmann/detail/diagnostics_t.hpp | 6 +++--- single_include/nlohmann/json.hpp | 6 +++--- test/src/unit-diagnostics.cpp | 7 +++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/diagnostics_t.hpp b/include/nlohmann/detail/diagnostics_t.hpp index a2f7f23a06..c9a1f46d5b 100644 --- a/include/nlohmann/detail/diagnostics_t.hpp +++ b/include/nlohmann/detail/diagnostics_t.hpp @@ -58,14 +58,14 @@ class diagnostics_t break; } - default: - break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } if (tokens.empty()) { - return ""; + return ""; // LCOV_EXCL_LINE } return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8d55debbf4..d434df940c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2556,14 +2556,14 @@ class diagnostics_t break; } - default: - break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } if (tokens.empty()) { - return ""; + return ""; // LCOV_EXCL_LINE } return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, diff --git a/test/src/unit-diagnostics.cpp b/test/src/unit-diagnostics.cpp index c77030b669..a75f3e9722 100644 --- a/test/src/unit-diagnostics.cpp +++ b/test/src/unit-diagnostics.cpp @@ -40,6 +40,13 @@ using nlohmann::json; TEST_CASE("Better diagnostics") { + SECTION("empty JSON Pointer") + { + json j = 1; + std::string s; + CHECK_THROWS_WITH_AS(s = j.get(), "[json.exception.type_error.302] type must be string, but is number", json::type_error); + } + SECTION("invalid type") { json j; From d6ff059a901fc69163d9a42e15fcef87278fb23d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 20 Jan 2021 15:05:07 +0100 Subject: [PATCH 32/38] :ok_hand: addressed review comments --- include/nlohmann/detail/diagnostics_t.hpp | 6 +++--- single_include/nlohmann/json.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/diagnostics_t.hpp b/include/nlohmann/detail/diagnostics_t.hpp index c9a1f46d5b..4e943cad56 100644 --- a/include/nlohmann/detail/diagnostics_t.hpp +++ b/include/nlohmann/detail/diagnostics_t.hpp @@ -39,7 +39,7 @@ class diagnostics_t if (current->m_parent->m_value.array->operator[](i) == *current) { tokens.emplace_back(std::to_string(i)); - continue; + break; } } break; @@ -52,7 +52,7 @@ class diagnostics_t if (element.second == *current) { tokens.emplace_back(element.first.c_str()); - continue; + break; } } break; @@ -79,7 +79,7 @@ class diagnostics_t } private: - const BasicJsonType* m_j = static_cast(nullptr); + const BasicJsonType* m_j = nullptr; }; } // namespace detail diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index d434df940c..6cb7dc1b94 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2537,7 +2537,7 @@ class diagnostics_t if (current->m_parent->m_value.array->operator[](i) == *current) { tokens.emplace_back(std::to_string(i)); - continue; + break; } } break; @@ -2550,7 +2550,7 @@ class diagnostics_t if (element.second == *current) { tokens.emplace_back(element.first.c_str()); - continue; + break; } } break; @@ -2577,7 +2577,7 @@ class diagnostics_t } private: - const BasicJsonType* m_j = static_cast(nullptr); + const BasicJsonType* m_j = nullptr; }; } // namespace detail From 51ac6000d2b1b55c0cf1032677261ed407d72230 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 21 Jan 2021 13:36:23 +0100 Subject: [PATCH 33/38] :white_check_mark: improve coverage --- test/src/unit-diagnostics.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/src/unit-diagnostics.cpp b/test/src/unit-diagnostics.cpp index a75f3e9722..8bd5c41fa4 100644 --- a/test/src/unit-diagnostics.cpp +++ b/test/src/unit-diagnostics.cpp @@ -90,4 +90,9 @@ TEST_CASE("Better diagnostics") std::string s; CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"].get(), "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error); } + + SECTION("Parse error") + { + 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); + } } From d00ad33e46fb2fe116467a9ef296cc70c4b6ac79 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 21 Jan 2021 21:47:19 +0100 Subject: [PATCH 34/38] :memo: update documentation --- doc/examples/diagnostics_extended.cpp | 22 +++++++++++++ doc/examples/diagnostics_extended.link | 1 + doc/examples/diagnostics_extended.output | 1 + doc/examples/diagnostics_standard.cpp | 20 ++++++++++++ doc/examples/diagnostics_standard.link | 1 + doc/examples/diagnostics_standard.output | 1 + doc/mkdocs/docs/features/macros.md | 10 ++++++ doc/mkdocs/docs/home/exceptions.md | 39 ++++++++++++++++++++++++ doc/mkdocs/docs/home/license.md | 2 +- 9 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 doc/examples/diagnostics_extended.cpp create mode 100644 doc/examples/diagnostics_extended.link create mode 100644 doc/examples/diagnostics_extended.output create mode 100644 doc/examples/diagnostics_standard.cpp create mode 100644 doc/examples/diagnostics_standard.link create mode 100644 doc/examples/diagnostics_standard.output diff --git a/doc/examples/diagnostics_extended.cpp b/doc/examples/diagnostics_extended.cpp new file mode 100644 index 0000000000..f4c43f05e6 --- /dev/null +++ b/doc/examples/diagnostics_extended.cpp @@ -0,0 +1,22 @@ +#include + +# define JSON_DIAGNOSTICS 1 +#include + +using json = nlohmann::json; + +int main() +{ + json j; + j["address"]["street"] = "Fake Street"; + j["address"]["housenumber"] = "12"; + + try + { + int housenumber = j["address"]["housenumber"]; + } + catch (json::exception& e) + { + std::cout << e.what() << '\n'; + } +} diff --git a/doc/examples/diagnostics_extended.link b/doc/examples/diagnostics_extended.link new file mode 100644 index 0000000000..9f10da9423 --- /dev/null +++ b/doc/examples/diagnostics_extended.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/diagnostics_extended.output b/doc/examples/diagnostics_extended.output new file mode 100644 index 0000000000..f142927a17 --- /dev/null +++ b/doc/examples/diagnostics_extended.output @@ -0,0 +1 @@ +[json.exception.type_error.302] (/address/housenumber) type must be number, but is string diff --git a/doc/examples/diagnostics_standard.cpp b/doc/examples/diagnostics_standard.cpp new file mode 100644 index 0000000000..575c409eb6 --- /dev/null +++ b/doc/examples/diagnostics_standard.cpp @@ -0,0 +1,20 @@ +#include +#include + +using json = nlohmann::json; + +int main() +{ + json j; + j["address"]["street"] = "Fake Street"; + j["address"]["housenumber"] = "12"; + + try + { + int housenumber = j["address"]["housenumber"]; + } + catch (json::exception& e) + { + std::cout << e.what() << '\n'; + } +} diff --git a/doc/examples/diagnostics_standard.link b/doc/examples/diagnostics_standard.link new file mode 100644 index 0000000000..cd0453b5ef --- /dev/null +++ b/doc/examples/diagnostics_standard.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/diagnostics_standard.output b/doc/examples/diagnostics_standard.output new file mode 100644 index 0000000000..79707a0cb9 --- /dev/null +++ b/doc/examples/diagnostics_standard.output @@ -0,0 +1 @@ +[json.exception.type_error.302] type must be number, but is string diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index 696438d2f0..d044bfd26b 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -12,6 +12,14 @@ This macro overrides `#!cpp catch` calls inside the library. The argument is the See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example. +## `JSON_DIAGNOSTICS` + +This macro enables extended diagnostics for exception messages. Possible values are `1` to enable or `0` to disable (default). + +When enabled, exception messages contain a [JSON Pointer](json_pointer.md) to the JSON value that triggered the exception, see [Extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) for an example. + +The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets `JSON_DIAGNOSTICS` accordingly. + ## `JSON_NOEXCEPTION` Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`. @@ -56,6 +64,8 @@ When defined to `0`, implicit conversions are switched off. By default, implicit auto s = j.get(); ``` +Implicit conversions can also be controlled with the CMake option `JSON_ImplicitConversions` (`ON` by default) which sets `JSON_USE_IMPLICIT_CONVERSIONS` accordingly. + ## `NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)` This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object. diff --git a/doc/mkdocs/docs/home/exceptions.md b/doc/mkdocs/docs/home/exceptions.md index 0475f53e2b..a3b1e9c763 100644 --- a/doc/mkdocs/docs/home/exceptions.md +++ b/doc/mkdocs/docs/home/exceptions.md @@ -50,6 +50,45 @@ Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or #include ``` +### Extended diagnostic messages + +Exceptions in the library are thrown in the local context of the JSON value they are detected. This makes detailed +diagnostics messages, and hence debugging, difficult. + +??? example + + ```cpp + --8<-- "examples/diagnostics_standard.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostics_standard.output" + ``` + + This exception can be hard to debug if storing the value `#!c "12"` and accessing it is further apart. + +To create better diagnostics messages, each JSON value needs a pointer to its parent value such that a global context (i.e., a path from the root value to the value that lead to the exception) can be created. That global context is provided as [JSON Pointer](../features/json_pointer.md). + +As this global context comes at the price of storing one additional pointer per JSON value and runtime overhead to maintain the parent relation, extended diagnostics are disabled by default. They can, however, be enabled by defining the preprocessor symbol [`JSON_DIAGNOSTICS`](../features/macros.md#json_diagnostics) to `1` before including `json.hpp`. + +??? example + + ```cpp + --8<-- "examples/diagnostics_extended.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostics_extended.output" + ``` + + Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type. + + + ## Parse errors This exception is thrown by the library when a parse error occurs. Parse errors diff --git a/doc/mkdocs/docs/home/license.md b/doc/mkdocs/docs/home/license.md index 4cd6ca2cc3..d359468e08 100644 --- a/doc/mkdocs/docs/home/license.md +++ b/doc/mkdocs/docs/home/license.md @@ -4,7 +4,7 @@ The class is licensed under the [MIT License](https://opensource.org/licenses/MIT): -Copyright © 2013-2020 [Niels Lohmann](https://nlohmann.me) +Copyright © 2013-2021 [Niels Lohmann](https://nlohmann.me) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: From 7b7da08fb643c75dd18516ac8835ce2f2d5509a6 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 23 Jan 2021 10:04:19 +0100 Subject: [PATCH 35/38] :memo: update documentation --- doc/mkdocs/docs/features/macros.md | 2 +- doc/mkdocs/docs/home/exceptions.md | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index d044bfd26b..b468c091af 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -16,7 +16,7 @@ See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an This macro enables extended diagnostics for exception messages. Possible values are `1` to enable or `0` to disable (default). -When enabled, exception messages contain a [JSON Pointer](json_pointer.md) to the JSON value that triggered the exception, see [Extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) for an example. +When enabled, exception messages contain a [JSON Pointer](json_pointer.md) to the JSON value that triggered the exception, see [Extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) for an example. Note that enabling this macro increases the size of every JSON value by one pointer and adds some runtime overhead. The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets `JSON_DIAGNOSTICS` accordingly. diff --git a/doc/mkdocs/docs/home/exceptions.md b/doc/mkdocs/docs/home/exceptions.md index a3b1e9c763..da68f21495 100644 --- a/doc/mkdocs/docs/home/exceptions.md +++ b/doc/mkdocs/docs/home/exceptions.md @@ -52,8 +52,7 @@ Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or ### Extended diagnostic messages -Exceptions in the library are thrown in the local context of the JSON value they are detected. This makes detailed -diagnostics messages, and hence debugging, difficult. +Exceptions in the library are thrown in the local context of the JSON value they are detected. This makes detailed diagnostics messages, and hence debugging, difficult. ??? example @@ -88,7 +87,6 @@ As this global context comes at the price of storing one additional pointer per Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type. - ## Parse errors This exception is thrown by the library when a parse error occurs. Parse errors From 380a613f2b5d32425021129cd1f371ddcfd54ddf Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 23 Jan 2021 20:58:59 +0100 Subject: [PATCH 36/38] :bug: fix bug in diagnostics_t --- include/nlohmann/detail/diagnostics_t.hpp | 4 ++-- single_include/nlohmann/json.hpp | 4 ++-- test/src/unit-diagnostics.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/detail/diagnostics_t.hpp b/include/nlohmann/detail/diagnostics_t.hpp index 4e943cad56..f89b0e3d13 100644 --- a/include/nlohmann/detail/diagnostics_t.hpp +++ b/include/nlohmann/detail/diagnostics_t.hpp @@ -36,7 +36,7 @@ class diagnostics_t { for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) { - if (current->m_parent->m_value.array->operator[](i) == *current) + if (¤t->m_parent->m_value.array->operator[](i) == current) { tokens.emplace_back(std::to_string(i)); break; @@ -49,7 +49,7 @@ class diagnostics_t { for (const auto& element : *current->m_parent->m_value.object) { - if (element.second == *current) + if (&element.second == current) { tokens.emplace_back(element.first.c_str()); break; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 6cb7dc1b94..fc9f6d630a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2534,7 +2534,7 @@ class diagnostics_t { for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) { - if (current->m_parent->m_value.array->operator[](i) == *current) + if (¤t->m_parent->m_value.array->operator[](i) == current) { tokens.emplace_back(std::to_string(i)); break; @@ -2547,7 +2547,7 @@ class diagnostics_t { for (const auto& element : *current->m_parent->m_value.object) { - if (element.second == *current) + if (&element.second == current) { tokens.emplace_back(element.first.c_str()); break; diff --git a/test/src/unit-diagnostics.cpp b/test/src/unit-diagnostics.cpp index 8bd5c41fa4..5885f6ecb7 100644 --- a/test/src/unit-diagnostics.cpp +++ b/test/src/unit-diagnostics.cpp @@ -95,4 +95,10 @@ TEST_CASE("Better diagnostics") { 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); } + + SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448") + { + CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error); + CHECK_THROWS_WITH_AS(json({"0", "1"})[1].get(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error); + } } From c190a72f3d8ae2a628ba57a9fedf58f91142cc83 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 24 Jan 2021 17:45:08 +0100 Subject: [PATCH 37/38] :ok_hand: apply suggestion Co-authored-by: Alexander Karzhenkov --- include/nlohmann/detail/json_pointer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 52cf171345..9fb7d0d22a 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -884,7 +884,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive", diagnostics_t(element))); + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", diagnostics_t(element.second))); } // assign value to reference pointed to by JSON pointer; Note that if From e8dba10f53dd44c11be3eca1c273779cb48a69b0 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 24 Jan 2021 17:45:48 +0100 Subject: [PATCH 38/38] :white_check_mark: add test --- single_include/nlohmann/json.hpp | 2 +- test/src/unit-diagnostics.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index fc9f6d630a..33ca9ad89a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12739,7 +12739,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive", diagnostics_t(element))); + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", diagnostics_t(element.second))); } // assign value to reference pointed to by JSON pointer; Note that if diff --git a/test/src/unit-diagnostics.cpp b/test/src/unit-diagnostics.cpp index 5885f6ecb7..1cea374a74 100644 --- a/test/src/unit-diagnostics.cpp +++ b/test/src/unit-diagnostics.cpp @@ -101,4 +101,11 @@ TEST_CASE("Better diagnostics") CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error); CHECK_THROWS_WITH_AS(json({"0", "1"})[1].get(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error); } + + SECTION("Regression test for https://github.com/nlohmann/json/pull/2562/files/380a613f2b5d32425021129cd1f371ddcfd54ddf#r563259793") + { + json j; + j["/foo"] = {1, 2, 3}; + CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error); + } }