@zuvia-software-solutions/code-mapper 1.4.0
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 +215 -0
- package/dist/cli/ai-context.d.ts +19 -0
- package/dist/cli/ai-context.js +168 -0
- package/dist/cli/analyze.d.ts +7 -0
- package/dist/cli/analyze.js +325 -0
- package/dist/cli/augment.d.ts +7 -0
- package/dist/cli/augment.js +27 -0
- package/dist/cli/clean.d.ts +5 -0
- package/dist/cli/clean.js +56 -0
- package/dist/cli/eval-server.d.ts +25 -0
- package/dist/cli/eval-server.js +365 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +102 -0
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +19 -0
- package/dist/cli/list.d.ts +2 -0
- package/dist/cli/list.js +27 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +35 -0
- package/dist/cli/refresh.d.ts +12 -0
- package/dist/cli/refresh.js +165 -0
- package/dist/cli/serve.d.ts +5 -0
- package/dist/cli/serve.js +8 -0
- package/dist/cli/setup.d.ts +6 -0
- package/dist/cli/setup.js +218 -0
- package/dist/cli/status.d.ts +2 -0
- package/dist/cli/status.js +33 -0
- package/dist/cli/tool.d.ts +28 -0
- package/dist/cli/tool.js +87 -0
- package/dist/config/ignore-service.d.ts +32 -0
- package/dist/config/ignore-service.js +282 -0
- package/dist/config/supported-languages.d.ts +23 -0
- package/dist/config/supported-languages.js +52 -0
- package/dist/core/augmentation/engine.d.ts +22 -0
- package/dist/core/augmentation/engine.js +232 -0
- package/dist/core/embeddings/embedder.d.ts +35 -0
- package/dist/core/embeddings/embedder.js +171 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
- package/dist/core/embeddings/embedding-pipeline.js +402 -0
- package/dist/core/embeddings/index.d.ts +5 -0
- package/dist/core/embeddings/index.js +6 -0
- package/dist/core/embeddings/text-generator.d.ts +20 -0
- package/dist/core/embeddings/text-generator.js +159 -0
- package/dist/core/embeddings/types.d.ts +60 -0
- package/dist/core/embeddings/types.js +23 -0
- package/dist/core/graph/graph.d.ts +4 -0
- package/dist/core/graph/graph.js +65 -0
- package/dist/core/graph/types.d.ts +69 -0
- package/dist/core/graph/types.js +3 -0
- package/dist/core/incremental/child-process.d.ts +8 -0
- package/dist/core/incremental/child-process.js +649 -0
- package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
- package/dist/core/incremental/refresh-coordinator.js +147 -0
- package/dist/core/incremental/types.d.ts +78 -0
- package/dist/core/incremental/types.js +153 -0
- package/dist/core/incremental/watcher.d.ts +63 -0
- package/dist/core/incremental/watcher.js +338 -0
- package/dist/core/ingestion/ast-cache.d.ts +12 -0
- package/dist/core/ingestion/ast-cache.js +34 -0
- package/dist/core/ingestion/call-processor.d.ts +34 -0
- package/dist/core/ingestion/call-processor.js +937 -0
- package/dist/core/ingestion/call-routing.d.ts +40 -0
- package/dist/core/ingestion/call-routing.js +97 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
- package/dist/core/ingestion/cluster-enricher.js +151 -0
- package/dist/core/ingestion/community-processor.d.ts +26 -0
- package/dist/core/ingestion/community-processor.js +272 -0
- package/dist/core/ingestion/constants.d.ts +5 -0
- package/dist/core/ingestion/constants.js +8 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
- package/dist/core/ingestion/entry-point-scoring.js +317 -0
- package/dist/core/ingestion/export-detection.d.ts +11 -0
- package/dist/core/ingestion/export-detection.js +203 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
- package/dist/core/ingestion/filesystem-walker.js +64 -0
- package/dist/core/ingestion/framework-detection.d.ts +42 -0
- package/dist/core/ingestion/framework-detection.js +405 -0
- package/dist/core/ingestion/heritage-processor.d.ts +15 -0
- package/dist/core/ingestion/heritage-processor.js +237 -0
- package/dist/core/ingestion/import-processor.d.ts +31 -0
- package/dist/core/ingestion/import-processor.js +416 -0
- package/dist/core/ingestion/language-config.d.ts +32 -0
- package/dist/core/ingestion/language-config.js +161 -0
- package/dist/core/ingestion/mro-processor.d.ts +32 -0
- package/dist/core/ingestion/mro-processor.js +343 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
- package/dist/core/ingestion/named-binding-extraction.js +343 -0
- package/dist/core/ingestion/parsing-processor.d.ts +20 -0
- package/dist/core/ingestion/parsing-processor.js +282 -0
- package/dist/core/ingestion/pipeline.d.ts +3 -0
- package/dist/core/ingestion/pipeline.js +416 -0
- package/dist/core/ingestion/process-processor.d.ts +42 -0
- package/dist/core/ingestion/process-processor.js +357 -0
- package/dist/core/ingestion/resolution-context.d.ts +40 -0
- package/dist/core/ingestion/resolution-context.js +171 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
- package/dist/core/ingestion/resolvers/csharp.js +101 -0
- package/dist/core/ingestion/resolvers/go.d.ts +8 -0
- package/dist/core/ingestion/resolvers/go.js +33 -0
- package/dist/core/ingestion/resolvers/index.d.ts +14 -0
- package/dist/core/ingestion/resolvers/index.js +10 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
- package/dist/core/ingestion/resolvers/jvm.js +74 -0
- package/dist/core/ingestion/resolvers/php.d.ts +7 -0
- package/dist/core/ingestion/resolvers/php.js +30 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
- package/dist/core/ingestion/resolvers/ruby.js +13 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
- package/dist/core/ingestion/resolvers/rust.js +62 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
- package/dist/core/ingestion/resolvers/standard.js +144 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
- package/dist/core/ingestion/resolvers/utils.js +113 -0
- package/dist/core/ingestion/structure-processor.d.ts +4 -0
- package/dist/core/ingestion/structure-processor.js +39 -0
- package/dist/core/ingestion/symbol-table.d.ts +34 -0
- package/dist/core/ingestion/symbol-table.js +48 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
- package/dist/core/ingestion/tree-sitter-queries.js +691 -0
- package/dist/core/ingestion/type-env.d.ts +52 -0
- package/dist/core/ingestion/type-env.js +349 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/csharp.js +224 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/go.js +261 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
- package/dist/core/ingestion/type-extractors/index.js +30 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/jvm.js +386 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/php.js +280 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/python.js +175 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
- package/dist/core/ingestion/type-extractors/ruby.js +218 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/rust.js +290 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
- package/dist/core/ingestion/type-extractors/shared.js +322 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/swift.js +140 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
- package/dist/core/ingestion/type-extractors/types.js +4 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/typescript.js +227 -0
- package/dist/core/ingestion/utils.d.ts +73 -0
- package/dist/core/ingestion/utils.js +992 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
- package/dist/core/ingestion/workers/parse-worker.js +1055 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
- package/dist/core/ingestion/workers/worker-pool.js +123 -0
- package/dist/core/lbug/csv-generator.d.ts +28 -0
- package/dist/core/lbug/csv-generator.js +355 -0
- package/dist/core/lbug/lbug-adapter.d.ts +96 -0
- package/dist/core/lbug/lbug-adapter.js +753 -0
- package/dist/core/lbug/schema.d.ts +46 -0
- package/dist/core/lbug/schema.js +402 -0
- package/dist/core/search/bm25-index.d.ts +20 -0
- package/dist/core/search/bm25-index.js +123 -0
- package/dist/core/search/hybrid-search.d.ts +32 -0
- package/dist/core/search/hybrid-search.js +131 -0
- package/dist/core/search/query-cache.d.ts +18 -0
- package/dist/core/search/query-cache.js +47 -0
- package/dist/core/search/query-expansion.d.ts +19 -0
- package/dist/core/search/query-expansion.js +75 -0
- package/dist/core/search/reranker.d.ts +29 -0
- package/dist/core/search/reranker.js +122 -0
- package/dist/core/search/types.d.ts +154 -0
- package/dist/core/search/types.js +51 -0
- package/dist/core/semantic/tsgo-service.d.ts +67 -0
- package/dist/core/semantic/tsgo-service.js +355 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
- package/dist/core/tree-sitter/parser-loader.js +71 -0
- package/dist/lib/memory-guard.d.ts +35 -0
- package/dist/lib/memory-guard.js +70 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.js +6 -0
- package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
- package/dist/mcp/compatible-stdio-transport.js +209 -0
- package/dist/mcp/core/embedder.d.ts +24 -0
- package/dist/mcp/core/embedder.js +168 -0
- package/dist/mcp/core/lbug-adapter.d.ts +29 -0
- package/dist/mcp/core/lbug-adapter.js +330 -0
- package/dist/mcp/local/local-backend.d.ts +188 -0
- package/dist/mcp/local/local-backend.js +2759 -0
- package/dist/mcp/resources.d.ts +22 -0
- package/dist/mcp/resources.js +379 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +217 -0
- package/dist/mcp/staleness.d.ts +10 -0
- package/dist/mcp/staleness.js +25 -0
- package/dist/mcp/tools.d.ts +21 -0
- package/dist/mcp/tools.js +202 -0
- package/dist/server/api.d.ts +5 -0
- package/dist/server/api.js +340 -0
- package/dist/server/mcp-http.d.ts +7 -0
- package/dist/server/mcp-http.js +95 -0
- package/dist/storage/git.d.ts +6 -0
- package/dist/storage/git.js +35 -0
- package/dist/storage/repo-manager.d.ts +87 -0
- package/dist/storage/repo-manager.js +249 -0
- package/dist/types/pipeline.d.ts +35 -0
- package/dist/types/pipeline.js +20 -0
- package/hooks/claude/code-mapper-hook.cjs +238 -0
- package/hooks/claude/pre-tool-use.sh +79 -0
- package/hooks/claude/session-start.sh +42 -0
- package/models/mlx-embedder.py +185 -0
- package/package.json +100 -0
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Per-file scoped type environment for receiver-type resolution
|
|
3
|
+
* @description Maps (scope, variableName) -> typeName using a three-tier strategy:
|
|
4
|
+
* Tier 0: explicit type annotations, Tier 1: constructor inference,
|
|
5
|
+
* Tier 2: single-pass assignment chain propagation
|
|
6
|
+
*/
|
|
7
|
+
import type { SyntaxNode } from './utils.js';
|
|
8
|
+
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
9
|
+
import type { SymbolTable } from './symbol-table.js';
|
|
10
|
+
/**
|
|
11
|
+
* Per-file scoped type environment: maps (scope, variableName) -> typeName
|
|
12
|
+
*
|
|
13
|
+
* Scope-aware: function-local variables are keyed by function name,
|
|
14
|
+
* file-level variables use the '' (empty string) scope.
|
|
15
|
+
* Built once per file, used for receiver resolution, then discarded
|
|
16
|
+
*/
|
|
17
|
+
export type TypeEnv = Map<string, Map<string, string>>;
|
|
18
|
+
/**
|
|
19
|
+
* Per-file type environment with receiver resolution
|
|
20
|
+
*
|
|
21
|
+
* Built once per file via `buildTypeEnv`, used for receiver-type filtering,
|
|
22
|
+
* then discarded. Encapsulates scope-aware type lookup and self/this/super
|
|
23
|
+
* AST resolution behind a single `.lookup()` method
|
|
24
|
+
*/
|
|
25
|
+
export interface TypeEnvironment {
|
|
26
|
+
/** Look up a variable's resolved type, with self/this/super AST resolution */
|
|
27
|
+
lookup(varName: string, callNode: SyntaxNode): string | undefined;
|
|
28
|
+
/** Unverified cross-file constructor bindings for SymbolTable verification */
|
|
29
|
+
readonly constructorBindings: readonly ConstructorBinding[];
|
|
30
|
+
/** Raw per-scope type bindings (for testing and debugging) */
|
|
31
|
+
readonly env: TypeEnv;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a TypeEnvironment from a tree-sitter AST in a single pass
|
|
35
|
+
*
|
|
36
|
+
* Collects class/struct names, type bindings, and constructor bindings that
|
|
37
|
+
* couldn't be resolved locally. When a symbolTable is provided, cross-project
|
|
38
|
+
* class names are available for constructor inference
|
|
39
|
+
*/
|
|
40
|
+
export declare const buildTypeEnv: (tree: {
|
|
41
|
+
rootNode: SyntaxNode;
|
|
42
|
+
}, language: SupportedLanguages, symbolTable?: SymbolTable) => TypeEnvironment;
|
|
43
|
+
/**
|
|
44
|
+
* Unverified constructor binding: a `val x = Callee()` pattern where
|
|
45
|
+
* the callee class is in another file. Caller must verify against SymbolTable
|
|
46
|
+
*/
|
|
47
|
+
export interface ConstructorBinding {
|
|
48
|
+
scope: string;
|
|
49
|
+
varName: string;
|
|
50
|
+
calleeName: string;
|
|
51
|
+
receiverClassName?: string;
|
|
52
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/type-env.ts
|
|
2
|
+
/**
|
|
3
|
+
* @file Per-file scoped type environment for receiver-type resolution
|
|
4
|
+
* @description Maps (scope, variableName) -> typeName using a three-tier strategy:
|
|
5
|
+
* Tier 0: explicit type annotations, Tier 1: constructor inference,
|
|
6
|
+
* Tier 2: single-pass assignment chain propagation
|
|
7
|
+
*/
|
|
8
|
+
import { FUNCTION_NODE_TYPES, extractFunctionName, CLASS_CONTAINER_TYPES } from './utils.js';
|
|
9
|
+
import { typeConfigs, TYPED_PARAMETER_TYPES } from './type-extractors/index.js';
|
|
10
|
+
import { extractSimpleTypeName, stripNullable } from './type-extractors/shared.js';
|
|
11
|
+
// File-level scope key
|
|
12
|
+
const FILE_SCOPE = '';
|
|
13
|
+
// Fallback for languages where class names aren't in a 'name' field (e.g. Kotlin uses type_identifier)
|
|
14
|
+
const findTypeIdentifierChild = (node) => {
|
|
15
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
16
|
+
const child = node.child(i);
|
|
17
|
+
if (child && child.type === 'type_identifier')
|
|
18
|
+
return child;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
// Shared lookup implementation
|
|
23
|
+
const lookupInEnv = (env, varName, callNode) => {
|
|
24
|
+
// self/this receiver: resolve to enclosing class name via AST walk
|
|
25
|
+
if (varName === 'self' || varName === 'this' || varName === '$this') {
|
|
26
|
+
return findEnclosingClassName(callNode);
|
|
27
|
+
}
|
|
28
|
+
// super/base/parent receiver: walk up to enclosing class, extract superclass from heritage
|
|
29
|
+
if (varName === 'super' || varName === 'base' || varName === 'parent') {
|
|
30
|
+
return findEnclosingParentClassName(callNode);
|
|
31
|
+
}
|
|
32
|
+
// Determine enclosing function scope
|
|
33
|
+
const scopeKey = findEnclosingScopeKey(callNode);
|
|
34
|
+
// Try function-local scope first
|
|
35
|
+
if (scopeKey) {
|
|
36
|
+
const scopeEnv = env.get(scopeKey);
|
|
37
|
+
if (scopeEnv) {
|
|
38
|
+
const result = scopeEnv.get(varName);
|
|
39
|
+
if (result)
|
|
40
|
+
return stripNullable(result);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Fall back to file-level scope
|
|
44
|
+
const fileEnv = env.get(FILE_SCOPE);
|
|
45
|
+
const raw = fileEnv?.get(varName);
|
|
46
|
+
return raw ? stripNullable(raw) : undefined;
|
|
47
|
+
};
|
|
48
|
+
/** Walk up AST to find the enclosing class/module name for self/this resolution */
|
|
49
|
+
const findEnclosingClassName = (node) => {
|
|
50
|
+
let current = node.parent;
|
|
51
|
+
while (current) {
|
|
52
|
+
if (CLASS_CONTAINER_TYPES.has(current.type)) {
|
|
53
|
+
const nameNode = current.childForFieldName('name')
|
|
54
|
+
?? findTypeIdentifierChild(current);
|
|
55
|
+
if (nameNode)
|
|
56
|
+
return nameNode.text;
|
|
57
|
+
}
|
|
58
|
+
current = current.parent;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
};
|
|
62
|
+
/** Walk up AST to find enclosing class and extract its parent class name for super/base/parent */
|
|
63
|
+
const findEnclosingParentClassName = (node) => {
|
|
64
|
+
let current = node.parent;
|
|
65
|
+
while (current) {
|
|
66
|
+
if (CLASS_CONTAINER_TYPES.has(current.type)) {
|
|
67
|
+
return extractParentClassFromNode(current);
|
|
68
|
+
}
|
|
69
|
+
current = current.parent;
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
};
|
|
73
|
+
/** Extract the parent/superclass name from a class declaration AST node */
|
|
74
|
+
const extractParentClassFromNode = (classNode) => {
|
|
75
|
+
// 1. Named fields: Java (superclass), Ruby (superclass), Python (superclasses)
|
|
76
|
+
const superclassNode = classNode.childForFieldName('superclass');
|
|
77
|
+
if (superclassNode) {
|
|
78
|
+
// Java: superclass > type_identifier or generic_type, Ruby: superclass > constant
|
|
79
|
+
const inner = superclassNode.childForFieldName('type')
|
|
80
|
+
?? superclassNode.firstNamedChild
|
|
81
|
+
?? superclassNode;
|
|
82
|
+
return extractSimpleTypeName(inner) ?? inner.text;
|
|
83
|
+
}
|
|
84
|
+
const superclassesNode = classNode.childForFieldName('superclasses');
|
|
85
|
+
if (superclassesNode) {
|
|
86
|
+
// Python: argument_list with identifiers or attribute nodes (e.g. models.Model)
|
|
87
|
+
const first = superclassesNode.firstNamedChild;
|
|
88
|
+
if (first)
|
|
89
|
+
return extractSimpleTypeName(first) ?? first.text;
|
|
90
|
+
}
|
|
91
|
+
// 2. Unnamed children: walk class node's children looking for heritage nodes
|
|
92
|
+
for (let i = 0; i < classNode.childCount; i++) {
|
|
93
|
+
const child = classNode.child(i);
|
|
94
|
+
if (!child)
|
|
95
|
+
continue;
|
|
96
|
+
switch (child.type) {
|
|
97
|
+
// TypeScript: class_heritage > extends_clause > type_identifier
|
|
98
|
+
// JavaScript: class_heritage > identifier (no extends_clause wrapper)
|
|
99
|
+
case 'class_heritage': {
|
|
100
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
101
|
+
const clause = child.child(j);
|
|
102
|
+
if (clause?.type === 'extends_clause') {
|
|
103
|
+
const typeNode = clause.firstNamedChild;
|
|
104
|
+
if (typeNode)
|
|
105
|
+
return extractSimpleTypeName(typeNode) ?? typeNode.text;
|
|
106
|
+
}
|
|
107
|
+
// JS: direct identifier child (no extends_clause wrapper)
|
|
108
|
+
if (clause?.type === 'identifier' || clause?.type === 'type_identifier') {
|
|
109
|
+
return clause.text;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
// C#: base_list > identifier or generic_name > identifier
|
|
115
|
+
case 'base_list': {
|
|
116
|
+
const first = child.firstNamedChild;
|
|
117
|
+
if (first) {
|
|
118
|
+
// generic_name wraps the identifier: BaseClass<T>
|
|
119
|
+
if (first.type === 'generic_name') {
|
|
120
|
+
const inner = first.childForFieldName('name') ?? first.firstNamedChild;
|
|
121
|
+
if (inner)
|
|
122
|
+
return inner.text;
|
|
123
|
+
}
|
|
124
|
+
return first.text;
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
// PHP: base_clause > name
|
|
129
|
+
case 'base_clause': {
|
|
130
|
+
const name = child.firstNamedChild;
|
|
131
|
+
if (name)
|
|
132
|
+
return name.text;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
// C++: base_class_clause > type_identifier (with optional access_specifier before it)
|
|
136
|
+
case 'base_class_clause': {
|
|
137
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
138
|
+
const inner = child.child(j);
|
|
139
|
+
if (inner?.type === 'type_identifier')
|
|
140
|
+
return inner.text;
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
// Kotlin: delegation_specifier > constructor_invocation > user_type > type_identifier
|
|
145
|
+
case 'delegation_specifier': {
|
|
146
|
+
const delegate = child.firstNamedChild;
|
|
147
|
+
if (delegate?.type === 'constructor_invocation') {
|
|
148
|
+
const userType = delegate.firstNamedChild;
|
|
149
|
+
if (userType?.type === 'user_type') {
|
|
150
|
+
const typeId = userType.firstNamedChild;
|
|
151
|
+
if (typeId)
|
|
152
|
+
return typeId.text;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Also handle plain user_type (interface conformance without parentheses)
|
|
156
|
+
if (delegate?.type === 'user_type') {
|
|
157
|
+
const typeId = delegate.firstNamedChild;
|
|
158
|
+
if (typeId)
|
|
159
|
+
return typeId.text;
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
// Swift: inheritance_specifier > user_type > type_identifier
|
|
164
|
+
case 'inheritance_specifier': {
|
|
165
|
+
const userType = child.childForFieldName('inherits_from') ?? child.firstNamedChild;
|
|
166
|
+
if (userType?.type === 'user_type') {
|
|
167
|
+
const typeId = userType.firstNamedChild;
|
|
168
|
+
if (typeId)
|
|
169
|
+
return typeId.text;
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
};
|
|
177
|
+
/** Find the enclosing function name for scope lookup */
|
|
178
|
+
const findEnclosingScopeKey = (node) => {
|
|
179
|
+
let current = node.parent;
|
|
180
|
+
while (current) {
|
|
181
|
+
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
182
|
+
const { funcName } = extractFunctionName(current);
|
|
183
|
+
if (funcName)
|
|
184
|
+
return `${funcName}@${current.startIndex}`;
|
|
185
|
+
}
|
|
186
|
+
current = current.parent;
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Create a class name lookup backed by local AST names + SymbolTable global index
|
|
192
|
+
*
|
|
193
|
+
* Distinguishes constructor calls from function calls (e.g. Kotlin `User()` vs `getUser()`)
|
|
194
|
+
* using cross-file type information. Results are memoized
|
|
195
|
+
*/
|
|
196
|
+
const createClassNameLookup = (localNames, symbolTable) => {
|
|
197
|
+
if (!symbolTable)
|
|
198
|
+
return localNames;
|
|
199
|
+
const memo = new Map();
|
|
200
|
+
return {
|
|
201
|
+
has(name) {
|
|
202
|
+
if (localNames.has(name))
|
|
203
|
+
return true;
|
|
204
|
+
const cached = memo.get(name);
|
|
205
|
+
if (cached !== undefined)
|
|
206
|
+
return cached;
|
|
207
|
+
const result = symbolTable.lookupFuzzy(name).some(def => def.type === 'Class' || def.type === 'Enum' || def.type === 'Struct');
|
|
208
|
+
memo.set(name, result);
|
|
209
|
+
return result;
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Build a TypeEnvironment from a tree-sitter AST in a single pass
|
|
215
|
+
*
|
|
216
|
+
* Collects class/struct names, type bindings, and constructor bindings that
|
|
217
|
+
* couldn't be resolved locally. When a symbolTable is provided, cross-project
|
|
218
|
+
* class names are available for constructor inference
|
|
219
|
+
*/
|
|
220
|
+
export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
221
|
+
const env = new Map();
|
|
222
|
+
const localClassNames = new Set();
|
|
223
|
+
const classNames = createClassNameLookup(localClassNames, symbolTable);
|
|
224
|
+
const config = typeConfigs[language];
|
|
225
|
+
const bindings = [];
|
|
226
|
+
const pendingAssignments = [];
|
|
227
|
+
// Maps `scope\0varName` -> type annotation AST node from the original declaration
|
|
228
|
+
// Allows pattern extractors to navigate back to generic type arguments
|
|
229
|
+
const declarationTypeNodes = new Map();
|
|
230
|
+
/**
|
|
231
|
+
* Extract a (variableName -> typeName) binding from a single AST node
|
|
232
|
+
*
|
|
233
|
+
* Tier 0: explicit type annotations, Tier 1: constructor-call inference
|
|
234
|
+
* Also populates declarationTypeNodes for pattern extractor access to generic args
|
|
235
|
+
*/
|
|
236
|
+
const extractTypeBinding = (node, scopeEnv, scope) => {
|
|
237
|
+
// Fast path: eliminates 90%+ of calls before language dispatch
|
|
238
|
+
if (TYPED_PARAMETER_TYPES.has(node.type)) {
|
|
239
|
+
const keysBefore = new Set(scopeEnv.keys());
|
|
240
|
+
config.extractParameter(node, scopeEnv);
|
|
241
|
+
// Capture the type node for newly introduced parameter bindings
|
|
242
|
+
const typeNode = node.childForFieldName('type');
|
|
243
|
+
if (typeNode) {
|
|
244
|
+
for (const varName of scopeEnv.keys()) {
|
|
245
|
+
if (!keysBefore.has(varName)) {
|
|
246
|
+
declarationTypeNodes.set(`${scope}\0${varName}`, typeNode);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
// For-each loop variable bindings (Java/C#/Kotlin)
|
|
253
|
+
// Checked before declarationNodeTypes — loop variables are not declarations
|
|
254
|
+
if (config.forLoopNodeTypes?.has(node.type)) {
|
|
255
|
+
config.extractForLoopBinding?.(node, scopeEnv);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (config.declarationNodeTypes.has(node.type)) {
|
|
259
|
+
const keysBefore = new Set(scopeEnv.keys());
|
|
260
|
+
config.extractDeclaration(node, scopeEnv);
|
|
261
|
+
// Capture type annotation AST node for newly introduced bindings
|
|
262
|
+
// Only declarations with an explicit 'type' field are recorded
|
|
263
|
+
const typeNode = node.childForFieldName('type');
|
|
264
|
+
if (typeNode) {
|
|
265
|
+
for (const varName of scopeEnv.keys()) {
|
|
266
|
+
if (!keysBefore.has(varName)) {
|
|
267
|
+
declarationTypeNodes.set(`${scope}\0${varName}`, typeNode);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Tier 1: constructor-call inference as fallback
|
|
272
|
+
// Each language's extractInitializer skips declarators with explicit annotations
|
|
273
|
+
if (config.extractInitializer) {
|
|
274
|
+
config.extractInitializer(node, scopeEnv, classNames);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
const walk = (node, currentScope) => {
|
|
279
|
+
// Collect class/struct names for constructor vs function call disambiguation
|
|
280
|
+
// Currently only C++ uses local names; other languages rely on the SymbolTable
|
|
281
|
+
if (CLASS_CONTAINER_TYPES.has(node.type)) {
|
|
282
|
+
// Most languages use 'name' field; Kotlin uses a type_identifier child instead
|
|
283
|
+
const nameNode = node.childForFieldName('name')
|
|
284
|
+
?? findTypeIdentifierChild(node);
|
|
285
|
+
if (nameNode)
|
|
286
|
+
localClassNames.add(nameNode.text);
|
|
287
|
+
}
|
|
288
|
+
// Detect scope boundaries (function/method definitions)
|
|
289
|
+
let scope = currentScope;
|
|
290
|
+
if (FUNCTION_NODE_TYPES.has(node.type)) {
|
|
291
|
+
const { funcName } = extractFunctionName(node);
|
|
292
|
+
if (funcName)
|
|
293
|
+
scope = `${funcName}@${node.startIndex}`;
|
|
294
|
+
}
|
|
295
|
+
// Get or create the sub-map for this scope
|
|
296
|
+
if (!env.has(scope))
|
|
297
|
+
env.set(scope, new Map());
|
|
298
|
+
const scopeEnv = env.get(scope);
|
|
299
|
+
extractTypeBinding(node, scopeEnv, scope);
|
|
300
|
+
// Pattern binding extraction: introduces new typed variables via pattern matching
|
|
301
|
+
// (e.g. `if let Some(x) = opt`, `x instanceof T t`)
|
|
302
|
+
// Runs after Tier 0/1 so scopeEnv already has the source variable's type
|
|
303
|
+
if (config.extractPatternBinding) {
|
|
304
|
+
const patternBinding = config.extractPatternBinding(node, scopeEnv, declarationTypeNodes, scope);
|
|
305
|
+
if (patternBinding && !scopeEnv.has(patternBinding.varName)) {
|
|
306
|
+
scopeEnv.set(patternBinding.varName, patternBinding.typeName);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Tier 2: collect plain-identifier RHS assignments for post-walk propagation
|
|
310
|
+
// Delegates to per-language extractPendingAssignment (AST shapes vary by language)
|
|
311
|
+
if (config.extractPendingAssignment && config.declarationNodeTypes.has(node.type)) {
|
|
312
|
+
const pending = config.extractPendingAssignment(node, scopeEnv);
|
|
313
|
+
if (pending) {
|
|
314
|
+
pendingAssignments.push({ scope, ...pending });
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Scan for constructor bindings that couldn't be resolved locally
|
|
318
|
+
if (config.scanConstructorBinding) {
|
|
319
|
+
const result = config.scanConstructorBinding(node);
|
|
320
|
+
if (result && !scopeEnv.has(result.varName)) {
|
|
321
|
+
bindings.push({ scope, ...result });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// Recurse into children
|
|
325
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
326
|
+
const child = node.child(i);
|
|
327
|
+
if (child)
|
|
328
|
+
walk(child, scope);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
walk(tree.rootNode, FILE_SCOPE);
|
|
332
|
+
// Tier 2: single-pass assignment chain propagation in source order
|
|
333
|
+
// Resolves `const b = a` where `a` has a known type from Tier 0/1
|
|
334
|
+
// No fixpoint iteration — covers 95%+ of real-world patterns
|
|
335
|
+
for (const { scope, lhs, rhs } of pendingAssignments) {
|
|
336
|
+
const scopeEnv = env.get(scope);
|
|
337
|
+
if (!scopeEnv || scopeEnv.has(lhs))
|
|
338
|
+
continue;
|
|
339
|
+
const rhsType = scopeEnv.get(rhs) ?? env.get(FILE_SCOPE)?.get(rhs);
|
|
340
|
+
if (rhsType) {
|
|
341
|
+
scopeEnv.set(lhs, rhsType);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
lookup: (varName, callNode) => lookupInEnv(env, varName, callNode),
|
|
346
|
+
constructorBindings: bindings,
|
|
347
|
+
env,
|
|
348
|
+
};
|
|
349
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/type-extractors/c-cpp.ts
|
|
2
|
+
/** @file c-cpp.ts
|
|
3
|
+
* @description Type extraction for C and C++ (typed declarations, auto inference, brace initialization) */
|
|
4
|
+
import { extractSimpleTypeName, extractVarName } from './shared.js';
|
|
5
|
+
const DECLARATION_NODE_TYPES = new Set([
|
|
6
|
+
'declaration',
|
|
7
|
+
'for_range_loop',
|
|
8
|
+
]);
|
|
9
|
+
// C++: Type x = ...; Type* x; Type& x;
|
|
10
|
+
const extractDeclaration = (node, env) => {
|
|
11
|
+
const typeNode = node.childForFieldName('type');
|
|
12
|
+
if (!typeNode)
|
|
13
|
+
return;
|
|
14
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
15
|
+
if (!typeName)
|
|
16
|
+
return;
|
|
17
|
+
const declarator = node.childForFieldName('declarator');
|
|
18
|
+
if (!declarator)
|
|
19
|
+
return;
|
|
20
|
+
// init_declarator: Type x = value
|
|
21
|
+
const nameNode = declarator.type === 'init_declarator'
|
|
22
|
+
? declarator.childForFieldName('declarator')
|
|
23
|
+
: declarator;
|
|
24
|
+
if (!nameNode)
|
|
25
|
+
return;
|
|
26
|
+
// Handle pointer/reference declarators
|
|
27
|
+
const finalName = nameNode.type === 'pointer_declarator' || nameNode.type === 'reference_declarator'
|
|
28
|
+
? nameNode.firstNamedChild
|
|
29
|
+
: nameNode;
|
|
30
|
+
if (!finalName)
|
|
31
|
+
return;
|
|
32
|
+
const varName = extractVarName(finalName);
|
|
33
|
+
if (varName)
|
|
34
|
+
env.set(varName, typeName);
|
|
35
|
+
};
|
|
36
|
+
// C++: auto x = new User(); auto x = User()
|
|
37
|
+
const extractInitializer = (node, env, classNames) => {
|
|
38
|
+
const typeNode = node.childForFieldName('type');
|
|
39
|
+
if (!typeNode)
|
|
40
|
+
return;
|
|
41
|
+
// Only handle auto/placeholder -- typed declarations are handled by extractDeclaration
|
|
42
|
+
const typeText = typeNode.text;
|
|
43
|
+
if (typeText !== 'auto' &&
|
|
44
|
+
typeText !== 'decltype(auto)' &&
|
|
45
|
+
typeNode.type !== 'placeholder_type_specifier')
|
|
46
|
+
return;
|
|
47
|
+
const declarator = node.childForFieldName('declarator');
|
|
48
|
+
if (!declarator)
|
|
49
|
+
return;
|
|
50
|
+
// Must be an init_declarator (i.e., has an initializer value)
|
|
51
|
+
if (declarator.type !== 'init_declarator')
|
|
52
|
+
return;
|
|
53
|
+
const value = declarator.childForFieldName('value');
|
|
54
|
+
if (!value)
|
|
55
|
+
return;
|
|
56
|
+
// Resolve the variable name, unwrapping pointer/reference declarators
|
|
57
|
+
const nameNode = declarator.childForFieldName('declarator');
|
|
58
|
+
if (!nameNode)
|
|
59
|
+
return;
|
|
60
|
+
const finalName = nameNode.type === 'pointer_declarator' || nameNode.type === 'reference_declarator'
|
|
61
|
+
? nameNode.firstNamedChild
|
|
62
|
+
: nameNode;
|
|
63
|
+
if (!finalName)
|
|
64
|
+
return;
|
|
65
|
+
const varName = extractVarName(finalName);
|
|
66
|
+
if (!varName)
|
|
67
|
+
return;
|
|
68
|
+
// auto x = new User() -- new_expression
|
|
69
|
+
if (value.type === 'new_expression') {
|
|
70
|
+
const ctorType = value.childForFieldName('type');
|
|
71
|
+
if (ctorType) {
|
|
72
|
+
const typeName = extractSimpleTypeName(ctorType);
|
|
73
|
+
if (typeName)
|
|
74
|
+
env.set(varName, typeName);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// auto x = User() -- call_expression where function is a type name
|
|
79
|
+
// tree-sitter-cpp may parse the constructor name as type_identifier or identifier
|
|
80
|
+
// For plain identifiers, verify against known class names from the file's AST
|
|
81
|
+
// to distinguish constructor calls (User()) from function calls (getUser())
|
|
82
|
+
if (value.type === 'call_expression') {
|
|
83
|
+
const func = value.childForFieldName('function');
|
|
84
|
+
if (!func)
|
|
85
|
+
return;
|
|
86
|
+
if (func.type === 'type_identifier') {
|
|
87
|
+
const typeName = func.text;
|
|
88
|
+
if (typeName)
|
|
89
|
+
env.set(varName, typeName);
|
|
90
|
+
}
|
|
91
|
+
else if (func.type === 'identifier') {
|
|
92
|
+
const text = func.text;
|
|
93
|
+
if (text && classNames.has(text))
|
|
94
|
+
env.set(varName, text);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// auto x = User{} -- compound_literal_expression (brace initialization)
|
|
99
|
+
// AST: compound_literal_expression > type_identifier + initializer_list
|
|
100
|
+
if (value.type === 'compound_literal_expression') {
|
|
101
|
+
const typeId = value.firstNamedChild;
|
|
102
|
+
const typeName = typeId ? extractSimpleTypeName(typeId) : undefined;
|
|
103
|
+
if (typeName)
|
|
104
|
+
env.set(varName, typeName);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
// C/C++: parameter_declaration -> type declarator
|
|
108
|
+
const extractParameter = (node, env) => {
|
|
109
|
+
let nameNode = null;
|
|
110
|
+
let typeNode = null;
|
|
111
|
+
if (node.type === 'parameter_declaration') {
|
|
112
|
+
typeNode = node.childForFieldName('type');
|
|
113
|
+
const declarator = node.childForFieldName('declarator');
|
|
114
|
+
if (declarator) {
|
|
115
|
+
nameNode = declarator.type === 'pointer_declarator' || declarator.type === 'reference_declarator'
|
|
116
|
+
? declarator.firstNamedChild
|
|
117
|
+
: declarator;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
|
|
122
|
+
typeNode = node.childForFieldName('type');
|
|
123
|
+
}
|
|
124
|
+
if (!nameNode || !typeNode)
|
|
125
|
+
return;
|
|
126
|
+
const varName = extractVarName(nameNode);
|
|
127
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
128
|
+
if (varName && typeName)
|
|
129
|
+
env.set(varName, typeName);
|
|
130
|
+
};
|
|
131
|
+
// C/C++: auto x = User() where function is an identifier (not type_identifier)
|
|
132
|
+
const scanConstructorBinding = (node) => {
|
|
133
|
+
if (node.type !== 'declaration')
|
|
134
|
+
return undefined;
|
|
135
|
+
const typeNode = node.childForFieldName('type');
|
|
136
|
+
if (!typeNode)
|
|
137
|
+
return undefined;
|
|
138
|
+
const typeText = typeNode.text;
|
|
139
|
+
if (typeText !== 'auto' && typeText !== 'decltype(auto)' && typeNode.type !== 'placeholder_type_specifier')
|
|
140
|
+
return undefined;
|
|
141
|
+
const declarator = node.childForFieldName('declarator');
|
|
142
|
+
if (!declarator || declarator.type !== 'init_declarator')
|
|
143
|
+
return undefined;
|
|
144
|
+
const value = declarator.childForFieldName('value');
|
|
145
|
+
if (!value || value.type !== 'call_expression')
|
|
146
|
+
return undefined;
|
|
147
|
+
const func = value.childForFieldName('function');
|
|
148
|
+
if (!func)
|
|
149
|
+
return undefined;
|
|
150
|
+
if (func.type === 'qualified_identifier' || func.type === 'scoped_identifier') {
|
|
151
|
+
const last = func.lastNamedChild;
|
|
152
|
+
if (!last)
|
|
153
|
+
return undefined;
|
|
154
|
+
const nameNode = declarator.childForFieldName('declarator');
|
|
155
|
+
if (!nameNode)
|
|
156
|
+
return undefined;
|
|
157
|
+
const finalName = nameNode.type === 'pointer_declarator' || nameNode.type === 'reference_declarator'
|
|
158
|
+
? nameNode.firstNamedChild : nameNode;
|
|
159
|
+
if (!finalName)
|
|
160
|
+
return undefined;
|
|
161
|
+
return { varName: finalName.text, calleeName: last.text };
|
|
162
|
+
}
|
|
163
|
+
if (func.type !== 'identifier')
|
|
164
|
+
return undefined;
|
|
165
|
+
const nameNode = declarator.childForFieldName('declarator');
|
|
166
|
+
if (!nameNode)
|
|
167
|
+
return undefined;
|
|
168
|
+
const finalName = nameNode.type === 'pointer_declarator' || nameNode.type === 'reference_declarator'
|
|
169
|
+
? nameNode.firstNamedChild : nameNode;
|
|
170
|
+
if (!finalName)
|
|
171
|
+
return undefined;
|
|
172
|
+
const varName = finalName.text;
|
|
173
|
+
if (!varName)
|
|
174
|
+
return undefined;
|
|
175
|
+
return { varName, calleeName: func.text };
|
|
176
|
+
};
|
|
177
|
+
// C++: auto alias = user -> declaration with auto type + init_declarator where value is identifier
|
|
178
|
+
const extractPendingAssignment = (node, scopeEnv) => {
|
|
179
|
+
if (node.type !== 'declaration')
|
|
180
|
+
return undefined;
|
|
181
|
+
const typeNode = node.childForFieldName('type');
|
|
182
|
+
if (!typeNode)
|
|
183
|
+
return undefined;
|
|
184
|
+
// Only handle auto -- typed declarations already resolved by extractDeclaration
|
|
185
|
+
const typeText = typeNode.text;
|
|
186
|
+
if (typeText !== 'auto' && typeText !== 'decltype(auto)'
|
|
187
|
+
&& typeNode.type !== 'placeholder_type_specifier')
|
|
188
|
+
return undefined;
|
|
189
|
+
const declarator = node.childForFieldName('declarator');
|
|
190
|
+
if (!declarator || declarator.type !== 'init_declarator')
|
|
191
|
+
return undefined;
|
|
192
|
+
const value = declarator.childForFieldName('value');
|
|
193
|
+
if (!value || value.type !== 'identifier')
|
|
194
|
+
return undefined;
|
|
195
|
+
const nameNode = declarator.childForFieldName('declarator');
|
|
196
|
+
if (!nameNode)
|
|
197
|
+
return undefined;
|
|
198
|
+
const finalName = nameNode.type === 'pointer_declarator' || nameNode.type === 'reference_declarator'
|
|
199
|
+
? nameNode.firstNamedChild : nameNode;
|
|
200
|
+
if (!finalName)
|
|
201
|
+
return undefined;
|
|
202
|
+
const lhs = extractVarName(finalName);
|
|
203
|
+
if (!lhs || scopeEnv.has(lhs))
|
|
204
|
+
return undefined;
|
|
205
|
+
return { lhs, rhs: value.text };
|
|
206
|
+
};
|
|
207
|
+
export const typeConfig = {
|
|
208
|
+
declarationNodeTypes: DECLARATION_NODE_TYPES,
|
|
209
|
+
extractDeclaration,
|
|
210
|
+
extractParameter,
|
|
211
|
+
extractInitializer,
|
|
212
|
+
scanConstructorBinding,
|
|
213
|
+
extractPendingAssignment,
|
|
214
|
+
};
|