A function name used as a VALUE — passed as an argument
(signal(SIGINT, handler), qsort(..., compare)), assigned to a function
pointer or field (ops->recv_cb = my_cb, OnClick := Handler), or placed in
a struct initializer / handler table ({ .recv_cb = my_cb },
{ "get", getCommand }) — produced no edge in ANY of the 19 tree-sitter
languages, so registered callbacks looked dead and their registration
sites were invisible to callers/impact.
This adds table-driven function-as-value capture across all 19 languages
(plus the wrapper forms: &fn, &Cls::method, Java Class::m, Kotlin ::f,
Swift #selector, ObjC @selector, Ruby method(:sym), Scala eta, Pascal
@Handler), gated at extraction (same-file definitions + imported
bindings; C-family file-scope initializers are constant-expression
contexts and skip the gate, which is how redis-style cross-file command
tables resolve), and resolved by a dedicated strategy: function/method
targets only, same-file first, unique-or-drop cross-file, no fuzzy
fallback ever. Edges persist as kind 'references' with metadata.fnRef,
so getCallers/getImpactRadius surface them with zero graph-layer
changes; MCP callers/callees label them "via callback registration".
Precision rules bought by real-repo false positives (full A/B record in
docs/design/function-ref-capture.md): C++ is &-explicit outside
file-scope tables (fmt's begin/out/size collisions; out-of-line member
defs are function-kind); TS/JS/Python bare ids resolve to functions only
(TS class fields extract as method-kind — pre-existing quirk); Swift
refuses same-file method overload-families; param-forward shapes
(this.x = x, value: value) and destructuring are skipped; minified
bundles (*.min.js) produce no candidates.
Validated on 17 public OSS repos (redis, excalidraw, gin, bytes, okhttp,
okio, Alamofire, flask, sinatra, Newtonsoft.Json, scopt, provider,
busted, Fusion, AFNetworking, PascalCoin, fmt): node counts identical,
zero calls edges lost or gained, references strictly additive
(+3,200 registration edges total), precision spot-checked by reading
sampled source lines (redis 30/30, flask 8/8). Deliberately NOT covered:
indirect-dispatch resolution (o->cb(x) → impl) — that needs data-flow
through struct fields, and a wrong edge is worse than none.
EXTRACTION_VERSION 18 → 19 (re-index to benefit).
Closes #756
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
82 lines
3.2 KiB
TypeScript
82 lines
3.2 KiB
TypeScript
/**
|
|
* Generated-file detection for symbol-disambiguation down-ranking.
|
|
*
|
|
* When a query like "Send" matches 17 symbols across protobuf scaffolding,
|
|
* test mocks, and the hand-written implementation, the FTS ranker often
|
|
* surfaces the generated stubs first because their names are identical
|
|
* to the implementation's name (validated empirically on cosmos-sdk —
|
|
* see project_go_multi_module_audit memory). Generated stubs frequently
|
|
* have no body to trace from, so the agent ends up reading source anyway.
|
|
*
|
|
* This helper is a pure path-based classifier consulted at disambiguation
|
|
* time (findSymbol / findAllSymbols / codegraph_search formatting), NOT
|
|
* a hard filter — generated nodes are still in the graph and remain
|
|
* reachable; they just rank LAST when there's a real implementation
|
|
* with the same name.
|
|
*
|
|
* Scope: suffix patterns only. Most generated files follow the
|
|
* `<basename>.<tool>.<ext>` convention (`.pb.go`, `_grpc.pb.go`,
|
|
* `.g.dart`, `_pb2.py`), and that covers ~all of what we saw in the
|
|
* Go audit. A future addition would be scanning for the canonical
|
|
* `// Code generated by` header during extraction, for the rare files
|
|
* that defy the suffix convention.
|
|
*/
|
|
|
|
const GENERATED_PATTERNS: ReadonlyArray<RegExp> = [
|
|
// Go — protobuf / gRPC / pulsar
|
|
/\.pb\.go$/,
|
|
/\.pulsar\.go$/,
|
|
/_grpc\.pb\.go$/,
|
|
// Go — mockgen output. Default emits `mock_<src>.go`; many projects
|
|
// (cosmos-sdk uses `expected_*_mocks.go`) rename to `*_mock.go` /
|
|
// `*_mocks.go`. Matching either suffix catches both conventions
|
|
// without false-positive risk on hand-written sources.
|
|
/_mock\.go$/,
|
|
/_mocks\.go$/,
|
|
/^mock_[^/]+\.go$/,
|
|
// TypeScript / JavaScript — common codegen suffixes (Apollo / GraphQL
|
|
// codegen, Prisma, Hasura, ts-proto, gRPC-web, swagger-codegen).
|
|
/\.generated\.[jt]sx?$/,
|
|
/\.gen\.[jt]sx?$/,
|
|
/\.pb\.[jt]s$/,
|
|
/_pb\.[jt]s$/,
|
|
/_grpc_pb\.[jt]s$/,
|
|
// Minified bundles vendored into a repo (docs sites, examples). Their
|
|
// single-letter symbols make name-based edges pure noise.
|
|
/\.min\.m?js$/,
|
|
// Python — protobuf / gRPC / openapi-codegen
|
|
/_pb2(_grpc)?\.py$/,
|
|
/_pb2\.pyi$/,
|
|
// C++ — protobuf
|
|
/\.pb\.(cc|h)$/,
|
|
// C# — protobuf / gRPC (protoc-gen-csharp puts output under obj/ but
|
|
// many projects also commit *.g.cs and *Grpc.cs siblings)
|
|
/\.g\.cs$/,
|
|
/Grpc\.cs$/,
|
|
// Java — protobuf / gRPC: protoc-gen-java emits `*OuterClass.java`,
|
|
// protoc-gen-grpc-java emits `*Grpc.java`. The XxxImplBase abstract
|
|
// class lives inside Xxx*Grpc.java.
|
|
/OuterClass\.java$/,
|
|
/Grpc\.java$/,
|
|
// Swift — protobuf
|
|
/\.pb\.swift$/,
|
|
// Dart — build_runner / freezed / json_serializable / chopper
|
|
/\.g\.dart$/,
|
|
/\.freezed\.dart$/,
|
|
/\.pb\.dart$/,
|
|
/\.pbgrpc\.dart$/,
|
|
/\.chopper\.dart$/,
|
|
// Rust — common build.rs OUT_DIR outputs are usually outside the source
|
|
// tree, but in-tree generated files often use `*.generated.rs`.
|
|
/\.generated\.rs$/,
|
|
];
|
|
|
|
/**
|
|
* Whether `filePath` looks like a tool-generated source file based on
|
|
* its filename. Path-only — does not read content. The result is a
|
|
* relevance hint for disambiguation, not a hard claim.
|
|
*/
|
|
export function isGeneratedFile(filePath: string): boolean {
|
|
return GENERATED_PATTERNS.some((p) => p.test(filePath));
|
|
}
|