TypeScript call resolution: ~3,000 lines of fixes ready (0 invented CALLS edges, full recall on a NestJS backend) — how would you like them split?
维护者通常 2 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 35/100
- Issue 类型
- 功能
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- c, typescript
调研方向
Start by reviewing the proposed PR dependency table and the existing related issues, especially #2718, #2293, and #2126; the author says the changes have not yet been built and tested as separate PRs. The work spans internal/cbm/lsp/ts_lsp.c, internal/cbm/helpers.c, extraction and pipeline passes, and tests including tests/test_lsp_resolution_probe.c. Done means maintainers agree on the split and representation, then each proposed change is validated independently against its probes and the TypeScript compiler oracle.
由索引模型根据 Issue 内容生成。
描述
Version
main @ 10ab613d (v0.12.1 + 281 commits), plus the changes described below
Platform
Windows (x64)
Install channel
GitHub release archive / install.sh / install.ps1
Binary variant
standard
What happened, and what did you expect?
This is actually my first attempt at contributing to an open source project ever! I'm very excited. I built this 100% using claude code and I had it prep a post here cause it's a huge chunk and I feel like we need to be strategic on how I partition up so many lines of code.
The background for this update is that I use Nest.JS and it uses dependency injection and the default codebase memory mcp project seemed to have not found all the nodes/edges and it was inventing nodes/edges. When I started about a month ago, codebase only found about 15% of the nodes/edges. Personally I'm not an expert at static analysis but I had claude doing a lot of checking and it seems extremely good with 100% found (with my particular code base) and 0 invented. There's probably still edge cases that my updates do not handle. Anyway, here's claude's post and I'm ok in doing whatever it takes to get this into the mix, but I want to avoid a ton of complicated stacked PRs.
What problem does this solve?
On TypeScript, and NestJS in particular, the graph both misses real calls and invents calls that
don't exist. "Who calls this?" questions come back incomplete in ways that look complete. For example, a
query for dead code would list live code that is only reached through dependency injection.
We measured against ground truth from the TypeScript compiler's own checker (ts.createProgram +
getSymbolAtLocation) on a production NestJS backend: 641 .ts files, one fixed commit, same scorer
for every build:
| v0.12.1 release | main @ 10ab613d |
our branch | |
|---|---|---|---|
| production calls found (recall) | 4,052 / 4,136 | 4,031 / 4,136 | 4,136 / 4,136 |
| production edges invented | 633 | 70 | 0 (of 3,627) |
| test-file edges invented | 211 | 189 | 0 (of 934) |
| calls made by functions held in object/array literals in tests | 0 / 1,585 | 3 / 1,585 | 1,585 / 1,585 |
On the same 641-file repo, our Windows build (MSYS2 clang, 4 workers) produces a CALLS edge set
identical to a Linux build of the same commit: 4,768 edges, 0 differences either way.
Proposed solution
The branch is 34 commits on main @ 10ab613d, across 23 files:
- +3,080 / −83 lines of source. Mostly
internal/cbm/lsp/ts_lsp.c(+1,404) andinternal/cbm/helpers.c
(+628), plus the extract and pipeline passes. - +3,854 lines of tests. Mostly new probes in
tests/test_lsp_resolution_probe.c, plus
test_pipeline.c,test_registry.candtest_ts_lsp.c.
Each fix has a probe that fails without it. The key ones were mutation-checked: the fix was reverted, the
probe went red for the stated reason, and the fix was restored.
It is too big for one PR under your 500-line guideline, and I don't want to dump 15 PRs on you
unannounced. So here is the shape. I'm asking how you'd like to receive it.
The PRs are not all dependent. I mapped the commits by which earlier commits' lines each one edits,
and which earlier commits' new functions it calls. That gives a dependency graph six levels deep, not a
chain of 15:
| PR | What it fixes | src / test lines | Needs |
|---|---|---|---|
| A | Receiver types imported through a barrel re-export | 94 / 66 | — |
| B | Generic calls misparsed under a prefix operator (!foo<T>(), await, …) |
213 / 238 | — |
| C | Calls in module-level code and top-level arrow functions | 78 / 197 | — |
| D | Calls in a class's helpers and fields credited to the right owner (incl. namespaces) | 98 / 171 | — |
| E | Calls through functions held in a class field's object | 297 / 341 | — |
| F | Stop name-only guesses and builtin calls binding to project symbols | 186 / 439 | B |
| G | Type services a test gets from a factory or DI container | 81 / 141 | B |
| H1 | Calls through typed object literals and their interfaces | 405 / 249 | B |
| H2 | Namespace import vs declared type; implementation dispatch on incremental runs; intersection aliases | 238 / 328 | H1 |
| I | Return type of helpers declared inside a test block | 163 / 219 | B, G |
| J | Remaining invented edges, stopped at their cause | 275 / 300 | B, C, F, G |
| K | Name a typed literal's functions after the variable that holds them | 399 / 209 | E, F, H2, J |
| L1 | Name each typed list entry apart, credit its calls to it (+ index version bump) | 689 / 551 | C, E, H1, J, K |
| L2 | Same for untyped file-level literals; tightening | 386 / 574 | E, I, K, L1 |
| M | A workspace sibling package's import stays a project binding | 38 / 49 | F, J |
- Wave 1 (A–E): five independent PRs, each off
main. They can all be open and reviewed at once. - Then: F, G, H1 → H2, I, J → K, M → L1 → L2.
Why not one linear stack of ~14 PRs: in a strict chain, PR k can't merge until 1…k−1 have. Every
change requested on an early PR forces a rebase of everything above it, which is up to 14 × 13 / 2 = 91
rebases in the worst case. With the graph above, the critical path is 6 merges, and a change to a wave-1
PR only ripples into its own descendants.
Caveats, so you can weigh this:
- The map is textual plus symbol-level. I have not yet built and tested each PR on its own. I'd do that
before opening any of them. - Most probes live in one file (
test_lsp_resolution_probe.c). PRs touching it will conflict on
append-only hunks: mechanical, but present. - K, L1 and L2 change stored qualified names, so they bump
CBM_SEMANTIC_INDEX_VERSION. I'd keep one bump
per PR that needs it rather than carry one early. - H1, H2, L1 and L2 are still over 500 lines. They are one rule applied to several syntax shapes.
I can split further by shape if you prefer.
Alternatives considered
- The wave plan above: 15 PRs, 5 open at a time.
- Fewer, larger PRs, one per wave (~600–1,800 lines each), if you'd rather review fewer threads.
- You take the findings and tests and implement it your way. We'd hand over the probes, the
compiler-checked oracle and the scoring script. The probes are the part that's most useful on its own.
Which do you prefer? Or is there a shape that fits your review flow better?
Separate, found on the way: MinGW gcc 13.2 build deadlocks at 4 workers
Happy to open this as its own issue if you'd like.
- What happens. Built with Strawberry Perl's MinGW-w64 gcc 13.2, indexing hangs forever at
parallel.resolve.startwith ~0 CPU when run with 4 workers (3 of 3 runs). 1 and 2 workers finish. The
MSYS2 clang build of the same tree runs fine at 4 workers. - Backtrace (gdb). An exiting worker is stuck in winpthreads TLS cleanup
(__dyn_tls_pthread→pthread_mutex_lock) while holding the loader lock. A new thread waits on the
loader lock, and main sits incbm_thread_join. - Likely cause. gcc 13.2 on MinGW compiles
_Thread_local(CBM_TLS,src/foundation/compat.h:26) as
emulated TLS through winpthreads: 166__emutlssymbols in the binary, and this gcc has no
-fno-emulated-tls. The clang build has none. - Not proven: I haven't built plain
mainwith the same gcc. The CI's gcc check cross-compiles from
Linux and runs--versionunder Wine, so it can't catch a runtime deadlock. - Two smaller build notes. gcc on MinGW checks
format(printf)attributes against Microsoft's printf
regardless of__USE_MINGW_ANSI_STDIO, so 7%zu/ extra-arg sites fail-Werror=format. Also,
scripts/build.shsetsCFLAGS_EXTRAitself, so a caller'sCFLAGS_EXTRA=only takes effect if it
comes later on the command line.
Reproduction
Measured 2026-10-25: v0.12.1 Linux release binary vs our branch (integration/ts-resolution-v3 @ 26203c64).
Plain main @ 10ab613d was NOT run against this fixture.
Minimal repro
Four files:
src/users/users.repository.ts
export class UsersRepository {
findById(id: string): string {
return `user-${id}`;
}
get(key: string): string {
return key;
}
}
src/users/index.ts
export * from './users.repository';
src/util.ts
export function isPresent<T>(value: T | undefined): value is T {
return value !== undefined;
}
src/orders/orders.service.ts
import { UsersRepository } from '../users';
import { isPresent } from '../util';
export class OrdersService {
constructor(private readonly users: UsersRepository) {}
// 1. Method call on an injected service whose class is imported through a barrel (index.ts)
ownerOf(orderId: string): string {
return this.users.findById(orderId);
}
// 2. Generic function call under a prefix operator
requireId(id: string | undefined): string {
if (!isPresent<string>(id)) throw new Error('missing id');
return id;
}
}
(plus a one-line tsconfig.json: { "compilerOptions": { "target": "ES2022", "module": "commonjs", "strict": true } })
Commands
cd /tmp/cbm-example && git init -q && git add -A && git commit -qm fixture
codebase-memory-mcp cli index_repository '{"repo_path":"/tmp/cbm-example"}'
codebase-memory-mcp cli query_graph '{"project":"tmp-cbm-example","query":"MATCH (a)-[r:CALLS]->(b) RETURN a.name, b.qualified_name"}'
Actual (v0.12.1)
rows: 0 (cols: a.name b.qualified_name)
total: 0
Expected (and what our branch returns)
rows: 2 (cols: a.name b.qualified_name)
ownerOf tmp-cbm-example.src.users.users.repository.UsersRepository.findById
requireId tmp-cbm-example.src.util.isPresent
total: 2
Control: each miss comes from one pattern
Change the import to from '../users/users.repository' (no barrel), and change the guard to
const ok = isPresent<string>(id); if (!ok) ... (no prefix operator). v0.12.1 then returns both edges.
So the first miss comes from the barrel re-export (PR A) and the second from the ! before a generic call
(PR B). These are the two smallest PRs in wave 1.
Duplicate check (2026-10-25): upstream issues and PRs searched, about 30 queries
Nothing duplicates the proposal. Related items to link in the issue:
- #2718 (open): calls through a barrel re-export. Same root cause as PR A, different shape: they
re-export a value (an objectconstand a function) and call it directly. PR A fixes a re-exported
class used as a type (constructor(private x: Foo)), through bothexport *andexport { Foo } from.
Measured: our branch does NOT fix #2718's repro (identical output to v0.12.1). Say "related", never "fixes". - #2210 (closed, fixed in v0.12.0 by PR #2242):
await foo<T>()only. PR B generalises it to the other
prefix operators.!foo<T>()is still missing on v0.12.1 (the fixture above). - #1974 / PR #2407, PR #2523 (merged): inferred receivers and constructor parameter properties. Our branch builds on them.
- #2293 (open, waiting on a maintainer design decision): calls inside a closure credited to the enclosing
function. Also #415 / #341: functions inside object literals. PRs K, L1 and L2 make that naming decision
for TS literals, so ask the maintainer whether that representation is acceptable. - #2126, #2121, PR #1128 (open): name-only guesses emitted as CALLS. Related to PR F, which handles the TS side only.
- #2606 (open): self-built MinGW gcc 15 hangs on main. Probably the same family as our gcc 13.2 deadlock. Link it.
- Not overlapping: #2297 / PR #2746 (renamed imports, not in our branch); PR #2402 (constructor parameter
properties, which merged PR #2523 appears to cover already); #2265, #2709 / PR #2721 (showing which calls went unresolved).
Logs
Diagnostics trajectory (memory / performance / leak issues)
Project scale (if relevant)
No response
Confirmations
- I searched existing issues and this is not a duplicate.
- My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.
- 主要语言
- C
- 星标
- 45.8k
- 派生
- 3.8k
- 平均合并
- 3 天 4 小时
- 30 天内合并 PR
- 208
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
DeusData/codebase-memory-mcp 的其他 Issue
-
bug editor/integration
难度 2/5 1-3 小时 新手友好度 72/100
DeusData/codebase-memory-mcp#2787 · 1 条评论 ·
维护者通常 2 天内回复
-
editor/integration parsing/quality
难度 2/5 1-3 小时 新手友好度 68/100
DeusData/codebase-memory-mcp#2286 ·
维护者通常 2 天内回复
-
install writes an unsupported `args` key into the Claude Code SubagentStart hook, so it never runs未关闭bug editor/integration priority/high stability/performance ux/behavior
难度 2/5 1-3 小时 新手友好度 72/100
DeusData/codebase-memory-mcp#2239 · 1 条评论 ·
维护者通常 2 天内回复
-
bug priority/high ux/behavior
难度 2/5 1-3 小时 新手友好度 72/100
DeusData/codebase-memory-mcp#2232 · 2 条评论 ·
维护者通常 2 天内回复
-
documentation editor/integration parsing/quality priority/normal
难度 2/5 1-3 小时 新手友好度 85/100
DeusData/codebase-memory-mcp#2169 ·
维护者通常 2 天内回复
查看 DeusData/codebase-memory-mcp 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 65/100
-
难度 2/5 1-3 小时 新手友好度 78/100
HexaCluster/credcheck#86 ·
-
难度 2/5 1-3 小时 新手友好度 66/100
mongodb/winkerberos#175 ·
-
bug
难度 2/5 1-3 小时 新手友好度 74/100
AOMediaCodec/SVT-AV1#3320 ·
-
difficulty: easy
难度 2/5 1-3 小时 新手友好度 64/100