gitnexus 1.6.4-rc.2 → 1.6.4-rc.21
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 +35 -0
- package/dist/_shared/index.d.ts +1 -1
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +1 -1
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +22 -14
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.js +298 -37
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts +23 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js +36 -2
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +47 -3
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/types.js +10 -2
- package/dist/_shared/scope-resolution/types.js.map +1 -1
- package/dist/cli/analyze.d.ts +6 -0
- package/dist/cli/analyze.js +35 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +31 -0
- package/dist/cli/index.js +13 -0
- package/dist/cli/setup.js +2 -2
- package/dist/core/embeddings/config.d.ts +2 -0
- package/dist/core/embeddings/config.js +36 -0
- package/dist/core/embeddings/embedder.js +11 -6
- package/dist/core/embeddings/embedding-pipeline.d.ts +7 -1
- package/dist/core/embeddings/embedding-pipeline.js +93 -29
- package/dist/core/embeddings/exact-search.d.ts +15 -0
- package/dist/core/embeddings/exact-search.js +27 -0
- package/dist/core/embeddings/types.d.ts +4 -0
- package/dist/core/embeddings/types.js +2 -0
- package/dist/core/group/config-parser.js +2 -0
- package/dist/core/group/matching.d.ts +3 -3
- package/dist/core/group/matching.js +46 -6
- package/dist/core/group/storage.js +2 -0
- package/dist/core/group/sync.js +1 -1
- package/dist/core/group/types.d.ts +18 -0
- package/dist/core/ingestion/call-processor.d.ts +3 -3
- package/dist/core/ingestion/call-processor.js +58 -65
- package/dist/core/ingestion/constants.d.ts +4 -3
- package/dist/core/ingestion/constants.js +8 -3
- package/dist/core/ingestion/finalize-orchestrator.js +6 -3
- package/dist/core/ingestion/heritage-processor.js +2 -2
- package/dist/core/ingestion/import-processor.js +1 -1
- package/dist/core/ingestion/language-provider.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp/captures.js +4 -1
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +14 -13
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +62 -50
- package/dist/core/ingestion/languages/python/captures.js +9 -1
- package/dist/core/ingestion/languages/python/index.d.ts +1 -1
- package/dist/core/ingestion/languages/python/index.js +1 -1
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +3 -1
- package/dist/core/ingestion/languages/python/simple-hooks.js +8 -0
- package/dist/core/ingestion/languages/python.js +28 -1
- package/dist/core/ingestion/languages/swift.js +14 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.js +103 -0
- package/dist/core/ingestion/languages/typescript/arity.d.ts +37 -0
- package/dist/core/ingestion/languages/typescript/arity.js +54 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/typescript/captures.d.ts +28 -0
- package/dist/core/ingestion/languages/typescript/captures.js +451 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.d.ts +49 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.js +371 -0
- package/dist/core/ingestion/languages/typescript/import-target.d.ts +50 -0
- package/dist/core/ingestion/languages/typescript/import-target.js +61 -0
- package/dist/core/ingestion/languages/typescript/index.d.ts +94 -0
- package/dist/core/ingestion/languages/typescript/index.js +94 -0
- package/dist/core/ingestion/languages/typescript/interpret.d.ts +35 -0
- package/dist/core/ingestion/languages/typescript/interpret.js +317 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.d.ts +62 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.js +158 -0
- package/dist/core/ingestion/languages/typescript/query.d.ts +77 -0
- package/dist/core/ingestion/languages/typescript/query.js +778 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.js +171 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.js +113 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +71 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.js +131 -0
- package/dist/core/ingestion/languages/typescript.js +19 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +3 -4
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +14 -1
- package/dist/core/ingestion/parsing-processor.js +20 -9
- package/dist/core/ingestion/pipeline-phases/processes.js +9 -4
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +10 -4
- package/dist/core/ingestion/registry-primary-flag.d.ts +3 -1
- package/dist/core/ingestion/registry-primary-flag.js +4 -1
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +5 -2
- package/dist/core/ingestion/scope-extractor-bridge.js +7 -2
- package/dist/core/ingestion/scope-extractor.js +19 -18
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +73 -11
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +48 -10
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +283 -14
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +23 -2
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +109 -37
- package/dist/core/ingestion/scope-resolution/passes/mro.js +3 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +13 -5
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +11 -2
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +8 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +21 -5
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.d.ts +39 -0
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +65 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +54 -11
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +105 -30
- package/dist/core/ingestion/type-extractors/swift.js +7 -4
- package/dist/core/ingestion/utils/ast-helpers.d.ts +2 -0
- package/dist/core/ingestion/utils/ast-helpers.js +12 -0
- package/dist/core/ingestion/utils/env.d.ts +10 -0
- package/dist/core/ingestion/utils/env.js +14 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +1 -0
- package/dist/core/ingestion/workers/parse-worker.js +15 -9
- package/dist/core/ingestion/workers/worker-pool.d.ts +11 -4
- package/dist/core/ingestion/workers/worker-pool.js +244 -48
- package/dist/core/lbug/extension-loader.d.ts +86 -0
- package/dist/core/lbug/extension-loader.js +184 -0
- package/dist/core/lbug/lbug-adapter.d.ts +18 -17
- package/dist/core/lbug/lbug-adapter.js +45 -73
- package/dist/core/lbug/pool-adapter.js +10 -28
- package/dist/core/platform/capabilities.d.ts +24 -0
- package/dist/core/platform/capabilities.js +54 -0
- package/dist/core/run-analyze.js +36 -9
- package/dist/core/search/bm25-index.d.ts +0 -17
- package/dist/core/search/bm25-index.js +10 -118
- package/dist/core/search/fts-indexes.d.ts +1 -0
- package/dist/core/search/fts-indexes.js +7 -0
- package/dist/core/search/fts-schema.d.ts +6 -0
- package/dist/core/search/fts-schema.js +7 -0
- package/dist/mcp/core/embedder.js +11 -4
- package/dist/mcp/local/local-backend.js +50 -15
- package/dist/server/api.d.ts +5 -0
- package/dist/server/api.js +113 -0
- package/hooks/claude/gitnexus-hook.cjs +11 -1
- package/package.json +6 -5
- package/scripts/build-tree-sitter-dart.cjs +42 -0
- package/scripts/build-tree-sitter-proto.cjs +1 -1
- package/scripts/build.js +22 -2
- package/scripts/install-duckdb-extension.mjs +37 -0
- package/vendor/tree-sitter-dart/README.md +18 -0
- package/vendor/tree-sitter-dart/binding.gyp +31 -0
- package/vendor/tree-sitter-dart/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-dart/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-dart/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-dart/grammar.js +2895 -0
- package/vendor/tree-sitter-dart/package.json +18 -0
- package/vendor/tree-sitter-dart/queries/highlights.scm +246 -0
- package/vendor/tree-sitter-dart/queries/tags.scm +92 -0
- package/vendor/tree-sitter-dart/queries/test.scm +1 -0
- package/vendor/tree-sitter-dart/src/grammar.json +12459 -0
- package/vendor/tree-sitter-dart/src/node-types.json +15055 -0
- package/vendor/tree-sitter-dart/src/parser.c +196127 -0
- package/vendor/tree-sitter-dart/src/scanner.c +130 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/array.h +290 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/parser.h +265 -0
- package/vendor/tree-sitter-swift/LICENSE +21 -0
- package/vendor/tree-sitter-swift/README.md +139 -0
- package/vendor/tree-sitter-swift/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-swift/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-swift/package.json +28 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/src/node-types.json +30694 -0
- package/web/assets/agent-DaprsFSX.js +597 -0
- package/web/assets/architecture-YZFGNWBL-S5CXDPWN-DEdGaPg2.js +1 -0
- package/web/assets/architectureDiagram-EMZXCZ2Q-Domyk_gO.js +36 -0
- package/web/assets/blockDiagram-IGV67L2C-B_2kD7tM.js +132 -0
- package/web/assets/c4Diagram-DFAF54RM-BhJJW8Gg.js +10 -0
- package/web/assets/chunk-3GS5O3IE-jlWIjPsl.js +231 -0
- package/web/assets/chunk-3YCYZ6SJ-Blq_IzZs.js +1 -0
- package/web/assets/chunk-6NTNNK5N-DyPc58pp.js +1 -0
- package/web/assets/chunk-7RZVMHOQ-BdIU-RGO.js +321 -0
- package/web/assets/chunk-A34GCYZU-BI2i_LdU.js +1 -0
- package/web/assets/chunk-AEOMTBSW-D7qjBMHW.js +1 -0
- package/web/assets/chunk-CilyBKbf.js +1 -0
- package/web/assets/chunk-DJ7UZH7F-i11ywiBl.js +1 -0
- package/web/assets/chunk-DKKBVRCY-1SffGI1N.js +4 -0
- package/web/assets/chunk-DU5LTGQ6-DaPeiwD5.js +1 -0
- package/web/assets/chunk-FXACKDTF-uhhi2PC2.js +159 -0
- package/web/assets/chunk-H3VCZNTA-IchcISDt.js +1 -0
- package/web/assets/chunk-HN6EAY2L-D7ZFMNrB.js +1 -0
- package/web/assets/chunk-KSICW3F5-C2tZmXwv.js +15 -0
- package/web/assets/chunk-O5ABG6QK-Bt-Km84H.js +1 -0
- package/web/assets/chunk-PK6DOVAG-ChlWY0BQ.js +206 -0
- package/web/assets/chunk-RNJOYNJ4-B724K7cW.js +1 -0
- package/web/assets/chunk-RWUO3TPN-DYn1XriD.js +1 -0
- package/web/assets/chunk-TBF5ZNIQ-DKtDz6ae.js +1 -0
- package/web/assets/chunk-TU3PZOEN-DE5Qhc0N.js +1 -0
- package/web/assets/chunk-TYMNRAUI-g1h33cq-.js +1 -0
- package/web/assets/chunk-VELTKBKT-C9dVN39o.js +1 -0
- package/web/assets/chunk-W7ZLLLMY-Du-Hb9yb.js +1 -0
- package/web/assets/chunk-WSB5WSVC-B123clsZ.js +1 -0
- package/web/assets/chunk-XGPFEOL4-BR7Eue38.js +1 -0
- package/web/assets/classDiagram-PPOCWD7C-BglfKSs_.js +1 -0
- package/web/assets/classDiagram-v2-23LJLIIU-BSzTM28O.js +1 -0
- package/web/assets/context-builder-CqQNhRj1.js +15 -0
- package/web/assets/cose-bilkent-PNC4W37J-DCfErU-A.js +1 -0
- package/web/assets/dagre-E77IOHMT-tDRRhDoN.js +4 -0
- package/web/assets/diagram-H7BISOXX-CUVHlmAh.js +43 -0
- package/web/assets/diagram-JC5VWROH-BoyOxulB.js +24 -0
- package/web/assets/diagram-LXUTUG65-osr9hb7N.js +10 -0
- package/web/assets/diagram-WEHSV5V5-d8nUqS39.js +24 -0
- package/web/assets/erDiagram-GCSMX5X6-b-IwOhPS.js +85 -0
- package/web/assets/flowDiagram-OTCZ4VVT-Ott2Q0AP.js +162 -0
- package/web/assets/ganttDiagram-MUNLMDZQ-BYtgN_5s.js +292 -0
- package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-CFyBIGZq.js +1 -0
- package/web/assets/gitGraphDiagram-3HKGZ4G3-CsVD2gn4.js +106 -0
- package/web/assets/index-BleGLU8S.css +2 -0
- package/web/assets/index-C_xK08EW.js +885 -0
- package/web/assets/info-OMHHGYJF-BF2H5H6G-yjAxKEzh.js +1 -0
- package/web/assets/infoDiagram-MN7RKWGX-DXK0Unn5.js +2 -0
- package/web/assets/ishikawaDiagram-YMYX4NHK-CXsnC2FA.js +70 -0
- package/web/assets/journeyDiagram-SO5T7YLQ-BzZ07B-X.js +139 -0
- package/web/assets/kanban-definition-LJHFXRCJ-C6_EpAd9.js +89 -0
- package/web/assets/katex-GD7MH7QM-CJiOjBBJ.js +261 -0
- package/web/assets/mindmap-definition-2EUWGEK5-CCYGWZ1m.js +96 -0
- package/web/assets/packet-4T2RLAQJ-EV4IVRXR-B8k4E3IT.js +1 -0
- package/web/assets/pie-ZZUOXDRM-N23DN5KN-DdvfY118.js +1 -0
- package/web/assets/pieDiagram-3IATQBI2-RyvRlQb4.js +30 -0
- package/web/assets/quadrantDiagram-E256RVCF-Bfb6sxCx.js +7 -0
- package/web/assets/radar-PYXPWWZC-P6TP7ZYP-1EEDC_yU.js +1 -0
- package/web/assets/requirementDiagram-M5DCFWZL-DjvHDyvN.js +84 -0
- package/web/assets/sankeyDiagram-L3NBLAOT-CBCbbl8s.js +10 -0
- package/web/assets/sequenceDiagram-ZOUHS735-BscU8TUR.js +157 -0
- package/web/assets/stateDiagram-MLPALWAM-CJusEK2D.js +1 -0
- package/web/assets/stateDiagram-v2-B5LQ5ZB2-DImJ3PXD.js +1 -0
- package/web/assets/timeline-definition-5SPVSISX-DigPA1X8.js +120 -0
- package/web/assets/treeView-SZITEDCU-5DXDK3XO-CzPDt3aG.js +1 -0
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-B9Iqiorr.js +1 -0
- package/web/assets/vennDiagram-IE5QUKF5-C91UkZIf.js +34 -0
- package/web/assets/wardley-RL74JXVD-BCRCBASE-x42Qw7hp.js +1 -0
- package/web/assets/wardleyDiagram-XU3VSMPF-DloBhI0U.js +20 -0
- package/web/assets/xychartDiagram-ZHJ5623Y-BGWJvgwI.js +7 -0
- package/web/index.html +21 -0
- package/scripts/patch-tree-sitter-swift.cjs +0 -78
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
12
12
|
import { pythonScopeResolver } from '../../languages/python/scope-resolver.js';
|
|
13
13
|
import { csharpScopeResolver } from '../../languages/csharp/scope-resolver.js';
|
|
14
|
+
import { typescriptScopeResolver } from '../../languages/typescript/scope-resolver.js';
|
|
14
15
|
/** Map of `SupportedLanguages` → `ScopeResolver`. The phase iterates
|
|
15
16
|
* this map intersected with `MIGRATED_LANGUAGES` (the per-language
|
|
16
17
|
* flag set) so adding a resolver here without flipping the flag is
|
|
@@ -18,4 +19,5 @@ import { csharpScopeResolver } from '../../languages/csharp/scope-resolver.js';
|
|
|
18
19
|
export const SCOPE_RESOLVERS = new Map([
|
|
19
20
|
[SupportedLanguages.Python, pythonScopeResolver],
|
|
20
21
|
[SupportedLanguages.CSharp, csharpScopeResolver],
|
|
22
|
+
[SupportedLanguages.TypeScript, typescriptScopeResolver],
|
|
21
23
|
]);
|
|
@@ -53,6 +53,14 @@ interface RunScopeResolutionInput {
|
|
|
53
53
|
readonly treeCache?: {
|
|
54
54
|
get(filePath: string): unknown;
|
|
55
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Opaque per-language import-resolution config (e.g. tsconfig path
|
|
58
|
+
* aliases for TypeScript). Loaded once by the caller via
|
|
59
|
+
* `provider.loadResolutionConfig(repoPath)` and threaded into every
|
|
60
|
+
* `provider.resolveImportTarget` call. `undefined` when the
|
|
61
|
+
* provider doesn't supply a config loader.
|
|
62
|
+
*/
|
|
63
|
+
readonly resolutionConfig?: unknown;
|
|
56
64
|
}
|
|
57
65
|
interface RunScopeResolutionStats {
|
|
58
66
|
readonly filesProcessed: number;
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* Plan: `docs/plans/2026-04-20-001-refactor-emit-pipeline-generalization-plan.md`.
|
|
24
24
|
*/
|
|
25
25
|
import { reconcileOwnership, validateOwnershipParity } from './reconcile-ownership.js';
|
|
26
|
+
import { validateBindingsImmutability } from './validate-bindings-immutability.js';
|
|
26
27
|
import { extractParsedFile } from '../../scope-extractor-bridge.js';
|
|
27
28
|
import { finalizeScopeModel } from '../../finalize-orchestrator.js';
|
|
28
29
|
import { resolveReferenceSites } from '../../resolve-references.js';
|
|
@@ -81,9 +82,10 @@ export function runScopeResolution(input, provider) {
|
|
|
81
82
|
const allFilePaths = new Set(parsedFiles.map((f) => f.filePath));
|
|
82
83
|
const nodeLookup = buildGraphNodeLookup(graph);
|
|
83
84
|
const mroByClassDefId = provider.buildMro(graph, parsedFiles, nodeLookup);
|
|
85
|
+
const resolutionConfig = input.resolutionConfig;
|
|
84
86
|
const finalized = finalizeScopeModel(parsedFiles, {
|
|
85
87
|
hooks: {
|
|
86
|
-
resolveImportTarget: (targetRaw, fromFile) => provider.resolveImportTarget(targetRaw, fromFile, allFilePaths),
|
|
88
|
+
resolveImportTarget: (targetRaw, fromFile) => provider.resolveImportTarget(targetRaw, fromFile, allFilePaths, resolutionConfig),
|
|
87
89
|
mergeBindings: (existing, incoming, scopeId) => provider.mergeBindings(existing, incoming, scopeId),
|
|
88
90
|
},
|
|
89
91
|
});
|
|
@@ -106,6 +108,8 @@ export function runScopeResolution(input, provider) {
|
|
|
106
108
|
// Cross-file implicit-namespace visibility (C#). Must run before
|
|
107
109
|
// propagateImportedReturnTypes so the latter pass sees siblings'
|
|
108
110
|
// class bindings when chasing return-type chains across files.
|
|
111
|
+
// The hook writes to `bindingAugmentations` only; finalized
|
|
112
|
+
// `indexes.bindings` remains immutable post-finalize (I8).
|
|
109
113
|
if (provider.populateNamespaceSiblings !== undefined) {
|
|
110
114
|
const fileContents = new Map();
|
|
111
115
|
for (const f of files)
|
|
@@ -115,12 +119,23 @@ export function runScopeResolution(input, provider) {
|
|
|
115
119
|
treeCache,
|
|
116
120
|
});
|
|
117
121
|
}
|
|
122
|
+
const tFinalize = PROF ? process.hrtime.bigint() : 0n;
|
|
118
123
|
// Cross-file return-type propagation (Contract Invariant I3 timing:
|
|
119
|
-
// after finalize, before resolve).
|
|
124
|
+
// after finalize, before resolve). Split-timed separately so the
|
|
125
|
+
// SCC-ordered pass's cost is observable (PR #1050 made this O(files)
|
|
126
|
+
// with chain-follow per importer; quadratic regressions show up
|
|
127
|
+
// here, not in finalize).
|
|
120
128
|
if (provider.propagatesReturnTypesAcrossImports !== false) {
|
|
121
129
|
propagateImportedReturnTypes(parsedFiles, indexes, workspaceIndex);
|
|
122
130
|
}
|
|
123
|
-
const
|
|
131
|
+
const tPropagate = PROF ? process.hrtime.bigint() : 0n;
|
|
132
|
+
// Opt-in I8 invariant guard. Runs once after all post-finalize hooks
|
|
133
|
+
// (`populateNamespaceSiblings`, `propagateImportedReturnTypes`) have
|
|
134
|
+
// had a chance to drift, so a single sweep covers the full
|
|
135
|
+
// post-finalize surface visible to `resolveReferenceSites`. No-op in
|
|
136
|
+
// default CLI runs; enabled by NODE_ENV=development or
|
|
137
|
+
// VALIDATE_SEMANTIC_MODEL=1.
|
|
138
|
+
validateBindingsImmutability(indexes, onWarn);
|
|
124
139
|
// ── Phase 3: resolve references via Registry.lookup ────────────────────
|
|
125
140
|
const registryProviders = {
|
|
126
141
|
arityCompatibility: provider.arityCompatibility,
|
|
@@ -140,8 +155,9 @@ export function runScopeResolution(input, provider) {
|
|
|
140
155
|
const tEnd = process.hrtime.bigint();
|
|
141
156
|
const ns = (a, b) => Number(b - a) / 1_000_000;
|
|
142
157
|
console.warn(`[scope-resolution prof] extract=${ns(tStart, tExtract).toFixed(0)}ms` +
|
|
143
|
-
` finalize
|
|
144
|
-
`
|
|
158
|
+
` finalize=${ns(tExtract, tFinalize).toFixed(0)}ms` +
|
|
159
|
+
` propagate=${ns(tFinalize, tPropagate).toFixed(0)}ms` +
|
|
160
|
+
` resolve=${ns(tPropagate, tResolve).toFixed(0)}ms` +
|
|
145
161
|
` emit=${ns(tResolve, tEnd).toFixed(0)}ms` +
|
|
146
162
|
` total=${ns(tStart, tEnd).toFixed(0)}ms` +
|
|
147
163
|
` (${parsedFiles.length} files)`);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-mode runtime validator for the two-channel binding lifecycle
|
|
3
|
+
* (Contract Invariant I8 in `contract/scope-resolver.ts`).
|
|
4
|
+
*
|
|
5
|
+
* The two channels:
|
|
6
|
+
* - `indexes.bindings` — finalize-output channel. After
|
|
7
|
+
* `finalizeScopeModel` returns, every inner `BindingRef[]` array
|
|
8
|
+
* here is deep-frozen by `materializeBindings`. NO post-finalize
|
|
9
|
+
* hook should ever mutate this map's inner arrays — drift here
|
|
10
|
+
* manifests at runtime as the `Cannot add property N, object is
|
|
11
|
+
* not extensible` crash (issue #1066) or, more insidiously, as
|
|
12
|
+
* a hook silently mutating one of the frozen arrays (a no-op in
|
|
13
|
+
* production where freezes can be elided, a `TypeError` in dev).
|
|
14
|
+
*
|
|
15
|
+
* - `indexes.bindingAugmentations` — post-finalize append-only
|
|
16
|
+
* channel. Inner arrays here are NEVER frozen; hooks like
|
|
17
|
+
* `populateNamespaceSiblings` `push()` directly. Walkers consult
|
|
18
|
+
* both channels via `lookupBindingsAt`.
|
|
19
|
+
*
|
|
20
|
+
* This validator runs after every post-finalize hook has executed
|
|
21
|
+
* (so the dev-mode envelope captures the FULL surface area visible
|
|
22
|
+
* to `resolveReferenceSites`) and asserts:
|
|
23
|
+
*
|
|
24
|
+
* 1. Every inner `BindingRef[]` array in `indexes.bindings` is
|
|
25
|
+
* `Object.isFrozen` — i.e. finalize produced a frozen bucket
|
|
26
|
+
* AND no hook accidentally `set()`-back a mutable replacement.
|
|
27
|
+
*
|
|
28
|
+
* 2. Every inner `BindingRef[]` array in
|
|
29
|
+
* `indexes.bindingAugmentations` is NOT frozen — i.e. the
|
|
30
|
+
* hook used the augmentation channel as designed (mutable
|
|
31
|
+
* `push()`) and didn't accidentally freeze its own scratch
|
|
32
|
+
* arrays. Self-documenting; mostly a sanity net.
|
|
33
|
+
*
|
|
34
|
+
* Mirrors `validateOwnershipParity` (#909): warns via `onWarn`,
|
|
35
|
+
* never throws, and is opt-in outside development. Gated by
|
|
36
|
+
* `isSemanticModelValidatorEnabled()` (`utils/env.ts`).
|
|
37
|
+
*/
|
|
38
|
+
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
|
|
39
|
+
export declare function validateBindingsImmutability(indexes: ScopeResolutionIndexes, onWarn: (message: string) => void): number;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-mode runtime validator for the two-channel binding lifecycle
|
|
3
|
+
* (Contract Invariant I8 in `contract/scope-resolver.ts`).
|
|
4
|
+
*
|
|
5
|
+
* The two channels:
|
|
6
|
+
* - `indexes.bindings` — finalize-output channel. After
|
|
7
|
+
* `finalizeScopeModel` returns, every inner `BindingRef[]` array
|
|
8
|
+
* here is deep-frozen by `materializeBindings`. NO post-finalize
|
|
9
|
+
* hook should ever mutate this map's inner arrays — drift here
|
|
10
|
+
* manifests at runtime as the `Cannot add property N, object is
|
|
11
|
+
* not extensible` crash (issue #1066) or, more insidiously, as
|
|
12
|
+
* a hook silently mutating one of the frozen arrays (a no-op in
|
|
13
|
+
* production where freezes can be elided, a `TypeError` in dev).
|
|
14
|
+
*
|
|
15
|
+
* - `indexes.bindingAugmentations` — post-finalize append-only
|
|
16
|
+
* channel. Inner arrays here are NEVER frozen; hooks like
|
|
17
|
+
* `populateNamespaceSiblings` `push()` directly. Walkers consult
|
|
18
|
+
* both channels via `lookupBindingsAt`.
|
|
19
|
+
*
|
|
20
|
+
* This validator runs after every post-finalize hook has executed
|
|
21
|
+
* (so the dev-mode envelope captures the FULL surface area visible
|
|
22
|
+
* to `resolveReferenceSites`) and asserts:
|
|
23
|
+
*
|
|
24
|
+
* 1. Every inner `BindingRef[]` array in `indexes.bindings` is
|
|
25
|
+
* `Object.isFrozen` — i.e. finalize produced a frozen bucket
|
|
26
|
+
* AND no hook accidentally `set()`-back a mutable replacement.
|
|
27
|
+
*
|
|
28
|
+
* 2. Every inner `BindingRef[]` array in
|
|
29
|
+
* `indexes.bindingAugmentations` is NOT frozen — i.e. the
|
|
30
|
+
* hook used the augmentation channel as designed (mutable
|
|
31
|
+
* `push()`) and didn't accidentally freeze its own scratch
|
|
32
|
+
* arrays. Self-documenting; mostly a sanity net.
|
|
33
|
+
*
|
|
34
|
+
* Mirrors `validateOwnershipParity` (#909): warns via `onWarn`,
|
|
35
|
+
* never throws, and is opt-in outside development. Gated by
|
|
36
|
+
* `isSemanticModelValidatorEnabled()` (`utils/env.ts`).
|
|
37
|
+
*/
|
|
38
|
+
import { isSemanticModelValidatorEnabled } from '../../utils/env.js';
|
|
39
|
+
export function validateBindingsImmutability(indexes, onWarn) {
|
|
40
|
+
if (!isSemanticModelValidatorEnabled())
|
|
41
|
+
return 0;
|
|
42
|
+
let violations = 0;
|
|
43
|
+
for (const [scopeId, bucketMap] of indexes.bindings) {
|
|
44
|
+
for (const [name, bucket] of bucketMap) {
|
|
45
|
+
if (!Object.isFrozen(bucket)) {
|
|
46
|
+
onWarn(`binding-immutability: indexes.bindings[${scopeId}][${name}] is NOT frozen — ` +
|
|
47
|
+
`finalize produced a mutable bucket OR a post-finalize hook replaced a frozen ` +
|
|
48
|
+
`bucket with a mutable one. Hooks must write to indexes.bindingAugmentations ` +
|
|
49
|
+
`instead. See ScopeResolver Invariant I8.`);
|
|
50
|
+
violations++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const [scopeId, bucketMap] of indexes.bindingAugmentations) {
|
|
55
|
+
for (const [name, bucket] of bucketMap) {
|
|
56
|
+
if (Object.isFrozen(bucket)) {
|
|
57
|
+
onWarn(`binding-immutability: indexes.bindingAugmentations[${scopeId}][${name}] is FROZEN — ` +
|
|
58
|
+
`the augmentation channel is mutable by contract; freezing it defeats the ` +
|
|
59
|
+
`append-only purpose. See ScopeResolver Invariant I8.`);
|
|
60
|
+
violations++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return violations;
|
|
65
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Scope-chain lookup primitives shared across language providers.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Five functions:
|
|
5
5
|
* - `findReceiverTypeBinding` — walk scope.typeBindings up the chain
|
|
6
6
|
* for a receiver name.
|
|
7
|
-
* - `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* - `lookupBindingsAt` — read finalized + augmented binding refs at
|
|
8
|
+
* one scope, deduped by `def.nodeId`. The dual-source-aware
|
|
9
|
+
* primitive every other binding lookup composes with.
|
|
10
|
+
* - `findClassBindingInScope` — walk scope.bindings + the indexes via
|
|
11
|
+
* `lookupBindingsAt` for a class-kind binding.
|
|
11
12
|
* - `findOwnedMember` — find a method/field owned by a class def
|
|
12
13
|
* across all parsed files by (ownerId, simpleName).
|
|
13
14
|
* - `findExportedDef` — find a file-level exported def (top-of-module
|
|
@@ -18,10 +19,44 @@
|
|
|
18
19
|
* "resolve member on owner with MRO" pattern. All four are reusable
|
|
19
20
|
* as-is for TypeScript, Java, Kotlin, Ruby, etc.
|
|
20
21
|
*/
|
|
21
|
-
import type { ParsedFile, ScopeId, SymbolDefinition, TypeRef } from '../../../../_shared/index.js';
|
|
22
|
+
import type { BindingRef, ParsedFile, ScopeId, SymbolDefinition, TypeRef } from '../../../../_shared/index.js';
|
|
22
23
|
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
|
|
23
24
|
import type { SemanticModel } from '../../model/semantic-model.js';
|
|
24
25
|
import type { WorkspaceResolutionIndex } from '../workspace-index.js';
|
|
26
|
+
/**
|
|
27
|
+
* Look up binding refs at `scopeId` for `name`, consulting both the
|
|
28
|
+
* finalize-owned `bindings` channel and the post-finalize
|
|
29
|
+
* `bindingAugmentations` channel (see invariant I8 in
|
|
30
|
+
* `contract/scope-resolver.ts`). Finalized refs come first; augmented
|
|
31
|
+
* refs append, deduped by `def.nodeId` so a sibling that's also
|
|
32
|
+
* explicitly imported doesn't double-emit.
|
|
33
|
+
*
|
|
34
|
+
* Returns a shared frozen empty array when neither channel has the
|
|
35
|
+
* name — callers can compare against `=== EMPTY_BINDINGS` if they
|
|
36
|
+
* want a fast-path miss check. The bucket arrays are returned by
|
|
37
|
+
* reference when only one channel populates them; the merged path
|
|
38
|
+
* allocates a fresh array.
|
|
39
|
+
*
|
|
40
|
+
* Walker primitives (`findClassBindingInScope`,
|
|
41
|
+
* `findCallableBindingInScope`, `findExportedDefByName`) and
|
|
42
|
+
* post-finalize passes that read finalized bindings (e.g.
|
|
43
|
+
* `propagateImportedReturnTypes`, `namespace-targets`) MUST go
|
|
44
|
+
* through this helper instead of `scopes.bindings.get(...)` directly,
|
|
45
|
+
* so the augmentation channel is always visible.
|
|
46
|
+
*/
|
|
47
|
+
export declare function lookupBindingsAt(scopeId: ScopeId, name: string, scopes: ScopeResolutionIndexes): readonly BindingRef[];
|
|
48
|
+
/**
|
|
49
|
+
* Return the union of bound names at `scopeId` across both the
|
|
50
|
+
* finalized and augmented channels. Companion to `lookupBindingsAt`
|
|
51
|
+
* for callers that need to iterate every name at a scope (e.g.
|
|
52
|
+
* `propagateImportedReturnTypes`). Order is not guaranteed; callers
|
|
53
|
+
* that need stable iteration should sort externally.
|
|
54
|
+
*
|
|
55
|
+
* Fast paths (zero allocation) when at most one channel is populated:
|
|
56
|
+
* returns the underlying `Map.keys()` iterator directly. Only when both
|
|
57
|
+
* channels carry names do we materialize a `Set` for deduplication.
|
|
58
|
+
*/
|
|
59
|
+
export declare function namesAtScope(scopeId: ScopeId, scopes: ScopeResolutionIndexes): Iterable<string>;
|
|
25
60
|
/**
|
|
26
61
|
* True when a def's `type` names a class-like declaration — every kind
|
|
27
62
|
* that collapses to `@scope.class` in the scope-extractor query contract.
|
|
@@ -48,8 +83,10 @@ export declare function findReceiverTypeBinding(startScope: ScopeId, receiverNam
|
|
|
48
83
|
* Walks the scope chain upward and consults TWO sources at each step:
|
|
49
84
|
* 1. `scope.bindings` — populated during scope-extraction Pass 2 with
|
|
50
85
|
* local declarations (`origin: 'local'`).
|
|
51
|
-
* 2.
|
|
52
|
-
*
|
|
86
|
+
* 2. The cross-file finalized + augmented bindings, via
|
|
87
|
+
* `lookupBindingsAt` (per I8: finalized = canonical immutable
|
|
88
|
+
* output; augmented = post-finalize hooks like
|
|
89
|
+
* `populateNamespaceSiblings`).
|
|
53
90
|
*
|
|
54
91
|
* Without (2) we'd miss every cross-file class-receiver call.
|
|
55
92
|
*/
|
|
@@ -57,8 +94,9 @@ export declare function findClassBindingInScope(startScope: ScopeId, receiverNam
|
|
|
57
94
|
/**
|
|
58
95
|
* Look up a callable (Function/Method/Constructor) by name in the
|
|
59
96
|
* given scope's chain. Uses the dual-source pattern (scope.bindings +
|
|
60
|
-
*
|
|
61
|
-
* free calls to imported functions
|
|
97
|
+
* `lookupBindingsAt` for finalized + augmented) so cross-file
|
|
98
|
+
* imports are visible — without it free calls to imported functions
|
|
99
|
+
* never resolve via the post-pass.
|
|
62
100
|
*
|
|
63
101
|
* Mirrors `findClassBindingInScope` exactly; only the accepted
|
|
64
102
|
* def-type predicate differs.
|
|
@@ -122,6 +160,11 @@ export declare function findOwnedMember(ownerDefId: string, memberName: string,
|
|
|
122
160
|
* excluded.
|
|
123
161
|
*
|
|
124
162
|
* Reads from `WorkspaceResolutionIndex.moduleScopeByFile` (scope-tied
|
|
125
|
-
* lookup that doesn't live on `SemanticModel`).
|
|
163
|
+
* lookup that doesn't live on `SemanticModel`). This intentionally
|
|
164
|
+
* does NOT call `lookupBindingsAt`: `findExportedDef` answers "what
|
|
165
|
+
* did the target file declare locally at module scope?", while
|
|
166
|
+
* `bindingAugmentations` models importer-side visibility created by
|
|
167
|
+
* post-finalize hooks. Callers that need importer-visible exports use
|
|
168
|
+
* `findExportedDefByName`, which is dual-channel aware.
|
|
126
169
|
*/
|
|
127
170
|
export declare function findExportedDef(targetFile: string, memberName: string, index: WorkspaceResolutionIndex): SymbolDefinition | undefined;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Scope-chain lookup primitives shared across language providers.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Five functions:
|
|
5
5
|
* - `findReceiverTypeBinding` — walk scope.typeBindings up the chain
|
|
6
6
|
* for a receiver name.
|
|
7
|
-
* - `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* - `lookupBindingsAt` — read finalized + augmented binding refs at
|
|
8
|
+
* one scope, deduped by `def.nodeId`. The dual-source-aware
|
|
9
|
+
* primitive every other binding lookup composes with.
|
|
10
|
+
* - `findClassBindingInScope` — walk scope.bindings + the indexes via
|
|
11
|
+
* `lookupBindingsAt` for a class-kind binding.
|
|
11
12
|
* - `findOwnedMember` — find a method/field owned by a class def
|
|
12
13
|
* across all parsed files by (ownerId, simpleName).
|
|
13
14
|
* - `findExportedDef` — find a file-level exported def (top-of-module
|
|
@@ -18,6 +19,80 @@
|
|
|
18
19
|
* "resolve member on owner with MRO" pattern. All four are reusable
|
|
19
20
|
* as-is for TypeScript, Java, Kotlin, Ruby, etc.
|
|
20
21
|
*/
|
|
22
|
+
const EMPTY_BINDINGS = Object.freeze([]);
|
|
23
|
+
/**
|
|
24
|
+
* Look up binding refs at `scopeId` for `name`, consulting both the
|
|
25
|
+
* finalize-owned `bindings` channel and the post-finalize
|
|
26
|
+
* `bindingAugmentations` channel (see invariant I8 in
|
|
27
|
+
* `contract/scope-resolver.ts`). Finalized refs come first; augmented
|
|
28
|
+
* refs append, deduped by `def.nodeId` so a sibling that's also
|
|
29
|
+
* explicitly imported doesn't double-emit.
|
|
30
|
+
*
|
|
31
|
+
* Returns a shared frozen empty array when neither channel has the
|
|
32
|
+
* name — callers can compare against `=== EMPTY_BINDINGS` if they
|
|
33
|
+
* want a fast-path miss check. The bucket arrays are returned by
|
|
34
|
+
* reference when only one channel populates them; the merged path
|
|
35
|
+
* allocates a fresh array.
|
|
36
|
+
*
|
|
37
|
+
* Walker primitives (`findClassBindingInScope`,
|
|
38
|
+
* `findCallableBindingInScope`, `findExportedDefByName`) and
|
|
39
|
+
* post-finalize passes that read finalized bindings (e.g.
|
|
40
|
+
* `propagateImportedReturnTypes`, `namespace-targets`) MUST go
|
|
41
|
+
* through this helper instead of `scopes.bindings.get(...)` directly,
|
|
42
|
+
* so the augmentation channel is always visible.
|
|
43
|
+
*/
|
|
44
|
+
export function lookupBindingsAt(scopeId, name, scopes) {
|
|
45
|
+
const finalized = scopes.bindings.get(scopeId)?.get(name);
|
|
46
|
+
const augmented = scopes.bindingAugmentations.get(scopeId)?.get(name);
|
|
47
|
+
const fLen = finalized?.length ?? 0;
|
|
48
|
+
const aLen = augmented?.length ?? 0;
|
|
49
|
+
if (fLen === 0 && aLen === 0)
|
|
50
|
+
return EMPTY_BINDINGS;
|
|
51
|
+
if (aLen === 0)
|
|
52
|
+
return finalized;
|
|
53
|
+
if (fLen === 0)
|
|
54
|
+
return augmented;
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const r of finalized) {
|
|
58
|
+
seen.add(r.def.nodeId);
|
|
59
|
+
out.push(r);
|
|
60
|
+
}
|
|
61
|
+
for (const r of augmented) {
|
|
62
|
+
if (seen.has(r.def.nodeId))
|
|
63
|
+
continue;
|
|
64
|
+
out.push(r);
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
const EMPTY_NAMES = Object.freeze([]);
|
|
69
|
+
/**
|
|
70
|
+
* Return the union of bound names at `scopeId` across both the
|
|
71
|
+
* finalized and augmented channels. Companion to `lookupBindingsAt`
|
|
72
|
+
* for callers that need to iterate every name at a scope (e.g.
|
|
73
|
+
* `propagateImportedReturnTypes`). Order is not guaranteed; callers
|
|
74
|
+
* that need stable iteration should sort externally.
|
|
75
|
+
*
|
|
76
|
+
* Fast paths (zero allocation) when at most one channel is populated:
|
|
77
|
+
* returns the underlying `Map.keys()` iterator directly. Only when both
|
|
78
|
+
* channels carry names do we materialize a `Set` for deduplication.
|
|
79
|
+
*/
|
|
80
|
+
export function namesAtScope(scopeId, scopes) {
|
|
81
|
+
const finalized = scopes.bindings.get(scopeId);
|
|
82
|
+
const augmented = scopes.bindingAugmentations.get(scopeId);
|
|
83
|
+
const fSize = finalized?.size ?? 0;
|
|
84
|
+
const aSize = augmented?.size ?? 0;
|
|
85
|
+
if (fSize === 0 && aSize === 0)
|
|
86
|
+
return EMPTY_NAMES;
|
|
87
|
+
if (aSize === 0)
|
|
88
|
+
return finalized.keys();
|
|
89
|
+
if (fSize === 0)
|
|
90
|
+
return augmented.keys();
|
|
91
|
+
const out = new Set(finalized.keys());
|
|
92
|
+
for (const name of augmented.keys())
|
|
93
|
+
out.add(name);
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
21
96
|
/**
|
|
22
97
|
* True when a def's `type` names a class-like declaration — every kind
|
|
23
98
|
* that collapses to `@scope.class` in the scope-extractor query contract.
|
|
@@ -67,8 +142,10 @@ export function findReceiverTypeBinding(startScope, receiverName, scopes) {
|
|
|
67
142
|
* Walks the scope chain upward and consults TWO sources at each step:
|
|
68
143
|
* 1. `scope.bindings` — populated during scope-extraction Pass 2 with
|
|
69
144
|
* local declarations (`origin: 'local'`).
|
|
70
|
-
* 2.
|
|
71
|
-
*
|
|
145
|
+
* 2. The cross-file finalized + augmented bindings, via
|
|
146
|
+
* `lookupBindingsAt` (per I8: finalized = canonical immutable
|
|
147
|
+
* output; augmented = post-finalize hooks like
|
|
148
|
+
* `populateNamespaceSiblings`).
|
|
72
149
|
*
|
|
73
150
|
* Without (2) we'd miss every cross-file class-receiver call.
|
|
74
151
|
*/
|
|
@@ -89,13 +166,10 @@ export function findClassBindingInScope(startScope, receiverName, scopes) {
|
|
|
89
166
|
return b.def;
|
|
90
167
|
}
|
|
91
168
|
}
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (isClassLike(b.def.type))
|
|
97
|
-
return b.def;
|
|
98
|
-
}
|
|
169
|
+
const importedBindings = lookupBindingsAt(currentId, receiverName, scopes);
|
|
170
|
+
for (const b of importedBindings) {
|
|
171
|
+
if (isClassLike(b.def.type))
|
|
172
|
+
return b.def;
|
|
99
173
|
}
|
|
100
174
|
currentId = scope.parent;
|
|
101
175
|
}
|
|
@@ -104,8 +178,9 @@ export function findClassBindingInScope(startScope, receiverName, scopes) {
|
|
|
104
178
|
/**
|
|
105
179
|
* Look up a callable (Function/Method/Constructor) by name in the
|
|
106
180
|
* given scope's chain. Uses the dual-source pattern (scope.bindings +
|
|
107
|
-
*
|
|
108
|
-
* free calls to imported functions
|
|
181
|
+
* `lookupBindingsAt` for finalized + augmented) so cross-file
|
|
182
|
+
* imports are visible — without it free calls to imported functions
|
|
183
|
+
* never resolve via the post-pass.
|
|
109
184
|
*
|
|
110
185
|
* Mirrors `findClassBindingInScope` exactly; only the accepted
|
|
111
186
|
* def-type predicate differs.
|
|
@@ -128,13 +203,10 @@ export function findCallableBindingInScope(startScope, callableName, scopes) {
|
|
|
128
203
|
}
|
|
129
204
|
}
|
|
130
205
|
}
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (b.def.type === 'Function' || b.def.type === 'Method' || b.def.type === 'Constructor') {
|
|
136
|
-
return b.def;
|
|
137
|
-
}
|
|
206
|
+
const importedBindings = lookupBindingsAt(currentId, callableName, scopes);
|
|
207
|
+
for (const b of importedBindings) {
|
|
208
|
+
if (b.def.type === 'Function' || b.def.type === 'Method' || b.def.type === 'Constructor') {
|
|
209
|
+
return b.def;
|
|
138
210
|
}
|
|
139
211
|
}
|
|
140
212
|
currentId = scope.parent;
|
|
@@ -272,12 +344,10 @@ export function findExportedDefByName(name, inScope, scopes, index) {
|
|
|
272
344
|
return b.def;
|
|
273
345
|
}
|
|
274
346
|
}
|
|
275
|
-
const finalized =
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return b.def;
|
|
280
|
-
}
|
|
347
|
+
const finalized = lookupBindingsAt(currentId, name, scopes);
|
|
348
|
+
for (const b of finalized) {
|
|
349
|
+
if (b.def.type === 'Function' || b.def.type === 'Method')
|
|
350
|
+
return b.def;
|
|
281
351
|
}
|
|
282
352
|
currentId = scope.parent;
|
|
283
353
|
}
|
|
@@ -332,7 +402,12 @@ export function findOwnedMember(ownerDefId, memberName, model) {
|
|
|
332
402
|
* excluded.
|
|
333
403
|
*
|
|
334
404
|
* Reads from `WorkspaceResolutionIndex.moduleScopeByFile` (scope-tied
|
|
335
|
-
* lookup that doesn't live on `SemanticModel`).
|
|
405
|
+
* lookup that doesn't live on `SemanticModel`). This intentionally
|
|
406
|
+
* does NOT call `lookupBindingsAt`: `findExportedDef` answers "what
|
|
407
|
+
* did the target file declare locally at module scope?", while
|
|
408
|
+
* `bindingAugmentations` models importer-side visibility created by
|
|
409
|
+
* post-finalize hooks. Callers that need importer-visible exports use
|
|
410
|
+
* `findExportedDefByName`, which is dual-channel aware.
|
|
336
411
|
*/
|
|
337
412
|
export function findExportedDef(targetFile, memberName, index) {
|
|
338
413
|
const moduleScope = index.moduleScopeByFile.get(targetFile);
|
|
@@ -21,6 +21,9 @@ function unwrapSwiftExpression(node) {
|
|
|
21
21
|
}
|
|
22
22
|
return node;
|
|
23
23
|
}
|
|
24
|
+
function swiftNavigationSuffixName(node) {
|
|
25
|
+
return node?.type === 'navigation_suffix' ? node.lastNamedChild?.text : node?.text;
|
|
26
|
+
}
|
|
24
27
|
/** Swift: let x: Foo = ... */
|
|
25
28
|
const extractDeclaration = (node, env) => {
|
|
26
29
|
// Swift property_declaration has pattern and type_annotation
|
|
@@ -99,8 +102,8 @@ const extractInitializer = (node, env, classNames) => {
|
|
|
99
102
|
// Explicit init: User.init(name: "alice") — navigation_expression with .init suffix
|
|
100
103
|
if (callee.type === 'navigation_expression') {
|
|
101
104
|
const receiver = callee.firstNamedChild;
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
if (receiver?.type === 'simple_identifier' &&
|
|
106
|
+
swiftNavigationSuffixName(callee.lastNamedChild) === 'init') {
|
|
104
107
|
const calleeName = receiver.text;
|
|
105
108
|
if (calleeName && classNames.has(calleeName)) {
|
|
106
109
|
env.set(varName, calleeName);
|
|
@@ -114,7 +117,7 @@ const scanConstructorBinding = (node) => {
|
|
|
114
117
|
return undefined;
|
|
115
118
|
if (hasTypeAnnotation(node))
|
|
116
119
|
return undefined;
|
|
117
|
-
const pattern = node.childForFieldName('pattern');
|
|
120
|
+
const pattern = node.childForFieldName('pattern') ?? findChild(node, 'pattern');
|
|
118
121
|
if (!pattern)
|
|
119
122
|
return undefined;
|
|
120
123
|
const varName = pattern.text;
|
|
@@ -147,7 +150,7 @@ const scanConstructorBinding = (node) => {
|
|
|
147
150
|
if (callee.type === 'navigation_expression') {
|
|
148
151
|
const receiver = callee.firstNamedChild;
|
|
149
152
|
const suffix = callee.lastNamedChild;
|
|
150
|
-
if (receiver?.type === 'simple_identifier' && suffix
|
|
153
|
+
if (receiver?.type === 'simple_identifier' && swiftNavigationSuffixName(suffix) === 'init') {
|
|
151
154
|
return { varName, calleeName: receiver.text };
|
|
152
155
|
}
|
|
153
156
|
// General qualified call: service.getUser() → extract method name.
|
|
@@ -31,6 +31,8 @@ export declare const FUNCTION_NODE_TYPES: Set<string>;
|
|
|
31
31
|
*/
|
|
32
32
|
export declare const CLASS_CONTAINER_TYPES: Set<string>;
|
|
33
33
|
export declare const CONTAINER_TYPE_TO_LABEL: Record<string, string>;
|
|
34
|
+
/** Return the first matching ancestor unless a boundary ancestor is reached first. */
|
|
35
|
+
export declare function findAncestorBeforeBoundary(node: SyntaxNode, targetTypes: ReadonlySet<string>, boundaryTypes: ReadonlySet<string>): SyntaxNode | null;
|
|
34
36
|
/**
|
|
35
37
|
* Determine the graph node label from a tree-sitter capture map.
|
|
36
38
|
* Handles language-specific reclassification via the provider's labelOverride hook
|
|
@@ -153,6 +153,18 @@ export const CONTAINER_TYPE_TO_LABEL = {
|
|
|
153
153
|
object_declaration: 'Class',
|
|
154
154
|
companion_object: 'Class',
|
|
155
155
|
};
|
|
156
|
+
/** Return the first matching ancestor unless a boundary ancestor is reached first. */
|
|
157
|
+
export function findAncestorBeforeBoundary(node, targetTypes, boundaryTypes) {
|
|
158
|
+
let current = node.parent;
|
|
159
|
+
while (current !== null) {
|
|
160
|
+
if (boundaryTypes.has(current.type))
|
|
161
|
+
return null;
|
|
162
|
+
if (targetTypes.has(current.type))
|
|
163
|
+
return current;
|
|
164
|
+
current = current.parent;
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
156
168
|
/**
|
|
157
169
|
* Determine the graph node label from a tree-sitter capture map.
|
|
158
170
|
* Handles language-specific reclassification via the provider's labelOverride hook
|
|
@@ -8,3 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/** Whether we're running in development mode (enables verbose console logging). */
|
|
10
10
|
export declare const isDev: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Whether scope-resolution dev validators (e.g. `validateBindingsImmutability`)
|
|
13
|
+
* should run AND emit warnings. Off by default in CLI runs to avoid silent
|
|
14
|
+
* O(n) scans on large repos; on in `NODE_ENV=development` or when explicitly
|
|
15
|
+
* opted-in via `VALIDATE_SEMANTIC_MODEL=1`. `VALIDATE_SEMANTIC_MODEL=0` is the
|
|
16
|
+
* explicit off switch and wins over both.
|
|
17
|
+
*
|
|
18
|
+
* Read every call (not memoized) so test setups using `vi.stubEnv` work.
|
|
19
|
+
*/
|
|
20
|
+
export declare const isSemanticModelValidatorEnabled: () => boolean;
|
|
@@ -8,3 +8,17 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/** Whether we're running in development mode (enables verbose console logging). */
|
|
10
10
|
export const isDev = process.env.NODE_ENV === 'development';
|
|
11
|
+
/**
|
|
12
|
+
* Whether scope-resolution dev validators (e.g. `validateBindingsImmutability`)
|
|
13
|
+
* should run AND emit warnings. Off by default in CLI runs to avoid silent
|
|
14
|
+
* O(n) scans on large repos; on in `NODE_ENV=development` or when explicitly
|
|
15
|
+
* opted-in via `VALIDATE_SEMANTIC_MODEL=1`. `VALIDATE_SEMANTIC_MODEL=0` is the
|
|
16
|
+
* explicit off switch and wins over both.
|
|
17
|
+
*
|
|
18
|
+
* Read every call (not memoized) so test setups using `vi.stubEnv` work.
|
|
19
|
+
*/
|
|
20
|
+
export const isSemanticModelValidatorEnabled = () => {
|
|
21
|
+
if (process.env.VALIDATE_SEMANTIC_MODEL === '0')
|
|
22
|
+
return false;
|
|
23
|
+
return process.env.NODE_ENV === 'development' || process.env.VALIDATE_SEMANTIC_MODEL === '1';
|
|
24
|
+
};
|