Skip to content

merge: reconcile upstream through df3ce972 with native auto-local, chain and member receivers - #487

Merged
bompus merged 4 commits into
fork/consolidatedfrom
reconcile/upstream-df3ce972
Oct 13, 2026
Merged

bompus merged 4 commits into
fork/consolidatedfrom
reconcile/upstream-df3ce972

Conversation

@bompus

@bompus bompus commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Merges upstream main through df3ce972 (two commits: colbymchenry#2532, colbymchenry#2533) into fork/consolidated.

Both commits change C++ receiver resolution in TypeScript files this fork deleted (name-matcher.ts, cpp-namespaces.ts, cpp-type-aliases.ts), so the merge keeps the deletions and ports the behavior into the native resolver (codegraph-kernel/src/resolve/cpp_range_for.rs, cpp_receivers.rs, cpp_implicit_this.rs, cpp_namespace.rs).

Behavior

  • An auto local has the type its initializer has where that is written: a cast or smart pointer written out, new / std::make_unique, a construction, or what a function or method is declared to return (the caller's own class's method first, inherited ones included). A type a template parameter decides is left unread.
  • A chained call a->b()->c() looks up the class b is declared to return, through a smart pointer too, and any number of calls deep. A standard or abseil type, and a capitalized type under a namespace the project declares nothing in (absl::Status), reaches no project method.
  • A data member of the caller's class is also looked up in the classes it derives from (a base that depends on the template's parameters is skipped), and in a gtest TEST_F / TEST_P body in the fixture.
  • A template <…> header followed by macros or specifiers still names its parameters.

Where this differs from upstream

Where a receiver's type cannot be read (a template parameter's, a std::shared_ptr<T> of one, a trait of one, what a function template returns), the fork leaves the call unlinked; upstream's by-name guess lands at 0.7. That is the fork's existing rule for receivers it cannot type, so five of upstream's new expectations are changed to [] (each with a comment naming upstream's value), two typed calls read @0.9 where upstream guessed @0.7, and the three-call chain test gains VersionStorageInfo::LevelFiles @0.85, which upstream's single-inner-call reading does not reach.

Checks

  • __tests__/cpp-auto-local-receiver.test.ts and __tests__/cpp-member-receiver.test.ts: 22/22 (16 failed before the port).
  • Full suite: 8,948 passed, 39 skipped (the only failure in the run was an ordering mistake in my own updated expectation, fixed and re-run with readme-sync: 28/28). Kernel golden dumps unchanged.
  • npm run eval:precision -- json (C++ corpus): absent 11/11 held, present 7/7 held.
  • tsc --noEmit and check:agent-docs clean.

README rows checked

"About this fork": the upstream merge point (now df3ce972) and the C++ differences list (one bullet added for auto locals, chains and member receivers). No other table row or measured number moves; CHANGELOG.md gets upstream's two entries plus a fork note.


Summary by cubic

