Add missing diagnostic-positions test coverage (lifetime, input adapters, SAX) - #5482
Merged
Merged
Conversation
5 tasks
nlohmann
force-pushed
the
issue-5420-diagnostic-positions-coverage
branch
from
September 9, 2026 11:16
0349745 to
4d22dd4
Compare
Base automatically changed from
issue-5417-dedup-diagnostic-positions-test
to
develop
September 11, 2026 06:18
nlohmann
force-pushed
the
issue-5420-diagnostic-positions-coverage
branch
from
September 11, 2026 06:18
4d22dd4 to
53c9694
Compare
…ers, SAX) Building on the merged unit-class_parser.cpp from #5417, add characterization tests (regression protection for existing behavior, not a behavior change) for JSON_DIAGNOSTIC_POSITIONS: - value lifetime: copy ctor copies positions recursively, move ctor resets the moved-from value to npos, and mutating a parsed document (operator[], push_back, erase) leaves the parent's stale span and siblings' positions untouched while new values get npos. - input adapters: wide-string input positions count transcoded UTF-8 bytes (not wide characters), BOM-prefixed input's start_pos() reflects the skipped 3-byte BOM, istringstream/ifstream/iterator-pair inputs report consistent (non-npos) positions, and binary formats (CBOR, MessagePack, UBJSON, BSON) always report npos. - a user-constructed json_sax_dom_parser with no lexer (as used when driving json::sax_parse() directly) reports npos for every value, since it has no m_lexer_ref to source positions from. While characterizing swap(), found that basic_json::swap() (and the friend swap() that forwards to it) does not swap start_position/end_position, unlike copy-assignment's operator=(basic_json), which does as part of its copy-and-swap implementation. This looks like a real inconsistency/bug, but per the scope of this test-only change it is only pinned (not fixed) here; see the comment at the "swap() does NOT exchange positions" section. Fixes #5420 Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Use é escapes instead of a literal UTF-8-encoded 'é' inside the L"" literal, so the wide string's content does not depend on the compiler's assumed source character set (MSVC without /utf-8 decodes raw non-ASCII source bytes using the system code page rather than as UTF-8, which was producing a wstring of unexpected length/content and failing the ws.size()/end_pos() assertions on Windows CI). Also reworded a comment that unintentionally embedded the literal substring "TODO check", which clang-tidy's google-readability-todo check flags regardless of quoting context. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann
force-pushed
the
issue-5420-diagnostic-positions-coverage
branch
from
September 11, 2026 15:56
53c9694 to
b37df25
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #5474 (fix for #5417), this PR adds the coverage requested in #5420 to the (now merged)
tests/src/unit-class_parser.cpp, inside a newTEST_CASE("diagnostic positions: value lifetime, input adapters, and SAX")guarded by#if JSON_DIAGNOSTIC_POSITIONS. Trailing-comma coverage under positions (item 4 of #5420) is already closed by #5417, so it is skipped here as instructed.Covered, as characterization tests (regression protection for existing behavior, not new behavior):
start_pos()/end_pos()recursively (root and every nested value).npos; the destination keeps the original positions recursively.swap()— see "Found while characterizing" below.operator[]adding a key,push_back,erase) leaves the parent's own (now stale) span and existing siblings' positions untouched; newly-added values getnpos.json::parse(std::wstring): positions count transcoded UTF-8 bytes, not wide characters (demonstrated withL"{\"a\":\"éé\"}", whereend_pos() == 12butws.size() == 10).start_pos()is3, reflecting the skipped BOM.std::istringstream,std::ifstream, and iterator-pair inputs all report fully consistent (non-npos) positions.from_cbor,from_msgpack,from_ubjson,from_bson) always reportnpos, sincebinary_readernever sets a text position.json_sax_dom_parser(no lexer pointer, e.g. built directly instead of going throughjson::parse()) reportsnposfor every produced value, since everyif (m_lexer_ref)guard injson_sax.hppis skipped without one.Found while characterizing (flagging for a maintainer, not fixed here)
basic_json::swap(reference)(include/nlohmann/json.hpp, around line 3540) and the friendswap(reference, reference)that forwards to it swapm_data.m_type/m_data.m_valuebut never swapstart_position/end_position— unlike copy-assignment'soperator=(basic_json)(around line 1291), which does swap them as part of its copy-and-swap implementation. Concretely:This looks like an oversight/inconsistency rather than intended behavior. Per the scope of this PR (test-only, characterization, no
include/changes), it is not fixed here — the test only pins the current behavior, with a comment pointing at the exact lines, so a future fix (or a deliberate decision to keep it) shows up as an intentional, visible change rather than a silent regression.Validation
Compiled and ran
unit-class_parser.cppoffline with-DJSON_DIAGNOSTIC_POSITIONS=1(new tests exercised, 10511 assertions passed) and without it (newTEST_CASEfully compiled out, 10299 assertions passed, unchanged from before). Also checked both variants compile warning-free under-Wall -Wextra -Wpedantic -Wshadow.Breaking change?
No breaking changes. This is a test-only change; nothing under
include/was modified.Fixes #5420
— opened by Claude Code on behalf of @nlohmann