Repository navigation
Release 0.7.0 - #249
Merged
Merged
Release 0.7.0#249
Conversation
A client that receives overlapping ranges drops tokens and warns, so highlighting silently degraded on any line declaring or indexing an array. Three causes, two of them real defects and one missing guard. `getChildren` pushed a VariableExpression's `subscripts` and the indices carried by its `accessChain`. Those are separate clones of the same indices, so every subscript index was walked twice and emitted twice. This was not only a highlighting problem: references and rename were counting index variables twice as well. `emitTypeReference` painted `name.length` characters from the type's start column, but an inline array's TypeReference is synthesised — `__INLINE_ARRAY_INT`, `__VLA_…` — and carries the whole declaration's span, so it painted 18 characters over the variable name. No token can be correct for a name that is not in the source, so skip them. `deltaEncode` now drops fully covered tokens and clips partial overlaps after the sort, which makes this class of defect unreachable from the client whatever the emitters do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P2Wpvri7jZKGPUqgWrpAkE
`startsWith("__")` suppressed more than the synthesised names it was
meant for. `__XWORD` is a real elementary type, so `addr : __XWORD;`
lost its type token entirely, and any user type whose name opens with
underscores lost its highlighting the same way.
Match the two prefixes the AST builder actually mints instead, and cover
both cases with a test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jjy1Ja62BFtay579KYhBC7
…antic-token-overlaps fix(lsp): stop emitting overlapping semantic tokens for arrays (RTOP-287)
The runtime is included by strucpp's emitted code, which on an Arduino target is compiled by the board core's own toolchain at whatever standard that core picked. Half the catalogue sits below C++17 — the five mbed cores hard-code `-std=gnu++14`, and avr, megaavr, samd and two vendor cores are on `gnu++11` — so every C++17 construct reachable from a generated file was a C++ block that could not build there. Several headers already carried a "KEEP THIS HEADER C++14-CLEAN" banner for exactly that reason; `iec_std_lib.hpp`, the largest one, never did and drifted. What changed, all of it inside the runtime: - `std::trait_v<T>` -> `std::trait<T>::value`, 31 sites across three headers. This alone took the full runtime surface from 147 errors to 0 under `-std=c++14`; the rest were cascades. - Inline variables: the 14 constants become plain `constexpr` (internal linkage per TU, the pre-C++17 idiom), and the three mutable slots — `__CURRENT_TIME_NS` in both its threaded and single-threaded forms, and `__CURRENT_DT_NS` — become a function-local static reached through a reference, so no use site changes. - `if constexpr`: `ABS`, `iec_convert_value` and `MOD` move to tag dispatch; `MAX` and `MIN` recurse onto the two-argument overload that already existed in the file instead of testing `sizeof...(rest)`. `iec_test.hpp` includes `iec_std_lib.hpp` rather than forward-declaring `__CURRENT_TIME_NS`: the slot is no longer a plain object a declaration can restate, and the header is the honest dependency. The CLI now drives g++ at `-std=c++14` for both `test` and `repl`, so the suite is the regression guard — a C++17 construct reintroduced into the runtime fails there rather than in a user's firmware build. Verified: `-std=c++14 -pedantic` clean (0 errors, 0 warnings) and `-std=c++17` unchanged at 0; the full suite green at 2435 passed / 7 skipped across 97 files, including the integration tests that compile and run the generated C++. Not addressed: `arduino:sam` (Arduino Due) stays out. Its gcc 4.8.3 tops out at `gnu++1y` and implements neither relaxed constexpr nor the C++14 rule that a constexpr member function is not implicitly const, and it rejects the generic lambda the structure-initializer codegen emits. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X3cRP8dMZScXDKhDXvimmM
`iec_traits.hpp` and `iec_types.hpp` declare their C++20 concepts inside
`namespace strucpp`, and the `#include <concepts>` that those declarations need
sat there with them, behind `#if __cplusplus >= 202002L`. Including a standard
header from inside a namespace nests everything it declares — and everything it
pulls in transitively — under that namespace.
The guard never opened before, so the bug never fired: generated code was
compiled at a standard the editor pinned, and it pinned C++17. Letting the
board's core choose the standard is enough to open it, and an ESP32 compiles the
sketch at `gnu++2b`. What comes out is the whole of `std` reachable only as
`strucpp::std`, with the failure surfacing deep in libstdc++:
error: 'is_constant_evaluated' is not a member of 'std';
did you mean 'strucpp::std::is_constant_evaluated'?
The include moves to file scope in both headers, keeping the version guard. The
runtime now compiles clean at C++14, 17, 20 and 23 — a range that was never
exercised, because nothing built it above 17.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X3cRP8dMZScXDKhDXvimmM
…est standard Three things the port left behind, all of them only reachable once something actually compiled at C++14. `iec_test.hpp` still used `std::is_same_v` in `assert_eq` / `assert_neq`, so `strucpp --test` failed to compile the moment the CLI moved to `-std=c++14`. Those were the last two `_v<>` spellings in the runtime. The variadic `MAX` / `MIN` had been narrowed: dropping `if constexpr` in favour of recursion introduced a named `T third`, which forced the third argument to the same type as the first two. `MAX(a, b, 0)` and `MAX(a, b, someDint)` compiled before and stopped compiling, at both c++14 and c++17, because the recursion could no longer reach the mixed-type two-argument overload that exists for OSCAT. Generated ST is unaffected — codegen casts every argument — but hand-written C++ calling into the runtime is. `third` now carries its own type again. The integration suite hard-coded `-std=c++17` at 17 call sites, which is why neither showed up: the suite stayed green by never compiling at the standard the product ships. It now goes through a single `CXX_STD` constant. Codegen still emits `inline` variables for VAR_GLOBAL, which are C++17 in C++14 code. GCC takes them as an extension and warns; strict C++14 and MSVC reject them. They stay, and the reason is now written at both emission sites: the debug table takes each variable's address into PROGMEM, which needs a constant expression, so a reference or a static-local does not work — on AVR a project declaring a global then fails with "variable 'debug_arr_0' with dynamic initialization put into program memory area". Replacing them needs `extern` in the header plus a definition in one designated translation unit, and codegen currently spreads implementation across several. `integer-literal-exact-cpp` carries `-Wno-c++17-extensions` for the same reason, since it opts into `-pedantic-errors` for an unrelated check. Also narrows the README toolchain claim to what is exercised (g++ 7.3+), and corrects comments describing `inline constexpr` constants that are plain `constexpr` now, and therefore one copy per translation unit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X3cRP8dMZScXDKhDXvimmM
The linkage note was added but the two sentences it contradicts were left in place: the table is no longer `inline`, so "kept inline so no separate .cpp is required" and "whatever `inline constexpr` suggests" both described a spelling that is gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X3cRP8dMZScXDKhDXvimmM
refactor(runtime): compile the header-only runtime as C++14 (DOPE-676)
A minor rather than a patch: the header-only runtime now compiles as C++14 instead of C++17, which changes what a consumer's toolchain has to offer. Produced with `npm version 0.7.0 --no-git-tag-version` plus a build, so both `package-lock.json` entries move together and `src/version-build.ts` is regenerated by `rebuild-libs.mjs` rather than edited — the version string the npm tarball ships comes from that file, and a hand edit is what leaves a re-bundling consumer reporting 0.0.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X3cRP8dMZScXDKhDXvimmM
chore(release): bump version to 0.7.0
dcoutinho1328
approved these changes
Sep 28, 2026
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.
Promotes
developmenttomainfor the 0.7.0 release. Taggingv0.7.0onmainafterwards kicks the Build & Release workflow.Minor, not patch: the header-only runtime now compiles as C++14 instead of C++17, which changes what a consumer's toolchain has to offer.
What is in it
#247 — the runtime compiles as C++14. The generated code is built by the Arduino core's own toolchain, and eleven of the seventeen cores in the catalogue sit below C++17 — the mbed cores hard-code
gnu++14. A C++17 runtime could only reach those through a separate pre-compile pass that ran outside arduino-cli, and that pass is what kept a C++ function block from resolving an Arduino#include. Removing the C++17 requirement is what lets the whole sketch go through arduino-cli again.The port itself: 31
std::trait_v<T>substitutions,if constexprreplaced by tag dispatch inABS,MODandiec_convert_value,MAX/MINfolded by recursion, three mutable globals moved behind an accessor, and 14inline constexprconstants losing theinline.Three things the first pass left behind, all fixed here:
iec_test.hppstill usedstd::is_same_v, sostrucpp --testfailed to compile the moment the CLI moved to-std=c++14;MAX/MINhad been narrowed —MAX(a, b, 0)andMAX(a, b, someDint)stopped compiling, because the recursion could no longer reach the mixed-type overload that exists for OSCAT. Measured againstdevelopment, the old code silently truncated that call:MAX(3, 9, 70000)returned 4464, since the declared return type forced the promoted value back into 16 bits. It returns 70000 now;-std=c++17at 17 call sites, which is why neither showed up. It now goes through a singleCXX_STDconstant, so the suite compiles at the standard the product ships.#include <concepts>sat insidenamespace strucppbehind a C++20 guard that only opens atgnu++2b— a latent bug the port exposed, since on an ESP32 it pulled the whole ofstdin understrucpp::. Hoisted to file scope.Codegen still emits
inlinevariables forVAR_GLOBAL. Replacing them was tried and reverted: the debug table takes each variable's address intoPROGMEM, which needs a constant expression, so a reference or a static-local breaks every AVR build of a project that declares a global. The constraint is now written at both emission sites.#237 — LSP semantic tokens: overlapping tokens for arrays, and the synthetic-type guard narrowed to the inline-array prefixes.
Verification
Why now
openplc-editor#1153 and openplc-web#793 (DOPE-676) pin this release and cannot merge without it: with the pre-compile gone, every strucpp translation unit needs a runtime that builds at the core's own standard.
🤖 Generated with Claude Code
https://claude.ai/code/session_01X3cRP8dMZScXDKhDXvimmM