fix(c): v1.6.1 translation-unit macro visibility and constructor overloads - #282
Conversation
…re a declaration does A function-like `#define`, at file scope or inside a body, becomes a `constant` carrying its directive, so resolution can tell a macro expansion from a call (upstream #1838). An in-class constructor prototype becomes a method whose signature keeps its `;`, so a default argument declared on it can reach the definition's overload, and a union constructor carries its parameters like a class's (#1839). A local declaration now emits one constructor reference per object it constructs: `extern` declarations, pointer, reference and function declarators construct nothing; a bare array of objects is one default construction; an array's braces hold elements, each constructed by its own arity, with one default construction for the elements a literal size leaves over; comments do not count as arguments.
…nstructor overloads The port suppressed a call whenever any header in the include closure defined a function-like macro of that name, ignoring line order, #if and #ifdef, #undef, guards and #pragma once (upstream #1838). Directives are now evaluated in include order with three-valued conditions: a call is an expansion only where the macro is definitely visible at that line, or when only macros bear the name. A macro under an undecided flag keeps the call, a wrapper macro that calls its own name hides nothing, unguarded headers replay on every inclusion and an include stack breaks cycles. An #undef under an undecidable #if leaves a never-seen flag unknown (#2069). A call whose name is a macro anywhere in the project binds only a function or method, never the macro or a same-named type, and neither counts toward the same-name ceiling (#2070); a #define is never a callee. Constructor resolution merges a prototype with its out-of-line definition by parameter types, so a default declared on the prototype admits the definition's overload, and targets the definition. An initializer_list overload declines brace construction, and an unreadable signature declines the whole overload set; a parameter pack is unreadable here (upstream admits it unbounded), so it can no longer make a lone T(int) look unique.
…ructor rows Macro constants, constructor prototypes and per-declarator constructions change what extraction writes, so existing indexes rebuild. The language notes and the v1.6.1 audit record the rows landed here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1472c12324
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let after = body[end..].trim_start(); | ||
| if after.starts_with('(') { | ||
| return true; |
There was a problem hiding this comment.
Mask literals before recognizing self-calling macros
When a macro replacement merely contains its own spelling followed by ( inside a string or character literal—for example, #define LOG(x) fprintf(stderr, "LOG(%d)", x)—this raw substring check classifies it as a wrapper that calls a real LOG function. The visibility timeline consequently records the macro as inactive, so if another file defines a same-named function, invocations of the definitely visible macro can resolve to that unrelated function and fabricate call edges. Scan replacement tokens with literals masked before setting wraps_itself.
Useful? React with 👍 / 👎.
| static NAMED: OnceLock<Regex> = OnceLock::new(); | ||
| let named = NAMED.get_or_init(|| { | ||
| Regex::new(r"^(.*[\s*&>])([A-Za-z_][A-Za-z0-9_]*)$").expect("parameter name regex is valid") | ||
| }); |
There was a problem hiding this comment.
Normalize names inside complex constructor declarators
This regex strips only a parameter name at the very end of the declaration, so names embedded in function-pointer, reference-to-function, or array declarators remain in the overload key. For example, C(void (*cb)() = nullptr); and C::C(void (*handler)()) {} receive different keys; a zero-argument construction then admits only the prototype and creates its call edge to the declaration instead of merging with and targeting the executable definition. Parameter-name normalization needs to cover nested declarators as well.
Useful? React with 👍 / 👎.
Codecov Report❌ Patch coverage is ❌ Your patch check has failed because the patch coverage (94.11%) is below the target coverage (95.00%). You can increase the patch coverage or adjust the target coverage. @@ Coverage Diff @@
## main #282 +/- ##
==========================================
+ Coverage 95.27% 95.29% +0.01%
==========================================
Files 157 158 +1
Lines 97031 97623 +592
==========================================
+ Hits 92446 93029 +583
- Misses 4585 4594 +9
... and 3 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Summary
Seventh and last porting PR of the v1.6.1 alignment, following #281: the C/C++ macro and constructor rows (upstream #1838, #1839, #2069, #2070). The basics landed in #275; this completes them. Dispositions and landing state are tracked in
docs/upstream-sync/V1_6_1_AUDIT_2026-09-30.md; tracked parity inUPSTREAM.mdstaysv1.6.0until the release gate.Macros — extraction version 16 → 17
#define, at file scope or inside a body, is aconstantcarrying its directive, so the index knows which names are macros.#if/#ifdef,#undef, guards and#pragma once. Directives are now evaluated in include order with three-valued conditions: a call is an expansion only where the macro is definitely visible at that line, or when only macros bear the name (so a looseSWAP→swapmatch cannot invent a callee). A macro under an undecided flag keeps the call to the function the other configuration compiles, and a wrapper macro that calls its own name hides nothing. Unguarded headers replay on every inclusion; an active include stack breaks cycles.#undefunder an undecidable#ifleaves a never-seen flag unknown rather than certainly undefined.#defineis never a callee, whichever strategy produced it.Constructors
;; resolution merges it with its out-of-line definition by parameter types, so a default declared on the prototype admits the definition's overload, and the definition is the target. Union constructors carry their parameters like a class's.externdeclarations and pointer, reference and function declarators construct nothing; a bare array of objects is one default construction; an array's braces hold elements, each constructed by its own arity, plus one default construction for the elements a literal size leaves over; comments are not arguments.initializer_listoverload declines brace construction (the choice needs argument types), and a signature that cannot be read declines the whole overload set. KEEP-RUST: a parameter pack is unreadable, so it declines the set — upstream admits it unbounded; the previous port excluded the pack and could therefore make a loneT(int)look unique.Golden impact
All 19 re-indexable corpora regenerated with this build are byte-identical; no corpus holds a function-like macro, a constructor prototype or an affected declaration. MCP structural fixtures are unchanged.
Verification
-D warnings,cargo fmt --check,scripts/guardrail.sh,docs-checkandoxfmt --checkclean; the pre-push gate re-runs them on the pushed head.cpp-macro-and-constructor-calls.test.ts, were run against the previous extractor and the previous resolver first:cpp_macros_and_constructions(5 extraction cases, all red before),c_macro_visibility(14 resolution cases: 7 red before — include order, guards and#pragma once, wrapper macros, macro-only names, macro-off callees, #2069 — and 7 controls that hold either way),cpp_constructor_resolution(5 new cases; the prototype-default merge and theinitializer_list/pack declines red before), and unit tests for directive parsing, raw-string masking, the guard idiom and the overload shape.cpp_constructor_arity_rangebecamecpp_constructor_shape, which adds the overload key; its pack case still expectsNone.BEGIN_COMMIT_OVERRIDE
fix(cpp): extract function-like macros and construct objects only where a declaration does
fix(c): judge macro visibility in translation-unit order and merge constructor overloads
END_COMMIT_OVERRIDE
🤖 Generated with Claude Code