Skip to content

fix(extraction): blank a C macro call's designated-initializer arguments before parsing - #1733

Open
danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:fix/1729-c-designated-macro-args
Open

fix(extraction): blank a C macro call's designated-initializer arguments before parsing#1733
danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:fix/1729-c-designated-macro-args

Conversation

@danusha2345

Copy link
Copy Markdown
Contributor

Fixes #1729. Standalone off main (b9ca4b7), one commit, pre-parse only — the blank runs before the kernel route point, so both extraction arms receive the same bytes and the kernel needs no change.

What

tree-sitter-c has no rule for .field = value as a call argument. A statement-level

RESET_CONFIG(pidProfile_t, pidProfile,
    .pid = { [PID_ROLL] = PID_ROLL_DEFAULT, … },
    .pidSumLimit = PIDSUM_LIMIT,
    …                                   // ~140 of these
);

parses with an ERROR per argument, and past roughly a hundred of them the grammar's error recovery gives up on the enclosing function: the function_definition runs to the end of the file, the function after it produces no node at all, and every later one is nested under the first. That is not a matter of shape — the issue's three-function reproduction, and every smaller variant I tried (trailing comma or not, nested designators, array designators), keeps correct extents with the ERRORs local. It is the count: a prefix search over betaflight's real resetPidProfile breaks at the 104th–106th argument whatever those lines contain.

blankCDesignatedMacroArgs runs at the head of preParseCSource, in the style of the other blanks there: a statement-level call (); follows) of a macro-cased name whose argument list holds a designator at argument depth — .name = or [index] =, not == — has that list emptied to spaces with newlines kept, so RESET_CONFIG(\n\n…\n); parses cleanly and every byte offset survives. log(.5) and OTHER_MACRO(a == b, c) are left alone. The references inside the initializer are the price; the broken parse was not yielding them either.

Measured

betaflight fork, 2,109 .c files, indexed at b9ca4b7 and at this branch, wasm arm:

base this
resetPidProfile extent 168–1667 168–309 (source: 168–309)
C functions nested inside another C function 310 265
C function nodes 39,991 39,993 (+isTpaActive and two more the old parse dropped)
edges 183,164 183,168

Edge-set delta, rows keyed with resolvedBy: 117 fuzzy calls into pid.c gone and 118 exact-match calls in their place — the same 117 callers now resolve through the strategy that should have had them (they were declined as unreachable closures, #1230, and picked up by the fuzzy fallback at 0.5), plus one call onto the recovered isTpaActive; 45 contains edges moved from resetPidProfile to the file node, 48 file-level contains in their place. Nothing else moved.

The 265 that remain nested are a different shape — #if-damaged STM32 HAL sources (stm32h7xx_ll_utils.c, …_ll_tim.c), where both preprocessor branches are kept and the braces do not balance — and are the deferral case the kernel route already documents; not this PR's.

Tests

extraction.test.ts: the blank itself (offsets and line count preserved, ); keeps its column, designators gone, log(.5) and a == b untouched) and an end-to-end index of a 120-field macro call followed by two functions — before the change resetProfile ran to the last line and g was missing; after it the three functions have their own extents and top-level qualified names. Suite: 624/624.

Interaction with #1718: that PR gates C/C++ out of the nesting check on the resolver side, which is the right guard regardless (C has no nested named functions) and still covers the 265; this PR fixes the extraction that produced the nesting for the designated-initializer case.

🤖 Generated with Claude Code

…nts before parsing

tree-sitter-c has no rule for `.field = value` as a call argument. A
statement-level `MACRO(a, b, .x = …, .y = …, …);` parses with an ERROR per
argument, and past roughly a hundred of them the grammar's error recovery
gives up on the enclosing function: the function_definition runs to the end
of the file, the function after it produces no node at all, and every later
function is nested under the first (colbymchenry#1729). betaflight resets each config
struct that way — `resetPidProfile` was lines 168–1667 in the graph against
168–309 in the source, with 45 functions as its children; 310 such functions
in 73 files across the tree. Name matching read the nesting as a scope
(colbymchenry#1230), so exact-match declined those 45 for every cross-file caller and
the fuzzy fallback carried 117 real calls at 0.5.

blankCDesignatedMacroArgs runs at the head of preParseCSource: a
statement-level call of a macro-cased name whose argument list holds a
designator (`.name =` or `[index] =` at argument depth) has that list
emptied to spaces, newlines kept, so `RESET_CONFIG(\n\n…\n);` parses
cleanly and every offset survives. The references inside the initializer
are the price; the broken parse was not yielding them either. The pre-parse
runs before the kernel route point, so both arms see the same bytes and the
kernel needs no change.

betaflight fork, 2,109 C files, against b9ca4b7: resetPidProfile 168–309,
nested C functions 310 → 265 (the rest are `#if`-damaged HAL sources, a
different shape), +3 function nodes (`isTpaActive` and two more the old
parse dropped), 117 fuzzy calls → 118 exact-match calls, 45 `contains` edges
moved from resetPidProfile to the file node. Nothing else moved.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant