Conversation
|
CI fixes for this PR (commits a8e9ce9 and 94cd91c, plus the fixes from #5617/#5618/#5620 merged in):
Verified with — posted by Claude Code on behalf of @nlohmann |
🔴 Amalgamation check failed! 🔴The source code has not been amalgamated and/or formatted correctly, or |
ab4f646 to
dc9899f
Compare
94cd91c to
a887559
Compare
a887559 to
b136677
Compare
The generator for BUILD.bazel only listed single_include/nlohmann/json.hpp in the singleheader-json cc_library's hdrs. Bazel's sandboxing only exposes declared headers, so a consumer of :singleheader-json that includes <nlohmann/json_fwd.hpp> failed with "file not found" (verified with Bazel 9.2.0). Meson and include.zip already ship json_fwd.hpp; the Bazel target was missing it. Add single_include/nlohmann/json_fwd.hpp to the hdrs list and regenerate BUILD.bazel. Verified that `bazel build //:json //:singleheader-json` succeeds and that a consumer cc_binary depending on :singleheader-json can now include <nlohmann/json_fwd.hpp>. Overlaps #5621, which adds json_view.hpp to the same hdrs list in gen_bazel_build_file.cmake; that PR will need a rebase. #5716 item 2 Signed-off-by: Niels Lohmann <mail@nlohmann.me>
fuzz_testing and fuzz_testing_{bon8,bson,cbor,msgpack,ubjson} seeded
fuzz-testing/testcases from tests/data, which was removed in dbf1a1f
(2020) when the test data moved to the external json_test_data repo.
The find command found nothing, but the pipeline's exit status was
that of xargs, so the recipe still reported success with an empty
corpus, and the printed afl-fuzz command would refuse to start.
The recipes were also six near-identical copies with unquoted -name
patterns, used the legacy CXX=afl-clang++, and fuzzing-start/stop were
missing from both the help output and .PHONY. tests/fuzzing.md already
documents the working flow (download json_test_data, then
`make -C tests fuzzers`), so replace the six broken targets and their
help lines with a single pointer to that document instead of trying
to keep six copies of a fragile shell pipeline in sync.
This does not affect OSS-Fuzz, which builds through tests/Makefile.
Overlaps #5621, which adds a seventh copy of the same broken line for
fuzz_testing_json_view.
#5717 item 2
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
ccd2753 to
80763fe
Compare
c195988 to
e96e298
Compare
…zel (#5715 item 5) The amalgamation/format check existed three times with three different file sets: the Makefile's pretty/check-amalgamation, the pull_request-only check_amalgamation.yml workflow, and the ci_test_amalgamation CMake target that also runs on direct pushes to develop/master/release/*. The CMake target's glob was a strict subset of the workflow's (missing the docs/mkdocs/docs/examples/*.hpp headers, tests/abi/, tests/cmake_*/project/, tests/cuda_example/, tests/fmt_formatter/, and tests/module_cpp20/), and it never checked BUILD.bazel at all, so a misformatted file in any of those paths, or a stale BUILD.bazel, could reach develop through a direct push even though the PR-only workflow would have caught it. Make ci_test_amalgamation glob the same roots (docs/mkdocs/docs/examples, include, tests) and extensions (*.hpp, *.cpp, *.cu) as check_amalgamation.yml, excluding tests/thirdparty/ and tests/abi/include/nlohmann/ the same way, and regenerate and diff BUILD.bazel next to json.hpp/json_fwd.hpp. Also add docs/mkdocs/docs/examples/*.hpp to the Makefile's pretty/pretty_format targets, which were missing the four custom_*_type.hpp example headers, and drop the stale "called by Travis" comment on check-amalgamation (Travis is gone; nothing currently calls that Makefile target from CI). Leaves the workflow itself untouched: it deliberately runs amalgamate.py from a fresh develop checkout so a PR cannot change the tool that checks it. Overlaps #5610 and #5621, which each add a new amalgamated header and touch the same INDENT_FILES/ci_test_amalgamation/check_amalgamation.yml hunks. Verified with `make check-amalgamation` on this branch: clean, no diff. #5715 item 5. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The public classes of the zero-copy view (#5295), in the new header <nlohmann/json_view.hpp>: - basic_json_document<BasicJsonType>: parse (borrowing contiguous byte inputs, owning rvalue strings, streams, and other inputs), parse_copy, accept, read, root, is_discarded, source, owns_source, node_count, memory_usage, shrink_to_fit - basic_json_view<BasicJsonType>: type and the is_* queries, size, empty, materialize (the value parse() would produce, built by the same SAX handler), source_offset - the aliases json_document, json_view, ordered_json_document, and ordered_json_view A parse error throws the exception basic_json::parse would throw for the same input: the library parser is run on the failing input, so messages, positions, and exception ids are the same. Inputs of 4 GiB or more are rejected with out_of_range.416. The single header single_include/nlohmann/json_view.hpp keeps including json.hpp; make amalgamate, check-amalgamation, include.zip, and release handle it. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
unit-json_view.cpp: type queries, size, and empty against basic_json; materialize() against parse() (json and ordered_json, generated documents, duplicate keys, 100,000 levels of nesting, parent pointers with JSON_DIAGNOSTICS); parse errors and their messages equal to parse() for malformed inputs and all option combinations; the overflow of a float document (1e39, 3.4028236e38) as in parse(); NUL and BOM; borrowed and owned inputs (strings, C strings, literals, vectors, string_view, streams, wide strings, parse_copy, and iterator ranges over pointers, vectors, strings, and lists); reuse with read(); moves; shrink_to_fit() of the index and of the decoded strings; source offsets. unit-json_view_macros.cpp includes the header without JSON_TEST_KEEP_MACROS, as users do: the view must not depend on the macros json.hpp undefines, and must not leak its own. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The new single header goes through the same checks and install steps as json.hpp and json_fwd.hpp: - cmake/ci.cmake: ci_test_amalgamation regenerates, formats, and compares json_view.hpp as well - check_amalgamation.yml: the pull request check does the same; it runs develop's tools, so it needs config_json_view.json on develop first - meson.build: installs single_include/nlohmann/json_view.hpp - gen_bazel_build_file.cmake, BUILD.bazel: json_view.hpp joins the single-header target; the glob of the other target already covers the new headers - labeler.yml: an "aspect: json_view" label for the header, its detail/view headers, tests, and documentation The CMake install rules for include/ and single_include/, the REUSE catch-all, and Package.swift cover the new files without changes. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The nlohmann.json module includes json_view.hpp in its global module fragment and exports basic_json_document, basic_json_view, and the four aliases next to basic_json, json, and ordered_json. features/modules.md lists them, and tests/module_cpp20 parses a document, so that a missing export fails the ci_module_cpp20 job. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
fuzzer-parse_json_view.cpp checks for every input that json_document::accept agrees with json::accept, that an accepted input materializes to the value json::parse returns, and that a rejected input makes both throw the same exception with the same message. It is built like the other fuzzers (tests/Makefile, and the root Makefile's fuzz_testing_json_view target, which starts from the JSON test corpus) and listed in tests/fuzzing.md. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
- API pages for basic_json_document and basic_json_view, one per member, and for the four aliases, each with an example - features/json_view.md: the problem the view solves, ownership and lifetime, what matches basic_json::parse() and what differs, and when to choose json, ordered_json, SAX, or the view - the examples show why one would use the view, not only how: borrowed vs. owned input, reading a few fields and materializing one subtree, reusing a document across many messages - registered in the mkdocs navigation, llms.txt, the docset, the exceptions page (out_of_range.416), architecture.md, the integration page, and the README; the yyjson credit is added to the README and license.md Signed-off-by: Niels Lohmann <mail@nlohmann.me>
benchmarks_view.cpp adds ViewParse, ViewRead (a reused document), ViewParseIndented, ViewAccept, and ViewMaterialize on the files of ParseString, so that each row can be read against the json::parse row of the same file; benchmarks.cpp gains Accept (json::accept) as the counterpart of ViewAccept. The view benchmarks are built only if the header directory has json_view.hpp, so that older versions can still be benchmarked. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
- the input dispatch takes byte ranges by const reference and reads the size once (which also settles a finding of the static analyzer); input adapters are taken by value - the classification of inputs keeps its nested conditional operators, a constant expression of C++11 (NOLINT) - the test's C arrays, fixed seed, and escaped literals are marked, as in the other tests Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The 4 GiB limit and the fallback for an input that parse() accepts but the view rejects (a bug) are excluded from the coverage; shrink_to_fit() of an empty document is tested. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
materialize.hpp includes <string> (build/include_what_you_use). Signed-off-by: Niels Lohmann <mail@nlohmann.me>
A new section describes the 16-byte node: its fields, how integers, floats, and object members are stored, how views navigate without pointers, and a worked example. The feature page and the pages of basic_json_document and node_count link to it where they mention the 16 bytes. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
…header - ci_test_noexceptions: the helpers that compare the exceptions of json_document::parse() and json::parse() catch them outside a CHECK_THROWS, so with JSON_NOEXCEPTION the first parse error aborted the test. Compile those comparisons only with exceptions, as unit-class_parser.cpp does. - ci_test_gcc: -Werror=unused-result for CHECK_THROWS_AS(json_document:: parse(...)); assign the result to a dummy document. - ci_test_compilers_clang (3.6): `const json_view invalid;` needs a user-provided default constructor there (CWG 253); value-initialize it. - ci_test_single_header: json_view.hpp now exists as a single header and contains the internal view headers, so unit-json_view_builder.cpp includes it instead of the detail headers in that mode, and the test is built again with the single header. - Regenerate single_include/nlohmann/json_view.hpp for the builder change merged from json-view/08-view-builder. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The "check" job (Check amalgamation) runs develop's amalgamate.py and read all configurations from the develop checkout, where config_json_view.json does not exist until this stack lands, so it failed with FileNotFoundError. Read that configuration from the pull request's checkout; the tool itself stays develop's. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
80763fe to
2d7024e
Compare
|
Restacked #5621–#5635 onto the current Conflicts (only in this PR,
The upper 14 branches rebased without conflicts; Checked before pushing, for every branch head:
Pushed with — posted by Claude Code on behalf of @nlohmann |
…er (#5735) * Remove dead doctest help entry and pretty_format target from Makefile The top-level Makefile still carried three leftovers: - The help text listed a "doctest" target that was removed in #4560, so "make doctest" fails with "No rule to make target". The example check now runs as "make check_output -C docs". - "pretty_format" ran clang-format on all sources, but .clang-format was deleted in #4573, so the target reformatted everything in the default LLVM style, against the Artistic Style formatting that "make pretty" applies and CI enforces. - "clean" removed benchmarks/files/numbers/*.json, a directory that no longer exists since the benchmarks moved to tests/benchmarks (#3462). Only maintainer tooling changes; the library is not affected. Part of #5717 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Document tools/macro_builder and tidy up serve_header.py tools/macro_builder generates the NLOHMANN_JSON_EXPAND, NLOHMANN_JSON_GET_MACRO and NLOHMANN_JSON_PASTE* macros in macro_scope.hpp, but nothing referred to it. Add a README that explains what it generates, how to run it and where the output goes, and which dependent tables (NLOHMANN_JSON_DOUBLE_PASTE, NLOHMANN_JSON_TYPE_BODY) are maintained by hand. Point to it from a comment above NLOHMANN_JSON_EXPAND. The generator itself is unchanged; following the README reproduces the header byte for byte. In serve_header.py, drop the LGTM suppression (LGTM.com shut down in 2022), replace the """.""" placeholder docstrings with real ones, and import socket and ssl at module level. DualStackServer.server_bind uses socket, which was only imported under __main__; when the module was imported instead, the NameError was swallowed and IPV6_V6ONLY was not cleared. The header change is a comment only; behavior, API and ABI are unchanged. Part of #5717 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Hash every release_files artifact, not a hardcoded subset The `release` target signed and copied json_fwd.hpp into release_files alongside json.hpp, but the shasum line that writes hashes.txt only listed json.hpp, include.zip and json.tar.xz. Users could not verify the published json_fwd.hpp against hashes.txt. Hash every file in release_files except the .asc signatures instead of naming files by hand, so a newly shipped header (such as the json_literals.hpp that #5610 adds to this target) cannot be missed again. Only affects the generated hashes.txt release artifact; the library itself is unaffected. Overlaps #5610, which touches the same lines to add json_literals.hpp to the release target. #5717 item 1 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove the broken fuzz_testing* Makefile targets fuzz_testing and fuzz_testing_{bon8,bson,cbor,msgpack,ubjson} seeded fuzz-testing/testcases from tests/data, which was removed in dbf1a1f (2020) when the test data moved to the external json_test_data repo. The find command found nothing, but the pipeline's exit status was that of xargs, so the recipe still reported success with an empty corpus, and the printed afl-fuzz command would refuse to start. The recipes were also six near-identical copies with unquoted -name patterns, used the legacy CXX=afl-clang++, and fuzzing-start/stop were missing from both the help output and .PHONY. tests/fuzzing.md already documents the working flow (download json_test_data, then `make -C tests fuzzers`), so replace the six broken targets and their help lines with a single pointer to that document instead of trying to keep six copies of a fragile shell pipeline in sync. This does not affect OSS-Fuzz, which builds through tests/Makefile. Overlaps #5621, which adds a seventh copy of the same broken line for fuzz_testing_json_view. #5717 item 2 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove stale Travis comment above check-amalgamation check-amalgamation carried "Note: this target is called by Travis", left over from before the project switched off Travis CI. The prior Makefile cleanup commit removed the other stale Travis-era leftovers (the doctest help entry, pretty_format, and the benchmarks/ path in clean) but missed this comment. #5717 item 4 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix stale install/usage instructions in the vendored amalgamate README tools/amalgamate/README.md is the unmodified upstream text and no longer matches how the tool is used here: - It named a Bitbucket origin that no longer exists; CHANGES.md already tracks the GitHub mirror commit this copy is based on. - It asked for Python 2.7, but CI and the Makefile run the script with python3. - It told readers to run ./test.sh (not vendored) and install to /usr/local/bin; in this repository the tool runs through `make amalgamate`. - Its usage synopsis showed `-v` taking no argument, but the script's own argparser requires `choices=["yes", "no"]`, so that form fails with "argument -v/--verbose: expected one argument". The Makefile calls it as `--verbose=yes`. - It pointed at test/source.c.json and test/include.h.json, which are not vendored; the configs actually used are config_json.json and config_json_fwd.json. Rewrote only the Installing and Using sections to match; left the "Here be dragons" caveats and the rest of the vendored code untouched to avoid diverging further from upstream. Overlaps #5615, which edits amalgamate.py, this README and CHANGES.md. #5717 item 6 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Derive generate_natvis.py's ABI tag list and version from abi_macros.hpp generate_natvis.py hard-coded abi_tags = ['_diag', '_ldvcmp', '_dp', '_bics', '_psp', '_snul'] and required --version on the command line. The source of truth is include/nlohmann/detail/abi_macros.hpp: the NLOHMANN_JSON_ABI_TAG_* defines, the argument order of NLOHMANN_JSON_ABI_TAGS_CONCAT, and NLOHMANN_JSON_VERSION_MAJOR/MINOR/ PATCH. Nothing checked that the copies stayed in sync, and they have drifted apart before: _dp was added in #4517 but missed here until #5544, and 3.11.3 shipped json_abi_v3_11_2 namespaces (#4340). Parse the tag list (in NLOHMANN_JSON_ABI_TAGS_CONCAT order) and the version from abi_macros.hpp instead of hard-coding them. Make --version optional (falling back to the parsed version) and default the output directory to the repository root the script lives in. Add a "natvis" Makefile target that runs the script, and extend check-amalgamation to regenerate nlohmann_json.natvis and fail on a diff, the same way it already does for the amalgamated headers and BUILD.bazel. Wire the same regeneration into check_amalgamation.yml, using the tool copy checked out from develop (as the workflow already does for amalgamate.py) and installing jinja2 from tools/generate_natvis/requirements.txt. Update the tool's README to say it must be re-run after adding an ABI tag or bumping the version. Verified: a run against develop produces no diff (with either the default or an explicit --version 3.12.0); adding a dummy NLOHMANN_JSON_ABI_TAG_* without a matching #define makes the script fail loudly instead of silently omitting the tag; xmllint --noout passes on the regenerated file; and running the script from a directory other than the one being checked (simulating the workflow's separate tool checkout) against this repository root also produces no diff. Overlaps #5600, which added _ekmo to the same hand-written abi_tags line and regenerated the file. #5717 item 3 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Strip the leading "./" find(1) prefix from release hashes.txt entries bc6e7db (#5717 item 1) switched the release target's shasum line from naming files by hand to $$(find . -type f -not -name '*.asc' | sort), so a newly shipped header is hashed automatically. Run from inside release_files, that find prints paths as "./json.hpp" instead of "json.hpp", so hashes.txt lists "./json.hpp" etc. instead of the plain filenames it always used. shasum -c still verifies "./json.hpp" fine, but it is a needless cosmetic regression for anyone reading the file or matching it against release notes. Strip the "./" prefix with sed before sorting, keeping the filenames exactly as before while still hashing every artifact automatically. Review fix for #5717 item 1 (PR #5735). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix check_amalgamation.yml: pass --version to generate_natvis.py bff45f1 (#5717 item 3) made --version optional in tools/generate_natvis/generate_natvis.py and wired the workflow's new "Regenerate nlohmann_json.natvis" step to call it without --version, relying on the script deriving the version from abi_macros.hpp itself. But NATVIS_TOOL_DIR is checked out from develop, the same way TOOL_DIR already is for amalgamate.py, precisely so an in-flight PR's tooling changes cannot mark themselves clean. Until this PR (or an equivalent) merges to develop, that checkout is the old generate_natvis.py, whose --version argument is still required=True. The new step's invocation of "generate_natvis.py $MAIN_DIR" (no --version) then fails argparse on this PR's own CI run with "the following arguments are required: --version", before the check ever gets to compare output. Extract the version from $MAIN_DIR's own abi_macros.hpp in the workflow and always pass it as --version. That satisfies the old script's required argument and is accepted as an explicit override by the new one, so the step behaves the same whether NATVIS_TOOL_DIR holds the pre- or post-merge tool, and stays correct for later PRs that bump the version. Verified by running the workflow step's shell logic locally against both the pre-#5717 generate_natvis.py (checked out at 633de8e) and the new one: both produce the identical nlohmann_json.natvis as the committed file. Review fix for #5717 item 3 (PR #5735). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Extend tools/macro_builder to also generate DOUBLE_PASTE and TYPE_BODY tools/macro_builder only emitted NLOHMANN_JSON_EXPAND..PASTE64, so two other tables that scale with the same max_args stayed hand-maintained with nothing checking them: NLOHMANN_JSON_DOUBLE_PASTE (added by hand in #4563 for the *_WITH_NAMES macros) and the 64-slot dispatch table of NLOHMANN_JSON_TYPE_BODY (the #4041 zero-member/one-or-more-member switch). Both tables pass one macro name per slot to the same NLOHMANN_JSON_GET_MACRO dispatch as PASTE, so they can drift out of sync with max_args exactly the way _dp did in the ABI tag list fixed by #5544. Extend main.cpp with build_double_paste_code() (same recursive-doubling shape as build_paste_code(), but DOUBLE_PASTE consumes two arguments per member, so an even slot index falls back to the next lower odd DOUBLE_PASTE<N>) and build_type_body_table() (max_args - 1 MEMBERS slots and one trailing EMPTY slot, 8 per line, matching how it is written by hand today). Add a "type_body" argument that selects the TYPE_BODY block, since it lives at a separate location in macro_scope.hpp from the EXPAND..DOUBLE_PASTE63 block; plain invocation is unchanged apart from covering the extended range. No longer emit the tool's old trailing blank line, so its output is directly diffable without post-processing. Verified with c++ -std=c++11: running the tool (with and without "type_body") and piping the raw output through the pinned astyle reproduces both blocks of the current macro_scope.hpp byte for byte. tests/src/unit-udt_macro.cpp (all NLOHMANN_DEFINE_TYPE_*/_WITH_NAMES/ zero-member variants) passes unchanged under -std=c++11 and -std=c++17 with -fsanitize=address,undefined. Add a "macro_builder_check" Makefile target that builds main.cpp, regenerates both blocks into a scratch directory inside the repository (astyle's --project lookup needs the target files under the same tree as .astylerc, unlike an external /tmp directory), and diffs them against the corresponding ranges of macro_scope.hpp; wire it into check-amalgamation next to the natvis check. Wire the same regeneration into check_amalgamation.yml, splicing the (still unindented) generated blocks back into the PR's own macro_scope.hpp before the existing astyle/amalgamation step runs, so that step's own tree-wide astyle pass both indents them and folds any drift into the amalgamation patch/diff the workflow already produces. Unlike amalgamate.py and generate_natvis.py, this step builds tools/macro_builder/main.cpp from the pull request's own checkout ($MAIN_DIR) rather than a separate checkout of tools/ at develop: this tool has no independent source of truth to regenerate against (its README documents that it must reproduce macro_scope.hpp byte for byte), so a develop-pinned copy would only reproduce the generate_natvis.py trap fixed in a previous commit on this branch, where a PR that teaches the tool to cover more of the file fails its own CI until that PR merges and updates the develop copy. Add tools/macro_builder/README.md documentation for both new tables and the two-invocation usage, and a short pointer comment above NLOHMANN_JSON_TYPE_BODY (the EXPAND pointer already covered the first block; extended its wording to include DOUBLE_PASTE63). Closes #5717 item 5 in full, completing what the documentation-only "Document tools/macro_builder..." commit already on this branch left open (that commit's README/pointer-comment half stands; it also covers item 7). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
) * Fix the ci_cmake_flags wiring so every option is checked The CMake 3.31.6 flag list referred to itself before it was defined, so only JSON_BuildTests was checked with that version. The targets for the CMake running the build ("_2") were created but never added to ci_cmake_flags, and the three versions shared one build directory. JSON_StrictNulHandling was not in the list at all. Use the 3.5.0 list for 3.31.6, add JSON_StrictNulHandling, and create one ci_cmake_flag_<flag> target per option for the running CMake with its own build directory. Also use the function parameter in the COMMENT, refresh the stale version comment, and let ci_clean remove the downloaded cmake-<version> directories instead of the long-gone cmake-3.5.0-Darwin64. Part of #5715 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix ci_test_clang_libcxx_cxx* jobs silently building without warnings CMake only seeds CMAKE_CXX_FLAGS from the CXXFLAGS environment variable when the cache entry is unset, so the explicit -DCMAKE_CXX_FLAGS="-stdlib=libc++" argument made it ignore CXXFLAGS="${CLANG_CXXFLAGS}" entirely. The six ci_test_standards_clang (..., libcxx) jobs therefore compiled without -Weverything/-Werror while their libstdc++ siblings did use them. Pass -stdlib=libc++ through the same CXXFLAGS value instead of a separate -D argument, and give the target its own build directory (build_clang_libcxx_cxx${CXX_STANDARD}) so it no longer shares a CMake cache with the libstdc++ variant. Suppress the resulting -Wthread-safety-negative finding from libc++'s std::mutex annotations, which fires on doctest's reporters in this translation unit only. Part of #5715 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove the no-op AppVeyor with_win_header job The with_win_header matrix entry patched Windows.h into single_include/nlohmann/json.hpp before building, but JSON_MultipleHeaders has defaulted to ON since #3532 (2022-06), so CMakeLists.txt points the tests at include/ and the patched single header is never compiled. The job has been a no-op VS2015 build since then. Windows.h coverage already exists through tests/src/unit-windows_h.cpp (#3631), which runs in every MSVC job. Delete the dead matrix entry and its before_build steps, and cite unit-windows_h.cpp from the QA page. Part of #5715 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove unused ci_oclint and ci_pvs_studio targets No workflow invokes ci_oclint, ci_pvs_studio, or their tool discovery. ci_oclint also had a side effect on every JSON_CI configure: it copied the single header into src_single/all.cpp and added an add_executable() for it without EXCLUDE_FROM_ALL, so a plain build compiled a 1.2 MB translation unit that only that unused target consumed. ci_pvs_studio duplicates the Makefile's pvs_studio target, which is kept. Also drop the duplicate --check-level=exhaustive flag passed twice to the same ci_cppcheck invocation. Part of #5715 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Stop Dependabot from proposing astyle bumps astyle is deliberately pinned at 3.4.13 because newer versions reformat unrelated lines and this version defines the formatting that check_amalgamation.yml enforces. Without an ignore rule, Dependabot keeps opening PRs for every new astyle release (most recently #4580, #4942, #5445, #5448), each of which fails the amalgamation check and gets closed unmerged. Part of #5715 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move Linux arm64 CI from dead Cirrus CI to ubuntu-24.04-arm Cirrus CI stopped reporting check runs on develop sometime after d10879b (2026-05-26); every commit since has only github-actions check runs, so .cirrus.yml silently lost its only consumer while README.md, FILES.md and the QA page kept advertising the coverage. Add a ci_test_arm64 job to ubuntu.yml using the same pinned actions/checkout and lukka/get-cmake actions as the other jobs, on the native ubuntu-24.04-arm runner, with a step that confirms uname -m reports aarch64. Delete .cirrus.yml and its README badge and FILES.md section, and update the QA page's arm64 row. Part of #5715 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make scan-build fail on findings and drop irrelevant checkers (#5715 item 4a) ci_clang_analyze ran scan-build without --status-bugs, so the job passed whenever the ninja build succeeded, no matter what the analyzer found ("No bugs found" in a green run gave no signal either way). It is also missing --use-analyzer=${CLANG_TOOL}, so scan-build picks whichever clang happens to be first on PATH inside the silkeh/clang:dev container instead of the one this file already selected and versioned. Add --status-bugs and --use-analyzer=${CLANG_TOOL} to the scan-build invocation. While here, drop the osx.*, webkit.*, fuchsia.*, and optin.mpi.* checkers from CLANG_ANALYZER_CHECKS: none of them apply to this portable C++ library, and leaving them enabled only adds noise once the job can actually fail on a finding. The job is currently clean (0 bugs), so this alone does not surface any new finding; it only makes the existing "no bugs found" result authoritative. This is 4a of 3 independent steps in #5715 item 4; 4b (Infer) and 4c (IWYU) still need their existing findings triaged before --fail-on-issue/-Xiwyu --error can be added, and are handled in separate commits. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Deduplicate the amalgamation/format check's file set and add BUILD.bazel (#5715 item 5) The amalgamation/format check existed three times with three different file sets: the Makefile's pretty/check-amalgamation, the pull_request-only check_amalgamation.yml workflow, and the ci_test_amalgamation CMake target that also runs on direct pushes to develop/master/release/*. The CMake target's glob was a strict subset of the workflow's (missing the docs/mkdocs/docs/examples/*.hpp headers, tests/abi/, tests/cmake_*/project/, tests/cuda_example/, tests/fmt_formatter/, and tests/module_cpp20/), and it never checked BUILD.bazel at all, so a misformatted file in any of those paths, or a stale BUILD.bazel, could reach develop through a direct push even though the PR-only workflow would have caught it. Make ci_test_amalgamation glob the same roots (docs/mkdocs/docs/examples, include, tests) and extensions (*.hpp, *.cpp, *.cu) as check_amalgamation.yml, excluding tests/thirdparty/ and tests/abi/include/nlohmann/ the same way, and regenerate and diff BUILD.bazel next to json.hpp/json_fwd.hpp. Also add docs/mkdocs/docs/examples/*.hpp to the Makefile's pretty/pretty_format targets, which were missing the four custom_*_type.hpp example headers, and drop the stale "called by Travis" comment on check-amalgamation (Travis is gone; nothing currently calls that Makefile target from CI). Leaves the workflow itself untouched: it deliberately runs amalgamate.py from a fresh develop checkout so a PR cannot change the tool that checks it. Overlaps #5610 and #5621, which each add a new amalgamated header and touch the same INDENT_FILES/ci_test_amalgamation/check_amalgamation.yml hunks. Verified with `make check-amalgamation` on this branch: clean, no diff. #5715 item 5. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Download prebuilt CMake binaries on Linux x86_64 instead of building from source (#5715 item 6) ci_get_cmake() downloaded the source tarball of CMake 3.5.0, 3.31.6, and 4.0.0 and compiled each one completely (including CMake's own test helpers) with -DCMAKE_POLICY_VERSION_MINIMUM=3.5 as a workaround for building old CMake with a newer one. On CI this made the ci_cmake_options (ci_cmake_flags) job take about 11 minutes, most of it spent building CMake itself, even though Kitware has published ready-to-run Linux x86_64 archives for all three of these releases since 3.20 (lowercase platform name). On Linux x86_64, download and unpack the prebuilt cmake-<version>-linux-x86_64.tar.gz archive instead and point the existing ${var} output at its bin/cmake, skipping the configure/build steps and CMAKE_POLICY_VERSION_MINIMUM entirely. Keep the previous source build as a fallback for any other platform (macOS, Linux aarch64), since Kitware does not publish binaries for every CMake/platform combination this project might build on. Verify the downloaded archive against Kitware's own published checksum before unpacking it: download cmake-<version>-SHA-256.txt alongside the archive and run `sha256sum -c` on the matching line. A CI job that wgets and untars a binary from a release page with no integrity check is a supply-chain gap; Kitware has published this file for every release since 3.20, so checking it costs one extra download and one grep. As a separate, mechanical change: the ci_cmake_options job's container only needed to stay on ubuntu:focal for the source build's libssl-dev dependency and its own aging toolchain; now that the Linux/x86_64 path never compiles CMake, drop libssl-dev from its apt install line and move the job to ubuntu:24.04 (Ubuntu 20.04 left standard support in May 2025). ci_clean already removes the cmake-3.5.0/cmake-3.31.6/cmake-4.0.0 directories from the #5715 item 3 fix, and the prebuilt path reuses those same directory names, so no further cleanup changes are needed. Overlaps #5598, which edits the same ci_cmake_options matrix line in ubuntu.yml; a rebase may be needed once that lands. Verified locally: `cmake -S . -B build -DJSON_CI=On` configures cleanly on macOS/arm64 (source-build fallback branch) and on Linux/x86_64 in an ubuntu:24.04 Docker container (47 `ci_cmake_flag_*` targets generated, one built and run successfully); `.github/workflows/ubuntu.yml` still parses as valid YAML; downloaded the real v3.31.6 Linux x86_64 archive and SHA-256 file from Kitware and confirmed the `grep | sha256sum -c` pipeline both accepts the genuine file and is anchored to the exact filename (not a prefix match). CI must confirm: the prebuilt-binary path actually runs on the ubuntu-latest/ubuntu:24.04 x86_64 runner, all `ci_cmake_options` entries still pass with the new container's GCC, and the job's runtime drops from roughly 11 minutes. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make Infer fail on findings, with a type-level baseline for the ~174 pre-existing ones (#5715 item 4b) ci_infer ran `infer run` without --fail-on-issue, so the job passed regardless of what Pulse found; the last recorded run (35829411620, commit 1054b20) logged "Found 174 issues" and still went green. report.txt was also never uploaded, so the full finding list was only ever visible in the truncated 5-issue console excerpt. Add a repository-root .inferconfig (auto-discovered by Infer; passing --project-root on the `infer run` invocation makes sure it is found even though the analysis runs from build/build_infer) that sets fail-on-issue and disables the six PULSE issue types that made up all 174 findings in that run: PULSE_UNNECESSARY_COPY_ASSIGNMENT (129), PULSE_UNNECESSARY_COPY (22), PULSE_UNNECESSARY_COPY_INTERMEDIATE (15), PULSE_RESOURCE_LEAK (5), PULSE_CONST_REFABLE (2), and PULSE_UNNECESSARY_COPY_OPTIONAL (1). This is a deliberate, narrower fix than "triage and fix everything in this PR": the visible sample is entirely doctest-macro copies in test code (for example tests/src/unit-algorithms.cpp:141 and tests/src/unit-bjdata.cpp:3706), but 169 of the 174 findings were never uploaded anywhere and this PR cannot respectably claim to have fixed issues it never saw, including the resource-leak and const-refable ones that are the most likely to be genuine bugs. Disabling by issue type is a coarser baseline than a per-finding one (Infer has no built-in per-finding baseline short of the two-run `infer reportdiff` workflow, which this repository does not have the CI infrastructure for), but it has the same effect today: the job goes from always green to green-only-when-clean-of-everything-else, so CI now fails the moment a *new* issue type appears, and report.txt is uploaded as a workflow artifact on every run (including failures) so the six disabled types can be triaged and re-enabled incrementally in follow-up PRs. #5715 item 4b. 4a (scan-build) and 4c (IWYU) are handled in separate commits. Verified: .inferconfig parses as JSON, ubuntu.yml still parses as YAML. Infer itself is not available in this environment (v1.3.0 tar.xz requires a Linux x86_64 runner), so CI must confirm that `infer run --project-root ... -- make` picks up .inferconfig, that fail-on-issue takes effect, and that the six disabled types actually suppress the existing findings without also hiding an unrelated new one. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix IWYU findings for json.hpp/json_fwd.hpp/ordered_map.hpp and make CI fail on new ones (#5715 item 4c) ci_single_binaries ran IWYU via CMake's CXX_INCLUDE_WHAT_YOU_USE launcher property, which only printed "Warning: include-what-you-use reported diagnostics" without failing the build: CMake's own __run_co_compile wrapper does not propagate the launched tool's exit code, so even `-Xiwyu --error` could never fail `cmake --build` this way. Verified this empirically by injecting a deliberately-unused #include and confirming the build still exited 0. Fix the findings from the last recorded run (issue #5715 item 4, log 35829411620): - ordered_map.hpp: add <new> (placement new) and nlohmann/detail/abi_macros.hpp; drop <memory> (std::allocator is still visible transitively via <vector>, confirmed by full local and containerized test suite runs). - json_fwd.hpp: drop <memory> (same reasoning). Keep every forward declaration IWYU wanted removed (adl_serializer, basic_json, json_pointer, ordered_map): this file's only job is to forward-declare them for downstream users, so "nothing in this TU uses them" is expected, not a real finding. Mark each with `// IWYU pragma: keep`. - json.hpp: add <cmath>, <cstdint>, <set>, <type_traits>, <unordered_map>, and the detail/abi_macros.hpp, detail/input/json_sax.hpp, detail/meta/detected.hpp, thirdparty/hedley/hedley.hpp includes IWYU says it needs. Do NOT remove adl_serializer.hpp, detail/conversions/from_json.hpp, detail/conversions/to_json.hpp, detail/macro_unscope.hpp, or ordered_map.hpp as IWYU suggests: nothing else in include/nlohmann includes adl_serializer.hpp or ordered_map.hpp, so basic_json<>'s own default template arguments (JSONSerializer = adl_serializer, and ordered_json = basic_json<ordered_map>) would lose their complete type; detail/macro_unscope.hpp is what undoes the JSON_* macros detail/macro_scope.hpp defines earlier in this same file, and removing it leaks those macros into every translation unit that includes <nlohmann/json.hpp>. Verified by actually removing them in a scratch test: the header still "compiles" stand-alone but ordered_json and every macro-using translation unit break. Marked each `// IWYU pragma: keep`. Enforce it with `iwyu_tool` (ships with IWYU, e.g. as /usr/bin/iwyu_tool on Debian/Ubuntu) instead of relying on the launcher property: it reads compile_commands.json (now exported project-wide under JSON_CI) and does return a real exit code for its own analysis, independent of CMake's wrapper. ci_single_binaries now runs it over every src_single/*.cpp with `-Xiwyu --error`, so a *new* finding fails CI. json.hpp itself is excluded from that hard gate: even after every fix above, IWYU's suggestion for one remaining symbol (a container `swap, operator!=` used somewhere via a templated comparator) is not deterministic — repeated, otherwise-identical containerized runs reported <set>, then <unordered_map>, then <map> as "the" header to add/remove for the exact same source. Gating a whole CI job on a nondeterministic suggestion would make ci_single_binaries flaky rather than informative, so json.hpp keeps the existing informational warning (still shown during its normal compile) without failing the build on it. Every other one of the ~50 single-header checks is included in the hard gate. #5715 item 4c. 4a (scan-build) and 4b (Infer) are separate commits. Verified: full local ctest suite (129/129) and the ci_single_binaries target itself both green in a containerized silkeh/clang:dev run (matching the actual CI job) after this fix; a deliberately-reintroduced unused #include in ordered_map.hpp was confirmed to fail `cmake --build ... --target ci_single_binaries` (exit 2) with this change, and to pass without it, on the same container/IWYU version CI uses. `make check-amalgamation` is clean. Compiled with Clang and GCC at -std=c++11/14/17/20 locally with no new warnings. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Signed-off-by: Niels Lohmann <mail@nlohmann.me> # Conflicts: # Makefile # cmake/ci.cmake
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
json_document::read is a member function, not POSIX read(), and the C string overload requires null-terminated input like json::parse. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Conflicts in the See also lists of nine basic_json pages, is_discarded.md, and features/index.md, where develop (#5638) and this branch both added entries: kept both. Ran make amalgamate. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
…HEAD Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Part of the stack for the zero-copy view (#5295). This PR adds the new header
<nlohmann/json_view.hpp>with the public classes, built on the parser from the PR below. Element access, values,dump(), and comparisons follow in the next PRs, so each one stays reviewable.Summary
json_documentparses JSON text into a compact, read-only index.json_viewis a cheap handle to one value in it. Strings and numbers are not copied out of the text.basic_json_document<BasicJsonType>:parse,parse_copy,accept,read(reuses the document's memory),root,is_discarded,source,owns_source,node_count,memory_usage,shrink_to_fit.basic_json_view<BasicJsonType>:type, theis_*queries,operator bool,size,empty,materialize,source_offset.json_document,json_view,ordered_json_document,ordered_json_view.std::string_view, C strings, character arrays, pointer ranges, and (in C++20) other contiguous iterator ranges;std::string(moved in) and anything elseparse()accepts (copied or read).basic_json::parsewould throw for the same input. The library parser is run on the failing input (a cold path), so message, position, and id are the same. Inputs of 4 GiB or more are rejected with the newout_of_range.416.floatordoubledoes not allocate.materialize(): builds the value with the SAX handlerparse()uses, soJSON_DIAGNOSTICSparent pointers are set as usual.Changes
include/nlohmann/json_view.hpp,detail/view/{errors,input,materialize,number}.hpp,single_include/nlohmann/json_view.hpp(amalgamated with theexternalkey from the first PR of the stack, so it includes json.hpp instead of copying it)Makefile(amalgamate, check-amalgamation, include.zip, release),cmake/ci.cmake,check_amalgamation.yml,meson.build, Bazel, the C++20 module,labeler.ymlunit-json_view.cpp,unit-json_view_macros.cpp,fuzzer-parse_json_view.cpptests/benchmarks/src/benchmarks_view.cpp(ViewParse, ViewRead, ViewParseIndented, ViewAccept, ViewMaterialize; built only if the header exists) andAcceptforjson::acceptfeatures/json_view.mdwith a "which representation to choose" section (linked from the feature overview, last in the Features navigation), 23 examples, the exceptions page, README, and a "Node index of JSON views" section on the architecture page (the 16-byte node: fields, navigation, and a worked example), linked from the feature page and the pages that mention the 16 bytesTests
unit-json_view.cpp:materialize()equalsparse()for handwritten and generated documents, with duplicate keys, 100,000 levels of nesting, andJSON_DIAGNOSTICSparents;parse()'s, message included, for malformed inputs under all option combinations;read(), moves,shrink_to_fit(), and source offsets.unit-json_view_macros.cpp: includes the header withoutJSON_TEST_KEEP_MACROS, as users do. It checks that the view does not rely on macros json.hpp undefines, and that none of its ownNLOHMANN_VIEW_*macros leak.json::parse; 637,000 runs without a finding at this PR, and 14.5 million on the complete stack.Benchmarks
tests/benchmarksat this PR (Google Benchmark, Apple M1, median of 5):json::parsejson_document::parsejson::acceptjson_document::acceptmaterialize()of the whole document (ViewMaterialize) takes 80.1, 4.65, 1.06, and 0.72 ms. So even when ajsonvalue is needed in the end,json_document::parsefollowed bymaterialize()is 1.4–2.1× faster thanjson::parse.x86-64 numbers are still missing.
Public API
No breaking change. This PR adds a new header, new class templates, and the exception id
out_of_range.416; nothing existing changes. The C++20 module exports the new names. The labelaspect: json_viewused bylabeler.ymlhas to be created in the repository.Written by Claude Code.
🤖 Generated with Claude Code