Skip to content

fix(resolve): follow C++ member chains through four more declaration shapes - #514

Merged
bompus merged 2 commits into
fork/consolidatedfrom
fix/cpp-member-chain-leveldb-gaps
Oct 17, 2026
Merged

bompus merged 2 commits into
fork/consolidatedfrom
fix/cpp-member-chain-leveldb-gaps

Conversation

@bompus

@bompus bompus commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

On google/leveldb (bb74ef7), this change adds 141 correct C++ call links and removes or moves none. It covers calls on a member reached through another member, like compact->compaction->level() and shared.mu.Lock(), in four places leveldb declares the class or the object. The fork linked none of the 109 such calls before. It now links 104 of them the way upstream (e8ec7fd) does, and 5 more correctly where upstream is wrong or has no link.

What each fix does

  1. A nested class defined outside its class. For struct DBImpl::CompactionState { … };, the member lookup compared the class's name with a qualified name, DBImpl::CompactionState, so it never matched. It now reads the last part of that name.
  2. A global struct whose member's type comes from a using-declaration. leveldb's C API declares struct leveldb_writebatch_t { WriteBatch rep; }; after using leveldb::WriteBatch;. A written member type that isn't visible as-is is now looked up through the using declarations above it, the same lookup local variables already use.
  3. A local built with call syntax. tree-sitter reads SharedState shared(n); as a function declaration. It now counts as an object of that type when every "parameter" is a bare name that a declaration in scope binds as a variable. C++ itself cannot read a name it finds as a variable as a type. This replaces the narrower std::unique_ptr / std::shared_ptr special case. Shared shared(Config);, with Config a type, still counts as a function declaration, and a name the resolver can't place still blocks the reading.
  4. A class written with an attribute macro. For class SCOPED_LOCKABLE MutexLock { or struct LEVELDB_EXPORT Options {, extraction blanks the macro before parsing, but the resolver's own re-parse did not, so tree-sitter took the macro for the class's name. The resolver's re-parse now blanks class-head macros with the same rules extraction uses (a port of blankCppExportMacros): a body or base list must follow, it handles final and template arguments, and it leaves preprocessor lines and forward declarations alone.

The 5 calls where the fork and upstream differ: rep_->index_block->NewIterator(…) at table.cc lines 210, 218 and 246 now reaches Block::NewIterator, where upstream reaches Table::NewIterator because index_block is a Block*. table.cc:102, rep_->options.filter_policy->Name(), reaches FilterPolicy::Name, and db_impl.cc:892 reaches VersionEdit::AddFile. Upstream links neither.

Verification

  • Tests that fail before and pass after. The new file __tests__/cpp-member-chain-declarations.test.ts has one test per shape, each against a Decoy class with methods of the same names: "reads a member of a struct defined out of line as a nested class", "reads a member of a global struct whose type a using-declaration brings in", "reads a local constructed with call syntax when its arguments are variables" and "reads a member of a class written with an attribute macro". All 4 fail on the unfixed kernel and pass with the fix. The Rust unit test blanks_class_head_macros_before_a_body_or_base_list covers the macro blanking, with 6 cases that blank and 5 that stay as written.
  • leveldb, before and after. The fork without the fix against the fork with it: 141 call edges added, 0 removed, 0 moved to another target. I read the source for each sampled site, at least 10 per shape: about 15 for the nested-class shape, 10 for the using-declaration shape, and all 6 and all 10 for the other two. Every one is the method C++ calls. The additions beyond the 109 calls above come from the same four causes, such as Iterator methods reached through class LEVELDB_EXPORT Iterator, EnvWrapper's target_->… calls and table_test.cc's Block block(contents);.
  • facebook/rocksdb (a3b0f18, 1,404 files), before and after. 1,557 call edges added, 0 removed, 0 moved. Most come from the C API (c.cc), BlockBasedTableBuilder::Rep and the cache's ALIGN_AS(…) handles. Twelve sampled sites I read in the source were all correct, for example std::vector<VersionEdit> edits(n), WriteThread write_thread(db_options), FaissIVFIndex::KNNContext and gtest's TestInfo.
  • Precision gate (eval:precision, which checks a fixed set of links that must exist and must not): jq stays at 5,307 call edges, unchanged. json goes from 12,722 to 12,741 call edges (+19, 0 removed). The checked links held both times: all 11 that must not exist and all 7 that must. Of the 19, 16 are cit.key() / cit.value() in unit-iterators1.cpp through json j_const(j);, and 3 are doctest's String::c_str through class DOCTEST_INTERFACE String. I deleted the result files the runs wrote.
  • Fork-side outside-call case. __tests__/cpp-member-chain-outside-calls-fork.test.ts, next to upstream's outside-calls file: void RawPointer(std::string* out) { out->clear(); } with a project Buffer::clear() has no calls edge to Buffer::clear, and a control, b->clear() on a Buffer*, still links to it. It passes with and without this fix, so the fork already handles it.
  • Golden dumps: re-baselined with UPDATE_GOLDEN=1, no .dump changed.
  • Full suite, after rebasing onto the e8ec7fd merge: 745 files, 9,260 tests passed, 40 skipped. No Native stack trace or Worker exited unexpectedly in its output. npx tsc, npm run check:agent-docs and cargo clippy --release --lib -- -D warnings pass.

README rows checked: the "About this fork" C++ rows on aliases, -> on smart pointers, the types of auto locals, chained calls and data members, namespace lookup, and object creation. This change makes none of them wrong and moves no number the README quotes, so the README is unchanged.

Not covered

  • A chain through what a method returns, like leveldb's compact->current_output()->smallest.DecodeFrom(key) at db_impl.cc lines 1004 and 1006. The fork still guesses VersionEdit::DecodeFrom by name; upstream reaches InternalKey::DecodeFrom.
  • A chain that starts with a subscript and a dereference, like (*flist_)[index_]->largest.Encode() at version_set.cc:191.
  • A member of a struct declared inside a function, like state.cvar.Wait() on env_test.cc's local RunState.
  • Call syntax whose arguments are class members, like Foo x(member_);. A member is not a proven variable, so this stays unread, as before.

Summary by CodeRabbit

  • Bug Fixes
    • Improved resolution of C++ calls through chained members involving out-of-line nested classes, types imported with using, local objects initialized with call syntax, and classes with attribute macros.
    • Calls through raw pointers now distinguish project-defined methods from similarly named methods on unrelated types.
    • Declarations such as Shared shared(Config); continue to be treated as function declarations when Config is a type.

…shapes

A call on a member reached through another member now links to the
member's class when the class or the object is declared in one of the
ways google/leveldb writes them:

- a nested class defined out of line, `struct DBImpl::CompactionState {`,
  whose qualified name the field lookup did not match;
- a global struct whose member's type comes in through
  `using leveldb::WriteBatch;`;
- a local built with call syntax, `SharedState shared(n);`, which
  tree-sitter reads as a function declaration; it now counts as an object
  when every "parameter" is a name a declaration in scope binds as a
  variable, replacing the smart-pointer-only special case;
- a class written with an attribute macro, `class SCOPED_LOCKABLE
  MutexLock`, `struct LEVELDB_EXPORT Options`; the resolver's re-parse now
  blanks class-head macros as extraction's pre-parse does.
@coderabbitai

coderabbitai Bot commented Oct 17, 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: 7f656423-6a29-4ec5-82f1-77bb4fdb3191
📥 Commits

Reviewing files that changed from the base of the PR and between 134814d and 548f13d.

📒 Files selected for processing: 6
  • CHANGELOG.md
  • __tests__/cpp-member-chain-declarations.test.ts
  • __tests__/cpp-member-chain-outside-calls-fork.test.ts
  • codegraph-kernel/src/resolve/cpp_annotations.rs
  • codegraph-kernel/src/resolve/cpp_range_for.rs
  • codegraph-kernel/src/resolve/cpp_receivers.rs

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

📝 Walkthrough

The C++ resolver now handles additional chained-member calls involving out-of-line nested classes, types introduced by using declarations, call-syntax local declarations, and macro-annotated class declarations. Tests also check raw-pointer receiver resolution and preserve function-declaration handling when an argument is a type.

Changes: C++ member-chain resolution

Layer / File(s) Summary
Recognize class-head macros
codegraph-kernel/src/resolve/cpp_annotations.rs, __tests__/cpp-member-chain-declarations.test.ts
The annotation masker blanks macros in class and struct heads for declarations with a body or base list. Tests cover supported macro forms and cases that remain unchanged, including calls through macro-annotated class members.
Resolve types from using declarations
codegraph-kernel/src/resolve/cpp_range_for.rs, __tests__/cpp-member-chain-declarations.test.ts
When visible-type lookup finds no owner, the resolver checks visible using declarations. Tests cover imported member types and an unknown member type.
Infer receiver types from declarations
codegraph-kernel/src/resolve/cpp_receivers.rs, __tests__/cpp-member-chain-declarations.test.ts, __tests__/cpp-member-chain-outside-calls-fork.test.ts, CHANGELOG.md
Receiver lookup handles out-of-line nested classes and call-syntax local objects whose arguments resolve to in-scope variables. Tests cover these calls, distinguish std::string* from Buffer*, and check that a type argument remains a function declaration.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 548f1

The reviewed changes are mergeable after normal checks; no concrete blocking issue was identified.

Pre-merge checks: Passed 6
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: extending C++ member-chain resolution to four additional declaration shapes.
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 The pull request adds no lint, type-check, or compiler-diagnostic suppression directive. The changed files add C++ resolver logic, tests, comments, and a changelog entry. No added line contains direct…
User-Visible Changes Documented Passed The pull request changes existing C++ resolution behavior, tests, and the changelog. The authoritative diff contains no CLI command or flag, MCP tool or argument, supported language or framework, agen…
Full details: Suppressions Explained

Explanation

The pull request adds no lint, type-check, or compiler-diagnostic suppression directive. The changed files add C++ resolver logic, tests, comments, and a changelog entry. No added line contains directives such as eslint-disable, @ts-ignore, #[allow(...)], noqa, or a new ignore configuration entry.

Full details: User-Visible Changes Documented

Explanation

The pull request changes existing C++ resolution behavior, tests, and the changelog. The authoritative diff contains no CLI command or flag, MCP tool or argument, supported language or framework, agent target, or config key additions, removals, or renames. Therefore README.md and a matching site documentation page are not required by this check.

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

@bompus
bompus merged commit d161ef7 into fork/consolidated Oct 17, 2026
7 of 8 checks passed
@bompus
bompus deleted the fix/cpp-member-chain-leveldb-gaps branch October 17, 2026 01:41
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