gitnexus 1.6.3-rc.8 → 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 +20 -2
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +11 -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/class-registry.d.ts +27 -0
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
- package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/context.js +44 -0
- package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
- package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
- package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
- package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
- package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
- package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
- package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
- 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,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C# `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed by
|
|
3
|
+
* the generic `runScopeResolution` orchestrator (RFC #909 Ring 3).
|
|
4
|
+
*
|
|
5
|
+
* Second migration after Python — see `pythonScopeResolver` for the
|
|
6
|
+
* canonical shape.
|
|
7
|
+
*/
|
|
8
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
9
|
+
import { buildMro, defaultLinearize } from '../../scope-resolution/passes/mro.js';
|
|
10
|
+
import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js';
|
|
11
|
+
import { csharpProvider } from '../csharp.js';
|
|
12
|
+
import { csharpArityCompatibility, csharpMergeBindings, resolveCsharpImportTarget, } from './index.js';
|
|
13
|
+
import { populateCsharpNamespaceSiblings } from './namespace-siblings.js';
|
|
14
|
+
import { unwrapCsharpCollectionAccessor } from './accessor-unwrap.js';
|
|
15
|
+
const csharpScopeResolver = {
|
|
16
|
+
language: SupportedLanguages.CSharp,
|
|
17
|
+
languageProvider: csharpProvider,
|
|
18
|
+
importEdgeReason: 'csharp-scope: using',
|
|
19
|
+
resolveImportTarget: (targetRaw, fromFile, allFilePaths) => {
|
|
20
|
+
const ws = { fromFile, allFilePaths };
|
|
21
|
+
// `WorkspaceIndex` is an opaque `unknown` placeholder in the
|
|
22
|
+
// shared contract, so `ws` passes structurally without a cast.
|
|
23
|
+
return resolveCsharpImportTarget({ kind: 'namespace', localName: '_', importedName: '_', targetRaw }, ws);
|
|
24
|
+
},
|
|
25
|
+
// C# shadowing: local > using > using static. The per-scope id is
|
|
26
|
+
// unused by the C# implementation (shadowing is computed purely
|
|
27
|
+
// from the binding tier), so we don't need to synthesize a Scope.
|
|
28
|
+
mergeBindings: (existing, incoming) => [...csharpMergeBindings([...existing, ...incoming])],
|
|
29
|
+
// Adapter: csharpArityCompatibility uses (def, callsite); the
|
|
30
|
+
// contract is (callsite, def).
|
|
31
|
+
arityCompatibility: (callsite, def) => csharpArityCompatibility(def, callsite),
|
|
32
|
+
buildMro: (graph, parsedFiles, nodeLookup) => buildMro(graph, parsedFiles, nodeLookup, defaultLinearize),
|
|
33
|
+
populateOwners: (parsed) => populateClassOwnedMembers(parsed),
|
|
34
|
+
// C# uses `base` for super-class dispatch, not `super`. Match as a
|
|
35
|
+
// plain identifier (no `()` call like Python's `super(...)`) — `base`
|
|
36
|
+
// is a keyword-like receiver, not a callable.
|
|
37
|
+
isSuperReceiver: (text) => text.trim() === 'base',
|
|
38
|
+
// Same-namespace cross-file visibility — C# makes every type
|
|
39
|
+
// declared in `namespace X` visible to other files declaring the
|
|
40
|
+
// same namespace, without any `using` directive. See
|
|
41
|
+
// `namespace-siblings.ts` for the implementation.
|
|
42
|
+
populateNamespaceSiblings: populateCsharpNamespaceSiblings,
|
|
43
|
+
// C# is statically typed — type information is reliable. Field-
|
|
44
|
+
// fallback heuristic stays off (the type-binding layer already
|
|
45
|
+
// produces precise owner types); return-type propagation on is fine
|
|
46
|
+
// since signatures are authoritative.
|
|
47
|
+
fieldFallbackOnMethodLookup: false,
|
|
48
|
+
propagatesReturnTypesAcrossImports: true,
|
|
49
|
+
// `data.Values` / `data.Keys` on Dictionary-like receivers unwrap
|
|
50
|
+
// to the value / key element type. Other languages use method-call
|
|
51
|
+
// syntax for the same access and leave this hook undefined.
|
|
52
|
+
unwrapCollectionAccessor: unwrapCsharpCollectionAccessor,
|
|
53
|
+
// C# matches legacy DAG by collapsing member-call CALLS edges to
|
|
54
|
+
// `(caller, target)` — multiple `g.Greet(...)` sites from Main
|
|
55
|
+
// yield ONE edge, not one per site.
|
|
56
|
+
collapseMemberCallsByCallerTarget: true,
|
|
57
|
+
// C# hoists method return-type bindings to the enclosing Module
|
|
58
|
+
// scope so `propagateImportedReturnTypes` can mirror them across
|
|
59
|
+
// files. The compound-receiver walker needs to walk up from the
|
|
60
|
+
// class scope to find them; see the contract field for rationale.
|
|
61
|
+
hoistTypeBindingsToModule: true,
|
|
62
|
+
};
|
|
63
|
+
export { csharpScopeResolver };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trivial / no-op-ish hooks for the C# provider. Kept together because
|
|
3
|
+
* each is a few lines and they share a common theme: they make the
|
|
4
|
+
* provider's choice explicit rather than relying on "absence == default"
|
|
5
|
+
* so reviewers don't have to re-derive the analysis.
|
|
6
|
+
*/
|
|
7
|
+
import type { CaptureMatch, ParsedImport, Scope, ScopeId, ScopeTree, TypeRef } from '../../../../_shared/index.js';
|
|
8
|
+
/** C# has block scope, but the central extractor's "innermost enclosing
|
|
9
|
+
* scope" default already handles it correctly: class-body declarations
|
|
10
|
+
* attach to the innermost Class scope, method-body declarations attach
|
|
11
|
+
* to the innermost Function scope, and namespace-body declarations
|
|
12
|
+
* attach to the innermost Namespace scope (which the scope query emits
|
|
13
|
+
* for both `namespace X { }` and `namespace X;` forms).
|
|
14
|
+
*
|
|
15
|
+
* Exception: **method return-type bindings** (`@type-binding.return`)
|
|
16
|
+
* must hoist all the way to the Module scope. The default auto-hoist
|
|
17
|
+
* in the central extractor only promotes one level (Function → its
|
|
18
|
+
* parent). For C# methods the parent is always a Class, so without
|
|
19
|
+
* this override the return binding gets stuck at the Class scope,
|
|
20
|
+
* where it's invisible to:
|
|
21
|
+
* - chain-follow's parent-chain walk in `followChainPostFinalize`
|
|
22
|
+
* (tests: `var u = GetUser(); u.Save()` single-file);
|
|
23
|
+
* - cross-file `propagateImportedReturnTypes`, which reads only
|
|
24
|
+
* `sourceModule.typeBindings`.
|
|
25
|
+
* Walking to Module restores both paths. */
|
|
26
|
+
export declare function csharpBindingScopeFor(decl: CaptureMatch, innermost: Scope, tree: ScopeTree): ScopeId | null;
|
|
27
|
+
/** `using` inside `namespace X { }` binds to that namespace's scope (its
|
|
28
|
+
* types are visible only within that namespace's members). File-level
|
|
29
|
+
* `using` delegates to the module default. Class-body `using` is not
|
|
30
|
+
* legal C# — defensively handle it by attaching to the class if it
|
|
31
|
+
* ever slips through.
|
|
32
|
+
*
|
|
33
|
+
* `global using X;` (C# 10+) at the compilation_unit level is treated
|
|
34
|
+
* as a file-scoped using for Unit 2's purposes; cross-file propagation
|
|
35
|
+
* will be addressed if Unit 7's parity gate flags it. */
|
|
36
|
+
export declare function csharpImportOwningScope(_imp: ParsedImport, innermost: Scope, _tree: ScopeTree): ScopeId | null;
|
|
37
|
+
/** Look up `this` or `base` in the function scope's type bindings.
|
|
38
|
+
*
|
|
39
|
+
* `this` and `base` are synthesized as type bindings on instance
|
|
40
|
+
* methods during capture emission (`receiver-binding.ts`) — `this`
|
|
41
|
+
* for every method inside a class/struct/record/interface body, and
|
|
42
|
+
* `base` additionally for methods of a class-like type with an
|
|
43
|
+
* explicit `base_list`. This hook therefore returns a non-null
|
|
44
|
+
* `TypeRef` for instance-method bodies.
|
|
45
|
+
*
|
|
46
|
+
* Returns `null` for:
|
|
47
|
+
* - static methods (no `this` synthesized)
|
|
48
|
+
* - free functions / module-level code (no enclosing class)
|
|
49
|
+
* - non-Function scopes
|
|
50
|
+
*
|
|
51
|
+
* Matches `pythonReceiverBinding`'s shape so the two provider
|
|
52
|
+
* wirings stay symmetric. */
|
|
53
|
+
export declare function csharpReceiverBinding(functionScope: Scope): TypeRef | null;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trivial / no-op-ish hooks for the C# provider. Kept together because
|
|
3
|
+
* each is a few lines and they share a common theme: they make the
|
|
4
|
+
* provider's choice explicit rather than relying on "absence == default"
|
|
5
|
+
* so reviewers don't have to re-derive the analysis.
|
|
6
|
+
*/
|
|
7
|
+
// ─── bindingScopeFor ──────────────────────────────────────────────────────
|
|
8
|
+
/** C# has block scope, but the central extractor's "innermost enclosing
|
|
9
|
+
* scope" default already handles it correctly: class-body declarations
|
|
10
|
+
* attach to the innermost Class scope, method-body declarations attach
|
|
11
|
+
* to the innermost Function scope, and namespace-body declarations
|
|
12
|
+
* attach to the innermost Namespace scope (which the scope query emits
|
|
13
|
+
* for both `namespace X { }` and `namespace X;` forms).
|
|
14
|
+
*
|
|
15
|
+
* Exception: **method return-type bindings** (`@type-binding.return`)
|
|
16
|
+
* must hoist all the way to the Module scope. The default auto-hoist
|
|
17
|
+
* in the central extractor only promotes one level (Function → its
|
|
18
|
+
* parent). For C# methods the parent is always a Class, so without
|
|
19
|
+
* this override the return binding gets stuck at the Class scope,
|
|
20
|
+
* where it's invisible to:
|
|
21
|
+
* - chain-follow's parent-chain walk in `followChainPostFinalize`
|
|
22
|
+
* (tests: `var u = GetUser(); u.Save()` single-file);
|
|
23
|
+
* - cross-file `propagateImportedReturnTypes`, which reads only
|
|
24
|
+
* `sourceModule.typeBindings`.
|
|
25
|
+
* Walking to Module restores both paths. */
|
|
26
|
+
export function csharpBindingScopeFor(decl, innermost, tree) {
|
|
27
|
+
if (decl['@type-binding.return'] !== undefined) {
|
|
28
|
+
let cur = innermost;
|
|
29
|
+
while (cur !== undefined && cur.kind !== 'Module') {
|
|
30
|
+
const parentId = cur.parent ?? null;
|
|
31
|
+
if (parentId === null)
|
|
32
|
+
break;
|
|
33
|
+
cur = tree.getScope(parentId);
|
|
34
|
+
}
|
|
35
|
+
if (cur !== undefined && cur.kind === 'Module')
|
|
36
|
+
return cur.id;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// ─── importOwningScope ────────────────────────────────────────────────────
|
|
41
|
+
/** `using` inside `namespace X { }` binds to that namespace's scope (its
|
|
42
|
+
* types are visible only within that namespace's members). File-level
|
|
43
|
+
* `using` delegates to the module default. Class-body `using` is not
|
|
44
|
+
* legal C# — defensively handle it by attaching to the class if it
|
|
45
|
+
* ever slips through.
|
|
46
|
+
*
|
|
47
|
+
* `global using X;` (C# 10+) at the compilation_unit level is treated
|
|
48
|
+
* as a file-scoped using for Unit 2's purposes; cross-file propagation
|
|
49
|
+
* will be addressed if Unit 7's parity gate flags it. */
|
|
50
|
+
export function csharpImportOwningScope(_imp, innermost, _tree) {
|
|
51
|
+
if (innermost.kind === 'Namespace' || innermost.kind === 'Class' || innermost.kind === 'Function')
|
|
52
|
+
return innermost.id;
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// ─── receiverBinding ──────────────────────────────────────────────────────
|
|
56
|
+
/** Look up `this` or `base` in the function scope's type bindings.
|
|
57
|
+
*
|
|
58
|
+
* `this` and `base` are synthesized as type bindings on instance
|
|
59
|
+
* methods during capture emission (`receiver-binding.ts`) — `this`
|
|
60
|
+
* for every method inside a class/struct/record/interface body, and
|
|
61
|
+
* `base` additionally for methods of a class-like type with an
|
|
62
|
+
* explicit `base_list`. This hook therefore returns a non-null
|
|
63
|
+
* `TypeRef` for instance-method bodies.
|
|
64
|
+
*
|
|
65
|
+
* Returns `null` for:
|
|
66
|
+
* - static methods (no `this` synthesized)
|
|
67
|
+
* - free functions / module-level code (no enclosing class)
|
|
68
|
+
* - non-Function scopes
|
|
69
|
+
*
|
|
70
|
+
* Matches `pythonReceiverBinding`'s shape so the two provider
|
|
71
|
+
* wirings stay symmetric. */
|
|
72
|
+
export function csharpReceiverBinding(functionScope) {
|
|
73
|
+
if (functionScope.kind !== 'Function')
|
|
74
|
+
return null;
|
|
75
|
+
return functionScope.typeBindings.get('this') ?? functionScope.typeBindings.get('base') ?? null;
|
|
76
|
+
}
|
|
@@ -24,6 +24,7 @@ import { csharpMethodConfig } from '../method-extractors/configs/csharp.js';
|
|
|
24
24
|
import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
25
25
|
import { csharpVariableConfig } from '../variable-extractors/configs/csharp.js';
|
|
26
26
|
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
27
|
+
import { emitCsharpScopeCaptures, interpretCsharpImport, interpretCsharpTypeBinding, csharpBindingScopeFor, csharpImportOwningScope, csharpMergeBindings, csharpReceiverBinding, csharpArityCompatibility, resolveCsharpImportTarget, } from './csharp/index.js';
|
|
27
28
|
const BUILT_INS = new Set([
|
|
28
29
|
'Console',
|
|
29
30
|
'WriteLine',
|
|
@@ -135,4 +136,17 @@ export const csharpProvider = defineLanguage({
|
|
|
135
136
|
classExtractor: createClassExtractor(csharpClassConfig),
|
|
136
137
|
heritageExtractor: createHeritageExtractor(SupportedLanguages.CSharp),
|
|
137
138
|
builtInNames: BUILT_INS,
|
|
139
|
+
// ── RFC #909 Ring 3: scope-based resolution hooks (RFC §5) ──────────
|
|
140
|
+
// C# is the second migration after Python. See ./csharp/index.ts for
|
|
141
|
+
// the full per-hook rationale and the canonical capture vocabulary
|
|
142
|
+
// in ./csharp/query.ts (CSHARP_SCOPE_QUERY constant).
|
|
143
|
+
emitScopeCaptures: emitCsharpScopeCaptures,
|
|
144
|
+
interpretImport: interpretCsharpImport,
|
|
145
|
+
interpretTypeBinding: interpretCsharpTypeBinding,
|
|
146
|
+
bindingScopeFor: csharpBindingScopeFor,
|
|
147
|
+
importOwningScope: csharpImportOwningScope,
|
|
148
|
+
mergeBindings: (_scope, bindings) => csharpMergeBindings(bindings),
|
|
149
|
+
receiverBinding: csharpReceiverBinding,
|
|
150
|
+
arityCompatibility: csharpArityCompatibility,
|
|
151
|
+
resolveImportTarget: resolveCsharpImportTarget,
|
|
138
152
|
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract Python arity metadata from a `function_definition` tree-sitter
|
|
3
|
+
* node — parameter count, required count, and (where present) a type
|
|
4
|
+
* list that the existing `pythonArityCompatibility` hook reads.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the legacy `buildMethodProps` conversion so scope-extracted
|
|
7
|
+
* defs carry the same arity semantics as the parse-worker path:
|
|
8
|
+
* - `self` / `cls` are stripped (consumed by `extractPythonParameters`).
|
|
9
|
+
* - Defaulted params contribute to `optionalCount`, flipping
|
|
10
|
+
* `requiredParameterCount = total − optionalCount`.
|
|
11
|
+
* - Variadic (`*args` / `**kwargs`) collapses `parameterCount` to
|
|
12
|
+
* `undefined`, which `pythonArityCompatibility` then treats as
|
|
13
|
+
* `'unknown'` — keeping the candidate in the registry's lookup set.
|
|
14
|
+
* - `parameterTypes` is populated only with real type text, matching
|
|
15
|
+
* legacy behavior.
|
|
16
|
+
*/
|
|
17
|
+
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
|
18
|
+
interface PythonArityMetadata {
|
|
19
|
+
readonly parameterCount: number | undefined;
|
|
20
|
+
readonly requiredParameterCount: number | undefined;
|
|
21
|
+
readonly parameterTypes: readonly string[] | undefined;
|
|
22
|
+
}
|
|
23
|
+
export declare function computePythonArityMetadata(fnNode: SyntaxNode): PythonArityMetadata;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract Python arity metadata from a `function_definition` tree-sitter
|
|
3
|
+
* node — parameter count, required count, and (where present) a type
|
|
4
|
+
* list that the existing `pythonArityCompatibility` hook reads.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the legacy `buildMethodProps` conversion so scope-extracted
|
|
7
|
+
* defs carry the same arity semantics as the parse-worker path:
|
|
8
|
+
* - `self` / `cls` are stripped (consumed by `extractPythonParameters`).
|
|
9
|
+
* - Defaulted params contribute to `optionalCount`, flipping
|
|
10
|
+
* `requiredParameterCount = total − optionalCount`.
|
|
11
|
+
* - Variadic (`*args` / `**kwargs`) collapses `parameterCount` to
|
|
12
|
+
* `undefined`, which `pythonArityCompatibility` then treats as
|
|
13
|
+
* `'unknown'` — keeping the candidate in the registry's lookup set.
|
|
14
|
+
* - `parameterTypes` is populated only with real type text, matching
|
|
15
|
+
* legacy behavior.
|
|
16
|
+
*/
|
|
17
|
+
import { pythonMethodConfig } from '../../method-extractors/configs/python.js';
|
|
18
|
+
export function computePythonArityMetadata(fnNode) {
|
|
19
|
+
const params = pythonMethodConfig.extractParameters?.(fnNode) ?? [];
|
|
20
|
+
let hasVariadic = false;
|
|
21
|
+
let optionalCount = 0;
|
|
22
|
+
const types = [];
|
|
23
|
+
for (const p of params) {
|
|
24
|
+
if (p.isVariadic)
|
|
25
|
+
hasVariadic = true;
|
|
26
|
+
else if (p.isOptional)
|
|
27
|
+
optionalCount++;
|
|
28
|
+
if (p.type !== null)
|
|
29
|
+
types.push(p.type);
|
|
30
|
+
}
|
|
31
|
+
const total = params.length;
|
|
32
|
+
const parameterCount = hasVariadic ? undefined : total;
|
|
33
|
+
// Unlike legacy `buildMethodProps`, we populate `requiredParameterCount`
|
|
34
|
+
// whenever the function isn't variadic — even when it equals
|
|
35
|
+
// `parameterCount`. The scope-resolution registry needs a concrete min
|
|
36
|
+
// to rule out under-application (e.g. picking `write_audit(x, y)` for
|
|
37
|
+
// a 1-arg call). Legacy could get away with leaving it undefined
|
|
38
|
+
// because its call-graph builder had a separate arity pre-filter.
|
|
39
|
+
const requiredParameterCount = hasVariadic ? undefined : total - optionalCount;
|
|
40
|
+
return {
|
|
41
|
+
parameterCount,
|
|
42
|
+
requiredParameterCount,
|
|
43
|
+
parameterTypes: types.length > 0 ? types : undefined,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python arity check, accommodating `*args`, `**kwargs`, and defaults.
|
|
3
|
+
*
|
|
4
|
+
* The `def` metadata we care about (set by the existing Python method/
|
|
5
|
+
* function extractor):
|
|
6
|
+
* - `parameterCount` — total positional + keyword params
|
|
7
|
+
* - `requiredParameterCount` — min required (excludes defaults / `*args` / `**kwargs`)
|
|
8
|
+
* - `parameterTypes` — present when types are known; we also use it
|
|
9
|
+
* as a "we have varargs" hint (`'*args'`,
|
|
10
|
+
* `'**kwargs'` literals appear in the array).
|
|
11
|
+
*
|
|
12
|
+
* Verdicts:
|
|
13
|
+
* - `'compatible'` — `requiredParameterCount <= argCount <= parameterCount`,
|
|
14
|
+
* OR the def takes `*args` (then any `argCount >= required` ok).
|
|
15
|
+
* - `'incompatible'` — argCount is below required, OR above max with no `*args`.
|
|
16
|
+
* - `'unknown'` — def metadata is absent / incomplete.
|
|
17
|
+
*
|
|
18
|
+
* `'incompatible'` is a soft signal in `Registry.lookup` (penalized but
|
|
19
|
+
* still considered when no compatible candidate exists), per RFC §4.
|
|
20
|
+
*/
|
|
21
|
+
import type { Callsite, SymbolDefinition } from '../../../../_shared/index.js';
|
|
22
|
+
export declare function pythonArityCompatibility(def: SymbolDefinition, callsite: Callsite): 'compatible' | 'unknown' | 'incompatible';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python arity check, accommodating `*args`, `**kwargs`, and defaults.
|
|
3
|
+
*
|
|
4
|
+
* The `def` metadata we care about (set by the existing Python method/
|
|
5
|
+
* function extractor):
|
|
6
|
+
* - `parameterCount` — total positional + keyword params
|
|
7
|
+
* - `requiredParameterCount` — min required (excludes defaults / `*args` / `**kwargs`)
|
|
8
|
+
* - `parameterTypes` — present when types are known; we also use it
|
|
9
|
+
* as a "we have varargs" hint (`'*args'`,
|
|
10
|
+
* `'**kwargs'` literals appear in the array).
|
|
11
|
+
*
|
|
12
|
+
* Verdicts:
|
|
13
|
+
* - `'compatible'` — `requiredParameterCount <= argCount <= parameterCount`,
|
|
14
|
+
* OR the def takes `*args` (then any `argCount >= required` ok).
|
|
15
|
+
* - `'incompatible'` — argCount is below required, OR above max with no `*args`.
|
|
16
|
+
* - `'unknown'` — def metadata is absent / incomplete.
|
|
17
|
+
*
|
|
18
|
+
* `'incompatible'` is a soft signal in `Registry.lookup` (penalized but
|
|
19
|
+
* still considered when no compatible candidate exists), per RFC §4.
|
|
20
|
+
*/
|
|
21
|
+
export function pythonArityCompatibility(def, callsite) {
|
|
22
|
+
const max = def.parameterCount;
|
|
23
|
+
const min = def.requiredParameterCount;
|
|
24
|
+
if (max === undefined && min === undefined)
|
|
25
|
+
return 'unknown';
|
|
26
|
+
const argCount = callsite.arity;
|
|
27
|
+
if (!Number.isFinite(argCount) || argCount < 0)
|
|
28
|
+
return 'unknown';
|
|
29
|
+
// Detect varargs/kwargs from parameterTypes if present (the Python
|
|
30
|
+
// method extractor stores `'*args'`/`'**kwargs'` in this list).
|
|
31
|
+
const hasVarArgs = def.parameterTypes !== undefined &&
|
|
32
|
+
def.parameterTypes.some((t) => t === '*args' || t === '**kwargs' || t.startsWith('*'));
|
|
33
|
+
if (min !== undefined && argCount < min)
|
|
34
|
+
return 'incompatible';
|
|
35
|
+
if (max !== undefined && argCount > max && !hasVarArgs)
|
|
36
|
+
return 'incompatible';
|
|
37
|
+
return 'compatible';
|
|
38
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-mode counters for the cross-phase scope-captures parse cache.
|
|
3
|
+
*
|
|
4
|
+
* Gated by `PROF_SCOPE_RESOLUTION=1`. In production the module-level
|
|
5
|
+
* `PROF` constant is `false` and V8 folds every increment site into
|
|
6
|
+
* dead code, so the hot path in `captures.ts` stays branch-free.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from `captures.ts` so the production hot-path module
|
|
9
|
+
* doesn't carry a module-global counter and its reset/export surface.
|
|
10
|
+
*/
|
|
11
|
+
export declare function recordCacheHit(): void;
|
|
12
|
+
export declare function recordCacheMiss(): void;
|
|
13
|
+
export declare function getPythonCaptureCacheStats(): {
|
|
14
|
+
hits: number;
|
|
15
|
+
misses: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function resetPythonCaptureCacheStats(): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-mode counters for the cross-phase scope-captures parse cache.
|
|
3
|
+
*
|
|
4
|
+
* Gated by `PROF_SCOPE_RESOLUTION=1`. In production the module-level
|
|
5
|
+
* `PROF` constant is `false` and V8 folds every increment site into
|
|
6
|
+
* dead code, so the hot path in `captures.ts` stays branch-free.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from `captures.ts` so the production hot-path module
|
|
9
|
+
* doesn't carry a module-global counter and its reset/export surface.
|
|
10
|
+
*/
|
|
11
|
+
const PROF = process.env.PROF_SCOPE_RESOLUTION === '1';
|
|
12
|
+
let CACHE_HITS = 0;
|
|
13
|
+
let CACHE_MISSES = 0;
|
|
14
|
+
export function recordCacheHit() {
|
|
15
|
+
if (PROF)
|
|
16
|
+
CACHE_HITS++;
|
|
17
|
+
}
|
|
18
|
+
export function recordCacheMiss() {
|
|
19
|
+
if (PROF)
|
|
20
|
+
CACHE_MISSES++;
|
|
21
|
+
}
|
|
22
|
+
export function getPythonCaptureCacheStats() {
|
|
23
|
+
return { hits: CACHE_HITS, misses: CACHE_MISSES };
|
|
24
|
+
}
|
|
25
|
+
export function resetPythonCaptureCacheStats() {
|
|
26
|
+
CACHE_HITS = 0;
|
|
27
|
+
CACHE_MISSES = 0;
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `emitScopeCaptures` for Python.
|
|
3
|
+
*
|
|
4
|
+
* Drives the scope query against `tree-sitter-python` and groups raw
|
|
5
|
+
* matches into `CaptureMatch[]` for the central extractor, then layers
|
|
6
|
+
* two synthesized streams on top:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Per-name import statements** — `import a, b` and
|
|
9
|
+
* `from m import x, y` decompose to one match per imported name
|
|
10
|
+
* (see `import-decomposer.ts`).
|
|
11
|
+
* 2. **Receiver type bindings** — each `function_definition` inside a
|
|
12
|
+
* class body emits a `@type-binding.self` (or `@type-binding.cls`
|
|
13
|
+
* for `@classmethod`) capture so Pass-4 attaches the implicit
|
|
14
|
+
* receiver (see `receiver-binding.ts`).
|
|
15
|
+
*
|
|
16
|
+
* Pure given the input source text. No I/O, no globals consulted.
|
|
17
|
+
*/
|
|
18
|
+
import type { CaptureMatch } from '../../../../_shared/index.js';
|
|
19
|
+
export declare function emitPythonScopeCaptures(sourceText: string, _filePath: string, cachedTree?: unknown): readonly CaptureMatch[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `emitScopeCaptures` for Python.
|
|
3
|
+
*
|
|
4
|
+
* Drives the scope query against `tree-sitter-python` and groups raw
|
|
5
|
+
* matches into `CaptureMatch[]` for the central extractor, then layers
|
|
6
|
+
* two synthesized streams on top:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Per-name import statements** — `import a, b` and
|
|
9
|
+
* `from m import x, y` decompose to one match per imported name
|
|
10
|
+
* (see `import-decomposer.ts`).
|
|
11
|
+
* 2. **Receiver type bindings** — each `function_definition` inside a
|
|
12
|
+
* class body emits a `@type-binding.self` (or `@type-binding.cls`
|
|
13
|
+
* for `@classmethod`) capture so Pass-4 attaches the implicit
|
|
14
|
+
* receiver (see `receiver-binding.ts`).
|
|
15
|
+
*
|
|
16
|
+
* Pure given the input source text. No I/O, no globals consulted.
|
|
17
|
+
*/
|
|
18
|
+
import { findNodeAtRange, nodeToCapture, syntheticCapture } from '../../utils/ast-helpers.js';
|
|
19
|
+
import { splitImportStatement } from './import-decomposer.js';
|
|
20
|
+
import { getPythonParser, getPythonScopeQuery } from './query.js';
|
|
21
|
+
import { synthesizeReceiverTypeBinding } from './receiver-binding.js';
|
|
22
|
+
import { computePythonArityMetadata } from './arity-metadata.js';
|
|
23
|
+
import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
|
|
24
|
+
export function emitPythonScopeCaptures(sourceText, _filePath, cachedTree) {
|
|
25
|
+
// Skip the parse when the caller (parse phase's ASTCache) already
|
|
26
|
+
// produced a Tree for this source. Cache miss = re-parse, same as
|
|
27
|
+
// before. The cachedTree parameter is typed as `unknown` at the
|
|
28
|
+
// contract layer (see `LanguageProvider.emitScopeCaptures`); cast
|
|
29
|
+
// here at the use site.
|
|
30
|
+
let tree = cachedTree;
|
|
31
|
+
if (tree === undefined) {
|
|
32
|
+
tree = getPythonParser().parse(sourceText);
|
|
33
|
+
recordCacheMiss();
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
recordCacheHit();
|
|
37
|
+
}
|
|
38
|
+
const rawMatches = getPythonScopeQuery().matches(tree.rootNode);
|
|
39
|
+
const out = [];
|
|
40
|
+
for (const m of rawMatches) {
|
|
41
|
+
// Group captures by their tag name. Tree-sitter strips the leading
|
|
42
|
+
// `@`; we put it back so the central extractor's prefix lookups
|
|
43
|
+
// (`@scope.`, `@declaration.`, …) work.
|
|
44
|
+
const grouped = {};
|
|
45
|
+
for (const c of m.captures) {
|
|
46
|
+
const tag = '@' + c.name;
|
|
47
|
+
grouped[tag] = nodeToCapture(tag, c.node);
|
|
48
|
+
}
|
|
49
|
+
if (Object.keys(grouped).length === 0)
|
|
50
|
+
continue;
|
|
51
|
+
if (grouped['@import.statement'] !== undefined) {
|
|
52
|
+
// Decompose multi-name imports. Both `import_statement` and
|
|
53
|
+
// `import_from_statement` share the matched range, so we try the
|
|
54
|
+
// `from` form first and fall back to plain.
|
|
55
|
+
const stmtCapture = grouped['@import.statement'];
|
|
56
|
+
const stmtNode = findNodeAtRange(tree.rootNode, stmtCapture.range, 'import_from_statement') ??
|
|
57
|
+
findNodeAtRange(tree.rootNode, stmtCapture.range, 'import_statement');
|
|
58
|
+
if (stmtNode !== null) {
|
|
59
|
+
for (const piece of splitImportStatement(stmtNode))
|
|
60
|
+
out.push(piece);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Defensive fallback: emit the raw match.
|
|
64
|
+
out.push(grouped);
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (grouped['@scope.function'] !== undefined) {
|
|
69
|
+
out.push(grouped);
|
|
70
|
+
const fnNode = findNodeAtRange(tree.rootNode, grouped['@scope.function'].range, 'function_definition');
|
|
71
|
+
if (fnNode !== null) {
|
|
72
|
+
const synth = synthesizeReceiverTypeBinding(fnNode);
|
|
73
|
+
if (synth !== null)
|
|
74
|
+
out.push(synth);
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (grouped['@declaration.function'] !== undefined) {
|
|
79
|
+
// Synthesize arity captures on the declaration match so the
|
|
80
|
+
// central scope-extractor picks them up alongside @declaration.name.
|
|
81
|
+
// The anchor range is the function_definition itself — we resolve
|
|
82
|
+
// the node and pipe it through the arity helper.
|
|
83
|
+
const anchorCap = grouped['@declaration.function'];
|
|
84
|
+
const fnNode = findNodeAtRange(tree.rootNode, anchorCap.range, 'function_definition');
|
|
85
|
+
if (fnNode !== null) {
|
|
86
|
+
const arity = computePythonArityMetadata(fnNode);
|
|
87
|
+
if (arity.parameterCount !== undefined) {
|
|
88
|
+
grouped['@declaration.parameter-count'] = syntheticCapture('@declaration.parameter-count', fnNode, String(arity.parameterCount));
|
|
89
|
+
}
|
|
90
|
+
if (arity.requiredParameterCount !== undefined) {
|
|
91
|
+
grouped['@declaration.required-parameter-count'] = syntheticCapture('@declaration.required-parameter-count', fnNode, String(arity.requiredParameterCount));
|
|
92
|
+
}
|
|
93
|
+
if (arity.parameterTypes !== undefined) {
|
|
94
|
+
// Serialize as JSON so the consumer can round-trip without
|
|
95
|
+
// inventing a quoting convention for type names that may
|
|
96
|
+
// contain commas (`Dict[str, int]`).
|
|
97
|
+
grouped['@declaration.parameter-types'] = syntheticCapture('@declaration.parameter-types', fnNode, JSON.stringify(arity.parameterTypes));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
out.push(grouped);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
out.push(grouped);
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decompose a Python `import_statement` / `import_from_statement` into
|
|
3
|
+
* one `CaptureMatch` per imported name.
|
|
4
|
+
*
|
|
5
|
+
* Why split here? The `LanguageProvider.interpretImport` contract is
|
|
6
|
+
* one `ParsedImport` per call. Tree-sitter delivers `import a, b as c`
|
|
7
|
+
* and `from m import x, y, z` as a single match each, so without
|
|
8
|
+
* decomposition we'd lose names. The synthesized markers
|
|
9
|
+
* (`@import.kind` / `@import.name` / `@import.alias` / `@import.source`)
|
|
10
|
+
* carry everything `interpretPythonImport` needs to recover the original
|
|
11
|
+
* `ParsedImport` shape — see `interpret.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import type { CaptureMatch } from '../../../../_shared/index.js';
|
|
14
|
+
import { type SyntaxNode } from '../../utils/ast-helpers.js';
|
|
15
|
+
export declare function splitImportStatement(stmtNode: SyntaxNode): CaptureMatch[];
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decompose a Python `import_statement` / `import_from_statement` into
|
|
3
|
+
* one `CaptureMatch` per imported name.
|
|
4
|
+
*
|
|
5
|
+
* Why split here? The `LanguageProvider.interpretImport` contract is
|
|
6
|
+
* one `ParsedImport` per call. Tree-sitter delivers `import a, b as c`
|
|
7
|
+
* and `from m import x, y, z` as a single match each, so without
|
|
8
|
+
* decomposition we'd lose names. The synthesized markers
|
|
9
|
+
* (`@import.kind` / `@import.name` / `@import.alias` / `@import.source`)
|
|
10
|
+
* carry everything `interpretPythonImport` needs to recover the original
|
|
11
|
+
* `ParsedImport` shape — see `interpret.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import { findChild, nodeToCapture, syntheticCapture, } from '../../utils/ast-helpers.js';
|
|
14
|
+
export function splitImportStatement(stmtNode) {
|
|
15
|
+
if (stmtNode.type === 'import_statement')
|
|
16
|
+
return splitImportStmt(stmtNode);
|
|
17
|
+
if (stmtNode.type === 'import_from_statement')
|
|
18
|
+
return splitImportFromStmt(stmtNode);
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
function splitImportStmt(stmtNode) {
|
|
22
|
+
// `import a, b as c, d.e`
|
|
23
|
+
const out = [];
|
|
24
|
+
for (let i = 0; i < stmtNode.namedChildCount; i++) {
|
|
25
|
+
const child = stmtNode.namedChild(i);
|
|
26
|
+
if (child === null)
|
|
27
|
+
continue;
|
|
28
|
+
if (child.type === 'dotted_name') {
|
|
29
|
+
out.push(buildImportMatch(stmtNode, {
|
|
30
|
+
kind: 'plain',
|
|
31
|
+
source: child.text,
|
|
32
|
+
name: child.text.split('.')[0],
|
|
33
|
+
atNode: child,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
else if (child.type === 'aliased_import') {
|
|
37
|
+
const dotted = findChild(child, 'dotted_name');
|
|
38
|
+
const alias = findChild(child, 'identifier');
|
|
39
|
+
if (dotted !== null && alias !== null) {
|
|
40
|
+
out.push(buildImportMatch(stmtNode, {
|
|
41
|
+
kind: 'aliased',
|
|
42
|
+
source: dotted.text,
|
|
43
|
+
name: dotted.text,
|
|
44
|
+
alias: alias.text,
|
|
45
|
+
atNode: child,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function splitImportFromStmt(stmtNode) {
|
|
53
|
+
// `from m import a, b as c` / `from m import *` / `from . import x`
|
|
54
|
+
const out = [];
|
|
55
|
+
const moduleField = stmtNode.childForFieldName('module_name');
|
|
56
|
+
const moduleText = moduleField?.text ?? '';
|
|
57
|
+
// Wildcard? tree-sitter-python represents `*` as a `wildcard_import`
|
|
58
|
+
// child and emits no name children.
|
|
59
|
+
const wildcardChild = findChild(stmtNode, 'wildcard_import');
|
|
60
|
+
if (wildcardChild !== null) {
|
|
61
|
+
out.push(buildImportMatch(stmtNode, {
|
|
62
|
+
kind: 'wildcard',
|
|
63
|
+
source: moduleText,
|
|
64
|
+
name: '*',
|
|
65
|
+
atNode: wildcardChild,
|
|
66
|
+
}));
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
// Names = every dotted_name / aliased_import that isn't the module.
|
|
70
|
+
for (let i = 0; i < stmtNode.namedChildCount; i++) {
|
|
71
|
+
const child = stmtNode.namedChild(i);
|
|
72
|
+
if (child === null)
|
|
73
|
+
continue;
|
|
74
|
+
if (moduleField !== null && child.startIndex === moduleField.startIndex)
|
|
75
|
+
continue;
|
|
76
|
+
if (child.type === 'dotted_name') {
|
|
77
|
+
out.push(buildImportMatch(stmtNode, {
|
|
78
|
+
kind: 'from',
|
|
79
|
+
source: moduleText,
|
|
80
|
+
name: child.text,
|
|
81
|
+
atNode: child,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
else if (child.type === 'aliased_import') {
|
|
85
|
+
const dotted = findChild(child, 'dotted_name');
|
|
86
|
+
const alias = findChild(child, 'identifier');
|
|
87
|
+
if (dotted !== null && alias !== null) {
|
|
88
|
+
out.push(buildImportMatch(stmtNode, {
|
|
89
|
+
kind: 'from-alias',
|
|
90
|
+
source: moduleText,
|
|
91
|
+
name: dotted.text,
|
|
92
|
+
alias: alias.text,
|
|
93
|
+
atNode: child,
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
function buildImportMatch(stmtNode, spec) {
|
|
101
|
+
const stmtCap = nodeToCapture('@import.statement', stmtNode);
|
|
102
|
+
const m = {
|
|
103
|
+
'@import.statement': stmtCap,
|
|
104
|
+
'@import.kind': syntheticCapture('@import.kind', spec.atNode, spec.kind),
|
|
105
|
+
'@import.source': syntheticCapture('@import.source', spec.atNode, spec.source),
|
|
106
|
+
'@import.name': syntheticCapture('@import.name', spec.atNode, spec.name),
|
|
107
|
+
};
|
|
108
|
+
if (spec.alias !== undefined) {
|
|
109
|
+
m['@import.alias'] = syntheticCapture('@import.alias', spec.atNode, spec.alias);
|
|
110
|
+
}
|
|
111
|
+
return m;
|
|
112
|
+
}
|