refactor(lint): single-declaration splits on the AST, per-declarator type
Tokens are not enough for this one, which is worth stating because it is the
opposite of the enum-class case. Splitting a multi-declarator statement by
copying the shared type prefix is wrong in C++:
int* a, b; -> int* a; int* b; // b was int, not int*
Only per-declarator types get it right, and clang has already resolved them —
`int *` for a, plain `int` for b. A token-based splitter cannot know.
The regex it replaces bailed on `*`, `&`, `<>`, parens and quotes, so pointers,
templates and call initialisers were all left alone. All three split now, and
the fixture proves the mixed pointer case above comes out correctly.
Groups are found structurally rather than by matching a line shape: the first
declarator's extent starts at the shared type, so begin < nameOffset, while a
continuation declarator's starts at its own name, so begin == nameOffset. That
signal comes from the AST itself. nameOffset is now on LintDecl, which is also
what lets the replacement reuse each declarator's original text verbatim
instead of reconstructing it.
Replacing a byte range rather than rewriting whole lines means a comment after
the ';' is outside the edit and survives — the line-based version refused to
touch any line carrying a comment. A comment INSIDE the statement still bails,
since the rewrite would swallow it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
693e3c7af9
commit
5ca2b3e1df
4 changed files with 112 additions and 35 deletions
|
|
@ -183,8 +183,15 @@ export namespace Crafter {
|
|||
std::string type; // clang's resolved spelling: "char *", "std::int32_t"
|
||||
std::size_t line = 0;
|
||||
std::size_t column = 0;
|
||||
std::size_t nameOffset = 0; // byte offset of the declared name
|
||||
std::size_t begin = 0; // byte offsets of the whole declaration, for
|
||||
std::size_t end = 0; // marking a region a transform must not touch
|
||||
// For the FIRST declarator of a statement, `begin` is the start of the
|
||||
// shared type and so precedes `nameOffset`. For a continuation
|
||||
// declarator — the `b` of `int* a, b;` — the extent starts at the name,
|
||||
// so begin == nameOffset. That is how a multi-declarator statement is
|
||||
// recognised without re-parsing the text, and each declarator's `type`
|
||||
// is its OWN resolved type: `int *` for a, plain `int` for b.
|
||||
std::size_t parent = LintNoParent;
|
||||
bool isDefinition = false;
|
||||
bool isStatic = false;
|
||||
|
|
|
|||
Loading…
Reference in a new issue