@remnic/coding-graph 9.6.23 → 9.6.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -19
- package/dist/{chunk-CPYJACC5.js → chunk-ABDBWCXU.js} +146 -8
- package/dist/chunk-ABDBWCXU.js.map +1 -0
- package/dist/{chunk-5I2DBHOQ.js → chunk-I4R6GAAA.js} +2 -2
- package/dist/cypher/query-parser.js +2 -2
- package/dist/graph-store.d.ts +47 -0
- package/dist/graph-store.js +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +318 -26
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/edge-provenance-scope.test.ts +457 -0
- package/src/engine/emit.ts +55 -8
- package/src/engine/extractors.ts +45 -11
- package/src/graph-store.ts +265 -9
- package/src/heuristic-resolution.test.ts +596 -0
- package/src/heuristic-resolution.ts +395 -0
- package/src/lsp/resolution.ts +52 -11
- package/src/lsp-reconciliation.test.ts +145 -0
- package/src/reindex.test.ts +57 -0
- package/src/reindex.ts +18 -6
- package/dist/chunk-CPYJACC5.js.map +0 -1
- /package/dist/{chunk-5I2DBHOQ.js.map → chunk-I4R6GAAA.js.map} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1190,6 +1190,18 @@ interface ResolveOptions {
|
|
|
1190
1190
|
* dstQualifiedName, type: "CALLS", confidence, provenance: "lsp"}`.
|
|
1191
1191
|
*/
|
|
1192
1192
|
readonly applyUpgrades: (upgrades: readonly EdgeUpgrade[]) => Promise<void>;
|
|
1193
|
+
/**
|
|
1194
|
+
* Optional stale-edge reconciliation (issue #1895): after applying a
|
|
1195
|
+
* file batch's upgrades, the caller retires prior `lsp`-provenance
|
|
1196
|
+
* edges owned by that file whose `(src, dst, type)` keys the current
|
|
1197
|
+
* batch does NOT assert. When absent, stale lsp edges persist until
|
|
1198
|
+
* node pruning — the soft-fail path documented in #1894.
|
|
1199
|
+
*/
|
|
1200
|
+
readonly reconcileLspEdges?: (filePath: string, assertedEdges: ReadonlyArray<{
|
|
1201
|
+
srcQualifiedName: string;
|
|
1202
|
+
dstQualifiedName: string;
|
|
1203
|
+
type: string;
|
|
1204
|
+
}>) => void;
|
|
1193
1205
|
/**
|
|
1194
1206
|
* Workspace root for resolving repo-relative file paths to absolute LSP
|
|
1195
1207
|
* URIs and normalizing returned URIs back to repo-relative paths.
|
package/dist/index.js
CHANGED
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
executeAst,
|
|
4
4
|
executeCypher,
|
|
5
5
|
parseCypher
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-I4R6GAAA.js";
|
|
7
7
|
import {
|
|
8
8
|
DEAD_CODE_EXCLUSION,
|
|
9
9
|
DEFAULT_TRAVERSE_PATHS_MAX,
|
|
10
10
|
GraphStore,
|
|
11
11
|
MAX_TRAVERSE_PATHS_HOPS,
|
|
12
12
|
nodeIdFor
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ABDBWCXU.js";
|
|
14
14
|
import {
|
|
15
15
|
CODING_GRAPH_SCHEMA_VERSION,
|
|
16
16
|
EDGE_PROVENANCE_VALUES,
|
|
@@ -175,15 +175,25 @@ var JS_FAMILY_DEFINITIONS = `
|
|
|
175
175
|
var JS_IMPORTS = `
|
|
176
176
|
(import_statement
|
|
177
177
|
source: (string (string_fragment) @import.module)) @__import.stmt
|
|
178
|
+
; Default and namespace imports bind LOCAL aliases whose exported symbol
|
|
179
|
+
; is unknowable to Phase A (issue #1894 round 12): they appear in
|
|
180
|
+
; importedNames (informational) but never in bindings (call-bindable).
|
|
178
181
|
(import_statement
|
|
179
|
-
(import_clause (identifier) @import.
|
|
182
|
+
(import_clause (identifier) @import.unboundName)
|
|
180
183
|
source: (string (string_fragment) @import.module)) @__import.stmt
|
|
181
184
|
(import_statement
|
|
182
|
-
(import_clause (namespace_import (identifier) @import.
|
|
185
|
+
(import_clause (namespace_import (identifier) @import.unboundName))
|
|
183
186
|
source: (string (string_fragment) @import.module)) @__import.stmt
|
|
184
187
|
(import_statement
|
|
185
188
|
(import_clause (named_imports (import_specifier name: (identifier) @import.name)))
|
|
186
189
|
source: (string (string_fragment) @import.module)) @__import.stmt
|
|
190
|
+
; Aliased named import (issue #1894 review): import { foo as bar } \u2014 the
|
|
191
|
+
; local binding is the alias; the exported name stays the binding target.
|
|
192
|
+
(import_statement
|
|
193
|
+
(import_clause (named_imports (import_specifier
|
|
194
|
+
name: (identifier) @import.aliasExported
|
|
195
|
+
alias: (identifier) @import.aliasLocal)))
|
|
196
|
+
source: (string (string_fragment) @import.module)) @__import.stmt
|
|
187
197
|
|
|
188
198
|
; CommonJS require("...") \u2014 capture the module specifier so dependency
|
|
189
199
|
; edges exist for Node/CommonJS codebases, not just ES-module imports.
|
|
@@ -242,7 +252,10 @@ var JS_EXPORTS = `
|
|
|
242
252
|
`.trim();
|
|
243
253
|
var JS_CALLS = `
|
|
244
254
|
(call_expression function: (identifier) @call.callee)
|
|
245
|
-
|
|
255
|
+
; Member calls are captured separately (issue #1894 review): obj.save()'s
|
|
256
|
+
; \`save\` must never bind a bare visible symbol \u2014 method dispatch is
|
|
257
|
+
; Phase B (LSP) territory. The emit layer marks these memberAccess: true.
|
|
258
|
+
(call_expression function: (member_expression property: (property_identifier) @call.member))
|
|
246
259
|
`.trim();
|
|
247
260
|
var JS_ROUTES = `
|
|
248
261
|
; Unified route pattern: captures verb + path + the arguments node.
|
|
@@ -290,6 +303,17 @@ var PYTHON_EXTRACTOR = {
|
|
|
290
303
|
importsQuery: `
|
|
291
304
|
(import_statement (dotted_name) @import.module) @__import.stmt
|
|
292
305
|
(import_from_statement module_name: (dotted_name) @import.module) @__import.stmt
|
|
306
|
+
; Imported names (issue #1894 review round 7): without these captures,
|
|
307
|
+
; extractImports emits importedNames: [] and the heuristic resolver's
|
|
308
|
+
; import bindings never fire for real Python parses.
|
|
309
|
+
(import_from_statement
|
|
310
|
+
module_name: (dotted_name) @import.module
|
|
311
|
+
name: (dotted_name) @import.name) @__import.stmt
|
|
312
|
+
(import_from_statement
|
|
313
|
+
module_name: (dotted_name) @import.module
|
|
314
|
+
name: (aliased_import
|
|
315
|
+
name: (dotted_name) @import.aliasExported
|
|
316
|
+
alias: (identifier) @import.aliasLocal)) @__import.stmt
|
|
293
317
|
; Python relative imports: from .models import User / from ..parent import X
|
|
294
318
|
; tree-sitter-python wraps the module inside a relative_import node, so the
|
|
295
319
|
; module_name field is NOT set. Capture the relative_import node itself so
|
|
@@ -297,11 +321,19 @@ var PYTHON_EXTRACTOR = {
|
|
|
297
321
|
; relative levels must not collapse to the same module name
|
|
298
322
|
; (chatgpt-codex-connector #1688 P2: 'Preserve dots in Python relative imports').
|
|
299
323
|
(import_from_statement (relative_import) @import.module) @__import.stmt
|
|
324
|
+
(import_from_statement
|
|
325
|
+
(relative_import) @import.module
|
|
326
|
+
name: (dotted_name) @import.name) @__import.stmt
|
|
327
|
+
(import_from_statement
|
|
328
|
+
(relative_import) @import.module
|
|
329
|
+
name: (aliased_import
|
|
330
|
+
name: (dotted_name) @import.aliasExported
|
|
331
|
+
alias: (identifier) @import.aliasLocal)) @__import.stmt
|
|
300
332
|
`.trim(),
|
|
301
333
|
exportsQuery: ``,
|
|
302
334
|
callSitesQuery: `
|
|
303
335
|
(call function: (identifier) @call.callee)
|
|
304
|
-
(call function: (attribute attribute: (identifier) @call.
|
|
336
|
+
(call function: (attribute attribute: (identifier) @call.member))
|
|
305
337
|
`.trim(),
|
|
306
338
|
routesQuery: `
|
|
307
339
|
(decorated_definition
|
|
@@ -337,7 +369,7 @@ var GO_EXTRACTOR = {
|
|
|
337
369
|
exportsQuery: ``,
|
|
338
370
|
callSitesQuery: `
|
|
339
371
|
(call_expression function: (identifier) @call.callee)
|
|
340
|
-
(call_expression function: (selector_expression field: (field_identifier) @call.
|
|
372
|
+
(call_expression function: (selector_expression field: (field_identifier) @call.member))
|
|
341
373
|
`.trim(),
|
|
342
374
|
routesQuery: ``
|
|
343
375
|
};
|
|
@@ -372,7 +404,7 @@ var RUST_EXTRACTOR = {
|
|
|
372
404
|
exportsQuery: ``,
|
|
373
405
|
callSitesQuery: `
|
|
374
406
|
(call_expression function: (identifier) @call.callee)
|
|
375
|
-
(call_expression function: (field_expression field: (field_identifier) @call.
|
|
407
|
+
(call_expression function: (field_expression field: (field_identifier) @call.member))
|
|
376
408
|
(call_expression function: (scoped_identifier) @call.callee)
|
|
377
409
|
`.trim(),
|
|
378
410
|
routesQuery: ``
|
|
@@ -391,7 +423,8 @@ var JAVA_EXTRACTOR = {
|
|
|
391
423
|
`.trim(),
|
|
392
424
|
exportsQuery: ``,
|
|
393
425
|
callSitesQuery: `
|
|
394
|
-
(method_invocation name: (identifier) @call.callee)
|
|
426
|
+
(method_invocation !object name: (identifier) @call.callee)
|
|
427
|
+
(method_invocation object: (_) name: (identifier) @call.member)
|
|
395
428
|
`.trim(),
|
|
396
429
|
routesQuery: ``
|
|
397
430
|
};
|
|
@@ -429,7 +462,7 @@ var CPP_EXTRACTOR = {
|
|
|
429
462
|
exportsQuery: ``,
|
|
430
463
|
callSitesQuery: `
|
|
431
464
|
(call_expression function: (identifier) @call.callee)
|
|
432
|
-
(call_expression function: (field_expression field: (field_identifier) @call.
|
|
465
|
+
(call_expression function: (field_expression field: (field_identifier) @call.member))
|
|
433
466
|
`.trim(),
|
|
434
467
|
routesQuery: ``
|
|
435
468
|
};
|
|
@@ -448,7 +481,7 @@ var CSHARP_EXTRACTOR = {
|
|
|
448
481
|
exportsQuery: ``,
|
|
449
482
|
callSitesQuery: `
|
|
450
483
|
(invocation_expression function: (identifier) @call.callee)
|
|
451
|
-
(invocation_expression function: (member_access_expression name: (identifier) @call.
|
|
484
|
+
(invocation_expression function: (member_access_expression name: (identifier) @call.member))
|
|
452
485
|
`.trim(),
|
|
453
486
|
routesQuery: ``
|
|
454
487
|
};
|
|
@@ -467,7 +500,8 @@ var RUBY_EXTRACTOR = {
|
|
|
467
500
|
`.trim(),
|
|
468
501
|
exportsQuery: ``,
|
|
469
502
|
callSitesQuery: `
|
|
470
|
-
(call method: (identifier) @call.callee)
|
|
503
|
+
(call !receiver method: (identifier) @call.callee)
|
|
504
|
+
(call receiver: (_) method: (identifier) @call.member)
|
|
471
505
|
`.trim(),
|
|
472
506
|
routesQuery: ``
|
|
473
507
|
};
|
|
@@ -485,7 +519,7 @@ var PHP_EXTRACTOR = {
|
|
|
485
519
|
exportsQuery: ``,
|
|
486
520
|
callSitesQuery: `
|
|
487
521
|
(function_call_expression function: (name) @call.callee)
|
|
488
|
-
(member_call_expression (name) @call.
|
|
522
|
+
(member_call_expression (name) @call.member)
|
|
489
523
|
`.trim(),
|
|
490
524
|
routesQuery: ``
|
|
491
525
|
};
|
|
@@ -691,11 +725,20 @@ function extractImports(root, language, lang) {
|
|
|
691
725
|
let stmtStart = -1;
|
|
692
726
|
let stmtEnd = -1;
|
|
693
727
|
const names = [];
|
|
728
|
+
const unboundNames = [];
|
|
729
|
+
let aliasExported = "";
|
|
730
|
+
let aliasLocal = "";
|
|
694
731
|
for (const cap of match.captures) {
|
|
695
732
|
if (cap.name === "import.module") {
|
|
696
733
|
moduleText = cleanModuleSpecifier(cap.node.text);
|
|
697
734
|
} else if (cap.name === "import.name") {
|
|
698
735
|
names.push(cap.node.text);
|
|
736
|
+
} else if (cap.name === "import.unboundName") {
|
|
737
|
+
unboundNames.push(cap.node.text);
|
|
738
|
+
} else if (cap.name === "import.aliasExported") {
|
|
739
|
+
aliasExported = cap.node.text;
|
|
740
|
+
} else if (cap.name === "import.aliasLocal") {
|
|
741
|
+
aliasLocal = cap.node.text;
|
|
699
742
|
} else if (cap.name === "__import.stmt") {
|
|
700
743
|
stmtStart = cap.node.startIndex;
|
|
701
744
|
stmtEnd = cap.node.endIndex;
|
|
@@ -711,21 +754,34 @@ function extractImports(root, language, lang) {
|
|
|
711
754
|
}
|
|
712
755
|
}
|
|
713
756
|
const key = `${stmtStart}:${moduleText}`;
|
|
714
|
-
|
|
715
|
-
if (
|
|
716
|
-
|
|
717
|
-
} else {
|
|
718
|
-
groups.set(key, {
|
|
757
|
+
let group = groups.get(key);
|
|
758
|
+
if (!group) {
|
|
759
|
+
group = {
|
|
719
760
|
module: moduleText,
|
|
720
|
-
names: new Set(
|
|
761
|
+
names: /* @__PURE__ */ new Set(),
|
|
762
|
+
bindings: /* @__PURE__ */ new Map(),
|
|
721
763
|
startByte: stmtStart,
|
|
722
764
|
endByte: stmtEnd
|
|
723
|
-
}
|
|
765
|
+
};
|
|
766
|
+
groups.set(key, group);
|
|
767
|
+
}
|
|
768
|
+
for (const n of names) {
|
|
769
|
+
group.names.add(n);
|
|
770
|
+
if (!group.bindings.has(n)) group.bindings.set(n, n);
|
|
771
|
+
}
|
|
772
|
+
for (const n of unboundNames) {
|
|
773
|
+
group.names.add(n);
|
|
774
|
+
}
|
|
775
|
+
if (aliasExported && aliasLocal) {
|
|
776
|
+
group.names.add(aliasExported);
|
|
777
|
+
group.bindings.delete(aliasExported);
|
|
778
|
+
group.bindings.set(aliasLocal, aliasExported);
|
|
724
779
|
}
|
|
725
780
|
}
|
|
726
781
|
return Array.from(groups.values()).map((g) => ({
|
|
727
782
|
module: g.module,
|
|
728
783
|
importedNames: Array.from(g.names).sort(),
|
|
784
|
+
bindings: Array.from(g.bindings.entries()).map(([local, exported]) => ({ exported, local })).sort((a, b) => a.local.localeCompare(b.local)),
|
|
729
785
|
span: { startByte: g.startByte, endByte: g.endByte }
|
|
730
786
|
})).sort((a, b) => a.span.startByte - b.span.startByte || a.module.localeCompare(b.module));
|
|
731
787
|
} finally {
|
|
@@ -786,6 +842,12 @@ function extractCallSites(root, language, lang) {
|
|
|
786
842
|
calleeNameCandidates: [cap.node.text],
|
|
787
843
|
span: { startByte: cap.node.startIndex, endByte: cap.node.endIndex }
|
|
788
844
|
});
|
|
845
|
+
} else if (cap.name === "call.member") {
|
|
846
|
+
callSites.push({
|
|
847
|
+
calleeNameCandidates: [cap.node.text],
|
|
848
|
+
memberAccess: true,
|
|
849
|
+
span: { startByte: cap.node.startIndex, endByte: cap.node.endIndex }
|
|
850
|
+
});
|
|
789
851
|
}
|
|
790
852
|
}
|
|
791
853
|
return callSites.sort(
|
|
@@ -1101,6 +1163,219 @@ function createCodingGraphEngine(_options = {}) {
|
|
|
1101
1163
|
// src/reindex.ts
|
|
1102
1164
|
import { readFile as fsReadFile, realpath as fsRealpath } from "fs/promises";
|
|
1103
1165
|
import path2 from "path";
|
|
1166
|
+
|
|
1167
|
+
// src/heuristic-resolution.ts
|
|
1168
|
+
import { posix } from "path";
|
|
1169
|
+
var EXPLICIT_RECEIVER_LANGUAGES = /* @__PURE__ */ new Set([
|
|
1170
|
+
"typescript",
|
|
1171
|
+
"tsx",
|
|
1172
|
+
"javascript",
|
|
1173
|
+
"python",
|
|
1174
|
+
// PHP method dispatch requires $this->/self:: — both arrive as
|
|
1175
|
+
// member_call_expression (memberAccess) — so a bare helper() inside a
|
|
1176
|
+
// method never means a sibling method (codex review round 12).
|
|
1177
|
+
"php"
|
|
1178
|
+
]);
|
|
1179
|
+
var HEURISTIC_CONFIDENCE_SAME_FILE = 0.9;
|
|
1180
|
+
var HEURISTIC_CONFIDENCE_IMPORT_BOUND = 0.8;
|
|
1181
|
+
var HEURISTIC_PROVENANCE_SCOPE = ["heuristic"];
|
|
1182
|
+
function deriveHeuristicEdges(batch) {
|
|
1183
|
+
let callSites = 0;
|
|
1184
|
+
let resolved = 0;
|
|
1185
|
+
let skippedUnresolved = 0;
|
|
1186
|
+
let skippedAmbiguous = 0;
|
|
1187
|
+
let skippedNoEnclosingSymbol = 0;
|
|
1188
|
+
let skippedMemberAccess = 0;
|
|
1189
|
+
const files = [];
|
|
1190
|
+
for (const ir of batch) {
|
|
1191
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
1192
|
+
const kindOf = /* @__PURE__ */ new Map();
|
|
1193
|
+
for (const sym of ir.symbols) kindOf.set(sym.qualifiedName, sym.kind);
|
|
1194
|
+
for (const sym of ir.symbols) {
|
|
1195
|
+
let parent;
|
|
1196
|
+
for (const other of ir.symbols) {
|
|
1197
|
+
if (other === sym) continue;
|
|
1198
|
+
const contains = other.span.startByte <= sym.span.startByte && sym.span.endByte <= other.span.endByte;
|
|
1199
|
+
const identical = other.span.startByte === sym.span.startByte && other.span.endByte === sym.span.endByte;
|
|
1200
|
+
if (!contains || identical) continue;
|
|
1201
|
+
const size = other.span.endByte - other.span.startByte;
|
|
1202
|
+
if (!parent || size < parent.size) {
|
|
1203
|
+
parent = { qualifiedName: other.qualifiedName, size };
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
parentOf.set(sym.qualifiedName, parent ? parent.qualifiedName : "");
|
|
1207
|
+
}
|
|
1208
|
+
const scopeByName = /* @__PURE__ */ new Map();
|
|
1209
|
+
for (const sym of ir.symbols) {
|
|
1210
|
+
const level = parentOf.get(sym.qualifiedName) ?? "";
|
|
1211
|
+
let names = scopeByName.get(level);
|
|
1212
|
+
if (!names) {
|
|
1213
|
+
names = /* @__PURE__ */ new Map();
|
|
1214
|
+
scopeByName.set(level, names);
|
|
1215
|
+
}
|
|
1216
|
+
const prior = names.get(sym.name);
|
|
1217
|
+
if (prior) {
|
|
1218
|
+
prior.count += 1;
|
|
1219
|
+
} else {
|
|
1220
|
+
names.set(sym.name, { qualifiedName: sym.qualifiedName, count: 1 });
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
const importBindings = /* @__PURE__ */ new Map();
|
|
1224
|
+
for (const imp of ir.imports) {
|
|
1225
|
+
let specifier;
|
|
1226
|
+
if (imp.module.startsWith("./") || imp.module.startsWith("../")) {
|
|
1227
|
+
specifier = imp.module;
|
|
1228
|
+
} else if (ir.language === "python" && imp.module.startsWith(".")) {
|
|
1229
|
+
const dots = /^\.+/.exec(imp.module)?.[0].length ?? 1;
|
|
1230
|
+
const rest = imp.module.slice(dots).replace(/\./g, "/");
|
|
1231
|
+
specifier = `${"../".repeat(dots - 1)}${rest}` || ".";
|
|
1232
|
+
}
|
|
1233
|
+
if (specifier === void 0) continue;
|
|
1234
|
+
const joined = posix.join(posix.dirname(ir.path), specifier);
|
|
1235
|
+
const hint = joined.replace(/\/+$/, "").replace(/\.[cm]?[jt]sx?$/, "");
|
|
1236
|
+
if (hint.startsWith("../")) continue;
|
|
1237
|
+
const bindings = imp.bindings ?? imp.importedNames.map((name) => ({ exported: name, local: name }));
|
|
1238
|
+
let enclosing;
|
|
1239
|
+
for (const sym of ir.symbols) {
|
|
1240
|
+
if (sym.span.startByte > imp.span.startByte || imp.span.endByte > sym.span.endByte) continue;
|
|
1241
|
+
const size = sym.span.endByte - sym.span.startByte;
|
|
1242
|
+
if (!enclosing || size < enclosing.size) {
|
|
1243
|
+
enclosing = { qualifiedName: sym.qualifiedName, size };
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
const enclosingKind = enclosing === void 0 ? void 0 : kindOf.get(enclosing.qualifiedName);
|
|
1247
|
+
const visible = enclosingKind === void 0 || enclosingKind !== "class" && enclosingKind !== "interface" && enclosingKind !== "enum";
|
|
1248
|
+
for (const b of bindings) {
|
|
1249
|
+
const list = importBindings.get(b.local) ?? [];
|
|
1250
|
+
list.push({
|
|
1251
|
+
exported: b.exported,
|
|
1252
|
+
hint,
|
|
1253
|
+
enclosing: visible ? enclosing?.qualifiedName : void 0,
|
|
1254
|
+
// A class-body import is genuinely invisible: store it with a
|
|
1255
|
+
// sentinel that can never match an ancestor chain.
|
|
1256
|
+
invisible: !visible
|
|
1257
|
+
});
|
|
1258
|
+
importBindings.set(b.local, list);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
const edges = [];
|
|
1262
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
1263
|
+
for (const site of ir.callSites) {
|
|
1264
|
+
callSites += 1;
|
|
1265
|
+
if (site.memberAccess === true) {
|
|
1266
|
+
skippedMemberAccess += 1;
|
|
1267
|
+
continue;
|
|
1268
|
+
}
|
|
1269
|
+
let src;
|
|
1270
|
+
for (const sym of ir.symbols) {
|
|
1271
|
+
if (sym.span.startByte > site.span.startByte || site.span.endByte > sym.span.endByte) continue;
|
|
1272
|
+
const size = sym.span.endByte - sym.span.startByte;
|
|
1273
|
+
if (!src || size < src.size) {
|
|
1274
|
+
src = { qualifiedName: sym.qualifiedName, size };
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
if (!src) {
|
|
1278
|
+
skippedNoEnclosingSymbol += 1;
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
1281
|
+
const excludeClassScopes = EXPLICIT_RECEIVER_LANGUAGES.has(ir.language);
|
|
1282
|
+
const scopeLevels = [src.qualifiedName];
|
|
1283
|
+
const ancestorChain = /* @__PURE__ */ new Set([src.qualifiedName]);
|
|
1284
|
+
let cursor = src.qualifiedName;
|
|
1285
|
+
while (cursor !== void 0 && cursor !== "") {
|
|
1286
|
+
cursor = parentOf.get(cursor) ?? "";
|
|
1287
|
+
if (cursor !== "") ancestorChain.add(cursor);
|
|
1288
|
+
if (excludeClassScopes) {
|
|
1289
|
+
const kind = cursor === "" ? void 0 : kindOf.get(cursor);
|
|
1290
|
+
if (kind === "class" || kind === "interface" || kind === "enum") continue;
|
|
1291
|
+
}
|
|
1292
|
+
scopeLevels.push(cursor);
|
|
1293
|
+
}
|
|
1294
|
+
let dstQualifiedName;
|
|
1295
|
+
let dstPathHint;
|
|
1296
|
+
let confidence = 0;
|
|
1297
|
+
let sawAmbiguous = false;
|
|
1298
|
+
for (const candidate of site.calleeNameCandidates) {
|
|
1299
|
+
let candidateAmbiguous = false;
|
|
1300
|
+
for (const level of scopeLevels) {
|
|
1301
|
+
const match = scopeByName.get(level)?.get(candidate);
|
|
1302
|
+
if (!match) continue;
|
|
1303
|
+
if (match.count === 1) {
|
|
1304
|
+
dstQualifiedName = match.qualifiedName;
|
|
1305
|
+
confidence = HEURISTIC_CONFIDENCE_SAME_FILE;
|
|
1306
|
+
} else {
|
|
1307
|
+
candidateAmbiguous = true;
|
|
1308
|
+
}
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
if (dstQualifiedName !== void 0) break;
|
|
1312
|
+
if (candidateAmbiguous) {
|
|
1313
|
+
sawAmbiguous = true;
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
const candidates = importBindings.get(candidate);
|
|
1317
|
+
if (candidates !== void 0) {
|
|
1318
|
+
let chosen;
|
|
1319
|
+
let chosenDepth = -1;
|
|
1320
|
+
for (const c of candidates) {
|
|
1321
|
+
if (c.invisible) continue;
|
|
1322
|
+
if (c.enclosing === void 0) {
|
|
1323
|
+
if (chosenDepth < 0) {
|
|
1324
|
+
chosen = c;
|
|
1325
|
+
chosenDepth = 0;
|
|
1326
|
+
}
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
if (ancestorChain.has(c.enclosing)) {
|
|
1330
|
+
const depth = [...ancestorChain].indexOf(c.enclosing);
|
|
1331
|
+
if (chosenDepth < 0 || depth < chosenDepth) {
|
|
1332
|
+
chosen = c;
|
|
1333
|
+
chosenDepth = depth;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
if (chosen) {
|
|
1338
|
+
dstQualifiedName = chosen.exported;
|
|
1339
|
+
dstPathHint = chosen.hint;
|
|
1340
|
+
confidence = HEURISTIC_CONFIDENCE_IMPORT_BOUND;
|
|
1341
|
+
break;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (dstQualifiedName === void 0) {
|
|
1346
|
+
if (sawAmbiguous) skippedAmbiguous += 1;
|
|
1347
|
+
else skippedUnresolved += 1;
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
resolved += 1;
|
|
1351
|
+
const key = `${src.qualifiedName}\0${dstQualifiedName}\0${dstPathHint ?? ""}`;
|
|
1352
|
+
if (seenKeys.has(key)) continue;
|
|
1353
|
+
seenKeys.add(key);
|
|
1354
|
+
edges.push({
|
|
1355
|
+
srcQualifiedName: src.qualifiedName,
|
|
1356
|
+
dstQualifiedName,
|
|
1357
|
+
type: "CALLS",
|
|
1358
|
+
confidence,
|
|
1359
|
+
provenance: "heuristic",
|
|
1360
|
+
...dstPathHint !== void 0 ? { dstPathHint, dstImporterLanguage: ir.language } : {}
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
files.push({ ...ir, edges });
|
|
1364
|
+
}
|
|
1365
|
+
return {
|
|
1366
|
+
files,
|
|
1367
|
+
stats: {
|
|
1368
|
+
callSites,
|
|
1369
|
+
resolved,
|
|
1370
|
+
skippedUnresolved,
|
|
1371
|
+
skippedAmbiguous,
|
|
1372
|
+
skippedNoEnclosingSymbol,
|
|
1373
|
+
skippedMemberAccess
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// src/reindex.ts
|
|
1104
1379
|
var META_KEY_LAST_HEAD = "last_indexed_head";
|
|
1105
1380
|
var META_KEY_PENDING_PARSE_FAILURES = "pending_parse_failures";
|
|
1106
1381
|
function planReindex(lastState, facts) {
|
|
@@ -1467,7 +1742,7 @@ function computeNextPending(args) {
|
|
|
1467
1742
|
return [...next];
|
|
1468
1743
|
}
|
|
1469
1744
|
async function ingestFiles(store, repoRoot, parseFile, readFile2, paths, deletePaths = []) {
|
|
1470
|
-
const
|
|
1745
|
+
const parsed = [];
|
|
1471
1746
|
const parseFailedPaths = [];
|
|
1472
1747
|
for (const relPath of paths) {
|
|
1473
1748
|
if (!isCanonicalRelativePath(relPath)) {
|
|
@@ -1488,8 +1763,13 @@ async function ingestFiles(store, repoRoot, parseFile, readFile2, paths, deleteP
|
|
|
1488
1763
|
parseFailedPaths.push(relPath);
|
|
1489
1764
|
continue;
|
|
1490
1765
|
}
|
|
1491
|
-
|
|
1766
|
+
parsed.push(parseResult.ir);
|
|
1492
1767
|
}
|
|
1768
|
+
const derived = deriveHeuristicEdges(parsed);
|
|
1769
|
+
const batch = derived.files.map((file) => ({
|
|
1770
|
+
...file,
|
|
1771
|
+
assertedEdgeProvenances: HEURISTIC_PROVENANCE_SCOPE
|
|
1772
|
+
}));
|
|
1493
1773
|
const upsertResult = await store.upsertFileBatch(batch, deletePaths);
|
|
1494
1774
|
if (!upsertResult.ok) {
|
|
1495
1775
|
return {
|
|
@@ -2667,6 +2947,7 @@ async function executeLspResolution(requests, options) {
|
|
|
2667
2947
|
for (const [filePath, batchReqs] of byFile) {
|
|
2668
2948
|
const upgrades = [];
|
|
2669
2949
|
let batchFailed = false;
|
|
2950
|
+
let batchExhaustive = true;
|
|
2670
2951
|
const firstReq = batchReqs[0];
|
|
2671
2952
|
client.didOpen({
|
|
2672
2953
|
uri: filePathToUri(filePath, options.workspaceRoot),
|
|
@@ -2691,6 +2972,7 @@ async function executeLspResolution(requests, options) {
|
|
|
2691
2972
|
batchFailed = true;
|
|
2692
2973
|
break;
|
|
2693
2974
|
}
|
|
2975
|
+
batchExhaustive = false;
|
|
2694
2976
|
unresolved++;
|
|
2695
2977
|
continue;
|
|
2696
2978
|
}
|
|
@@ -2720,13 +3002,23 @@ async function executeLspResolution(requests, options) {
|
|
|
2720
3002
|
unresolved += upgrades.length;
|
|
2721
3003
|
break;
|
|
2722
3004
|
}
|
|
2723
|
-
|
|
2724
|
-
|
|
3005
|
+
try {
|
|
3006
|
+
if (upgrades.length > 0) {
|
|
2725
3007
|
await applyUpgrades(upgrades);
|
|
2726
3008
|
upgraded += upgrades.length;
|
|
2727
|
-
} catch {
|
|
2728
|
-
unresolved += upgrades.length;
|
|
2729
3009
|
}
|
|
3010
|
+
if (batchExhaustive && options.reconcileLspEdges) {
|
|
3011
|
+
options.reconcileLspEdges(
|
|
3012
|
+
filePath,
|
|
3013
|
+
upgrades.map((u) => ({
|
|
3014
|
+
srcQualifiedName: u.srcQualifiedName,
|
|
3015
|
+
dstQualifiedName: u.dstQualifiedName,
|
|
3016
|
+
type: u.type
|
|
3017
|
+
}))
|
|
3018
|
+
);
|
|
3019
|
+
}
|
|
3020
|
+
} catch {
|
|
3021
|
+
unresolved += upgrades.length;
|
|
2730
3022
|
}
|
|
2731
3023
|
}
|
|
2732
3024
|
return {
|