nlohmann / json

JSON for Modern C++
https://json.nlohmann.me
MIT License
41.3k stars 6.58k forks source link

Suppress Clang-Tidy warning #4311

Open nlohmann opened 3 months ago

nlohmann commented 3 months ago

modernize-use-designated-initializers does not work with C++11

coveralls commented 3 months ago

Coverage Status

coverage: 100.0%. remained the same when pulling 4b7721c392eb3241865893da5286caebea187c5f on clang-tidy into 0457de21cffb298c22b629e538036bfeb96130b7 on develop.

github-actions[bot] commented 3 months ago

🔴 Amalgamation check failed! 🔴

The source code has not been amalgamated. @nlohmann Please read and follow the Contribution Guidelines.

nlohmann commented 2 months ago

I am currently blocked here:

template < typename BasicJsonType, typename T, std::size_t... Idx >
std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
        identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
{
    return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
}

gives

/__w/json/json/include/nlohmann/detail/conversions/from_json.hpp:280:44: error: 'j' used after it was forwarded [bugprone-use-after-move,hicpp-invalid-access-moved,-warnings-as-errors]
  280 |     return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
      |                                            ^
/__w/json/json/include/nlohmann/detail/conversions/from_json.hpp:280:47: note: forward occurred here
  280 |     return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
      |                                               ^
/__w/json/json/include/nlohmann/detail/conversions/from_json.hpp:280:44: note: the use happens in a later loop iteration than the forward
  280 |     return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
      |                                            ^

Any ideas?

gregmarr commented 2 months ago

Every other from_json function except for auto from_json(BasicJsonType&& j, TupleRelated&& t) takes const BasicJsonType &. If this was the same, that eliminates the need for the std::forward.

Alternatively, if there is a defined need for moving from the json object into an std::array, then have separate const BasicJsonType & and BasicJsonType && functions and put the && on the array element type: template get<T&&>() in the latter.

nlohmann commented 2 months ago

I made some progress and now have an interesting issue:

template<class KeyType, detail::enable_if_t<
             detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
reference at(KeyType && key)
{
    auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
    if (it == m_data.m_value.object->end())
    {
        JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this)); // <-- warning here: 'key' used after it was forwarded
    }
    return set_parent(it->second);
}

To fix this, I could either make a copy of key which defeats the purpose of taking KeyType && in the first place - or to dumb down the error message and not mention key. What do you think?

fsandhei commented 2 months ago

I made some progress and now have an interesting issue:

template<class KeyType, detail::enable_if_t<
             detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
reference at(KeyType && key)
{
    auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
    if (it == m_data.m_value.object->end())
    {
        JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this)); // <-- warning here: 'key' used after it was forwarded
    }
    return set_parent(it->second);
}

To fix this, I could either make a copy of key which defeats the purpose of taking KeyType && in the first place - or to dumb down the error message and not mention key. What do you think?

If key is an rvalue this would indeed be a double move so that could be unfortunate behavior. key is already "stolen" in the call above.

If key is an lvalue this is completely fine. Are there any clear downsides of constraining this to be just for lvalues?

I suppose that if you'd like to keep this to be a universal reference the exception message should perhaps not contain key so you don't try to move an already-moved object.

gregmarr commented 2 months ago

I would say don't mention key in the error message. About the only thing I can think you might be able to do here would be to split const KeyType & vs KeyType && and only put key in the message in the lvalue case.

nlohmann commented 2 months ago

We already have two versions for const references and rvalues. Unfortunately, it seems to depend on the compiler and C++ standard which version is used, so I am currently struggling to adjust the test cases (with and without key in the exception).