Merges upstream main through df3ce972 (colbymchenry#2532, colbymchenry#2533) into fork/consolidated, porting both upstream C++ receiver-resolution changes into the native resolver, since the fork deleted the TypeScript files they touched (name-matcher.ts, cpp-namespaces.ts, cpp-type-aliases.ts).

Behavior

  • auto locals now take the type their initializer writes where it is declared: a cast or smart pointer, new/std::make_unique, a construction, or a function or method's declared return type (the caller's own class's method first, inherited included).
  • A chained call resolves the class its inner call is declared to return, any number of calls deep and through smart pointers; a type from outside the project, or under a namespace the project declares nothing in, reaches no project method.
  • A data member of the caller's class is looked up in the class's declarations wherever they are, in classes it derives from (skipping bases that depend on template parameters), and in a gtest TEST_F/TEST_P body in its fixture.
  • A . chain on a returned std::unique_ptr or std::optional is that type's own member; only -> reaches what it holds. A variable declared in a for, if or switch initializer shadows a same-named inherited member.
  • Where a receiver's type cannot be read (a template parameter's, a trait of one, a function template's return), the call stays unlinked rather than receiving upstream's by-name guess at 0.7; five upstream expectations change to [], two typed calls read @0.9 where upstream guessed @0.7, and the three-call chain test gains VersionStorageInfo::LevelFiles @0.85.
  • New tests pass 22/22; full suite 8,948 passed; kernel golden dumps unchanged.

Written for commit 03ae96b. Summary will update on new commits.

View guided diff Turn on auto-fix

Summary by CodeRabbit

  • Bug Fixes
    • Improved C++ call resolution for auto variables initialized by casts, constructors, factories, and function returns.
    • Resolved calls through chained expressions and class data members using declared types, including inherited members and test fixtures.
    • Improved resolution for members declared across files and variables declared in control-flow initializers.
    • Correctly handled member access through returned smart pointers and optionals.
    • Avoided linking calls when types are unresolved or template-dependent, or when external types merely share a name with project classes.
  • Documentation
    • Updated the README with C++ call-resolution details and the upstream baseline.

colbymchenry and others added 3 commits October 13, 2026 00:42
…class C++ finds, not a namesake (colbymchenry#2532)

colbymchenry#2493 resolved calls on declared pointers, values and references by C++
lookup. Calls whose receiver type came from an initializer or a return
type still went by the type's last name:

- An `auto` local now has the type its initializer has, as written where
  that is declared: a cast or smart pointer written out
  (`std::unique_ptr<Iterator>(…)`, `static_cast<InstrumentedMutex*>(arg)`),
  `new` / `std::make_unique` / a construction of a class, or what a
  function or method is declared to return (`txn->GetIterator(…)`), with
  the initializer read to its `;` across lines. Its calls then go through
  matchCppDeclaredTypeCall from that site. A type that depends on a
  template parameter (`T*`, `std::shared_ptr<T>`) is left to the old path.
- A chained call `a->b()->c()` looks `c` up on the class C++ finds for the
  written return type of `b`, through a smart pointer too
  (matchCppChainOnWrittenType), before the last-name path.
- A type from outside the project gets no project method: a std/abseil
  library type (colbymchenry#2413's gate, now applied before the last-name match, so a
  namesake class like fmt's own `ostream` no longer wins), and a
  capitalized type under a namespace the project declares no type in
  (`absl::Status`, which reached `upb::Status::ok`).
- A bare call in a method types an `auto` local from the class's own or
  inherited method; a `template <…>` header followed by macros or
  specifiers still names its parameters; a trailing annotation macro
  (`T* PROTOBUF_NONNULL`) is no part of a return type; and memoized
  `auto` types are worked out with a fresh depth budget, so a deep chain
  that reads a local first no longer leaves it untyped for its other calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ype's method, wherever the class is declared (colbymchenry#2533)

Receiver inference read back through the call's file and the header of the
same name only, so a member of a class declared elsewhere — rocksdb's
DBImpl is declared in db_impl.h, its methods in db_impl_write.cc,
db_impl_compaction_flush.cc, db_impl_open.cc and more — was never read, and
calls on it (`versions_->GetColumnFamilySet()`, `mutex_.Lock()`) were
guessed by the receiver's name.

- Where the scan leaves the calling function, the name is looked up as a
  data member of the function's class before anything outside it
  (cppCallersMember): the class's own declaration lines wherever it is
  declared (the declaration in the caller's own translation unit, of
  classes sharing a name), then the classes it derives from through their
  base edges, skipping a base that depends on the template's parameters.
  A gtest `TEST_F` / `TEST_P` body is a method of a class derived from its
  fixture, so `db_` there is the fixture's.
- The member goes into the declaration record as the caller's own, with
  its class as the lookup site, so colbymchenry#2493's lookup, the library-type gate and
  colbymchenry#2440's held-type path apply. A member of a template parameter's type,
  or of an alias of one, and a name the scan saw an unreadable declaration
  of, stay as before. (isCppTemplateDependentType now also counts a std
  trait of a parameter: protobuf's `using MapFieldBase =
  std::conditional_t<kIsMutable, …>`.)
- cppDataMemberIn reads only declarations outside parentheses and not
  followed by an argument list (method declarations have no nodes, so their
  parameters are in the class's lines), skips nested functions and classes,
  follows block comments across lines, and blanks string literals.
- A member declared below an inline method that uses it, and a member of
  the class itself rather than of a class nested in it, are found the same
  way; cppExpressionType's own member fallback became redundant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in and member receivers

Upstream's colbymchenry#2532 and colbymchenry#2533 change C++ receiver resolution in TypeScript files
this fork deleted (name-matcher.ts, cpp-namespaces.ts, cpp-type-aliases.ts).
The same behavior runs in the native resolver:

- An `auto` local has the type its initializer has where that is written: a
  cast or smart pointer, `new` / `std::make_unique`, a construction, or what a
  function or method (the caller's own, inherited ones first) is declared to
  return. A type a template parameter decides is left unread.
- A chained call looks up the class its inner call is declared to return, any
  number of calls deep, through a smart pointer too; a type from outside the
  project, or one under a namespace the project declares nothing in, reaches
  no project method.
- A data member of the caller's class is also looked up in the classes it
  derives from (skipping a base that depends on the template's parameters),
  and in a gtest TEST_F / TEST_P body in the test's fixture.
- A template header followed by macros or specifiers still names its
  parameters.

Where the receiver's type cannot be read the fork leaves the call unlinked
instead of guessing a method by the receiver's name; five of upstream's new
expectations are adjusted to that, and one gains the third call of a chain.
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 30979ec7-b362-4482-b9b3-24d77f20ac76
📥 Commits

Reviewing files that changed from the base of the PR and between 0838d54 and 03ae96b.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • README.md
  • __tests__/cpp-auto-local-receiver.test.ts
  • codegraph-kernel/src/resolve/cpp_range_for.rs
  • codegraph-kernel/src/resolve/cpp_receivers.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • CHANGELOG.md

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

📝 Walkthrough

C++ call resolution now infers receiver types from auto initializers, declared returns, and class data members. It follows chained calls and leaves calls unresolved when types are unknown, ambiguous, or template-dependent. Regression tests and documentation cover these cases.

Changes: C++ Receiver Resolution

Layer / File(s) Summary
Automatic and chained receiver inference
codegraph-kernel/src/resolve/cpp_range_for.rs, codegraph-kernel/src/resolve/cpp_receivers.rs, codegraph-kernel/src/resolve/cpp_namespace.rs, codegraph-kernel/src/resolve/cpp_implicit_this.rs, codegraph-kernel/src/resolve/mod.rs, __tests__/cpp-auto-local-receiver.test.ts, README.md, CHANGELOG.md
The resolver infers auto receiver types from initializers and declared returns, and follows chained calls. It does not match calls to same-named project methods for recognized external types or types it cannot determine. Tests cover constructions, casts, smart pointers, chains, and unresolved cases. The documentation describes the behavior and updates the upstream baseline commit.
Class data-member receiver resolution
codegraph-kernel/src/resolve/cpp_range_for.rs, codegraph-kernel/src/resolve/cpp_receivers.rs, codegraph-kernel/src/resolve/cpp_implicit_this.rs, __tests__/cpp-member-receiver.test.ts, CHANGELOG.md
Member lookup uses declared types across class declarations, inherited members, and test fixtures. Conflicting declarations, shadowing, and template-dependent types are covered by tests; ambiguous and dependent types remain unresolved.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 03ae9

No actionable issue remains from the reviewed changes; this PR is mergeable after normal checks.

Security architecture risk: 🟡 Moderate · up to 03ae9

Long chains of source declarations can bypass the existing recursion limit and potentially exhaust the analysis process's stack. The demonstrated exposure is availability of that process; broader service impact and runtime protections are not established.

Retained concerns

  • Medium · security · inferred: Following each auto initializer with a fresh depth budget removes the global recursion bound across local declarations. Source containing a sufficiently long sequence of auto copies followed by a receiver call can drive stack usage proportional to sequence length, potentially exhausting the analysis process instead of returning a bounded unresolved result.
Security review details

Security Blast Radius

  • inferred — The supported attack surface is C++ source processed by the resolver. An attacker able to supply that source can influence recursive analysis depth without gaining execution authority. Potential availability impact reaches the process performing analysis; tenant-wide, service-wide, or cross-process exposure is not established.

Security Findings and Attack Paths

  • inferred — A sequence such as auto a0 = new Foo; auto a1 = a0; continuing through auto aN = aPrevious; followed by aN->method() makes each identifier enter the preceding initializer at depth zero. The chain is finite but has no independent declaration-hop bound, creating a plausible stack-exhaustion path. Runtime exhaustion has not been demonstrated.

Trust Boundaries and Controls

  • observed — The inspected path transforms parsed source and indexed declarations into graph-resolution metadata. Its internal visibility and result-publication path do not introduce a new identity transition or privileged execution sink. This does not establish protections in uninspected downstream consumers.

Hardening Proposals

  • proposed — Preserve a separate total initializer-hop budget, or use bounded iterative traversal, while allowing individual expressions their own depth budget. Exhaustion should return an unresolved result rather than relying on the host stack limit.
Pre-merge checks · Passed 6
Passed Passed checks (6 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly identifies the upstream merge point and the native C++ auto-local, chain, and member receiver resolution changes.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Suppressions Explained Passed No changed line adds or broadens a lint, type-check, or compiler-suppression directive. The PR changes source, tests, and documentation only; the added-line scan found no eslint, TypeScript, Rust allo…
User-Visible Changes Documented Passed The diff changes existing C++ receiver resolution, tests, README, and CHANGELOG only. It does not add, remove, or rename a CLI command or flag, MCP tool or argument, supported language or framework, a…
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 LanguageTool
CHANGELOG.md

LanguageTool checks are incomplete because the per-file request limit of 5 was reached. Remaining text was skipped; findings from completed checks are retained.

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 9 files

You’re at about 98% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread codegraph-kernel/src/resolve/cpp_range_for.rs
Comment thread codegraph-kernel/src/resolve/cpp_range_for.rs Outdated
Comment thread codegraph-kernel/src/resolve/cpp_receivers.rs
Comment thread README.md Outdated
…riables for C++ receivers

A chained call spelled with '.' on a returned smart pointer or optional is the
type's own member; only '->' reaches what it holds. A variable declared in a
for, if or switch initializer shadows an inherited member of the same name.
The call-chain length the docs claim is now stated as four calls.
@bompus
bompus merged commit d59ca2b into fork/consolidated Oct 13, 2026
5 checks passed
@bompus
bompus deleted the reconcile/upstream-df3ce972 branch October 13, 2026 02:42
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.

2 participants