Skip to content

fix(c): v1.6.1 translation-unit macro visibility and constructor overloads - #282

Merged
sunerpy merged 3 commits into
mainfrom
sync/v1.6.1-alignment-5
Oct 1, 2026
Merged

sunerpy merged 3 commits into
mainfrom
sync/v1.6.1-alignment-5

Conversation

@sunerpy

@sunerpy sunerpy commented Oct 1, 2026

Copy link
Copy Markdown
Owner

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 in UPSTREAM.md stays v1.6.0 until the release gate.

Macros — extraction version 16 → 17

  • Macro nodes: every function-like #define, at file scope or inside a body, is a constant carrying its directive, so the index knows which names are macros.
  • Translation-unit visibility (#1838): the previous port suppressed a call when any header in the include closure defined the name, ignoring line order, #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 loose SWAP → swap match 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.
  • #2069: an #undef under an undecidable #if leaves a never-seen flag unknown rather than certainly undefined.
  • #2070: a call whose name is a function-like macro anywhere in the project binds only a function or method — never the macro, never a same-named type — and neither counts toward the same-name ceiling. A #define is never a callee, whichever strategy produced it.

Constructors

  • Prototypes: an in-class constructor prototype is a method whose signature keeps its ;; 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.
  • Per-declarator construction: extern declarations 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.
  • Declines: an initializer_list overload 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 lone T(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

  • Full workspace test suite: 3,903 passed, 0 failed; clippy -D warnings, cargo fmt --check, scripts/guardrail.sh, docs-check and oxfmt --check clean; the pre-push gate re-runs them on the pushed head.
  • New tests, ported from upstream's 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 the initializer_list/pack declines red before), and unit tests for directive parsing, raw-string masking, the guard idiom and the overload shape.
  • One existing unit test was rewritten deliberately: cpp_constructor_arity_range became cpp_constructor_shape, which adds the overload key; its pack case still expects None.

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

CodeGraph Test added 3 commits October 1, 2026 12:56
…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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +632 to +634
let after = body[end..].trim_start();
if after.starts_with('(') {
return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +307 to +310
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")
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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

codecov Bot commented Oct 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 53 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/codegraph-resolve/src/c_macro_visibility.rs 92.51% 45 Missing ⚠️
crates/codegraph-extract/src/lang/cpp.rs 96.07% 4 Missing ⚠️
crates/codegraph-extract/src/walker.rs 98.37% 2 Missing ⚠️
crates/codegraph-resolve/src/resolver.rs 96.87% 2 Missing ⚠️

❌ 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.

Impacted file tree graph

@@            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     
Files with missing lines Coverage Δ
crates/codegraph-extract/src/lang/mod.rs 100.00% <ø> (ø)
crates/codegraph-resolve/src/name_matcher.rs 96.77% <100.00%> (+<0.01%) ⬆️
crates/codegraph-store/src/index_state.rs 83.33% <ø> (ø)
crates/codegraph-extract/src/walker.rs 93.15% <98.37%> (+0.06%) ⬆️
crates/codegraph-resolve/src/resolver.rs 97.23% <96.87%> (+0.59%) ⬆️
crates/codegraph-extract/src/lang/cpp.rs 93.93% <96.07%> (+0.94%) ⬆️
crates/codegraph-resolve/src/c_macro_visibility.rs 92.51% <92.51%> (ø)

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sunerpy
sunerpy merged commit 34bc8dc into main Oct 1, 2026
9 checks passed
@sunerpy sunerpy mentioned this pull request Oct 1, 2026
@sunerpy
sunerpy deleted the sync/v1.6.1-alignment-5 branch October 1, 2026 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant