gitnexus 1.6.3-rc.9 → 1.6.3
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 +21 -5
- package/dist/_shared/graph/types.d.ts +16 -0
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +4 -2
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +2 -0
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/def-index.js +2 -2
- package/dist/_shared/scope-resolution/def-index.js.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +8 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.js +2 -2
- package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.d.ts +8 -0
- package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.js +10 -2
- package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/parsed-file.js +54 -0
- package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
- package/dist/_shared/scope-resolution/position-index.d.ts +12 -0
- package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/position-index.js +2 -2
- package/dist/_shared/scope-resolution/position-index.js.map +1 -1
- package/dist/_shared/scope-resolution/qualified-name-index.js +2 -2
- package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -1
- package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
- package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/reference-site.js +24 -0
- package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.js +5 -0
- package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/lookup-core.js +21 -5
- package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js +3 -2
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
- package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +11 -0
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/cli/ai-context.js +35 -4
- package/dist/cli/analyze.d.ts +27 -0
- package/dist/cli/analyze.js +31 -1
- package/dist/cli/clean.js +19 -1
- package/dist/cli/group.js +73 -0
- package/dist/cli/index-repo.js +8 -1
- package/dist/cli/index.js +26 -1
- package/dist/cli/list.js +11 -1
- package/dist/cli/remove.d.ts +30 -0
- package/dist/cli/remove.js +99 -0
- package/dist/cli/setup.js +185 -57
- package/dist/cli/tool.d.ts +5 -0
- package/dist/cli/tool.js +42 -0
- package/dist/config/ignore-service.d.ts +9 -0
- package/dist/config/ignore-service.js +80 -13
- package/dist/core/embedding-mode.d.ts +30 -0
- package/dist/core/embedding-mode.js +30 -0
- package/dist/core/embeddings/ast-utils.js +22 -22
- package/dist/core/embeddings/chunker.js +30 -25
- package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
- package/dist/core/embeddings/embedding-pipeline.js +15 -6
- package/dist/core/embeddings/text-generator.d.ts +1 -1
- package/dist/core/embeddings/text-generator.js +33 -24
- package/dist/core/embeddings/types.d.ts +43 -1
- package/dist/core/embeddings/types.js +101 -29
- package/dist/core/git-staleness.d.ts +18 -0
- package/dist/core/git-staleness.js +108 -0
- package/dist/core/graph/graph.js +115 -20
- package/dist/core/graph/types.d.ts +12 -1
- package/dist/core/group/config-parser.d.ts +4 -0
- package/dist/core/group/config-parser.js +18 -1
- package/dist/core/group/cross-impact.d.ts +41 -0
- package/dist/core/group/cross-impact.js +441 -0
- package/dist/core/group/extractors/http-patterns/php.js +126 -18
- package/dist/core/group/group-path-utils.d.ts +17 -0
- package/dist/core/group/group-path-utils.js +40 -0
- package/dist/core/group/resolve-at-member.d.ts +10 -0
- package/dist/core/group/resolve-at-member.js +31 -0
- package/dist/core/group/service.d.ts +9 -0
- package/dist/core/group/service.js +259 -25
- package/dist/core/group/types.d.ts +30 -0
- package/dist/core/ingestion/ast-cache.d.ts +16 -1
- package/dist/core/ingestion/ast-cache.js +14 -2
- package/dist/core/ingestion/call-processor.js +9 -0
- package/dist/core/ingestion/emit-references.d.ts +88 -0
- package/dist/core/ingestion/emit-references.js +229 -0
- package/dist/core/ingestion/filesystem-walker.js +6 -4
- package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
- package/dist/core/ingestion/finalize-orchestrator.js +139 -0
- package/dist/core/ingestion/framework-detection.js +6 -2
- package/dist/core/ingestion/import-processor.js +4 -0
- package/dist/core/ingestion/import-resolvers/python.js +9 -6
- package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
- package/dist/core/ingestion/import-target-adapter.js +95 -0
- package/dist/core/ingestion/language-provider.d.ts +36 -33
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
- package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
- package/dist/core/ingestion/languages/csharp/arity.js +37 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/captures.js +249 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
- package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
- package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
- package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
- package/dist/core/ingestion/languages/csharp/index.js +82 -0
- package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
- package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
- package/dist/core/ingestion/languages/csharp/query.js +515 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
- package/dist/core/ingestion/languages/csharp.js +14 -0
- package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
- package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
- package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
- package/dist/core/ingestion/languages/python/arity.js +38 -0
- package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/python/captures.js +106 -0
- package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
- package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
- package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
- package/dist/core/ingestion/languages/python/import-target.js +99 -0
- package/dist/core/ingestion/languages/python/index.d.ts +80 -0
- package/dist/core/ingestion/languages/python/index.js +80 -0
- package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/python/interpret.js +191 -0
- package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
- package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
- package/dist/core/ingestion/languages/python/query.d.ts +9 -0
- package/dist/core/ingestion/languages/python/query.js +267 -0
- package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
- package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
- package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
- package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
- package/dist/core/ingestion/languages/python.js +14 -0
- package/dist/core/ingestion/model/method-registry.d.ts +9 -0
- package/dist/core/ingestion/model/method-registry.js +4 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
- package/dist/core/ingestion/model/semantic-model.js +55 -0
- package/dist/core/ingestion/mro-processor.js +38 -22
- package/dist/core/ingestion/parsing-processor.d.ts +18 -1
- package/dist/core/ingestion/parsing-processor.js +45 -11
- package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/index.js +1 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
- package/dist/core/ingestion/pipeline.js +2 -1
- package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
- package/dist/core/ingestion/registry-primary-flag.js +111 -0
- package/dist/core/ingestion/resolve-references.d.ts +63 -0
- package/dist/core/ingestion/resolve-references.js +175 -0
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
- package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
- package/dist/core/ingestion/scope-extractor.d.ts +86 -0
- package/dist/core/ingestion/scope-extractor.js +758 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
- package/dist/core/ingestion/shadow-harness.d.ts +113 -0
- package/dist/core/ingestion/shadow-harness.js +148 -0
- package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
- package/dist/core/ingestion/utils/ast-helpers.js +70 -0
- package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
- package/dist/core/ingestion/utils/max-file-size.js +52 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
- package/dist/core/ingestion/workers/parse-worker.js +57 -21
- package/dist/core/lbug/lbug-adapter.d.ts +22 -2
- package/dist/core/lbug/lbug-adapter.js +58 -14
- package/dist/core/lbug/pool-adapter.d.ts +17 -0
- package/dist/core/lbug/pool-adapter.js +24 -14
- package/dist/core/run-analyze.d.ts +32 -0
- package/dist/core/run-analyze.js +74 -19
- package/dist/core/search/bm25-index.d.ts +18 -0
- package/dist/core/search/bm25-index.js +125 -12
- package/dist/core/tree-sitter/parser-loader.js +6 -1
- package/dist/mcp/local/local-backend.d.ts +67 -3
- package/dist/mcp/local/local-backend.js +296 -34
- package/dist/mcp/resources.d.ts +31 -0
- package/dist/mcp/resources.js +100 -17
- package/dist/mcp/tools.d.ts +4 -1
- package/dist/mcp/tools.js +75 -54
- package/dist/server/api.js +6 -2
- package/dist/storage/git.d.ts +49 -0
- package/dist/storage/git.js +111 -0
- package/dist/storage/repo-manager.d.ts +246 -1
- package/dist/storage/repo-manager.js +391 -9
- package/package.json +7 -6
- package/scripts/bench-scope-resolution.ts +134 -0
- package/scripts/ci-list-migrated-languages.ts +24 -0
- package/skills/gitnexus-cli.md +1 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a per-file `localName → targetFilePath` map over the file's
|
|
3
|
+
* module-scope namespace-kind import edges.
|
|
4
|
+
*
|
|
5
|
+
* Namespace imports (`import X`, `import X as Y`) bind a name that can
|
|
6
|
+
* appear as a receiver in member calls (`X.foo()`, `Y.foo()`). Named
|
|
7
|
+
* imports (`from X import foo`) bind `foo` directly and are a different
|
|
8
|
+
* resolution path.
|
|
9
|
+
*
|
|
10
|
+
* Why not consult `scope.bindings` directly? For namespace imports
|
|
11
|
+
* where the target module has no self-named def,
|
|
12
|
+
* `finalize-algorithm.ts:540` skips binding creation entirely, so
|
|
13
|
+
* `scope.bindings.get('X')` returns undefined. We iterate
|
|
14
|
+
* `indexes.imports` to recover those targets.
|
|
15
|
+
*
|
|
16
|
+
* Next-consumer contract: any language with namespace-style imports
|
|
17
|
+
* (TypeScript `import * as X`, Java static import, Ruby `require`)
|
|
18
|
+
* uses this directly. `ParsedImport.kind === 'namespace'` is the
|
|
19
|
+
* cross-language hook.
|
|
20
|
+
*
|
|
21
|
+
* Scope-chain concern (verified 2026-04-21): `pythonImportOwningScope`
|
|
22
|
+
* documents that function-local and class-body imports bind to the
|
|
23
|
+
* inner scope, which would make a module-only read incomplete. In
|
|
24
|
+
* practice `finalize-algorithm` places ALL of a file's ImportEdges
|
|
25
|
+
* onto `indexes.imports[moduleScope]` regardless of where the
|
|
26
|
+
* `import` statement appears — the integration fixtures
|
|
27
|
+
* `python-function-local-namespace-import` and
|
|
28
|
+
* `python-class-body-namespace-import` both emit correct CALLS edges
|
|
29
|
+
* with reason "namespace-receiver", demonstrating that the module-
|
|
30
|
+
* scope read is sufficient today. If finalize routing ever changes to
|
|
31
|
+
* honor the hook's per-scope contract, this function must walk the
|
|
32
|
+
* reference-site scope chain (mirror `findExportedDefByName`).
|
|
33
|
+
*/
|
|
34
|
+
export function collectNamespaceTargets(parsed, scopes) {
|
|
35
|
+
const out = new Map();
|
|
36
|
+
const moduleEdges = scopes.imports.get(parsed.moduleScope);
|
|
37
|
+
if (moduleEdges === undefined)
|
|
38
|
+
return out;
|
|
39
|
+
const namespaceLocals = new Set();
|
|
40
|
+
for (const imp of parsed.parsedImports) {
|
|
41
|
+
if (imp.kind === 'namespace')
|
|
42
|
+
namespaceLocals.add(imp.localName);
|
|
43
|
+
}
|
|
44
|
+
for (const edge of moduleEdges) {
|
|
45
|
+
if (edge.targetFile === null)
|
|
46
|
+
continue;
|
|
47
|
+
if (!namespaceLocals.has(edge.localName))
|
|
48
|
+
continue;
|
|
49
|
+
out.set(edge.localName, edge.targetFile);
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope-chain lookup primitives shared across language providers.
|
|
3
|
+
*
|
|
4
|
+
* Four functions:
|
|
5
|
+
* - `findReceiverTypeBinding` — walk scope.typeBindings up the chain
|
|
6
|
+
* for a receiver name.
|
|
7
|
+
* - `findClassBindingInScope` — walk scope.bindings + indexes.bindings
|
|
8
|
+
* (pre-finalize + post-finalize) for a class-kind binding. Dual-
|
|
9
|
+
* source is required because the cross-file finalize pass produces
|
|
10
|
+
* a separate bindings map that is not merged back into scope.bindings.
|
|
11
|
+
* - `findOwnedMember` — find a method/field owned by a class def
|
|
12
|
+
* across all parsed files by (ownerId, simpleName).
|
|
13
|
+
* - `findExportedDef` — find a file-level exported def (top-of-module
|
|
14
|
+
* class / function) by simpleName.
|
|
15
|
+
*
|
|
16
|
+
* Next-consumer contract: every OO or module-capable language hits the
|
|
17
|
+
* same pre-finalize / post-finalize binding split and the same
|
|
18
|
+
* "resolve member on owner with MRO" pattern. All four are reusable
|
|
19
|
+
* as-is for TypeScript, Java, Kotlin, Ruby, etc.
|
|
20
|
+
*/
|
|
21
|
+
import type { ParsedFile, ScopeId, SymbolDefinition, TypeRef } from '../../../../_shared/index.js';
|
|
22
|
+
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
|
|
23
|
+
import type { SemanticModel } from '../../model/semantic-model.js';
|
|
24
|
+
import type { WorkspaceResolutionIndex } from '../workspace-index.js';
|
|
25
|
+
/**
|
|
26
|
+
* True when a def's `type` names a class-like declaration — every kind
|
|
27
|
+
* that collapses to `@scope.class` in the scope-extractor query contract.
|
|
28
|
+
*
|
|
29
|
+
* Semantics widened historically from `'Class' | 'Interface'` to cover
|
|
30
|
+
* C#-shape languages (struct, record, enum, trait). Languages that emit
|
|
31
|
+
* only `'Class'` are unaffected — the extra kinds never appear in their
|
|
32
|
+
* parsed output.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isClassLike(t: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Walk the scope chain from `startScope` looking for a typeBinding
|
|
37
|
+
* named `receiverName`. Returns the TypeRef or undefined if no binding
|
|
38
|
+
* exists in the chain.
|
|
39
|
+
*/
|
|
40
|
+
export declare function findReceiverTypeBinding(startScope: ScopeId, receiverName: string, scopes: ScopeResolutionIndexes): TypeRef | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Look up a class-like binding by name in the given scope's chain.
|
|
43
|
+
*
|
|
44
|
+
* "Class-like" covers `Class | Interface | Struct | Record | Enum |
|
|
45
|
+
* Trait` via the shared `isClassLike` predicate — every kind that
|
|
46
|
+
* collapses to `@scope.class` in the scope-extractor query contract.
|
|
47
|
+
*
|
|
48
|
+
* Walks the scope chain upward and consults TWO sources at each step:
|
|
49
|
+
* 1. `scope.bindings` — populated during scope-extraction Pass 2 with
|
|
50
|
+
* local declarations (`origin: 'local'`).
|
|
51
|
+
* 2. `indexes.bindings` — populated by the cross-file finalize pass
|
|
52
|
+
* with import/namespace/wildcard/reexport origins.
|
|
53
|
+
*
|
|
54
|
+
* Without (2) we'd miss every cross-file class-receiver call.
|
|
55
|
+
*/
|
|
56
|
+
export declare function findClassBindingInScope(startScope: ScopeId, receiverName: string, scopes: ScopeResolutionIndexes): SymbolDefinition | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Look up a callable (Function/Method/Constructor) by name in the
|
|
59
|
+
* given scope's chain. Uses the dual-source pattern (scope.bindings +
|
|
60
|
+
* indexes.bindings) so cross-file imports are visible — without it
|
|
61
|
+
* free calls to imported functions never resolve via the post-pass.
|
|
62
|
+
*
|
|
63
|
+
* Mirrors `findClassBindingInScope` exactly; only the accepted
|
|
64
|
+
* def-type predicate differs.
|
|
65
|
+
*/
|
|
66
|
+
export declare function findCallableBindingInScope(startScope: ScopeId, callableName: string, scopes: ScopeResolutionIndexes): SymbolDefinition | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Populate `ownerId` on every def structurally owned by a Class
|
|
69
|
+
* scope — methods (defs in Function scopes whose parent is Class)
|
|
70
|
+
* and class-body fields (defs directly in Class scopes).
|
|
71
|
+
*
|
|
72
|
+
* Generic OO ownership rule. Languages that want richer ownership
|
|
73
|
+
* (e.g. inner-class qualification) can compose with this as a base
|
|
74
|
+
* step.
|
|
75
|
+
*
|
|
76
|
+
* Mutates `parsed.localDefs` in place via type cast — `SymbolDefinition`
|
|
77
|
+
* is `readonly` for consumers but the extractor returns plain objects.
|
|
78
|
+
* Defs are shared by reference between `localDefs` and `Scope.ownedDefs`,
|
|
79
|
+
* so this single mutation is visible from both sides.
|
|
80
|
+
*/
|
|
81
|
+
export declare function populateClassOwnedMembers(parsed: ParsedFile): void;
|
|
82
|
+
/**
|
|
83
|
+
* Walk a scope chain upward looking for the innermost enclosing
|
|
84
|
+
* Class scope and return that class's def. Used by per-language
|
|
85
|
+
* `super` receiver branches to discover the dispatch base.
|
|
86
|
+
*/
|
|
87
|
+
export declare function findEnclosingClassDef(startScope: ScopeId, scopes: ScopeResolutionIndexes): SymbolDefinition | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Find a free-function def by simple name across all parsed files,
|
|
90
|
+
* preferring scope-chain-visible bindings (import + finalized scope
|
|
91
|
+
* bindings) before falling back to a workspace-wide simple-name scan.
|
|
92
|
+
*
|
|
93
|
+
* The fallback scan is intentionally loose so per-language compound
|
|
94
|
+
* resolvers can find a callable target even when the binding chain
|
|
95
|
+
* doesn't surface it (e.g. cross-package re-exports the finalize
|
|
96
|
+
* pass missed). Strictly-typed languages may want to disable the
|
|
97
|
+
* fallback by simply not calling this helper from their compound
|
|
98
|
+
* resolver.
|
|
99
|
+
*/
|
|
100
|
+
export declare function findExportedDefByName(name: string, inScope: ScopeId, scopes: ScopeResolutionIndexes, index: WorkspaceResolutionIndex): SymbolDefinition | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Find a member of a class by simple name — delegates to
|
|
103
|
+
* `SemanticModel.methods` (methods / functions / constructors) with a
|
|
104
|
+
* fallback to `SemanticModel.fields` (properties / fields /
|
|
105
|
+
* variables). After `runScopeResolution`'s reconciliation pass
|
|
106
|
+
* populates both registries from `parsed.localDefs[i].ownerId`
|
|
107
|
+
* (post-`populateOwners`), this is the single authoritative view of
|
|
108
|
+
* class membership — no parallel scope-resolution index needed.
|
|
109
|
+
*
|
|
110
|
+
* Returns the first-seen overload for methods without arity or
|
|
111
|
+
* return-type narrowing. Callers that need arity-aware dispatch use
|
|
112
|
+
* `lookupMethodByOwner(owner, name, argCount)` directly.
|
|
113
|
+
*/
|
|
114
|
+
export declare function findOwnedMember(ownerDefId: string, memberName: string, model: SemanticModel): SymbolDefinition | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* Find a file-level def (top-of-module class / function / variable)
|
|
117
|
+
* by simple name — consults the target file's Module scope's
|
|
118
|
+
* finalized bindings. Only defs bound at module-scope with
|
|
119
|
+
* `origin === 'local'` qualify, matching the historical
|
|
120
|
+
* "module-export-visible" semantics. Class methods and class-body
|
|
121
|
+
* fields bind at their containing class scope and are naturally
|
|
122
|
+
* excluded.
|
|
123
|
+
*
|
|
124
|
+
* Reads from `WorkspaceResolutionIndex.moduleScopeByFile` (scope-tied
|
|
125
|
+
* lookup that doesn't live on `SemanticModel`).
|
|
126
|
+
*/
|
|
127
|
+
export declare function findExportedDef(targetFile: string, memberName: string, index: WorkspaceResolutionIndex): SymbolDefinition | undefined;
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope-chain lookup primitives shared across language providers.
|
|
3
|
+
*
|
|
4
|
+
* Four functions:
|
|
5
|
+
* - `findReceiverTypeBinding` — walk scope.typeBindings up the chain
|
|
6
|
+
* for a receiver name.
|
|
7
|
+
* - `findClassBindingInScope` — walk scope.bindings + indexes.bindings
|
|
8
|
+
* (pre-finalize + post-finalize) for a class-kind binding. Dual-
|
|
9
|
+
* source is required because the cross-file finalize pass produces
|
|
10
|
+
* a separate bindings map that is not merged back into scope.bindings.
|
|
11
|
+
* - `findOwnedMember` — find a method/field owned by a class def
|
|
12
|
+
* across all parsed files by (ownerId, simpleName).
|
|
13
|
+
* - `findExportedDef` — find a file-level exported def (top-of-module
|
|
14
|
+
* class / function) by simpleName.
|
|
15
|
+
*
|
|
16
|
+
* Next-consumer contract: every OO or module-capable language hits the
|
|
17
|
+
* same pre-finalize / post-finalize binding split and the same
|
|
18
|
+
* "resolve member on owner with MRO" pattern. All four are reusable
|
|
19
|
+
* as-is for TypeScript, Java, Kotlin, Ruby, etc.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* True when a def's `type` names a class-like declaration — every kind
|
|
23
|
+
* that collapses to `@scope.class` in the scope-extractor query contract.
|
|
24
|
+
*
|
|
25
|
+
* Semantics widened historically from `'Class' | 'Interface'` to cover
|
|
26
|
+
* C#-shape languages (struct, record, enum, trait). Languages that emit
|
|
27
|
+
* only `'Class'` are unaffected — the extra kinds never appear in their
|
|
28
|
+
* parsed output.
|
|
29
|
+
*/
|
|
30
|
+
export function isClassLike(t) {
|
|
31
|
+
return (t === 'Class' ||
|
|
32
|
+
t === 'Interface' ||
|
|
33
|
+
t === 'Struct' ||
|
|
34
|
+
t === 'Record' ||
|
|
35
|
+
t === 'Enum' ||
|
|
36
|
+
t === 'Trait');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Walk the scope chain from `startScope` looking for a typeBinding
|
|
40
|
+
* named `receiverName`. Returns the TypeRef or undefined if no binding
|
|
41
|
+
* exists in the chain.
|
|
42
|
+
*/
|
|
43
|
+
export function findReceiverTypeBinding(startScope, receiverName, scopes) {
|
|
44
|
+
let currentId = startScope;
|
|
45
|
+
const visited = new Set();
|
|
46
|
+
while (currentId !== null) {
|
|
47
|
+
if (visited.has(currentId))
|
|
48
|
+
return undefined;
|
|
49
|
+
visited.add(currentId);
|
|
50
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
51
|
+
if (scope === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
const typeRef = scope.typeBindings.get(receiverName);
|
|
54
|
+
if (typeRef !== undefined)
|
|
55
|
+
return typeRef;
|
|
56
|
+
currentId = scope.parent;
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Look up a class-like binding by name in the given scope's chain.
|
|
62
|
+
*
|
|
63
|
+
* "Class-like" covers `Class | Interface | Struct | Record | Enum |
|
|
64
|
+
* Trait` via the shared `isClassLike` predicate — every kind that
|
|
65
|
+
* collapses to `@scope.class` in the scope-extractor query contract.
|
|
66
|
+
*
|
|
67
|
+
* Walks the scope chain upward and consults TWO sources at each step:
|
|
68
|
+
* 1. `scope.bindings` — populated during scope-extraction Pass 2 with
|
|
69
|
+
* local declarations (`origin: 'local'`).
|
|
70
|
+
* 2. `indexes.bindings` — populated by the cross-file finalize pass
|
|
71
|
+
* with import/namespace/wildcard/reexport origins.
|
|
72
|
+
*
|
|
73
|
+
* Without (2) we'd miss every cross-file class-receiver call.
|
|
74
|
+
*/
|
|
75
|
+
export function findClassBindingInScope(startScope, receiverName, scopes) {
|
|
76
|
+
let currentId = startScope;
|
|
77
|
+
const visited = new Set();
|
|
78
|
+
while (currentId !== null) {
|
|
79
|
+
if (visited.has(currentId))
|
|
80
|
+
return undefined;
|
|
81
|
+
visited.add(currentId);
|
|
82
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
83
|
+
if (scope === undefined)
|
|
84
|
+
return undefined;
|
|
85
|
+
const localBindings = scope.bindings.get(receiverName);
|
|
86
|
+
if (localBindings !== undefined) {
|
|
87
|
+
for (const b of localBindings) {
|
|
88
|
+
if (isClassLike(b.def.type))
|
|
89
|
+
return b.def;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const finalizedScopeBindings = scopes.bindings.get(currentId);
|
|
93
|
+
const importedBindings = finalizedScopeBindings?.get(receiverName);
|
|
94
|
+
if (importedBindings !== undefined) {
|
|
95
|
+
for (const b of importedBindings) {
|
|
96
|
+
if (isClassLike(b.def.type))
|
|
97
|
+
return b.def;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
currentId = scope.parent;
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Look up a callable (Function/Method/Constructor) by name in the
|
|
106
|
+
* given scope's chain. Uses the dual-source pattern (scope.bindings +
|
|
107
|
+
* indexes.bindings) so cross-file imports are visible — without it
|
|
108
|
+
* free calls to imported functions never resolve via the post-pass.
|
|
109
|
+
*
|
|
110
|
+
* Mirrors `findClassBindingInScope` exactly; only the accepted
|
|
111
|
+
* def-type predicate differs.
|
|
112
|
+
*/
|
|
113
|
+
export function findCallableBindingInScope(startScope, callableName, scopes) {
|
|
114
|
+
let currentId = startScope;
|
|
115
|
+
const visited = new Set();
|
|
116
|
+
while (currentId !== null) {
|
|
117
|
+
if (visited.has(currentId))
|
|
118
|
+
return undefined;
|
|
119
|
+
visited.add(currentId);
|
|
120
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
121
|
+
if (scope === undefined)
|
|
122
|
+
return undefined;
|
|
123
|
+
const localBindings = scope.bindings.get(callableName);
|
|
124
|
+
if (localBindings !== undefined) {
|
|
125
|
+
for (const b of localBindings) {
|
|
126
|
+
if (b.def.type === 'Function' || b.def.type === 'Method' || b.def.type === 'Constructor') {
|
|
127
|
+
return b.def;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const finalizedScopeBindings = scopes.bindings.get(currentId);
|
|
132
|
+
const importedBindings = finalizedScopeBindings?.get(callableName);
|
|
133
|
+
if (importedBindings !== undefined) {
|
|
134
|
+
for (const b of importedBindings) {
|
|
135
|
+
if (b.def.type === 'Function' || b.def.type === 'Method' || b.def.type === 'Constructor') {
|
|
136
|
+
return b.def;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
currentId = scope.parent;
|
|
141
|
+
}
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Populate `ownerId` on every def structurally owned by a Class
|
|
146
|
+
* scope — methods (defs in Function scopes whose parent is Class)
|
|
147
|
+
* and class-body fields (defs directly in Class scopes).
|
|
148
|
+
*
|
|
149
|
+
* Generic OO ownership rule. Languages that want richer ownership
|
|
150
|
+
* (e.g. inner-class qualification) can compose with this as a base
|
|
151
|
+
* step.
|
|
152
|
+
*
|
|
153
|
+
* Mutates `parsed.localDefs` in place via type cast — `SymbolDefinition`
|
|
154
|
+
* is `readonly` for consumers but the extractor returns plain objects.
|
|
155
|
+
* Defs are shared by reference between `localDefs` and `Scope.ownedDefs`,
|
|
156
|
+
* so this single mutation is visible from both sides.
|
|
157
|
+
*/
|
|
158
|
+
export function populateClassOwnedMembers(parsed) {
|
|
159
|
+
const scopesById = new Map();
|
|
160
|
+
for (const scope of parsed.scopes)
|
|
161
|
+
scopesById.set(scope.id, scope);
|
|
162
|
+
// Promote a def's qualifiedName from `methodName` to `ClassName.methodName`
|
|
163
|
+
// when the def sits inside a class. Without this, two classes in the
|
|
164
|
+
// same file that share a method name collide at the graph-bridge lookup
|
|
165
|
+
// (`node-lookup.ts` keys by (filePath, qualifiedName) and falls back to
|
|
166
|
+
// simple name only). Python's scope query doesn't emit
|
|
167
|
+
// `@declaration.qualified_name` for nested methods, so the finalized
|
|
168
|
+
// defs arrive here with simple names — we stamp the qualifier while
|
|
169
|
+
// we're already walking class scopes for ownerId.
|
|
170
|
+
const qualify = (def, classDef) => {
|
|
171
|
+
const q = def.qualifiedName;
|
|
172
|
+
if (q === undefined || q.length === 0)
|
|
173
|
+
return;
|
|
174
|
+
if (q.includes('.'))
|
|
175
|
+
return; // already qualified (dotted)
|
|
176
|
+
const classQ = classDef.qualifiedName;
|
|
177
|
+
if (classQ === undefined || classQ.length === 0)
|
|
178
|
+
return;
|
|
179
|
+
def.qualifiedName = `${classQ}.${q}`;
|
|
180
|
+
};
|
|
181
|
+
// Depth invariant (verified empirically against Python scope-extractor
|
|
182
|
+
// 2026-04-21): a nested `def helper` declared inside a method body
|
|
183
|
+
// lives in its OWN Function scope whose parent is the method's Function
|
|
184
|
+
// scope (not the Class scope). That means the `parentScope.kind ===
|
|
185
|
+
// 'Class'` branch below only matches DIRECT class-scope children —
|
|
186
|
+
// method defs themselves — and never stamps arbitrary nested defs with
|
|
187
|
+
// `ownerId = classDef.nodeId`. If an adversarial reviewer raises this
|
|
188
|
+
// as a potential false-attribution bug, verify first with a scope dump
|
|
189
|
+
// on `class U: def save(self): def helper(): ...` — helper.ownerId will
|
|
190
|
+
// remain undefined. The theoretical concern is real only if the
|
|
191
|
+
// extractor ever stops creating scopes for inner defs.
|
|
192
|
+
for (const scope of parsed.scopes) {
|
|
193
|
+
// Methods: function scope whose parent is a Class scope. Owner is
|
|
194
|
+
// the parent's class-like def.
|
|
195
|
+
if (scope.parent !== null) {
|
|
196
|
+
const parentScope = scopesById.get(scope.parent);
|
|
197
|
+
if (parentScope !== undefined && parentScope.kind === 'Class') {
|
|
198
|
+
const classDef = parentScope.ownedDefs.find((d) => isClassLike(d.type));
|
|
199
|
+
if (classDef !== undefined) {
|
|
200
|
+
for (const def of scope.ownedDefs) {
|
|
201
|
+
def.ownerId = classDef.nodeId;
|
|
202
|
+
qualify(def, classDef);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Class-body fields: defs directly owned by a Class scope (the
|
|
208
|
+
// class-like def itself excluded).
|
|
209
|
+
if (scope.kind === 'Class') {
|
|
210
|
+
const classDef = scope.ownedDefs.find((d) => isClassLike(d.type));
|
|
211
|
+
if (classDef !== undefined) {
|
|
212
|
+
for (const def of scope.ownedDefs) {
|
|
213
|
+
if (def === classDef)
|
|
214
|
+
continue;
|
|
215
|
+
def.ownerId = classDef.nodeId;
|
|
216
|
+
qualify(def, classDef);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Walk a scope chain upward looking for the innermost enclosing
|
|
224
|
+
* Class scope and return that class's def. Used by per-language
|
|
225
|
+
* `super` receiver branches to discover the dispatch base.
|
|
226
|
+
*/
|
|
227
|
+
export function findEnclosingClassDef(startScope, scopes) {
|
|
228
|
+
let currentId = startScope;
|
|
229
|
+
const visited = new Set();
|
|
230
|
+
while (currentId !== null) {
|
|
231
|
+
if (visited.has(currentId))
|
|
232
|
+
return undefined;
|
|
233
|
+
visited.add(currentId);
|
|
234
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
235
|
+
if (scope === undefined)
|
|
236
|
+
return undefined;
|
|
237
|
+
if (scope.kind === 'Class') {
|
|
238
|
+
const cd = scope.ownedDefs.find((d) => isClassLike(d.type));
|
|
239
|
+
if (cd !== undefined)
|
|
240
|
+
return cd;
|
|
241
|
+
}
|
|
242
|
+
currentId = scope.parent;
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Find a free-function def by simple name across all parsed files,
|
|
248
|
+
* preferring scope-chain-visible bindings (import + finalized scope
|
|
249
|
+
* bindings) before falling back to a workspace-wide simple-name scan.
|
|
250
|
+
*
|
|
251
|
+
* The fallback scan is intentionally loose so per-language compound
|
|
252
|
+
* resolvers can find a callable target even when the binding chain
|
|
253
|
+
* doesn't surface it (e.g. cross-package re-exports the finalize
|
|
254
|
+
* pass missed). Strictly-typed languages may want to disable the
|
|
255
|
+
* fallback by simply not calling this helper from their compound
|
|
256
|
+
* resolver.
|
|
257
|
+
*/
|
|
258
|
+
export function findExportedDefByName(name, inScope, scopes, index) {
|
|
259
|
+
let currentId = inScope;
|
|
260
|
+
const visited = new Set();
|
|
261
|
+
while (currentId !== null) {
|
|
262
|
+
if (visited.has(currentId))
|
|
263
|
+
break;
|
|
264
|
+
visited.add(currentId);
|
|
265
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
266
|
+
if (scope === undefined)
|
|
267
|
+
break;
|
|
268
|
+
const local = scope.bindings.get(name);
|
|
269
|
+
if (local !== undefined) {
|
|
270
|
+
for (const b of local) {
|
|
271
|
+
if (b.def.type === 'Function' || b.def.type === 'Method')
|
|
272
|
+
return b.def;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const finalized = scopes.bindings.get(currentId)?.get(name);
|
|
276
|
+
if (finalized !== undefined) {
|
|
277
|
+
for (const b of finalized) {
|
|
278
|
+
if (b.def.type === 'Function' || b.def.type === 'Method')
|
|
279
|
+
return b.def;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
currentId = scope.parent;
|
|
283
|
+
}
|
|
284
|
+
// Workspace-wide fallback: iterate every file's Module scope (via
|
|
285
|
+
// the scope-tied `moduleScopeByFile` lookup) and return the first
|
|
286
|
+
// locally-declared callable binding matching `name`. First-seen-
|
|
287
|
+
// by-file wins; bindings filtered to `origin === 'local'` and the
|
|
288
|
+
// callable types Function/Method/Constructor. We walk scopes here
|
|
289
|
+
// rather than consult `SemanticModel.symbols.lookupCallableByName`
|
|
290
|
+
// because the `origin === 'local'` module-export-visibility filter
|
|
291
|
+
// is a scope concept the raw symbol index doesn't express.
|
|
292
|
+
for (const [, moduleScope] of index.moduleScopeByFile) {
|
|
293
|
+
const refs = moduleScope.bindings.get(name);
|
|
294
|
+
if (refs === undefined)
|
|
295
|
+
continue;
|
|
296
|
+
for (const ref of refs) {
|
|
297
|
+
if (ref.origin !== 'local')
|
|
298
|
+
continue;
|
|
299
|
+
const t = ref.def.type;
|
|
300
|
+
if (t === 'Function' || t === 'Method' || t === 'Constructor')
|
|
301
|
+
return ref.def;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Find a member of a class by simple name — delegates to
|
|
308
|
+
* `SemanticModel.methods` (methods / functions / constructors) with a
|
|
309
|
+
* fallback to `SemanticModel.fields` (properties / fields /
|
|
310
|
+
* variables). After `runScopeResolution`'s reconciliation pass
|
|
311
|
+
* populates both registries from `parsed.localDefs[i].ownerId`
|
|
312
|
+
* (post-`populateOwners`), this is the single authoritative view of
|
|
313
|
+
* class membership — no parallel scope-resolution index needed.
|
|
314
|
+
*
|
|
315
|
+
* Returns the first-seen overload for methods without arity or
|
|
316
|
+
* return-type narrowing. Callers that need arity-aware dispatch use
|
|
317
|
+
* `lookupMethodByOwner(owner, name, argCount)` directly.
|
|
318
|
+
*/
|
|
319
|
+
export function findOwnedMember(ownerDefId, memberName, model) {
|
|
320
|
+
const method = model.methods.lookupAllByOwner(ownerDefId, memberName)[0];
|
|
321
|
+
if (method !== undefined)
|
|
322
|
+
return method;
|
|
323
|
+
return model.fields.lookupFieldByOwner(ownerDefId, memberName);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Find a file-level def (top-of-module class / function / variable)
|
|
327
|
+
* by simple name — consults the target file's Module scope's
|
|
328
|
+
* finalized bindings. Only defs bound at module-scope with
|
|
329
|
+
* `origin === 'local'` qualify, matching the historical
|
|
330
|
+
* "module-export-visible" semantics. Class methods and class-body
|
|
331
|
+
* fields bind at their containing class scope and are naturally
|
|
332
|
+
* excluded.
|
|
333
|
+
*
|
|
334
|
+
* Reads from `WorkspaceResolutionIndex.moduleScopeByFile` (scope-tied
|
|
335
|
+
* lookup that doesn't live on `SemanticModel`).
|
|
336
|
+
*/
|
|
337
|
+
export function findExportedDef(targetFile, memberName, index) {
|
|
338
|
+
const moduleScope = index.moduleScopeByFile.get(targetFile);
|
|
339
|
+
if (moduleScope === undefined)
|
|
340
|
+
return undefined;
|
|
341
|
+
const refs = moduleScope.bindings.get(memberName);
|
|
342
|
+
if (refs === undefined)
|
|
343
|
+
return undefined;
|
|
344
|
+
for (const ref of refs) {
|
|
345
|
+
if (ref.origin === 'local')
|
|
346
|
+
return ref.def;
|
|
347
|
+
}
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `WorkspaceResolutionIndex` — scope-tied lookup tables built ONCE
|
|
3
|
+
* per resolution run, after `populateOwners` and before any
|
|
4
|
+
* resolution pass.
|
|
5
|
+
*
|
|
6
|
+
* ## Scope (what lives here vs. what lives in `SemanticModel`)
|
|
7
|
+
*
|
|
8
|
+
* This index carries only the lookups that return a `Scope` — things
|
|
9
|
+
* `SemanticModel` structurally cannot provide:
|
|
10
|
+
*
|
|
11
|
+
* - `classScopeByDefId` — class def `nodeId` → `Scope`. Needed so
|
|
12
|
+
* passes can read `scope.bindings`, `scope.typeBindings`, and
|
|
13
|
+
* `scope.ownedDefs`. SemanticModel's `TypeRegistry` carries class
|
|
14
|
+
* metadata but not the `Scope`.
|
|
15
|
+
* - `classScopeIdToDefId` — inverse of `classScopeByDefId`. O(1)
|
|
16
|
+
* reverse lookup (Scope.id → class def nodeId) for the implicit-
|
|
17
|
+
* `this` overload picker.
|
|
18
|
+
* - `moduleScopeByFile` — file path → `Scope` of the root `Module`.
|
|
19
|
+
* Used by cross-file return-type propagation, `findExportedDef`,
|
|
20
|
+
* and `findExportedDefByName`'s workspace-wide fallback.
|
|
21
|
+
* SymbolTable indexes symbols, not scopes.
|
|
22
|
+
*
|
|
23
|
+
* Symbol lookups live on `SemanticModel`:
|
|
24
|
+
* - Owner-keyed method lookup → `model.methods.lookupAllByOwner`
|
|
25
|
+
* (populated by the legacy parse phase via `symbolTable.add` AND
|
|
26
|
+
* by scope-resolution's reconciliation pass in `runScopeResolution`,
|
|
27
|
+
* which adds `parsed.localDefs[i].ownerId` entries missed by the
|
|
28
|
+
* legacy extractor for registry-primary languages).
|
|
29
|
+
* - Name-keyed callable lookup → `model.methods.lookupMethodByName`
|
|
30
|
+
* and `model.symbols.lookupCallableByName`.
|
|
31
|
+
* - File-indexed symbol lookup → `model.symbols.lookupExactAll`.
|
|
32
|
+
*
|
|
33
|
+
* This split preserves the single-source-of-truth invariant
|
|
34
|
+
* documented in `ScopeResolver`'s contract file: symbol-indexed
|
|
35
|
+
* lookups live on `SemanticModel` for the whole codebase; only
|
|
36
|
+
* scope-shaped lookups (which `SemanticModel` doesn't carry) live
|
|
37
|
+
* here.
|
|
38
|
+
*
|
|
39
|
+
* Build cost is O(totalScopes). Read-only after construction.
|
|
40
|
+
*/
|
|
41
|
+
import type { ParsedFile, Scope, ScopeId } from '../../../_shared/index.js';
|
|
42
|
+
export interface WorkspaceResolutionIndex {
|
|
43
|
+
/** Class def `nodeId` → that class's `Scope`. */
|
|
44
|
+
readonly classScopeByDefId: ReadonlyMap<string, Scope>;
|
|
45
|
+
/** Inverse of `classScopeByDefId`: class `Scope.id` → class def `nodeId`.
|
|
46
|
+
* Built in the same pass; used by the implicit-`this` overload picker
|
|
47
|
+
* in `free-call-fallback.ts` to skip an O(C) reverse scan. */
|
|
48
|
+
readonly classScopeIdToDefId: ReadonlyMap<ScopeId, string>;
|
|
49
|
+
/** Module scope by file path. */
|
|
50
|
+
readonly moduleScopeByFile: ReadonlyMap<string, Scope>;
|
|
51
|
+
}
|
|
52
|
+
export declare function buildWorkspaceResolutionIndex(parsedFiles: readonly ParsedFile[]): WorkspaceResolutionIndex;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `WorkspaceResolutionIndex` — scope-tied lookup tables built ONCE
|
|
3
|
+
* per resolution run, after `populateOwners` and before any
|
|
4
|
+
* resolution pass.
|
|
5
|
+
*
|
|
6
|
+
* ## Scope (what lives here vs. what lives in `SemanticModel`)
|
|
7
|
+
*
|
|
8
|
+
* This index carries only the lookups that return a `Scope` — things
|
|
9
|
+
* `SemanticModel` structurally cannot provide:
|
|
10
|
+
*
|
|
11
|
+
* - `classScopeByDefId` — class def `nodeId` → `Scope`. Needed so
|
|
12
|
+
* passes can read `scope.bindings`, `scope.typeBindings`, and
|
|
13
|
+
* `scope.ownedDefs`. SemanticModel's `TypeRegistry` carries class
|
|
14
|
+
* metadata but not the `Scope`.
|
|
15
|
+
* - `classScopeIdToDefId` — inverse of `classScopeByDefId`. O(1)
|
|
16
|
+
* reverse lookup (Scope.id → class def nodeId) for the implicit-
|
|
17
|
+
* `this` overload picker.
|
|
18
|
+
* - `moduleScopeByFile` — file path → `Scope` of the root `Module`.
|
|
19
|
+
* Used by cross-file return-type propagation, `findExportedDef`,
|
|
20
|
+
* and `findExportedDefByName`'s workspace-wide fallback.
|
|
21
|
+
* SymbolTable indexes symbols, not scopes.
|
|
22
|
+
*
|
|
23
|
+
* Symbol lookups live on `SemanticModel`:
|
|
24
|
+
* - Owner-keyed method lookup → `model.methods.lookupAllByOwner`
|
|
25
|
+
* (populated by the legacy parse phase via `symbolTable.add` AND
|
|
26
|
+
* by scope-resolution's reconciliation pass in `runScopeResolution`,
|
|
27
|
+
* which adds `parsed.localDefs[i].ownerId` entries missed by the
|
|
28
|
+
* legacy extractor for registry-primary languages).
|
|
29
|
+
* - Name-keyed callable lookup → `model.methods.lookupMethodByName`
|
|
30
|
+
* and `model.symbols.lookupCallableByName`.
|
|
31
|
+
* - File-indexed symbol lookup → `model.symbols.lookupExactAll`.
|
|
32
|
+
*
|
|
33
|
+
* This split preserves the single-source-of-truth invariant
|
|
34
|
+
* documented in `ScopeResolver`'s contract file: symbol-indexed
|
|
35
|
+
* lookups live on `SemanticModel` for the whole codebase; only
|
|
36
|
+
* scope-shaped lookups (which `SemanticModel` doesn't carry) live
|
|
37
|
+
* here.
|
|
38
|
+
*
|
|
39
|
+
* Build cost is O(totalScopes). Read-only after construction.
|
|
40
|
+
*/
|
|
41
|
+
import { isClassLike } from './scope/walkers.js';
|
|
42
|
+
export function buildWorkspaceResolutionIndex(parsedFiles) {
|
|
43
|
+
const classScopeByDefId = new Map();
|
|
44
|
+
const classScopeIdToDefId = new Map();
|
|
45
|
+
const moduleScopeByFile = new Map();
|
|
46
|
+
for (const parsed of parsedFiles) {
|
|
47
|
+
const moduleScope = parsed.scopes.find((s) => s.kind === 'Module');
|
|
48
|
+
if (moduleScope !== undefined)
|
|
49
|
+
moduleScopeByFile.set(parsed.filePath, moduleScope);
|
|
50
|
+
for (const scope of parsed.scopes) {
|
|
51
|
+
if (scope.kind !== 'Class')
|
|
52
|
+
continue;
|
|
53
|
+
const cd = scope.ownedDefs.find((d) => isClassLike(d.type));
|
|
54
|
+
if (cd !== undefined) {
|
|
55
|
+
classScopeByDefId.set(cd.nodeId, scope);
|
|
56
|
+
classScopeIdToDefId.set(scope.id, cd.nodeId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { classScopeByDefId, classScopeIdToDefId, moduleScopeByFile };
|
|
61
|
+
}
|