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,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `emitScopeCaptures` for C#.
|
|
3
|
+
*
|
|
4
|
+
* Drives the C# scope query against tree-sitter-c-sharp and groups raw
|
|
5
|
+
* matches into `CaptureMatch[]` for the central extractor. Layers one
|
|
6
|
+
* synthesized stream on top today:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Decomposed using directives** — each `using_directive` is
|
|
9
|
+
* re-emitted with `@import.kind/source/name/alias` markers so
|
|
10
|
+
* `interpretCsharpImport` can recover the ParsedImport shape
|
|
11
|
+
* without re-parsing raw text (see `import-decomposer.ts`).
|
|
12
|
+
*
|
|
13
|
+
* Receiver-binding synthesis (`this` / `base` type anchors) and arity
|
|
14
|
+
* metadata synthesis (Unit 5) layer on top later.
|
|
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 { splitUsingDirective } from './import-decomposer.js';
|
|
20
|
+
import { computeCsharpArityMetadata } from './arity-metadata.js';
|
|
21
|
+
import { synthesizeCsharpReceiverBinding } from './receiver-binding.js';
|
|
22
|
+
import { getCsharpParser, getCsharpScopeQuery } from './query.js';
|
|
23
|
+
import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
|
|
24
|
+
/** Declaration anchors that carry function-like arity metadata. */
|
|
25
|
+
const FUNCTION_DECL_TAGS = [
|
|
26
|
+
'@declaration.method',
|
|
27
|
+
'@declaration.constructor',
|
|
28
|
+
'@declaration.function',
|
|
29
|
+
];
|
|
30
|
+
/** tree-sitter-c-sharp node types that the method extractor accepts. */
|
|
31
|
+
const FUNCTION_NODE_TYPES = [
|
|
32
|
+
'method_declaration',
|
|
33
|
+
'constructor_declaration',
|
|
34
|
+
'destructor_declaration',
|
|
35
|
+
'operator_declaration',
|
|
36
|
+
'conversion_operator_declaration',
|
|
37
|
+
'local_function_statement',
|
|
38
|
+
];
|
|
39
|
+
export function emitCsharpScopeCaptures(sourceText, _filePath, cachedTree) {
|
|
40
|
+
// Skip the parse when the caller (parse phase's scopeTreeCache)
|
|
41
|
+
// already produced a Tree for this source. Cache miss = re-parse,
|
|
42
|
+
// same as before. The cachedTree parameter is typed as `unknown` at
|
|
43
|
+
// the LanguageProvider contract layer; cast here at the use site.
|
|
44
|
+
let tree = cachedTree;
|
|
45
|
+
if (tree === undefined) {
|
|
46
|
+
tree = getCsharpParser().parse(sourceText);
|
|
47
|
+
recordCacheMiss();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
recordCacheHit();
|
|
51
|
+
}
|
|
52
|
+
const rawMatches = getCsharpScopeQuery().matches(tree.rootNode);
|
|
53
|
+
const out = [];
|
|
54
|
+
for (const m of rawMatches) {
|
|
55
|
+
// Group captures by their tag name. Tree-sitter strips the leading
|
|
56
|
+
// `@`; we put it back so the central extractor's prefix lookups
|
|
57
|
+
// (`@scope.`, `@declaration.`, …) work.
|
|
58
|
+
const grouped = {};
|
|
59
|
+
for (const c of m.captures) {
|
|
60
|
+
const tag = '@' + c.name;
|
|
61
|
+
grouped[tag] = nodeToCapture(tag, c.node);
|
|
62
|
+
}
|
|
63
|
+
if (Object.keys(grouped).length === 0)
|
|
64
|
+
continue;
|
|
65
|
+
// Decompose each `using_directive` so `interpretCsharpImport` sees
|
|
66
|
+
// the kind/source/name/alias markers it consumes. Raw query match
|
|
67
|
+
// only carries the @import.statement anchor.
|
|
68
|
+
if (grouped['@import.statement'] !== undefined) {
|
|
69
|
+
const stmtCapture = grouped['@import.statement'];
|
|
70
|
+
const stmtNode = findNodeAtRange(tree.rootNode, stmtCapture.range, 'using_directive');
|
|
71
|
+
if (stmtNode !== null) {
|
|
72
|
+
const decomposed = splitUsingDirective(stmtNode);
|
|
73
|
+
if (decomposed !== null) {
|
|
74
|
+
out.push(decomposed);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Defensive fallback: emit the raw match so the extractor at
|
|
79
|
+
// least sees an anchor, even without markers.
|
|
80
|
+
out.push(grouped);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// Synthesize `this` / `base` receiver type-bindings on every
|
|
84
|
+
// instance method-like. Tree-sitter can't cleanly express "the
|
|
85
|
+
// implicit receiver of a non-static member of a class/struct/
|
|
86
|
+
// record/interface" via a static `.scm` pattern, so we walk up
|
|
87
|
+
// the AST in code. Mirrors Python's `self`/`cls` synthesis on
|
|
88
|
+
// `@scope.function` matches.
|
|
89
|
+
if (grouped['@scope.function'] !== undefined) {
|
|
90
|
+
out.push(grouped);
|
|
91
|
+
const anchor = grouped['@scope.function'];
|
|
92
|
+
const fnNode = findFunctionNode(tree.rootNode, anchor.range);
|
|
93
|
+
if (fnNode !== null) {
|
|
94
|
+
for (const synth of synthesizeCsharpReceiverBinding(fnNode)) {
|
|
95
|
+
out.push(synth);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
// Synthesize arity metadata on function-like declarations so the
|
|
101
|
+
// registry can narrow overloads (C# relies heavily on this). Mirrors
|
|
102
|
+
// Python's captures.ts pattern — one anchor per match, so we find
|
|
103
|
+
// the first tag that matches.
|
|
104
|
+
const declTag = FUNCTION_DECL_TAGS.find((t) => grouped[t] !== undefined);
|
|
105
|
+
if (declTag !== undefined) {
|
|
106
|
+
const anchor = grouped[declTag];
|
|
107
|
+
const fnNode = findFunctionNode(tree.rootNode, anchor.range);
|
|
108
|
+
if (fnNode !== null) {
|
|
109
|
+
const arity = computeCsharpArityMetadata(fnNode);
|
|
110
|
+
if (arity.parameterCount !== undefined) {
|
|
111
|
+
grouped['@declaration.parameter-count'] = syntheticCapture('@declaration.parameter-count', fnNode, String(arity.parameterCount));
|
|
112
|
+
}
|
|
113
|
+
if (arity.requiredParameterCount !== undefined) {
|
|
114
|
+
grouped['@declaration.required-parameter-count'] = syntheticCapture('@declaration.required-parameter-count', fnNode, String(arity.requiredParameterCount));
|
|
115
|
+
}
|
|
116
|
+
if (arity.parameterTypes !== undefined) {
|
|
117
|
+
grouped['@declaration.parameter-types'] = syntheticCapture('@declaration.parameter-types', fnNode, JSON.stringify(arity.parameterTypes));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Synthesize `@reference.arity` on every callsite so the
|
|
122
|
+
// registry's arity filter can narrow overloads. Count the
|
|
123
|
+
// `argument` named children of the backing `argument_list`.
|
|
124
|
+
// Python doesn't synthesize this today; C# needs it because the
|
|
125
|
+
// language has method overloading and the suite asserts overload
|
|
126
|
+
// resolution.
|
|
127
|
+
const callTag = ['@reference.call.free', '@reference.call.member', '@reference.call.constructor'].find((t) => grouped[t] !== undefined);
|
|
128
|
+
if (callTag !== undefined && grouped['@reference.arity'] === undefined) {
|
|
129
|
+
const anchor = grouped[callTag];
|
|
130
|
+
const callNode = findNodeAtRange(tree.rootNode, anchor.range, 'invocation_expression') ??
|
|
131
|
+
findNodeAtRange(tree.rootNode, anchor.range, 'object_creation_expression');
|
|
132
|
+
if (callNode !== null) {
|
|
133
|
+
const argList = callNode.childForFieldName('arguments');
|
|
134
|
+
const args = argList === null
|
|
135
|
+
? []
|
|
136
|
+
: argList.namedChildren.filter((c) => c !== null && c.type === 'argument');
|
|
137
|
+
grouped['@reference.arity'] = syntheticCapture('@reference.arity', callNode, String(args.length));
|
|
138
|
+
// Infer argument types from literal nodes so overload
|
|
139
|
+
// disambiguation can narrow same-arity candidates by param
|
|
140
|
+
// type. Non-literal arguments emit empty string to indicate
|
|
141
|
+
// "unknown" — consumers treat unknown as any-match.
|
|
142
|
+
const argTypes = args.map((arg) => inferArgType(arg));
|
|
143
|
+
grouped['@reference.parameter-types'] = syntheticCapture('@reference.parameter-types', callNode, JSON.stringify(argTypes));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
out.push(grouped);
|
|
147
|
+
// Synthesize primary-constructor declarations on class/record
|
|
148
|
+
// declarations that carry a `parameter_list` child (C# 12 syntax
|
|
149
|
+
// `public class User(string name, int age) { ... }` or
|
|
150
|
+
// `public record Person(string FirstName, string LastName)`).
|
|
151
|
+
// Legacy `csharpMethodConfig.extractPrimaryConstructor` runs via
|
|
152
|
+
// the parse phase; the scope-resolution path needs its own emit so
|
|
153
|
+
// `new User(...)` resolves to a Constructor def in memberByOwner.
|
|
154
|
+
if (grouped['@declaration.class'] !== undefined ||
|
|
155
|
+
grouped['@declaration.record'] !== undefined) {
|
|
156
|
+
const anchor = grouped['@declaration.class'] ?? grouped['@declaration.record'];
|
|
157
|
+
const typeNode = findNodeAtRange(tree.rootNode, anchor.range, 'class_declaration') ??
|
|
158
|
+
findNodeAtRange(tree.rootNode, anchor.range, 'record_declaration');
|
|
159
|
+
if (typeNode !== null) {
|
|
160
|
+
const synth = synthesizePrimaryConstructor(typeNode);
|
|
161
|
+
if (synth !== null)
|
|
162
|
+
out.push(synth);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
/** C# 12 primary constructor: `class X(a, b) { }` / `record X(a, b)`.
|
|
169
|
+
* The parameters are a bare `parameter_list` named child of the type
|
|
170
|
+
* declaration (no `constructor_declaration` node). Emit a synthetic
|
|
171
|
+
* @declaration.constructor match so the extractor creates a
|
|
172
|
+
* Constructor def in memberByOwner — free-call-fallback's
|
|
173
|
+
* `pickConstructorOrClass` then targets it for `new X(...)` calls. */
|
|
174
|
+
function synthesizePrimaryConstructor(typeNode) {
|
|
175
|
+
// Skip types with an explicit constructor_declaration — that would
|
|
176
|
+
// create duplicate defs.
|
|
177
|
+
const body = typeNode.childForFieldName('body');
|
|
178
|
+
if (body !== null) {
|
|
179
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
180
|
+
const child = body.namedChild(i);
|
|
181
|
+
if (child !== null && child.type === 'constructor_declaration')
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
let paramList = null;
|
|
186
|
+
for (let i = 0; i < typeNode.namedChildCount; i++) {
|
|
187
|
+
const child = typeNode.namedChild(i);
|
|
188
|
+
if (child !== null && child.type === 'parameter_list') {
|
|
189
|
+
paramList = child;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (paramList === null)
|
|
194
|
+
return null;
|
|
195
|
+
const nameNode = typeNode.childForFieldName('name');
|
|
196
|
+
if (nameNode === null)
|
|
197
|
+
return null;
|
|
198
|
+
const paramCount = paramList.namedChildren.filter((c) => c !== null && c.type === 'parameter').length;
|
|
199
|
+
const m = {
|
|
200
|
+
'@declaration.constructor': nodeToCapture('@declaration.constructor', paramList),
|
|
201
|
+
'@declaration.name': syntheticCapture('@declaration.name', nameNode, nameNode.text),
|
|
202
|
+
'@declaration.parameter-count': syntheticCapture('@declaration.parameter-count', paramList, String(paramCount)),
|
|
203
|
+
'@declaration.required-parameter-count': syntheticCapture('@declaration.required-parameter-count', paramList, String(paramCount)),
|
|
204
|
+
};
|
|
205
|
+
return m;
|
|
206
|
+
}
|
|
207
|
+
/** Infer a C# argument's static type from literal / constructor
|
|
208
|
+
* patterns. Returns `''` when the arg has no statically-derivable
|
|
209
|
+
* type (e.g. identifier — would require full type inference). */
|
|
210
|
+
function inferArgType(argNode) {
|
|
211
|
+
// `argument > expression` — tree-sitter-c-sharp wraps the value.
|
|
212
|
+
const expr = argNode.namedChild(0);
|
|
213
|
+
if (expr === null)
|
|
214
|
+
return '';
|
|
215
|
+
switch (expr.type) {
|
|
216
|
+
case 'integer_literal':
|
|
217
|
+
return 'int';
|
|
218
|
+
case 'real_literal':
|
|
219
|
+
return 'double';
|
|
220
|
+
case 'string_literal':
|
|
221
|
+
case 'verbatim_string_literal':
|
|
222
|
+
case 'interpolated_string_expression':
|
|
223
|
+
case 'raw_string_literal':
|
|
224
|
+
return 'string';
|
|
225
|
+
case 'character_literal':
|
|
226
|
+
return 'char';
|
|
227
|
+
case 'boolean_literal':
|
|
228
|
+
return 'bool';
|
|
229
|
+
case 'null_literal':
|
|
230
|
+
return 'null';
|
|
231
|
+
case 'object_creation_expression': {
|
|
232
|
+
const typeNode = expr.childForFieldName('type');
|
|
233
|
+
return typeNode?.text ?? '';
|
|
234
|
+
}
|
|
235
|
+
default:
|
|
236
|
+
return '';
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** Find the first C# function-like node at the given range. The
|
|
240
|
+
* declaration anchor range covers the whole method/constructor/etc.
|
|
241
|
+
* node, but the tag alone doesn't tell us which node type. */
|
|
242
|
+
function findFunctionNode(rootNode, range) {
|
|
243
|
+
for (const nodeType of FUNCTION_NODE_TYPES) {
|
|
244
|
+
const n = findNodeAtRange(rootNode, range, nodeType);
|
|
245
|
+
if (n !== null)
|
|
246
|
+
return n;
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decompose a C# `using_directive` into a `CaptureMatch` carrying the
|
|
3
|
+
* synthesized markers `@import.kind` / `@import.source` / `@import.name`
|
|
4
|
+
* / `@import.alias` that `interpretCsharpImport` consumes.
|
|
5
|
+
*
|
|
6
|
+
* Unlike Python's decomposer this is 1:1 — each `using` produces exactly
|
|
7
|
+
* one import. The split layer exists to expose the kind (namespace vs
|
|
8
|
+
* alias vs static) without pushing raw-text parsing into `interpret.ts`.
|
|
9
|
+
*
|
|
10
|
+
* using System; → namespace
|
|
11
|
+
* using System.Collections.Generic; → namespace
|
|
12
|
+
* using Foo = System.Bar; → alias
|
|
13
|
+
* using static System.Math; → static
|
|
14
|
+
* global using System.IO; → namespace (treated as file-scoped)
|
|
15
|
+
* using global::System.IO; → namespace (global:: alias stripped)
|
|
16
|
+
*/
|
|
17
|
+
import type { CaptureMatch } from '../../../../_shared/index.js';
|
|
18
|
+
import { type SyntaxNode } from '../../utils/ast-helpers.js';
|
|
19
|
+
export declare function splitUsingDirective(stmtNode: SyntaxNode): CaptureMatch | null;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decompose a C# `using_directive` into a `CaptureMatch` carrying the
|
|
3
|
+
* synthesized markers `@import.kind` / `@import.source` / `@import.name`
|
|
4
|
+
* / `@import.alias` that `interpretCsharpImport` consumes.
|
|
5
|
+
*
|
|
6
|
+
* Unlike Python's decomposer this is 1:1 — each `using` produces exactly
|
|
7
|
+
* one import. The split layer exists to expose the kind (namespace vs
|
|
8
|
+
* alias vs static) without pushing raw-text parsing into `interpret.ts`.
|
|
9
|
+
*
|
|
10
|
+
* using System; → namespace
|
|
11
|
+
* using System.Collections.Generic; → namespace
|
|
12
|
+
* using Foo = System.Bar; → alias
|
|
13
|
+
* using static System.Math; → static
|
|
14
|
+
* global using System.IO; → namespace (treated as file-scoped)
|
|
15
|
+
* using global::System.IO; → namespace (global:: alias stripped)
|
|
16
|
+
*/
|
|
17
|
+
import { nodeToCapture, syntheticCapture } from '../../utils/ast-helpers.js';
|
|
18
|
+
export function splitUsingDirective(stmtNode) {
|
|
19
|
+
if (stmtNode.type !== 'using_directive')
|
|
20
|
+
return null;
|
|
21
|
+
const spec = parseUsingDirective(stmtNode);
|
|
22
|
+
if (spec === null)
|
|
23
|
+
return null;
|
|
24
|
+
return buildImportMatch(stmtNode, spec);
|
|
25
|
+
}
|
|
26
|
+
function parseUsingDirective(node) {
|
|
27
|
+
// tree-sitter-c-sharp's using_directive exposes named children
|
|
28
|
+
// corresponding to the parts of the directive but omits keyword tokens
|
|
29
|
+
// (`using`, `static`, `global`) from the named-child list. We inspect
|
|
30
|
+
// the raw source text to detect the flavor — the grammar doesn't give
|
|
31
|
+
// us a cleaner signal.
|
|
32
|
+
const raw = node.text;
|
|
33
|
+
// Named child layout:
|
|
34
|
+
// namespace form: [pathNode]
|
|
35
|
+
// alias form: [aliasIdNode, pathNode] (name field = aliasId)
|
|
36
|
+
// static form: [pathNode] (same as namespace)
|
|
37
|
+
// global using: [pathNode] (same as namespace)
|
|
38
|
+
const aliasField = node.childForFieldName('name');
|
|
39
|
+
const children = node.namedChildren;
|
|
40
|
+
if (children.length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
// Alias form — the `name:` field is the alias identifier; the
|
|
43
|
+
// remaining named child is the type/namespace path.
|
|
44
|
+
if (aliasField !== null) {
|
|
45
|
+
const pathNode = children.find((c) => c !== null && c.startIndex !== aliasField.startIndex);
|
|
46
|
+
if (pathNode === undefined)
|
|
47
|
+
return null;
|
|
48
|
+
return {
|
|
49
|
+
kind: 'alias',
|
|
50
|
+
source: stripGenericArgs(unwrapGlobalAlias(pathNode.text)),
|
|
51
|
+
name: aliasField.text,
|
|
52
|
+
alias: aliasField.text,
|
|
53
|
+
atNode: node,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const pathNode = children[0];
|
|
57
|
+
if (pathNode === null)
|
|
58
|
+
return null;
|
|
59
|
+
const source = stripGenericArgs(unwrapGlobalAlias(pathNode.text));
|
|
60
|
+
if (source === '')
|
|
61
|
+
return null;
|
|
62
|
+
const lastSegment = source.split('.').pop() ?? source;
|
|
63
|
+
// `using static X.Y;` — detect by scanning the raw text before the path.
|
|
64
|
+
// `global using` behaves semantically as a file-scoped using for our
|
|
65
|
+
// purposes, so it isn't a separate kind here.
|
|
66
|
+
if (/^\s*(?:global\s+)?using\s+static\s/.test(raw)) {
|
|
67
|
+
return { kind: 'static', source, name: '*', atNode: node };
|
|
68
|
+
}
|
|
69
|
+
return { kind: 'namespace', source, name: lastSegment, atNode: node };
|
|
70
|
+
}
|
|
71
|
+
/** Strip `global::` prefix — `global::System.IO` → `System.IO`. */
|
|
72
|
+
function unwrapGlobalAlias(text) {
|
|
73
|
+
return text.replace(/^global::/, '');
|
|
74
|
+
}
|
|
75
|
+
/** Strip generic type arguments — `Dictionary<string, int>` → `Dictionary`. */
|
|
76
|
+
function stripGenericArgs(text) {
|
|
77
|
+
const lt = text.indexOf('<');
|
|
78
|
+
if (lt === -1)
|
|
79
|
+
return text;
|
|
80
|
+
return text.slice(0, lt);
|
|
81
|
+
}
|
|
82
|
+
function buildImportMatch(stmtNode, spec) {
|
|
83
|
+
const m = {
|
|
84
|
+
'@import.statement': nodeToCapture('@import.statement', stmtNode),
|
|
85
|
+
'@import.kind': syntheticCapture('@import.kind', spec.atNode, spec.kind),
|
|
86
|
+
'@import.source': syntheticCapture('@import.source', spec.atNode, spec.source),
|
|
87
|
+
'@import.name': syntheticCapture('@import.name', spec.atNode, spec.name),
|
|
88
|
+
};
|
|
89
|
+
if (spec.alias !== undefined) {
|
|
90
|
+
m['@import.alias'] = syntheticCapture('@import.alias', spec.atNode, spec.alias);
|
|
91
|
+
}
|
|
92
|
+
return m;
|
|
93
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter from `(ParsedImport, WorkspaceIndex)` → concrete file path.
|
|
3
|
+
*
|
|
4
|
+
* Unit 2 shape: suffix-match against the repo's `.cs` files. Each
|
|
5
|
+
* `using System.Collections.Generic;` could legally expand to multiple
|
|
6
|
+
* files (every `.cs` that declares `namespace System.Collections.Generic`
|
|
7
|
+
* — partial classes, assembly-wide namespaces). The scope-resolver
|
|
8
|
+
* contract returns a single primary target, so we pick the first
|
|
9
|
+
* match. Cross-file partial-class aggregation runs at graph-bridge
|
|
10
|
+
* time (Unit 6) via `populateOwners`.
|
|
11
|
+
*
|
|
12
|
+
* The legacy csproj-based `resolveCSharpImportInternal` needs config
|
|
13
|
+
* objects the scope-resolver doesn't carry; the Unit 7 parity gate
|
|
14
|
+
* will surface cases where the suffix-match diverges from the
|
|
15
|
+
* namespace-based resolver and we'll adjust the contract if needed.
|
|
16
|
+
*
|
|
17
|
+
* Returning `null` lets the finalize algorithm mark the edge as
|
|
18
|
+
* `linkStatus: 'unresolved'`.
|
|
19
|
+
*/
|
|
20
|
+
import type { ParsedImport, WorkspaceIndex } from '../../../../_shared/index.js';
|
|
21
|
+
export interface CsharpResolveContext {
|
|
22
|
+
readonly fromFile: string;
|
|
23
|
+
readonly allFilePaths: ReadonlySet<string>;
|
|
24
|
+
}
|
|
25
|
+
export declare function resolveCsharpImportTarget(parsedImport: ParsedImport, workspaceIndex: WorkspaceIndex): string | null;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter from `(ParsedImport, WorkspaceIndex)` → concrete file path.
|
|
3
|
+
*
|
|
4
|
+
* Unit 2 shape: suffix-match against the repo's `.cs` files. Each
|
|
5
|
+
* `using System.Collections.Generic;` could legally expand to multiple
|
|
6
|
+
* files (every `.cs` that declares `namespace System.Collections.Generic`
|
|
7
|
+
* — partial classes, assembly-wide namespaces). The scope-resolver
|
|
8
|
+
* contract returns a single primary target, so we pick the first
|
|
9
|
+
* match. Cross-file partial-class aggregation runs at graph-bridge
|
|
10
|
+
* time (Unit 6) via `populateOwners`.
|
|
11
|
+
*
|
|
12
|
+
* The legacy csproj-based `resolveCSharpImportInternal` needs config
|
|
13
|
+
* objects the scope-resolver doesn't carry; the Unit 7 parity gate
|
|
14
|
+
* will surface cases where the suffix-match diverges from the
|
|
15
|
+
* namespace-based resolver and we'll adjust the contract if needed.
|
|
16
|
+
*
|
|
17
|
+
* Returning `null` lets the finalize algorithm mark the edge as
|
|
18
|
+
* `linkStatus: 'unresolved'`.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveCsharpImportTarget(parsedImport, workspaceIndex) {
|
|
21
|
+
// WorkspaceIndex is `unknown` in the shared contract (Ring 1
|
|
22
|
+
// placeholder). The scope-resolution orchestrator hands us a
|
|
23
|
+
// CsharpResolveContext-shaped object; narrow structurally rather
|
|
24
|
+
// than via a cast chain so unexpected shapes return null cleanly.
|
|
25
|
+
const ctx = workspaceIndex;
|
|
26
|
+
if (ctx === undefined ||
|
|
27
|
+
typeof ctx.fromFile !== 'string' ||
|
|
28
|
+
!(ctx.allFilePaths instanceof Set)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (parsedImport.kind === 'dynamic-unresolved')
|
|
32
|
+
return null;
|
|
33
|
+
if (parsedImport.targetRaw === null || parsedImport.targetRaw === '')
|
|
34
|
+
return null;
|
|
35
|
+
// Namespace path: `System.Collections.Generic` → `System/Collections/Generic`.
|
|
36
|
+
const pathLike = parsedImport.targetRaw.replace(/\./g, '/');
|
|
37
|
+
const suffix = `/${pathLike}`;
|
|
38
|
+
// Exact file match: `System/Collections/Generic.cs` (rare but legal).
|
|
39
|
+
// Suffix match for nested layouts: `src/lib/System/Collections/Generic.cs`.
|
|
40
|
+
// Directory match: first `.cs` file directly inside the namespace dir
|
|
41
|
+
// (e.g. `System/Collections/Generic/List.cs` matches namespace Generic).
|
|
42
|
+
let exactFile = null;
|
|
43
|
+
let suffixFile = null;
|
|
44
|
+
let directoryChild = null;
|
|
45
|
+
const dirPrefix = `${pathLike}/`;
|
|
46
|
+
const suffixDirPrefix = `/${dirPrefix}`;
|
|
47
|
+
for (const raw of ctx.allFilePaths) {
|
|
48
|
+
const f = raw.replace(/\\/g, '/');
|
|
49
|
+
if (!f.endsWith('.cs'))
|
|
50
|
+
continue;
|
|
51
|
+
if (f === `${pathLike}.cs`) {
|
|
52
|
+
exactFile = raw;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (suffixFile === null && f.endsWith(`${suffix}.cs`)) {
|
|
56
|
+
suffixFile = raw;
|
|
57
|
+
}
|
|
58
|
+
if (directoryChild === null) {
|
|
59
|
+
// Namespace-to-directory match: pick the first `.cs` directly in
|
|
60
|
+
// the namespace dir (not nested deeper). Legacy resolver emits
|
|
61
|
+
// all of them; we take one so the scope-resolver contract stays
|
|
62
|
+
// single-target.
|
|
63
|
+
const atRoot = f.startsWith(dirPrefix);
|
|
64
|
+
const atNested = f.includes(suffixDirPrefix);
|
|
65
|
+
if (atRoot || atNested) {
|
|
66
|
+
const idx = atRoot ? 0 : f.indexOf(suffixDirPrefix) + 1;
|
|
67
|
+
const after = f.slice(idx + dirPrefix.length);
|
|
68
|
+
if (after.length > 0 && !after.includes('/')) {
|
|
69
|
+
directoryChild = raw;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (exactFile !== null)
|
|
75
|
+
return exactFile;
|
|
76
|
+
if (suffixFile !== null)
|
|
77
|
+
return suffixFile;
|
|
78
|
+
if (directoryChild !== null)
|
|
79
|
+
return directoryChild;
|
|
80
|
+
// Progressive prefix stripping — mirrors csproj's root-namespace
|
|
81
|
+
// mapping without the csproj. `using CrossFile.Models;` in a repo
|
|
82
|
+
// laid out `Models/User.cs` (no `CrossFile/` prefix) works because
|
|
83
|
+
// the legacy resolver consults csproj; the scope-resolver layer
|
|
84
|
+
// doesn't have csproj, so we try each suffix of the namespace path
|
|
85
|
+
// against `.cs` files and directories.
|
|
86
|
+
//
|
|
87
|
+
// Also handles `using static CrossFile.Models.UserFactory;` —
|
|
88
|
+
// strip the leading segment, try `Models/UserFactory.cs`; strip
|
|
89
|
+
// two, try `UserFactory.cs`.
|
|
90
|
+
const segments = pathLike.split('/').filter(Boolean);
|
|
91
|
+
for (let skip = 1; skip < segments.length; skip++) {
|
|
92
|
+
const tail = segments.slice(skip).join('/');
|
|
93
|
+
if (tail === '')
|
|
94
|
+
continue;
|
|
95
|
+
const tailFile = `${tail}.cs`;
|
|
96
|
+
const tailSuffix = `/${tailFile}`;
|
|
97
|
+
const tailDir = `${tail}/`;
|
|
98
|
+
const tailSuffixDir = `/${tailDir}`;
|
|
99
|
+
let tailDirectChild = null;
|
|
100
|
+
for (const raw of ctx.allFilePaths) {
|
|
101
|
+
const f = raw.replace(/\\/g, '/');
|
|
102
|
+
if (!f.endsWith('.cs'))
|
|
103
|
+
continue;
|
|
104
|
+
if (f === tailFile)
|
|
105
|
+
return raw;
|
|
106
|
+
if (f.endsWith(tailSuffix))
|
|
107
|
+
return raw;
|
|
108
|
+
if (tailDirectChild === null) {
|
|
109
|
+
const atRoot = f.startsWith(tailDir);
|
|
110
|
+
const atNested = f.includes(tailSuffixDir);
|
|
111
|
+
if (atRoot || atNested) {
|
|
112
|
+
const idx = atRoot ? 0 : f.indexOf(tailSuffixDir) + 1;
|
|
113
|
+
const after = f.slice(idx + tailDir.length);
|
|
114
|
+
if (after.length > 0 && !after.includes('/'))
|
|
115
|
+
tailDirectChild = raw;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (tailDirectChild !== null)
|
|
120
|
+
return tailDirectChild;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C# scope-resolution hooks (RFC #909 Ring 3, RFC §5).
|
|
3
|
+
*
|
|
4
|
+
* Public API barrel. Consumers should import from this file rather than
|
|
5
|
+
* the individual modules.
|
|
6
|
+
*
|
|
7
|
+
* Module layout (each file is a single concern):
|
|
8
|
+
*
|
|
9
|
+
* - `query.ts` — tree-sitter query + lazy parser/query singletons
|
|
10
|
+
* - `captures.ts` — `emitCsharpScopeCaptures` orchestrator
|
|
11
|
+
* - `import-decomposer.ts` — each `using` → ParsedImport-shaped captures
|
|
12
|
+
* - `interpret.ts` — capture-match → `ParsedImport` / `ParsedTypeBinding`
|
|
13
|
+
* - `simple-hooks.ts` — small/no-op hooks made explicit
|
|
14
|
+
* - `receiver-binding.ts` — synthesize `this`/`base` type-bindings on
|
|
15
|
+
* instance-method entry
|
|
16
|
+
* - `merge-bindings.ts` — C# `using` precedence
|
|
17
|
+
* - `arity.ts` — C# arity compatibility (`params`, default values)
|
|
18
|
+
* - `arity-metadata.ts` — synthesize arity metadata from declarations
|
|
19
|
+
* - `accessor-unwrap.ts` — `.Values` / `.Keys` receiver-type unwrap for
|
|
20
|
+
* `Dictionary<K,V>` chains
|
|
21
|
+
* - `namespace-siblings.ts` — AST-driven cross-file implicit-namespace
|
|
22
|
+
* visibility (file/namespace attribution, no
|
|
23
|
+
* regex; reuses orchestrator's treeCache)
|
|
24
|
+
* - `import-target.ts` — `(ParsedImport, WorkspaceIndex) → file path` adapter
|
|
25
|
+
* - `scope-resolver.ts` — `ScopeResolver` registered in `SCOPE_RESOLVERS`
|
|
26
|
+
* - `cache-stats.ts` — PROF_SCOPE_RESOLUTION cache hit/miss counters
|
|
27
|
+
*
|
|
28
|
+
* ## Known limitations
|
|
29
|
+
*
|
|
30
|
+
* The C# registry-primary path intentionally does NOT resolve the
|
|
31
|
+
* following. Each is a conscious trade-off at migration time.
|
|
32
|
+
*
|
|
33
|
+
* 1. **csproj-driven namespace resolution** — the legacy path
|
|
34
|
+
* consults `csharpConfigs` (the parsed .csproj workspace) to map
|
|
35
|
+
* `using X.Y;` back to the exact files declaring `namespace X.Y`.
|
|
36
|
+
* The scope-resolver contract passes only `allFilePaths`, so we
|
|
37
|
+
* fall back to suffix matching on `.cs` files. Unit 7's parity
|
|
38
|
+
* gate flags any divergence.
|
|
39
|
+
* 2. **Multi-file namespace expansion** — a single `using X.Y;` in
|
|
40
|
+
* the legacy path can emit multiple IMPORTS edges (every file
|
|
41
|
+
* declaring that namespace). The scope-resolver contract returns
|
|
42
|
+
* a single target, so we pick the first match; partial-class
|
|
43
|
+
* aggregation runs at graph-bridge time.
|
|
44
|
+
* 3. **Overload resolution by parameter type** — arity narrowing is
|
|
45
|
+
* wired (`arity.ts` + `arity-metadata.ts`), but type-based
|
|
46
|
+
* disambiguation (`F(int)` vs `F(string)` at a call with a typed
|
|
47
|
+
* argument) is left to the registry's type-binding layer.
|
|
48
|
+
* 4. **Generic type parameter resolution** — `List<User>` binds the
|
|
49
|
+
* bound name to `User` via the single-arg-generic stripper;
|
|
50
|
+
* nested generics (`Dictionary<K, List<V>>`) fall through the
|
|
51
|
+
* receiver-type heuristic.
|
|
52
|
+
* 5. **`dynamic` typed expressions** — runtime dispatch through
|
|
53
|
+
* `dynamic` is not followed.
|
|
54
|
+
* 6. **Preprocessor-conditional code** — `#if DEBUG` blocks parse
|
|
55
|
+
* as usual; branch selection is ignored, so both arms contribute
|
|
56
|
+
* bindings.
|
|
57
|
+
* 7. **Global using propagation across files** — treated as a
|
|
58
|
+
* file-scoped using for the declaring file. Unit 7 parity gate
|
|
59
|
+
* will flag cases where this matters.
|
|
60
|
+
* 8. **Expression-bodied `=>` members** — handled by the method
|
|
61
|
+
* extractor, but receiver synthesis for `=> this.Field` shortcuts
|
|
62
|
+
* follows the same path as block-bodied methods.
|
|
63
|
+
* 9. **Multi-namespace file attribution** — when a single file
|
|
64
|
+
* declares two namespaces (rare), all top-level classes are
|
|
65
|
+
* attributed to the first declared namespace via a `first-wins`
|
|
66
|
+
* rule in `namespace-siblings.ts`. Namespace detection itself is
|
|
67
|
+
* AST-driven (tree-sitter), so `global using static`, aliased
|
|
68
|
+
* `using static X = Y.Z;`, attributes, and preprocessor-gated
|
|
69
|
+
* declarations are all recognized correctly.
|
|
70
|
+
*
|
|
71
|
+
* Shadow-harness corpus parity is the authoritative signal for which
|
|
72
|
+
* of these matter in practice. The CI parity gate blocks any PR that
|
|
73
|
+
* regresses either the legacy or registry-primary run of
|
|
74
|
+
* `test/integration/resolvers/csharp.test.ts`.
|
|
75
|
+
*/
|
|
76
|
+
export { emitCsharpScopeCaptures } from './captures.js';
|
|
77
|
+
export { getCsharpCaptureCacheStats, resetCsharpCaptureCacheStats } from './cache-stats.js';
|
|
78
|
+
export { interpretCsharpImport, interpretCsharpTypeBinding } from './interpret.js';
|
|
79
|
+
export { csharpMergeBindings } from './merge-bindings.js';
|
|
80
|
+
export { csharpArityCompatibility } from './arity.js';
|
|
81
|
+
export { resolveCsharpImportTarget, type CsharpResolveContext } from './import-target.js';
|
|
82
|
+
export { csharpBindingScopeFor, csharpImportOwningScope, csharpReceiverBinding, } from './simple-hooks.js';
|