mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-27 21:55:14 +08:00
* fix(extraction): record callers for methods passed as values (#1820) pool.submit(obj.method), thread_pool_exec(self.store.fetch), and Go Submit(c.store.Fetch) produced no callers/impact edge because only obj.method(...) call expressions were captured. Member values are now emitted as *.method function-refs and resolve unique-or-drop (same-file first). Two methods of the same name still produce no edge. Go go c.store.Fetch(ids) remains a calls edge (it is a call expression). EXTRACTION_VERSION 27: MCP daemon catch-up rebuilds a stale index on start so the watcher does not keep serving the hole after upgrade. * fix(resolution): do not let test mocks veto #1820 method-value callers A unique production method plus a unit-test double of the same name was treated as ambiguous, so retrieve_vision_vectors still looked callback-invisible on a real repo. Prefer the non-test file when exactly one production target remains. * fix(resolution): prefer a real override over a NotImplementedError base #1820 unique-or-drop treated DocStoreConnection.delete_payload_fields (the ABC stub) and QdrantConnection.delete_payload_fields as ambiguous, so gc_stale_member_stamps stayed invisible. Skip short not-implemented stubs when exactly one real implementation remains. * fix(extraction): preserve receivers for method values (#1820) Python attributes and Go selectors used as callback values were dropped or reduced to bare method names. Preserve receiver paths in native and WASM extraction, resolve through type/import scope, and reject ambiguous or noncallable targets. Cover callback, assignment, collection, and query behavior, with extraction version 27 signaling the required reindex. Co-authored-by: Josh66 <info@aska-systems.de> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Josh66 <info@aska-systems.de> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Josh66
Claude Opus 5.5
parent
2a2f71ec41
commit
5eaa6fedd5
@@ -25,9 +25,10 @@
|
||||
*
|
||||
* Deliberately NOT covered (resolving the *dispatch* — `o->cb(x)` → the
|
||||
* registered function — needs data-flow through struct fields; a wrong edge
|
||||
* is worse than none): indirect-call resolution and `obj.method` member
|
||||
* values where `obj` isn't `this`/`self` (the receiver's type is statically
|
||||
* unknowable without local data-flow).
|
||||
* is worse than none): indirect-call resolution. Member values where the
|
||||
* receiver isn't `this`/`self` (`pool.submit(obj.method)`, `Submit(c.store.Fetch)`)
|
||||
* retain their receiver (#1820), so resolution can use type/import scope
|
||||
* before considering a unique method name.
|
||||
*/
|
||||
|
||||
import type { Node as SyntaxNode } from 'web-tree-sitter';
|
||||
@@ -51,7 +52,8 @@ export interface FnRefCandidate {
|
||||
* referenced cross-file WITHOUT imports (global namespace), so the gate
|
||||
* can't see them — the strong positional prior (a string argument to
|
||||
* `usort`/`array_map`/…) plus resolution's unique-or-drop rule carry the
|
||||
* precision instead.
|
||||
* precision instead. Python/Go member values also skip the name gate,
|
||||
* retaining their receiver for scoped resolution.
|
||||
*/
|
||||
skipGate?: boolean;
|
||||
}
|
||||
@@ -231,6 +233,7 @@ const GO_SPEC: FnRefSpec = {
|
||||
['literal_element', null],
|
||||
['expression_list', null],
|
||||
]),
|
||||
special: new Set(['selector_expression']),
|
||||
};
|
||||
|
||||
const RUST_SPEC: FnRefSpec = {
|
||||
@@ -699,16 +702,23 @@ function normalizeSpecial(
|
||||
return [];
|
||||
}
|
||||
|
||||
// Swift `#selector(Holder.fire)` → fire. ObjC `@selector(storeImage:)` →
|
||||
// `storeImage:` verbatim (ObjC method nodes keep their selector colons).
|
||||
// Go `c.store.Fetch` (has a `field` child) vs Swift `#selector(...)` /
|
||||
// ObjC `@selector(...)` (no `field` — inner identifier / selector text).
|
||||
case 'selector_expression': {
|
||||
const field = getChildByField(node, 'field');
|
||||
if (field) {
|
||||
const name = getNodeText(field, source);
|
||||
const receiver = getChildByField(node, 'operand');
|
||||
const value = receiver ? `${getNodeText(receiver, source)}.${name}` : '';
|
||||
return /^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)+$/.test(value)
|
||||
? [{ name: value, node: field, skipGate: true }] : [];
|
||||
}
|
||||
const inner = node.namedChild(0);
|
||||
if (!inner) return [];
|
||||
if (inner.type === 'identifier' || inner.type === 'simple_identifier') {
|
||||
return [{ name: getNodeText(inner, source), node: inner }];
|
||||
}
|
||||
// Swift dotted form: rightmost simple_identifier. ObjC keyword selector:
|
||||
// text as-is.
|
||||
// Swift dotted form: rightmost simple_identifier; ObjC keeps selector text.
|
||||
const last = lastNamedOfType(node, new Set(['simple_identifier']));
|
||||
if (last) return [{ name: getNodeText(last, source), node: last }];
|
||||
return [{ name: getNodeText(inner, source).trim(), node: inner }];
|
||||
@@ -739,14 +749,13 @@ function normalizeSpecial(
|
||||
return [];
|
||||
}
|
||||
|
||||
// `self.handle_click` (Python) — object must be EXACTLY `self`.
|
||||
// Keep the receiver on Python member values; calls/subscripts are not
|
||||
// statically named receivers and must not collapse to a bare method.
|
||||
case 'attribute': {
|
||||
const obj = getChildByField(node, 'object');
|
||||
const attr = getChildByField(node, 'attribute');
|
||||
if (obj && attr && obj.type === 'identifier' && getNodeText(obj, source) === 'self') {
|
||||
return [{ name: getNodeText(attr, source), node: attr }];
|
||||
}
|
||||
return [];
|
||||
const name = getNodeText(node, source);
|
||||
return attr && /^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)+$/.test(name)
|
||||
? [{ name, node: attr, skipGate: true }] : [];
|
||||
}
|
||||
|
||||
// `this.Run0` (C#) — receiver must be EXACTLY `this`. Two grammar shapes:
|
||||
|
||||
Reference in New Issue
Block a user