Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress Clang-Tidy warning #4311

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open

Suppress Clang-Tidy warning #4311

wants to merge 15 commits into from

Conversation

nlohmann
Copy link
Owner

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

@nlohmann nlohmann added this to the Release 3.11.4 milestone Mar 15, 2024
@github-actions github-actions bot added the S label Mar 15, 2024
@coveralls
Copy link

coveralls commented Mar 15, 2024

Coverage Status

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

Copy link

🔴 Amalgamation check failed! 🔴

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

: json_base_class_t(std::forward<json_base_class_t>(other)),
m_data(std::move(other.m_data))
// check that passed value is valid (has to be done before forwarding)
: json_base_class_t((other.assert_invariant(false), std::forward<json_base_class_t>(other))),
Copy link
Contributor

@gregmarr gregmarr Mar 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just do assert_invariant(false); in the body (assert it in this after moving instead of asserting in other) to avoid this mess?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another possibility for avoiding all these issues is to do std::swap() on m_data and other.m_data in the body.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not at all happy with this. I also thought about swapping...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for asking a question for something that is probably obvious, but which constructor of json_base_class_t is this call valid for? I suppose it is one of the basic_json constructors being used here?

FWIW, it should be completely fine to check the invariants after the initializer list has been evaluated. Is there a possibility to consider rewriting the data move constructor so these assignments don't need to be done at this level?

@github-actions github-actions bot added the tests label Mar 26, 2024
@nlohmann
Copy link
Owner Author

nlohmann commented Apr 8, 2024

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
Copy link
Contributor

gregmarr commented Apr 8, 2024

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.

@github-actions github-actions bot added L and removed M labels Apr 14, 2024
@nlohmann
Copy link
Owner Author

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
Copy link

fsandhei commented Apr 14, 2024

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
Copy link
Contributor

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
Copy link
Owner Author

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants