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,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter query for Python scope captures (RFC §5.1).
|
|
3
|
+
*
|
|
4
|
+
* Exposes lazy `Parser` and `Query` singletons so callers don't
|
|
5
|
+
* pay tree-sitter init cost per file.
|
|
6
|
+
*/
|
|
7
|
+
import Parser from 'tree-sitter';
|
|
8
|
+
import Python from 'tree-sitter-python';
|
|
9
|
+
const PYTHON_SCOPE_QUERY = `
|
|
10
|
+
;; Scopes
|
|
11
|
+
(module) @scope.module
|
|
12
|
+
(class_definition) @scope.class
|
|
13
|
+
(function_definition) @scope.function
|
|
14
|
+
|
|
15
|
+
;; Declarations
|
|
16
|
+
(class_definition
|
|
17
|
+
name: (identifier) @declaration.name) @declaration.class
|
|
18
|
+
|
|
19
|
+
(function_definition
|
|
20
|
+
name: (identifier) @declaration.name) @declaration.function
|
|
21
|
+
|
|
22
|
+
(assignment
|
|
23
|
+
left: (identifier) @declaration.name) @declaration.variable
|
|
24
|
+
|
|
25
|
+
;; Declarations: for-loop target — Python for-statements do NOT introduce
|
|
26
|
+
;; a new scope, so the loop variable binds in the enclosing function/module
|
|
27
|
+
;; scope. We emit it as a Variable declaration so Pass-2 attaches it.
|
|
28
|
+
(for_statement
|
|
29
|
+
left: (identifier) @declaration.name) @declaration.variable
|
|
30
|
+
|
|
31
|
+
;; Imports — single anchor per statement; interpretImport decomposes
|
|
32
|
+
(import_statement) @import.statement
|
|
33
|
+
(import_from_statement) @import.statement
|
|
34
|
+
|
|
35
|
+
;; Type bindings (parameter annotations)
|
|
36
|
+
(typed_parameter
|
|
37
|
+
(identifier) @type-binding.name
|
|
38
|
+
type: (type) @type-binding.type) @type-binding.parameter
|
|
39
|
+
|
|
40
|
+
(typed_default_parameter
|
|
41
|
+
name: (identifier) @type-binding.name
|
|
42
|
+
type: (type) @type-binding.type) @type-binding.parameter
|
|
43
|
+
|
|
44
|
+
;; Type bindings (constructor-inferred: \`u = User(...)\`)
|
|
45
|
+
;; Listed BEFORE the annotation pattern so \`u: User = find()\` — which
|
|
46
|
+
;; matches BOTH patterns — has the annotation (stronger source) win over
|
|
47
|
+
;; the constructor-inferred guess via the scope-extractor's source-
|
|
48
|
+
;; strength tie-break in pass4CollectTypeBindings.
|
|
49
|
+
(assignment
|
|
50
|
+
left: (identifier) @type-binding.name
|
|
51
|
+
right: (call
|
|
52
|
+
function: (identifier) @type-binding.type)) @type-binding.constructor
|
|
53
|
+
|
|
54
|
+
;; Qualified constructor (\`u = models.User(...)\`). Captures the
|
|
55
|
+
;; attribute node as the type — its \`.text\` is the full dotted path
|
|
56
|
+
;; (\`models.User\`), which \`resolveTypeRef\` resolves via
|
|
57
|
+
;; \`QualifiedNameIndex\` Phase 2.
|
|
58
|
+
(assignment
|
|
59
|
+
left: (identifier) @type-binding.name
|
|
60
|
+
right: (call
|
|
61
|
+
function: (attribute) @type-binding.type)) @type-binding.constructor
|
|
62
|
+
|
|
63
|
+
;; Walrus operator: \`(u := User(...))\`. Python 3.8+ named expression.
|
|
64
|
+
;; Shares the constructor-inferred shape so the binding lands in the
|
|
65
|
+
;; enclosing function/module scope's typeBindings the same way a plain
|
|
66
|
+
;; assignment would.
|
|
67
|
+
(named_expression
|
|
68
|
+
name: (identifier) @type-binding.name
|
|
69
|
+
value: (call
|
|
70
|
+
function: (identifier) @type-binding.type)) @type-binding.constructor
|
|
71
|
+
|
|
72
|
+
(named_expression
|
|
73
|
+
name: (identifier) @type-binding.name
|
|
74
|
+
value: (call
|
|
75
|
+
function: (attribute) @type-binding.type)) @type-binding.constructor
|
|
76
|
+
|
|
77
|
+
;; Match-case as-pattern: \`case User() as u:\` → \`u: User\`. The
|
|
78
|
+
;; class_pattern's dotted_name carries the type; the outer as_pattern's
|
|
79
|
+
;; second child is the binding name.
|
|
80
|
+
(as_pattern
|
|
81
|
+
(case_pattern
|
|
82
|
+
(class_pattern
|
|
83
|
+
(dotted_name) @type-binding.type))
|
|
84
|
+
(identifier) @type-binding.name) @type-binding.constructor
|
|
85
|
+
|
|
86
|
+
;; Assignment chain: \`alias = user\` — the new name inherits the
|
|
87
|
+
;; RHS-identifier's type. The pattern emits a TypeRef whose rawName is
|
|
88
|
+
;; the RHS identifier's text; the scope-extractor's post-pass follows
|
|
89
|
+
;; the chain so \`alias\` ends up pointing at whatever type \`user\` has.
|
|
90
|
+
(assignment
|
|
91
|
+
left: (identifier) @type-binding.name
|
|
92
|
+
right: (identifier) @type-binding.type) @type-binding.alias
|
|
93
|
+
|
|
94
|
+
;; For-loop iterable of an already-typed variable:
|
|
95
|
+
;; def f(users: list[User]):
|
|
96
|
+
;; for u in users: # u: users (chained via post-pass)
|
|
97
|
+
;; u.save()
|
|
98
|
+
;; The chain post-pass resolves \`users\` → its own type \`User\` via
|
|
99
|
+
;; the generic-arg stripping in \`interpret.ts\`.
|
|
100
|
+
(for_statement
|
|
101
|
+
left: (identifier) @type-binding.name
|
|
102
|
+
right: (identifier) @type-binding.type) @type-binding.alias
|
|
103
|
+
|
|
104
|
+
;; For-loop iterable of a free-call result:
|
|
105
|
+
;; def get_users() -> list[User]: ...
|
|
106
|
+
;; for u in get_users(): # u: get_users → User via chain follow
|
|
107
|
+
;; u.save()
|
|
108
|
+
;; Captures the call's function identifier as the rawName. With
|
|
109
|
+
;; \`propagateImportedReturnTypes\`, this works cross-file too.
|
|
110
|
+
(for_statement
|
|
111
|
+
left: (identifier) @type-binding.name
|
|
112
|
+
right: (call
|
|
113
|
+
function: (identifier) @type-binding.type)) @type-binding.alias
|
|
114
|
+
|
|
115
|
+
;; for (i, u) in enumerate(X) — paren-tuple, bind last element to X's
|
|
116
|
+
;; element type. \`enumerate(X)\` yields (int, X-element); the second
|
|
117
|
+
;; pattern var takes X (which the chain-follow then unwraps to its
|
|
118
|
+
;; element type via generic-strip in interpret.ts).
|
|
119
|
+
(for_statement
|
|
120
|
+
left: (tuple_pattern
|
|
121
|
+
(identifier)
|
|
122
|
+
(identifier) @type-binding.name)
|
|
123
|
+
right: (call
|
|
124
|
+
function: (identifier) @_enum
|
|
125
|
+
arguments: (argument_list
|
|
126
|
+
(identifier) @type-binding.type))
|
|
127
|
+
(#eq? @_enum "enumerate")) @type-binding.alias
|
|
128
|
+
|
|
129
|
+
;; for i, u in enumerate(X) — pattern_list (no parens) variant.
|
|
130
|
+
(for_statement
|
|
131
|
+
left: (pattern_list
|
|
132
|
+
(identifier)
|
|
133
|
+
(identifier) @type-binding.name)
|
|
134
|
+
right: (call
|
|
135
|
+
function: (identifier) @_enum
|
|
136
|
+
arguments: (argument_list
|
|
137
|
+
(identifier) @type-binding.type))
|
|
138
|
+
(#eq? @_enum "enumerate")) @type-binding.alias
|
|
139
|
+
|
|
140
|
+
;; for k, v in d.items() — bind v to d. The chain-follow unwraps d's
|
|
141
|
+
;; dict[K, V] annotation to V via the dict-aware stripGeneric in
|
|
142
|
+
;; interpret.ts. Covers both pattern_list and tuple_pattern shapes.
|
|
143
|
+
(for_statement
|
|
144
|
+
left: (pattern_list
|
|
145
|
+
(identifier)
|
|
146
|
+
(identifier) @type-binding.name)
|
|
147
|
+
right: (call
|
|
148
|
+
function: (attribute
|
|
149
|
+
object: (identifier) @type-binding.type
|
|
150
|
+
attribute: (identifier) @_items))
|
|
151
|
+
(#eq? @_items "items")) @type-binding.alias
|
|
152
|
+
|
|
153
|
+
(for_statement
|
|
154
|
+
left: (tuple_pattern
|
|
155
|
+
(identifier)
|
|
156
|
+
(identifier) @type-binding.name)
|
|
157
|
+
right: (call
|
|
158
|
+
function: (attribute
|
|
159
|
+
object: (identifier) @type-binding.type
|
|
160
|
+
attribute: (identifier) @_items))
|
|
161
|
+
(#eq? @_items "items")) @type-binding.alias
|
|
162
|
+
|
|
163
|
+
;; for i, (k, v) in enumerate(d.items()) — nested tuple destructuring.
|
|
164
|
+
;; Bind v (last id of the nested tuple) to d (the dict).
|
|
165
|
+
(for_statement
|
|
166
|
+
left: (pattern_list
|
|
167
|
+
(identifier)
|
|
168
|
+
(tuple_pattern
|
|
169
|
+
(identifier)
|
|
170
|
+
(identifier) @type-binding.name))
|
|
171
|
+
right: (call
|
|
172
|
+
function: (identifier) @_enum
|
|
173
|
+
arguments: (argument_list
|
|
174
|
+
(call
|
|
175
|
+
function: (attribute
|
|
176
|
+
object: (identifier) @type-binding.type
|
|
177
|
+
attribute: (identifier) @_items))))
|
|
178
|
+
(#eq? @_enum "enumerate")
|
|
179
|
+
(#eq? @_items "items")) @type-binding.alias
|
|
180
|
+
|
|
181
|
+
;; for i, k, v in enumerate(d.items()) — 3-var flat destructuring of
|
|
182
|
+
;; the (i, (k,v)) tuple emitted by enumerate over items(). Bind v
|
|
183
|
+
;; (last id) to d.
|
|
184
|
+
(for_statement
|
|
185
|
+
left: (pattern_list
|
|
186
|
+
(identifier)
|
|
187
|
+
(identifier)
|
|
188
|
+
(identifier) @type-binding.name)
|
|
189
|
+
right: (call
|
|
190
|
+
function: (identifier) @_enum
|
|
191
|
+
arguments: (argument_list
|
|
192
|
+
(call
|
|
193
|
+
function: (attribute
|
|
194
|
+
object: (identifier) @type-binding.type
|
|
195
|
+
attribute: (identifier) @_items))))
|
|
196
|
+
(#eq? @_enum "enumerate")
|
|
197
|
+
(#eq? @_items "items")) @type-binding.alias
|
|
198
|
+
|
|
199
|
+
;; for u in self.X — heuristic: bind u to X (the attribute name).
|
|
200
|
+
;; The chain-follow then resolves X via the enclosing method's
|
|
201
|
+
;; parameter typeBinding, supporting fixtures that reference
|
|
202
|
+
;; \`self.X\` as a stand-in for a parameter X (matches legacy DAG
|
|
203
|
+
;; behavior).
|
|
204
|
+
(for_statement
|
|
205
|
+
left: (identifier) @type-binding.name
|
|
206
|
+
right: (attribute
|
|
207
|
+
object: (identifier) @_self
|
|
208
|
+
attribute: (identifier) @type-binding.type)
|
|
209
|
+
(#eq? @_self "self")) @type-binding.alias
|
|
210
|
+
|
|
211
|
+
;; for v in d.values() — bind v to d (dict-strip yields value type).
|
|
212
|
+
(for_statement
|
|
213
|
+
left: (identifier) @type-binding.name
|
|
214
|
+
right: (call
|
|
215
|
+
function: (attribute
|
|
216
|
+
object: (identifier) @type-binding.type
|
|
217
|
+
attribute: (identifier) @_values))
|
|
218
|
+
(#eq? @_values "values")) @type-binding.alias
|
|
219
|
+
|
|
220
|
+
;; Type bindings (variable annotations: \`u: User\` / \`u: User = x\`)
|
|
221
|
+
(assignment
|
|
222
|
+
left: (identifier) @type-binding.name
|
|
223
|
+
type: (type) @type-binding.type) @type-binding.annotation
|
|
224
|
+
|
|
225
|
+
;; Return-type annotation: \`def get_user() -> User:\` binds the
|
|
226
|
+
;; FUNCTION'S NAME to its return type in the enclosing scope. Combined
|
|
227
|
+
;; with the constructor-inferred + chain-follow path, \`u = get_user()\`
|
|
228
|
+
;; then resolves \`u: User\` cross-call. The Python provider hoists the
|
|
229
|
+
;; binding via \`pythonBindingScopeFor\` to the function's parent scope
|
|
230
|
+
;; so callers in module/class scope see it.
|
|
231
|
+
(function_definition
|
|
232
|
+
name: (identifier) @type-binding.name
|
|
233
|
+
return_type: (type) @type-binding.type) @type-binding.return
|
|
234
|
+
|
|
235
|
+
;; References — calls
|
|
236
|
+
(call
|
|
237
|
+
function: (identifier) @reference.name) @reference.call.free
|
|
238
|
+
|
|
239
|
+
(call
|
|
240
|
+
function: (attribute
|
|
241
|
+
object: (_) @reference.receiver
|
|
242
|
+
attribute: (identifier) @reference.name)) @reference.call.member
|
|
243
|
+
|
|
244
|
+
;; References — attribute writes: \`obj.name = "x"\` emits a write
|
|
245
|
+
;; ACCESSES edge from the enclosing function to the field on obj's
|
|
246
|
+
;; class. The receiver-bound emit pass resolves obj → its class and
|
|
247
|
+
;; \`name\` → the field def via the existing typeref-receiver path.
|
|
248
|
+
(assignment
|
|
249
|
+
left: (attribute
|
|
250
|
+
object: (_) @reference.receiver
|
|
251
|
+
attribute: (identifier) @reference.name)) @reference.write.member
|
|
252
|
+
`;
|
|
253
|
+
let _parser = null;
|
|
254
|
+
let _query = null;
|
|
255
|
+
export function getPythonParser() {
|
|
256
|
+
if (_parser === null) {
|
|
257
|
+
_parser = new Parser();
|
|
258
|
+
_parser.setLanguage(Python);
|
|
259
|
+
}
|
|
260
|
+
return _parser;
|
|
261
|
+
}
|
|
262
|
+
export function getPythonScopeQuery() {
|
|
263
|
+
if (_query === null) {
|
|
264
|
+
_query = new Parser.Query(Python, PYTHON_SCOPE_QUERY);
|
|
265
|
+
}
|
|
266
|
+
return _query;
|
|
267
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synthesize `@type-binding.self` / `@type-binding.cls` captures for
|
|
3
|
+
* methods.
|
|
4
|
+
*
|
|
5
|
+
* Tree-sitter can't easily express "the first parameter of a function
|
|
6
|
+
* defined directly inside a class body" via a single static query.
|
|
7
|
+
* Doing this in code keeps the embedded scope query declarative and
|
|
8
|
+
* lets us encode the `@classmethod` / `@staticmethod` decorator
|
|
9
|
+
* awareness that Python's runtime depends on.
|
|
10
|
+
*/
|
|
11
|
+
import type { CaptureMatch } from '../../../../_shared/index.js';
|
|
12
|
+
import { type SyntaxNode } from '../../utils/ast-helpers.js';
|
|
13
|
+
/**
|
|
14
|
+
* Build a `@type-binding.self` (instance method) or `@type-binding.cls`
|
|
15
|
+
* (`@classmethod`) match for `fnNode`, or `null` if `fnNode` is not a
|
|
16
|
+
* method, is `@staticmethod`, or has no parameters.
|
|
17
|
+
*
|
|
18
|
+
* The caller is responsible for guaranteeing `fnNode.type ===
|
|
19
|
+
* 'function_definition'`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function synthesizeReceiverTypeBinding(fnNode: SyntaxNode): CaptureMatch | null;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synthesize `@type-binding.self` / `@type-binding.cls` captures for
|
|
3
|
+
* methods.
|
|
4
|
+
*
|
|
5
|
+
* Tree-sitter can't easily express "the first parameter of a function
|
|
6
|
+
* defined directly inside a class body" via a single static query.
|
|
7
|
+
* Doing this in code keeps the embedded scope query declarative and
|
|
8
|
+
* lets us encode the `@classmethod` / `@staticmethod` decorator
|
|
9
|
+
* awareness that Python's runtime depends on.
|
|
10
|
+
*/
|
|
11
|
+
import { nodeToCapture, syntheticCapture } from '../../utils/ast-helpers.js';
|
|
12
|
+
/** Walk up to the enclosing `class_definition`, ignoring the immediate
|
|
13
|
+
* `decorated_definition` wrapper. Returns `null` when the function is
|
|
14
|
+
* free, lambda-bodied, or nested inside another function. */
|
|
15
|
+
function findEnclosingClassDefinition(node) {
|
|
16
|
+
let cur = node.parent;
|
|
17
|
+
while (cur !== null) {
|
|
18
|
+
if (cur.type === 'class_definition')
|
|
19
|
+
return cur;
|
|
20
|
+
if (cur.type === 'function_definition')
|
|
21
|
+
return null;
|
|
22
|
+
cur = cur.parent;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function classDefinitionName(classNode) {
|
|
27
|
+
return classNode.childForFieldName('name')?.text ?? null;
|
|
28
|
+
}
|
|
29
|
+
/** Does the function carry a `@<decoratorName>` decorator? Matches both
|
|
30
|
+
* bare `@classmethod` and module-qualified `@functools.classmethod`. */
|
|
31
|
+
function hasDecorator(fnNode, decoratorName) {
|
|
32
|
+
const parent = fnNode.parent;
|
|
33
|
+
if (parent === null || parent.type !== 'decorated_definition')
|
|
34
|
+
return false;
|
|
35
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
36
|
+
const child = parent.namedChild(i);
|
|
37
|
+
if (child === null || child.type !== 'decorator')
|
|
38
|
+
continue;
|
|
39
|
+
const text = child.text.replace(/^@/, '').split('(')[0].trim();
|
|
40
|
+
const tail = text.split('.').pop();
|
|
41
|
+
if (tail === decoratorName)
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function firstNamedParameter(parameters) {
|
|
47
|
+
for (let i = 0; i < parameters.namedChildCount; i++) {
|
|
48
|
+
const child = parameters.namedChild(i);
|
|
49
|
+
if (child === null)
|
|
50
|
+
continue;
|
|
51
|
+
// Skip `*` / `/` markers.
|
|
52
|
+
if (child.type === 'positional_separator' || child.type === 'keyword_separator')
|
|
53
|
+
continue;
|
|
54
|
+
return child;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function firstParameterName(param) {
|
|
59
|
+
if (param.type === 'identifier')
|
|
60
|
+
return param.text;
|
|
61
|
+
// typed_parameter / default_parameter / typed_default_parameter:
|
|
62
|
+
// first child holds the identifier / pattern.
|
|
63
|
+
const ident = param.childForFieldName('name') ?? findIdentifierChild(param);
|
|
64
|
+
return ident?.text ?? null;
|
|
65
|
+
}
|
|
66
|
+
function findIdentifierChild(node) {
|
|
67
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
68
|
+
const child = node.namedChild(i);
|
|
69
|
+
if (child !== null && child.type === 'identifier')
|
|
70
|
+
return child;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build a `@type-binding.self` (instance method) or `@type-binding.cls`
|
|
76
|
+
* (`@classmethod`) match for `fnNode`, or `null` if `fnNode` is not a
|
|
77
|
+
* method, is `@staticmethod`, or has no parameters.
|
|
78
|
+
*
|
|
79
|
+
* The caller is responsible for guaranteeing `fnNode.type ===
|
|
80
|
+
* 'function_definition'`.
|
|
81
|
+
*/
|
|
82
|
+
export function synthesizeReceiverTypeBinding(fnNode) {
|
|
83
|
+
const enclosingClass = findEnclosingClassDefinition(fnNode);
|
|
84
|
+
if (enclosingClass === null)
|
|
85
|
+
return null;
|
|
86
|
+
// Skip @staticmethod-decorated methods (no implicit receiver).
|
|
87
|
+
if (hasDecorator(fnNode, 'staticmethod'))
|
|
88
|
+
return null;
|
|
89
|
+
const isClassmethod = hasDecorator(fnNode, 'classmethod');
|
|
90
|
+
const params = fnNode.childForFieldName('parameters');
|
|
91
|
+
if (params === null)
|
|
92
|
+
return null;
|
|
93
|
+
const first = firstNamedParameter(params);
|
|
94
|
+
if (first === null)
|
|
95
|
+
return null;
|
|
96
|
+
const className = classDefinitionName(enclosingClass);
|
|
97
|
+
if (className === null)
|
|
98
|
+
return null;
|
|
99
|
+
const firstName = firstParameterName(first);
|
|
100
|
+
if (firstName === null)
|
|
101
|
+
return null;
|
|
102
|
+
// Receiver convention: instance methods get `self`, classmethods get `cls`.
|
|
103
|
+
// We trust the AST literal name (Python convention is strict in practice).
|
|
104
|
+
if (isClassmethod) {
|
|
105
|
+
return {
|
|
106
|
+
'@type-binding.cls': nodeToCapture('@type-binding.cls', first),
|
|
107
|
+
'@type-binding.name': syntheticCapture('@type-binding.name', first, firstName),
|
|
108
|
+
'@type-binding.type': syntheticCapture('@type-binding.type', first, className),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
'@type-binding.self': nodeToCapture('@type-binding.self', first),
|
|
113
|
+
'@type-binding.name': syntheticCapture('@type-binding.name', first, firstName),
|
|
114
|
+
'@type-binding.type': syntheticCapture('@type-binding.type', first, className),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed
|
|
3
|
+
* by the generic `runScopeResolution` orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* The provider is a thin wiring object — Python's specific bits
|
|
6
|
+
* (super recognizer, LEGB merge precedence, Python's relative-import
|
|
7
|
+
* resolver, the simplified MRO walk) plug into `runScopeResolution`.
|
|
8
|
+
*
|
|
9
|
+
* Migration reference: when bringing up the next language
|
|
10
|
+
* (TypeScript / Java / Kotlin / Ruby), copy this file's structure —
|
|
11
|
+
* implement the 6 required `ScopeResolver` fields, optionally toggle
|
|
12
|
+
* the 2 booleans, and register in `scope-resolution/pipeline/registry.ts`.
|
|
13
|
+
*/
|
|
14
|
+
import type { ScopeResolver } from '../../scope-resolution/contract/scope-resolver.js';
|
|
15
|
+
declare const pythonScopeResolver: ScopeResolver;
|
|
16
|
+
export { pythonScopeResolver };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed
|
|
3
|
+
* by the generic `runScopeResolution` orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* The provider is a thin wiring object — Python's specific bits
|
|
6
|
+
* (super recognizer, LEGB merge precedence, Python's relative-import
|
|
7
|
+
* resolver, the simplified MRO walk) plug into `runScopeResolution`.
|
|
8
|
+
*
|
|
9
|
+
* Migration reference: when bringing up the next language
|
|
10
|
+
* (TypeScript / Java / Kotlin / Ruby), copy this file's structure —
|
|
11
|
+
* implement the 6 required `ScopeResolver` fields, optionally toggle
|
|
12
|
+
* the 2 booleans, and register in `scope-resolution/pipeline/registry.ts`.
|
|
13
|
+
*/
|
|
14
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
15
|
+
import { buildMro, defaultLinearize } from '../../scope-resolution/passes/mro.js';
|
|
16
|
+
import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js';
|
|
17
|
+
import { pythonProvider } from '../python.js';
|
|
18
|
+
import { pythonArityCompatibility, pythonMergeBindings, resolvePythonImportTarget, } from './index.js';
|
|
19
|
+
const pythonScopeResolver = {
|
|
20
|
+
language: SupportedLanguages.Python,
|
|
21
|
+
languageProvider: pythonProvider,
|
|
22
|
+
importEdgeReason: 'python-scope: import',
|
|
23
|
+
resolveImportTarget: (targetRaw, fromFile, allFilePaths) => {
|
|
24
|
+
// Copy the orchestrator's `ReadonlySet` into a `Set` because the
|
|
25
|
+
// legacy Python resolver chain (`resolvePythonImportInternal` →
|
|
26
|
+
// `resolveAbsoluteFromFiles` / `hasRepoCandidate`) is typed to
|
|
27
|
+
// receive a mutable `Set<string>`. The copy is O(N) but called
|
|
28
|
+
// once per import — trivial compared to the parser work.
|
|
29
|
+
const ws = { fromFile, allFilePaths: new Set(allFilePaths) };
|
|
30
|
+
// `WorkspaceIndex` is an opaque `unknown` placeholder in the
|
|
31
|
+
// shared contract, so `ws` passes structurally without a cast.
|
|
32
|
+
return resolvePythonImportTarget({ kind: 'named', localName: '_', importedName: '_', targetRaw }, ws);
|
|
33
|
+
},
|
|
34
|
+
// Python LEGB precedence: local > import/namespace/reexport > wildcard.
|
|
35
|
+
// The per-scope id is unused by pythonMergeBindings (tier ordering
|
|
36
|
+
// is computed purely from BindingRef.origin), so we don't need to
|
|
37
|
+
// synthesize a Scope.
|
|
38
|
+
mergeBindings: (existing, incoming) => [...pythonMergeBindings([...existing, ...incoming])],
|
|
39
|
+
// Adapter: pythonArityCompatibility predates RegistryProviders and
|
|
40
|
+
// uses (def, callsite). ScopeResolver contract is (callsite, def).
|
|
41
|
+
// Wrapper kept to honor both contracts without altering the legacy
|
|
42
|
+
// shape that LanguageProvider.arityCompatibility consumes.
|
|
43
|
+
arityCompatibility: (callsite, def) => pythonArityCompatibility(def, callsite),
|
|
44
|
+
buildMro: (graph, parsedFiles, nodeLookup) => buildMro(graph, parsedFiles, nodeLookup, defaultLinearize),
|
|
45
|
+
populateOwners: (parsed) => populateClassOwnedMembers(parsed),
|
|
46
|
+
isSuperReceiver: (text) => /^super\s*\(/.test(text),
|
|
47
|
+
// Python is dynamically typed — field-fallback heuristic on, return-
|
|
48
|
+
// type propagation across imports on. Both default to true; listed
|
|
49
|
+
// explicitly here for documentation.
|
|
50
|
+
fieldFallbackOnMethodLookup: true,
|
|
51
|
+
propagatesReturnTypesAcrossImports: true,
|
|
52
|
+
};
|
|
53
|
+
export { pythonScopeResolver };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trivial / no-op-ish hooks for the Python provider. Kept together
|
|
3
|
+
* because each is a few lines and they share a common theme: they exist
|
|
4
|
+
* to make the provider's choice explicit (rather than relying on
|
|
5
|
+
* "absence == default") so reviewers don't have to re-derive the
|
|
6
|
+
* analysis.
|
|
7
|
+
*/
|
|
8
|
+
import type { CaptureMatch, ParsedImport, Scope, ScopeId, ScopeTree, TypeRef } from '../../../../_shared/index.js';
|
|
9
|
+
/** Python has no block scope, so the central extractor's "innermost
|
|
10
|
+
* enclosing scope" default is already correct: `for x in …` creates
|
|
11
|
+
* `x` in the enclosing function/module scope (because we never emit a
|
|
12
|
+
* `@scope.block` for the for-loop body), comprehension variables stay
|
|
13
|
+
* in their expression context, etc. Returns `null` to delegate. */
|
|
14
|
+
export declare function pythonBindingScopeFor(_decl: CaptureMatch, _innermost: Scope, _tree: ScopeTree): ScopeId | null;
|
|
15
|
+
/** Function-local `from x import Y` should attach the binding to the
|
|
16
|
+
* function scope, not the module. Class-body imports (rare but legal —
|
|
17
|
+
* `class A: import x` makes `x` a class attribute) attach to the class.
|
|
18
|
+
* Module-level imports delegate to the central default. */
|
|
19
|
+
export declare function pythonImportOwningScope(_imp: ParsedImport, innermost: Scope, _tree: ScopeTree): ScopeId | null;
|
|
20
|
+
/** Look up `self` or `cls` in the function scope's type bindings.
|
|
21
|
+
* Returns `null` for free functions (no `self`/`cls`) and for
|
|
22
|
+
* non-Function scopes. */
|
|
23
|
+
export declare function pythonReceiverBinding(functionScope: Scope): TypeRef | null;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trivial / no-op-ish hooks for the Python provider. Kept together
|
|
3
|
+
* because each is a few lines and they share a common theme: they exist
|
|
4
|
+
* to make the provider's choice explicit (rather than relying on
|
|
5
|
+
* "absence == default") so reviewers don't have to re-derive the
|
|
6
|
+
* analysis.
|
|
7
|
+
*/
|
|
8
|
+
// ─── bindingScopeFor ──────────────────────────────────────────────────────
|
|
9
|
+
/** Python has no block scope, so the central extractor's "innermost
|
|
10
|
+
* enclosing scope" default is already correct: `for x in …` creates
|
|
11
|
+
* `x` in the enclosing function/module scope (because we never emit a
|
|
12
|
+
* `@scope.block` for the for-loop body), comprehension variables stay
|
|
13
|
+
* in their expression context, etc. Returns `null` to delegate. */
|
|
14
|
+
export function pythonBindingScopeFor(_decl, _innermost, _tree) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
// ─── importOwningScope ────────────────────────────────────────────────────
|
|
18
|
+
/** Function-local `from x import Y` should attach the binding to the
|
|
19
|
+
* function scope, not the module. Class-body imports (rare but legal —
|
|
20
|
+
* `class A: import x` makes `x` a class attribute) attach to the class.
|
|
21
|
+
* Module-level imports delegate to the central default. */
|
|
22
|
+
export function pythonImportOwningScope(_imp, innermost, _tree) {
|
|
23
|
+
if (innermost.kind === 'Function' || innermost.kind === 'Class')
|
|
24
|
+
return innermost.id;
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
// ─── receiverBinding ──────────────────────────────────────────────────────
|
|
28
|
+
/** Look up `self` or `cls` in the function scope's type bindings.
|
|
29
|
+
* Returns `null` for free functions (no `self`/`cls`) and for
|
|
30
|
+
* non-Function scopes. */
|
|
31
|
+
export function pythonReceiverBinding(functionScope) {
|
|
32
|
+
if (functionScope.kind !== 'Function')
|
|
33
|
+
return null;
|
|
34
|
+
return functionScope.typeBindings.get('self') ?? functionScope.typeBindings.get('cls') ?? null;
|
|
35
|
+
}
|
|
@@ -28,6 +28,7 @@ import { pythonVariableConfig } from '../variable-extractors/configs/python.js';
|
|
|
28
28
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
29
29
|
import { pythonCallConfig } from '../call-extractors/configs/python.js';
|
|
30
30
|
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
31
|
+
import { emitPythonScopeCaptures, interpretPythonImport, interpretPythonTypeBinding, pythonArityCompatibility, pythonBindingScopeFor, pythonImportOwningScope, pythonMergeBindings, pythonReceiverBinding, resolvePythonImportTarget, } from './python/index.js';
|
|
31
32
|
const BUILT_INS = new Set([
|
|
32
33
|
'print',
|
|
33
34
|
'len',
|
|
@@ -74,4 +75,17 @@ export const pythonProvider = defineLanguage({
|
|
|
74
75
|
classExtractor: createClassExtractor(pythonClassConfig),
|
|
75
76
|
heritageExtractor: createHeritageExtractor(SupportedLanguages.Python),
|
|
76
77
|
builtInNames: BUILT_INS,
|
|
78
|
+
// ── RFC #909 Ring 3: scope-based resolution hooks (RFC §5) ──────────
|
|
79
|
+
// Python is the first migration. See ./python/index.ts for the
|
|
80
|
+
// full per-hook rationale and the canonical capture vocabulary in
|
|
81
|
+
// ./python/query.ts (PYTHON_SCOPE_QUERY constant).
|
|
82
|
+
emitScopeCaptures: emitPythonScopeCaptures,
|
|
83
|
+
interpretImport: interpretPythonImport,
|
|
84
|
+
interpretTypeBinding: interpretPythonTypeBinding,
|
|
85
|
+
bindingScopeFor: pythonBindingScopeFor,
|
|
86
|
+
importOwningScope: pythonImportOwningScope,
|
|
87
|
+
mergeBindings: (_scope, bindings) => pythonMergeBindings(bindings),
|
|
88
|
+
receiverBinding: pythonReceiverBinding,
|
|
89
|
+
arityCompatibility: pythonArityCompatibility,
|
|
90
|
+
resolveImportTarget: resolvePythonImportTarget,
|
|
77
91
|
});
|
|
@@ -37,6 +37,15 @@ export interface MethodRegistry {
|
|
|
37
37
|
* allocation reachable from two indexes.
|
|
38
38
|
*/
|
|
39
39
|
lookupMethodByName(name: string): readonly SymbolDefinition[];
|
|
40
|
+
/**
|
|
41
|
+
* Return every overload registered under `(ownerNodeId, methodName)`,
|
|
42
|
+
* unfiltered by arity or return type. This is the raw owner-scoped
|
|
43
|
+
* view — callers that need arity narrowing or unambiguous single-
|
|
44
|
+
* result semantics should use `lookupMethodByOwner` instead.
|
|
45
|
+
*
|
|
46
|
+
* Returns `[]` on miss so callers can iterate without null checks.
|
|
47
|
+
*/
|
|
48
|
+
lookupAllByOwner(ownerNodeId: string, methodName: string): readonly SymbolDefinition[];
|
|
40
49
|
/**
|
|
41
50
|
* True iff at least one registered def has `type === 'Function'` — i.e.,
|
|
42
51
|
* a Python/Rust/Kotlin class method emitted by the worker as
|
|
@@ -88,6 +88,9 @@ export const createMethodRegistry = () => {
|
|
|
88
88
|
const lookupMethodByName = (name) => {
|
|
89
89
|
return methodsByName.get(name) ?? EMPTY;
|
|
90
90
|
};
|
|
91
|
+
const lookupAllByOwner = (ownerNodeId, methodName) => {
|
|
92
|
+
return methodByOwner.get(`${ownerNodeId}\0${methodName}`) ?? EMPTY;
|
|
93
|
+
};
|
|
91
94
|
const register = (ownerNodeId, methodName, def) => {
|
|
92
95
|
const key = `${ownerNodeId}\0${methodName}`;
|
|
93
96
|
const existing = methodByOwner.get(key);
|
|
@@ -121,6 +124,7 @@ export const createMethodRegistry = () => {
|
|
|
121
124
|
return {
|
|
122
125
|
lookupMethodByOwner,
|
|
123
126
|
lookupMethodByName,
|
|
127
|
+
lookupAllByOwner,
|
|
124
128
|
register,
|
|
125
129
|
clear,
|
|
126
130
|
get hasFunctionMethods() {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ScopeResolutionIndexes` — the bundle of materialized indexes produced
|
|
3
|
+
* by the finalize-orchestrator (RFC #909 Ring 2 PKG #921) and attached
|
|
4
|
+
* to `MutableSemanticModel`.
|
|
5
|
+
*
|
|
6
|
+
* Produced by `finalizeScopeModel(parsedFiles, hooks)` in
|
|
7
|
+
* `finalize-orchestrator.ts`. Consumed by the resolution phase (future
|
|
8
|
+
* tickets) where `Registry.lookup` / `resolveTypeRef` query this bundle
|
|
9
|
+
* to answer call-resolution questions without re-walking any AST.
|
|
10
|
+
*
|
|
11
|
+
* ## Lifecycle
|
|
12
|
+
*
|
|
13
|
+
* 1. Pipeline collects `ParsedFile[]` from the parsing-processor (#920).
|
|
14
|
+
* 2. Pipeline invokes `finalizeScopeModel(parsedFiles, hooks)` →
|
|
15
|
+
* returns a `ScopeResolutionIndexes` (this interface).
|
|
16
|
+
* 3. Pipeline calls `model.attachScopeIndexes(indexes)` to stamp them
|
|
17
|
+
* onto the `MutableSemanticModel`. This is a **one-shot write**;
|
|
18
|
+
* subsequent calls throw. After attachment, the indexes are frozen
|
|
19
|
+
* at the type level (everything is `readonly`) and at runtime via
|
|
20
|
+
* `Object.freeze` on the bundle.
|
|
21
|
+
* 4. Resolution callers hold a `SemanticModel` reference and read
|
|
22
|
+
* `model.scopes` to query.
|
|
23
|
+
*
|
|
24
|
+
* ## Content
|
|
25
|
+
*
|
|
26
|
+
* - `scopeTree` / `moduleScopes` / `defs` / `qualifiedNames` — the
|
|
27
|
+
* four Ring 2 SHARED indexes built over per-file artifacts.
|
|
28
|
+
* - `methodDispatch` — MRO + implements materialized view (#914).
|
|
29
|
+
* - `imports` — finalized `ImportEdge[]` per module scope (`parsedImports`
|
|
30
|
+
* resolved through cross-file link + wildcard expansion).
|
|
31
|
+
* - `bindings` — merged bindings per module scope (local + import +
|
|
32
|
+
* wildcard + re-export), with the provider's precedence applied.
|
|
33
|
+
* - `referenceSites` — union of every file's pre-resolution usage
|
|
34
|
+
* facts. Consumed by the resolution phase (future) to emit
|
|
35
|
+
* `Reference` records into `ReferenceIndex`.
|
|
36
|
+
* - `stats` — coarse-grained counts from the shared finalize algorithm
|
|
37
|
+
* (total files/edges, linked vs unresolved, SCC topology).
|
|
38
|
+
*
|
|
39
|
+
* `ReferenceIndex` is deliberately NOT here — it is populated in a later
|
|
40
|
+
* phase (RFC §3.2 Phase 4 / Ring 2 PKG #925) and owned separately.
|
|
41
|
+
*/
|
|
42
|
+
import type { BindingRef, DefIndex, FinalizedScc, FinalizeStats, ImportEdge, MethodDispatchIndex, ModuleScopeIndex, QualifiedNameIndex, ReferenceSite, ScopeId, ScopeTree } from '../../../_shared/index.js';
|
|
43
|
+
export interface ScopeResolutionIndexes {
|
|
44
|
+
readonly scopeTree: ScopeTree;
|
|
45
|
+
readonly defs: DefIndex;
|
|
46
|
+
readonly qualifiedNames: QualifiedNameIndex;
|
|
47
|
+
readonly moduleScopes: ModuleScopeIndex;
|
|
48
|
+
readonly methodDispatch: MethodDispatchIndex;
|
|
49
|
+
/** Finalized `ImportEdge[]` per module scope. */
|
|
50
|
+
readonly imports: ReadonlyMap<ScopeId, readonly ImportEdge[]>;
|
|
51
|
+
/** Merged bindings (local + imports + wildcards) per module scope. */
|
|
52
|
+
readonly bindings: ReadonlyMap<ScopeId, ReadonlyMap<string, readonly BindingRef[]>>;
|
|
53
|
+
/** Pre-resolution usage facts; consumed by the resolution phase. */
|
|
54
|
+
readonly referenceSites: readonly ReferenceSite[];
|
|
55
|
+
/** SCC condensation of the file-level import graph — callers that want
|
|
56
|
+
* parallel per-SCC processing in the resolution phase read this. */
|
|
57
|
+
readonly sccs: readonly FinalizedScc[];
|
|
58
|
+
readonly stats: FinalizeStats;
|
|
59
|
+
}
|