mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 17:51:07 +00:00
#127 made an unknown C++ TYPE a build error instead of a silent `any`. This is the same hole one level down, in the record scanner: a line inside a `struct` body that the scanner could not read as a field was `continue`d, and the struct was published anyway — MINUS that field. A field list is not a detail of a record, it IS the record: the promise every other language binds to. A contract with two fields is exactly as well-formed as one with three, so nothing downstream could tell. Measured on the generator built from master: struct SplitRecord { -> type SplitRecord { exit 0, no diagnostic std::string id; id: tstr std::vector<std::string> n: uint tags; } uint64_t n; }; `tags` is simply GONE struct Outer { -> type Outer { a: int } exit 0 struct Inner { the record is made of the INNER type's int64_t a; field and has none of its own }; Inner inner; std::string label; }; struct Pair { -> method echoPair(v: Pair) with NO `type Pair` std::string first; std::string second; emitted — a contract naming a }; type it never declares struct Defaulted { -> the brace-initialised field is dropped std::string id{"none"}; (only the `= v` form was recognised) int64_t n = 0; }; struct Allman -> not a record AT ALL, so every mention of it { falls to the `any` fallback — since #127 an std::string id; error whose hint says "declare a struct", }; which is the thing the author declared The scanner now reads a body as DECLARATIONS rather than lines. Physical lines are joined until the declaration is whole — a `;` at the struct's own brace depth, or a `}` there, which is how a member function defined inline ends — exactly as the caller already joins a method signature until its parentheses balance. Where a brace sits, and where a line wraps, cannot decide what a header means. Allman and base-clause openings are recognised for the same reason. Whatever is left after that, and after the constructs that definitively are NOT fields (member functions, `using`/`typedef`/`friend`/`static`, access specifiers), is reported instead of skipped, naming the struct, the declaration and the fix. Same withdrawal discipline as #127: the diagnostic is keyed on the struct and tested against the API-REFERENCED set, so a helper struct the module never publishes may be as unreadable as it likes — every real module carries one (openmetrics' `ModuleSource`, the package manager's `PendingAction`). Comments come off with a literal-aware strip. The old bare `indexOf("//")` truncated `std::string url = "http://x";` inside the literal, which merely lost the field before and would now reject valid code. Blast radius, measured: all 27 universal-interface impl headers in the workspace emit BYTE-IDENTICAL .lidl and stderr, with identical exit codes. All 6 structs those headers declare are K&R with no unparsed body line, so nothing in tree changes. logos-qt-generator, which compiles this same file, builds; and test_fullapi_ext_cpp — the records-heavy module — builds end to end. 14 new tests. 10 of them fail on the unpatched parser; the rest are the "must still work" controls, including one for an inline member-function body that an earlier cut of this change would have broken.