fix: parse declarations on same line as logos_events/access specifier (#76)

The impl header parser updated its section state and immediately broke out
of line processing when it matched `logos_events:` (or `public:`/`private:`),
discarding any declaration on the same physical line. This meant clang-format
/ prettier output like

    logos_events : void versionReady(const std::string &version);

silently dropped the event, while the newline-separated form parsed fine —
the same valid C++ was handled differently based on formatting.

Strip any leading section specifiers in a loop, updating the section state,
then let the remainder of the line fall through to the declaration parser.
Brace counting still happens once per physical line and blank-line doc-comment
reset is preserved.

Adds a regression test (SameLineSectionSpecifiers) with a fixture covering the
exact prettier form from the issue, a follow-on same-line event, the newline
form alongside it, and the symmetric inline `public:` method case.

Fixes #76

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dario Gabriel Lipicar
2026-06-08 19:48:16 -03:00
co-authored by Claude Opus 4.8
parent 40e7631402
commit e2f28b9245
4 changed files with 122 additions and 8 deletions
@@ -322,26 +322,39 @@ ImplParseResult parseImplHeader(const QString& headerPath,
}
}
// A section specifier may be followed by a declaration on the
// *same* physical line — e.g. clang-format / prettier collapse
// logos_events:
// void versionReady(const std::string& version);
// into `logos_events : void versionReady(const std::string& version);`.
// Strip any leading specifiers, updating the section state, and
// let whatever remains fall through to the declaration parser
// below — otherwise everything after the colon is discarded and
// the same valid C++ is parsed differently based on formatting.
//
// `logos_events:` takes precedence over the standard access
// specifiers: it's a separate section that the codegen pulls
// event prototypes from. (At preprocess time, `logos_events`
// expands to `public`, but the raw source still carries the
// token we recognise here.)
if (eventsRe.match(line).hasMatch()) {
state = InLogosEvents;
pendingDoc.clear();
break;
}
{
while (true) {
QRegularExpressionMatch em = eventsRe.match(line);
if (em.hasMatch()) {
state = InLogosEvents;
pendingDoc.clear();
line = line.mid(em.capturedEnd()).trimmed();
continue;
}
QRegularExpressionMatch am = accessRe.match(line);
if (am.hasMatch()) {
QString spec = am.captured(1);
if (spec == "public") state = InPublic;
else state = InPrivate;
pendingDoc.clear();
break;
line = line.mid(am.capturedEnd()).trimmed();
continue;
}
break;
}
// Only doc comments (/// or /** ... */ / /*! ... */) accumulate as