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

Add assertion if nullptr is passed to parse function #3593

Merged
merged 14 commits into from
Jul 22, 2022
Merged

Conversation

nlohmann
Copy link
Owner

@nlohmann nlohmann commented Jul 20, 2022

Addresses #3584 by adding an assertion and documenting the preconditions.

@nlohmann nlohmann marked this pull request as draft July 20, 2022 11:10
@nlohmann nlohmann marked this pull request as ready for review July 20, 2022 11:10
@nlohmann nlohmann added the review needed It would be great if someone could review the proposed changes. label Jul 20, 2022
@coveralls
Copy link

coveralls commented Jul 20, 2022

Coverage Status

Coverage remained the same at 100.0% when pulling 043a0d7 on file_exception into feef0eb on develop.

@Nhattan9961
Copy link

A

docs/mkdocs/docs/api/basic_json/accept.md Outdated Show resolved Hide resolved
docs/mkdocs/docs/home/exceptions.md Outdated Show resolved Hide resolved
tests/src/unit-deserialization.cpp Outdated Show resolved Hide resolved
@nlohmann
Copy link
Owner Author

nlohmann commented Jul 21, 2022

It seems I cannot suppress the issue in the test suite, but I need to add the attribute to the input adapter's constructor. Which makes no sense, because we added the attribute exactly to help the compiler/sanitizer find such an issue.

I am not sure how to continue:

  • Removing the test yields untested code that throws an exception.
  • That exception only serves the purpose of enforcing that passed files are usable - and this only for the C file API.

I currently tend more to adding an assertion and making the documentation more clear that passing a null pointer yields undefined behavior.

Alternatively, we could make the behavior more similar to the std::ifstream case and (1) check in the constructor of the passed pointer is null and (2) in get_character function return eof() in that case. This would add one if statement to a const bool member which, when annotating with UNLIKELY should not be too expensive for the happy path.

Both alternatives also have the advantage that the constructor remains noexcept - this PR silently removes the specifier which is a breaking change...

What do you think?

@nlohmann nlohmann added state: please discuss please discuss the issue or vote for your favorite option and removed review needed It would be great if someone could review the proposed changes. labels Jul 21, 2022
@falbrechtskirchinger
Copy link
Contributor

falbrechtskirchinger commented Jul 21, 2022

I agree that keeping exceptions out of constructors is a good idea. I'd still prefer a way to differentiate parse errors from invalid input adapters. This goes beyond the scope of this PR but is what I've been asking for from the start.

To that end, an alternative might be to add operator bool() to input_adapter, check that somewhere in or near the parse/sax_parse/accept functions, and throw an exception from there.

For file_input_adapter the operator would simply return m_file != nullptr, for input_stream_adapter (which ought to be renamed to stream_input_adapter before being added to the public API) is->good(), for iterator_input_adapter current != end, etc.

@nlohmann
Copy link
Owner Author

nlohmann commented Jul 21, 2022

I agree that keeping exceptions out of constructors is a good idea. I'd still prefer a way to differentiate parse errors from invalid input adapters. This goes beyond the scope of this PR but is what I've been asking for from the start.

Yes, this is also a pain point for me, because there are issues again and again where people seem to not understand that "unexpected end of file" at position 0 means that a file could not be opened in the first place.

To that end, an alternative might be to add operator bool() to input_adapter, check that somewhere in or near the parse/sax_parse/accept functions, and throw an exception from there.

For file_input_adapter the operator would simply return m_file != nullptr, for input_stream_adapter (which ought to be renamed to stream_input_adapter before being added to the public API) is->good(), for iterator_input_adapter current != end, etc.

Sounds good.

But for this PR, should we go for the behavior that a null pointer yields EOF?

Edit:

We can also not test that behavior, because again we cannot pass a null pointer to the input adapter without being punished by UBSAN... This is how CLion tells me that the parameter cannot be null:

image

I get the feeling that an assertion and updated documentation is the best we can do without investing too much for this edge case. I like the idea of adding some kind of ok() function - but not for this issue.

@gregmarr
Copy link
Contributor

I guess we assert in the constructor for easy diagnosis in the cases where assert is enabled, and add the nullptr check in get_character() with the unlikely attribute.

@falbrechtskirchinger
Copy link
Contributor

And we can remove the nullptr check from get_character() once my idea (or equivalent) is implemented.

Co-authored-by: Florian Albrechtskirchinger <falbrechtskirchinger@gmail.com>
@nlohmann nlohmann changed the title Throw exception if nullptr is passed to parse function Add assertion if nullptr is passed to parse function Jul 22, 2022
@nlohmann nlohmann added release item: 🔨 further change and removed state: please discuss please discuss the issue or vote for your favorite option labels Jul 22, 2022
@nlohmann nlohmann self-assigned this Jul 22, 2022
@nlohmann nlohmann added this to the Release 3.11.0 milestone Jul 22, 2022
@nlohmann nlohmann merged commit dbfd33a into develop Jul 22, 2022
@nlohmann nlohmann deleted the file_exception branch July 22, 2022 23:26
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.

5 participants