@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,386 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/type-extractors/jvm.ts
|
|
2
|
+
/** @file jvm.ts
|
|
3
|
+
* @description Type extraction for Java and Kotlin */
|
|
4
|
+
import { extractSimpleTypeName, extractVarName, findChildByType } from './shared.js';
|
|
5
|
+
const JAVA_DECLARATION_NODE_TYPES = new Set([
|
|
6
|
+
'local_variable_declaration',
|
|
7
|
+
'field_declaration',
|
|
8
|
+
]);
|
|
9
|
+
// Java: Type x = ...; Type x;
|
|
10
|
+
const extractJavaDeclaration = (node, env) => {
|
|
11
|
+
const typeNode = node.childForFieldName('type');
|
|
12
|
+
if (!typeNode)
|
|
13
|
+
return;
|
|
14
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
15
|
+
if (!typeName || typeName === 'var')
|
|
16
|
+
return; // skip Java 10 var -- handled by extractInitializer
|
|
17
|
+
// Find variable_declarator children
|
|
18
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
19
|
+
const child = node.namedChild(i);
|
|
20
|
+
if (child?.type !== 'variable_declarator')
|
|
21
|
+
continue;
|
|
22
|
+
const nameNode = child.childForFieldName('name');
|
|
23
|
+
if (nameNode) {
|
|
24
|
+
const varName = extractVarName(nameNode);
|
|
25
|
+
if (varName)
|
|
26
|
+
env.set(varName, typeName);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
// Java 10+: var x = new User() -- infer type from object_creation_expression
|
|
31
|
+
const extractJavaInitializer = (node, env, _classNames) => {
|
|
32
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
33
|
+
const child = node.namedChild(i);
|
|
34
|
+
if (child?.type !== 'variable_declarator')
|
|
35
|
+
continue;
|
|
36
|
+
const nameNode = child.childForFieldName('name');
|
|
37
|
+
const valueNode = child.childForFieldName('value');
|
|
38
|
+
if (!nameNode || !valueNode)
|
|
39
|
+
continue;
|
|
40
|
+
// Skip declarators that already have a binding from extractDeclaration
|
|
41
|
+
const varName = extractVarName(nameNode);
|
|
42
|
+
if (!varName || env.has(varName))
|
|
43
|
+
continue;
|
|
44
|
+
if (valueNode.type !== 'object_creation_expression')
|
|
45
|
+
continue;
|
|
46
|
+
const ctorType = valueNode.childForFieldName('type');
|
|
47
|
+
if (!ctorType)
|
|
48
|
+
continue;
|
|
49
|
+
const typeName = extractSimpleTypeName(ctorType);
|
|
50
|
+
if (typeName)
|
|
51
|
+
env.set(varName, typeName);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
// Java: formal_parameter -> type name
|
|
55
|
+
const extractJavaParameter = (node, env) => {
|
|
56
|
+
let nameNode = null;
|
|
57
|
+
let typeNode = null;
|
|
58
|
+
if (node.type === 'formal_parameter') {
|
|
59
|
+
typeNode = node.childForFieldName('type');
|
|
60
|
+
nameNode = node.childForFieldName('name');
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Generic fallback
|
|
64
|
+
nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
|
|
65
|
+
typeNode = node.childForFieldName('type');
|
|
66
|
+
}
|
|
67
|
+
if (!nameNode || !typeNode)
|
|
68
|
+
return;
|
|
69
|
+
const varName = extractVarName(nameNode);
|
|
70
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
71
|
+
if (varName && typeName)
|
|
72
|
+
env.set(varName, typeName);
|
|
73
|
+
};
|
|
74
|
+
// Java: var x = SomeFactory.create() -- constructor binding for `var` with method_invocation
|
|
75
|
+
const scanJavaConstructorBinding = (node) => {
|
|
76
|
+
if (node.type !== 'local_variable_declaration')
|
|
77
|
+
return undefined;
|
|
78
|
+
const typeNode = node.childForFieldName('type');
|
|
79
|
+
if (!typeNode)
|
|
80
|
+
return undefined;
|
|
81
|
+
if (typeNode.text !== 'var')
|
|
82
|
+
return undefined;
|
|
83
|
+
const declarator = findChildByType(node, 'variable_declarator');
|
|
84
|
+
if (!declarator)
|
|
85
|
+
return undefined;
|
|
86
|
+
const nameNode = declarator.childForFieldName('name');
|
|
87
|
+
const value = declarator.childForFieldName('value');
|
|
88
|
+
if (!nameNode || !value)
|
|
89
|
+
return undefined;
|
|
90
|
+
if (value.type === 'object_creation_expression')
|
|
91
|
+
return undefined;
|
|
92
|
+
if (value.type !== 'method_invocation')
|
|
93
|
+
return undefined;
|
|
94
|
+
const methodName = value.childForFieldName('name');
|
|
95
|
+
if (!methodName)
|
|
96
|
+
return undefined;
|
|
97
|
+
return { varName: nameNode.text, calleeName: methodName.text };
|
|
98
|
+
};
|
|
99
|
+
const JAVA_FOR_LOOP_NODE_TYPES = new Set([
|
|
100
|
+
'enhanced_for_statement',
|
|
101
|
+
]);
|
|
102
|
+
// Java: for (User user : users) -- extract loop variable binding
|
|
103
|
+
const extractJavaForLoopBinding = (node, scopeEnv) => {
|
|
104
|
+
const typeNode = node.childForFieldName('type');
|
|
105
|
+
const nameNode = node.childForFieldName('name');
|
|
106
|
+
if (!typeNode || !nameNode)
|
|
107
|
+
return;
|
|
108
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
109
|
+
const varName = extractVarName(nameNode);
|
|
110
|
+
if (typeName && varName)
|
|
111
|
+
scopeEnv.set(varName, typeName);
|
|
112
|
+
};
|
|
113
|
+
// Java: var alias = u -> local_variable_declaration > variable_declarator with name/value
|
|
114
|
+
const extractJavaPendingAssignment = (node, scopeEnv) => {
|
|
115
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
116
|
+
const child = node.namedChild(i);
|
|
117
|
+
if (!child || child.type !== 'variable_declarator')
|
|
118
|
+
continue;
|
|
119
|
+
const nameNode = child.childForFieldName('name');
|
|
120
|
+
const valueNode = child.childForFieldName('value');
|
|
121
|
+
if (!nameNode || !valueNode)
|
|
122
|
+
continue;
|
|
123
|
+
const lhs = nameNode.text;
|
|
124
|
+
if (scopeEnv.has(lhs))
|
|
125
|
+
continue;
|
|
126
|
+
if (valueNode.type === 'identifier' || valueNode.type === 'simple_identifier')
|
|
127
|
+
return { lhs, rhs: valueNode.text };
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
};
|
|
131
|
+
// Java 16+ `instanceof` pattern variable: `x instanceof User user`
|
|
132
|
+
// AST: instanceof_expression { left, instanceof, right: type, name: identifier (Java 16+) }
|
|
133
|
+
// Conservative: returns undefined when name field is absent (plain instanceof) or
|
|
134
|
+
// type cannot be extracted. Source variable's type is NOT used -- the pattern
|
|
135
|
+
// explicitly declares the new type, so no scopeEnv lookup is needed
|
|
136
|
+
const extractJavaPatternBinding = (node) => {
|
|
137
|
+
if (node.type !== 'instanceof_expression')
|
|
138
|
+
return undefined;
|
|
139
|
+
const nameNode = node.childForFieldName('name');
|
|
140
|
+
if (!nameNode)
|
|
141
|
+
return undefined;
|
|
142
|
+
const typeNode = node.childForFieldName('right');
|
|
143
|
+
if (!typeNode)
|
|
144
|
+
return undefined;
|
|
145
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
146
|
+
const varName = extractVarName(nameNode);
|
|
147
|
+
if (!typeName || !varName)
|
|
148
|
+
return undefined;
|
|
149
|
+
return { varName, typeName };
|
|
150
|
+
};
|
|
151
|
+
export const javaTypeConfig = {
|
|
152
|
+
declarationNodeTypes: JAVA_DECLARATION_NODE_TYPES,
|
|
153
|
+
extractDeclaration: extractJavaDeclaration,
|
|
154
|
+
extractParameter: extractJavaParameter,
|
|
155
|
+
extractInitializer: extractJavaInitializer,
|
|
156
|
+
scanConstructorBinding: scanJavaConstructorBinding,
|
|
157
|
+
forLoopNodeTypes: JAVA_FOR_LOOP_NODE_TYPES,
|
|
158
|
+
extractForLoopBinding: extractJavaForLoopBinding,
|
|
159
|
+
extractPendingAssignment: extractJavaPendingAssignment,
|
|
160
|
+
extractPatternBinding: extractJavaPatternBinding,
|
|
161
|
+
};
|
|
162
|
+
// Kotlin
|
|
163
|
+
const KOTLIN_DECLARATION_NODE_TYPES = new Set([
|
|
164
|
+
'property_declaration',
|
|
165
|
+
'variable_declaration',
|
|
166
|
+
]);
|
|
167
|
+
// Kotlin: val x: Foo = ...
|
|
168
|
+
const extractKotlinDeclaration = (node, env) => {
|
|
169
|
+
if (node.type === 'property_declaration') {
|
|
170
|
+
// Kotlin property_declaration: name/type are inside a variable_declaration child
|
|
171
|
+
const varDecl = findChildByType(node, 'variable_declaration');
|
|
172
|
+
if (varDecl) {
|
|
173
|
+
const nameNode = findChildByType(varDecl, 'simple_identifier');
|
|
174
|
+
const typeNode = findChildByType(varDecl, 'user_type');
|
|
175
|
+
if (!nameNode || !typeNode)
|
|
176
|
+
return;
|
|
177
|
+
const varName = extractVarName(nameNode);
|
|
178
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
179
|
+
if (varName && typeName)
|
|
180
|
+
env.set(varName, typeName);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Fallback: try direct fields
|
|
184
|
+
const nameNode = node.childForFieldName('name')
|
|
185
|
+
?? findChildByType(node, 'simple_identifier');
|
|
186
|
+
const typeNode = node.childForFieldName('type')
|
|
187
|
+
?? findChildByType(node, 'user_type');
|
|
188
|
+
if (!nameNode || !typeNode)
|
|
189
|
+
return;
|
|
190
|
+
const varName = extractVarName(nameNode);
|
|
191
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
192
|
+
if (varName && typeName)
|
|
193
|
+
env.set(varName, typeName);
|
|
194
|
+
}
|
|
195
|
+
else if (node.type === 'variable_declaration') {
|
|
196
|
+
// variable_declaration directly inside functions
|
|
197
|
+
const nameNode = findChildByType(node, 'simple_identifier');
|
|
198
|
+
const typeNode = findChildByType(node, 'user_type');
|
|
199
|
+
if (nameNode && typeNode) {
|
|
200
|
+
const varName = extractVarName(nameNode);
|
|
201
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
202
|
+
if (varName && typeName)
|
|
203
|
+
env.set(varName, typeName);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
// Kotlin: formal_parameter -> type name
|
|
208
|
+
const extractKotlinParameter = (node, env) => {
|
|
209
|
+
let nameNode = null;
|
|
210
|
+
let typeNode = null;
|
|
211
|
+
if (node.type === 'formal_parameter') {
|
|
212
|
+
typeNode = node.childForFieldName('type');
|
|
213
|
+
nameNode = node.childForFieldName('name');
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
|
|
217
|
+
typeNode = node.childForFieldName('type');
|
|
218
|
+
}
|
|
219
|
+
if (!nameNode || !typeNode)
|
|
220
|
+
return;
|
|
221
|
+
const varName = extractVarName(nameNode);
|
|
222
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
223
|
+
if (varName && typeName)
|
|
224
|
+
env.set(varName, typeName);
|
|
225
|
+
};
|
|
226
|
+
// Kotlin: val user = User() -- infer type from call_expression when callee is a known class
|
|
227
|
+
// Kotlin constructors are syntactically identical to function calls, so we verify
|
|
228
|
+
// against classNames (which may include cross-file SymbolTable lookups)
|
|
229
|
+
const extractKotlinInitializer = (node, env, classNames) => {
|
|
230
|
+
if (node.type !== 'property_declaration')
|
|
231
|
+
return;
|
|
232
|
+
// Skip if there's an explicit type annotation -- Tier 0 already handled it
|
|
233
|
+
const varDecl = findChildByType(node, 'variable_declaration');
|
|
234
|
+
if (varDecl && findChildByType(varDecl, 'user_type'))
|
|
235
|
+
return;
|
|
236
|
+
// Get the initializer value -- the call_expression after '='
|
|
237
|
+
const value = node.childForFieldName('value')
|
|
238
|
+
?? findChildByType(node, 'call_expression');
|
|
239
|
+
if (!value || value.type !== 'call_expression')
|
|
240
|
+
return;
|
|
241
|
+
// The callee is the first child of call_expression (simple_identifier for direct calls)
|
|
242
|
+
const callee = value.firstNamedChild;
|
|
243
|
+
if (!callee || callee.type !== 'simple_identifier')
|
|
244
|
+
return;
|
|
245
|
+
const calleeName = callee.text;
|
|
246
|
+
if (!calleeName || !classNames.has(calleeName))
|
|
247
|
+
return;
|
|
248
|
+
// Extract the variable name from the variable_declaration inside property_declaration
|
|
249
|
+
const nameNode = varDecl
|
|
250
|
+
? findChildByType(varDecl, 'simple_identifier')
|
|
251
|
+
: findChildByType(node, 'simple_identifier');
|
|
252
|
+
if (!nameNode)
|
|
253
|
+
return;
|
|
254
|
+
const varName = extractVarName(nameNode);
|
|
255
|
+
if (varName)
|
|
256
|
+
env.set(varName, calleeName);
|
|
257
|
+
};
|
|
258
|
+
// Kotlin: val x = User(...) -- constructor binding for property_declaration with call_expression
|
|
259
|
+
const scanKotlinConstructorBinding = (node) => {
|
|
260
|
+
if (node.type !== 'property_declaration')
|
|
261
|
+
return undefined;
|
|
262
|
+
const varDecl = findChildByType(node, 'variable_declaration');
|
|
263
|
+
if (!varDecl)
|
|
264
|
+
return undefined;
|
|
265
|
+
if (findChildByType(varDecl, 'user_type'))
|
|
266
|
+
return undefined;
|
|
267
|
+
const callExpr = findChildByType(node, 'call_expression');
|
|
268
|
+
if (!callExpr)
|
|
269
|
+
return undefined;
|
|
270
|
+
const callee = callExpr.firstNamedChild;
|
|
271
|
+
if (!callee)
|
|
272
|
+
return undefined;
|
|
273
|
+
let calleeName;
|
|
274
|
+
if (callee.type === 'simple_identifier') {
|
|
275
|
+
calleeName = callee.text;
|
|
276
|
+
}
|
|
277
|
+
else if (callee.type === 'navigation_expression') {
|
|
278
|
+
// Extract method name from qualified call: service.getUser() -> getUser
|
|
279
|
+
const suffix = callee.lastNamedChild;
|
|
280
|
+
if (suffix?.type === 'navigation_suffix') {
|
|
281
|
+
const methodName = suffix.lastNamedChild;
|
|
282
|
+
if (methodName?.type === 'simple_identifier') {
|
|
283
|
+
calleeName = methodName.text;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (!calleeName)
|
|
288
|
+
return undefined;
|
|
289
|
+
const nameNode = findChildByType(varDecl, 'simple_identifier');
|
|
290
|
+
if (!nameNode)
|
|
291
|
+
return undefined;
|
|
292
|
+
return { varName: nameNode.text, calleeName };
|
|
293
|
+
};
|
|
294
|
+
const KOTLIN_FOR_LOOP_NODE_TYPES = new Set([
|
|
295
|
+
'for_statement',
|
|
296
|
+
]);
|
|
297
|
+
// Kotlin: for (user: User in users) -- extract loop variable binding with explicit type annotation
|
|
298
|
+
const extractKotlinForLoopBinding = (node, scopeEnv) => {
|
|
299
|
+
// Kotlin loop variable: variable_declaration child with optional user_type annotation
|
|
300
|
+
const varDecl = findChildByType(node, 'variable_declaration');
|
|
301
|
+
if (!varDecl)
|
|
302
|
+
return;
|
|
303
|
+
// Only extract when there is an explicit type annotation (user_type node)
|
|
304
|
+
const typeNode = findChildByType(varDecl, 'user_type');
|
|
305
|
+
if (!typeNode)
|
|
306
|
+
return;
|
|
307
|
+
const nameNode = findChildByType(varDecl, 'simple_identifier');
|
|
308
|
+
if (!nameNode)
|
|
309
|
+
return;
|
|
310
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
311
|
+
const varName = extractVarName(nameNode);
|
|
312
|
+
if (typeName && varName)
|
|
313
|
+
scopeEnv.set(varName, typeName);
|
|
314
|
+
};
|
|
315
|
+
// Kotlin: val alias = u -> property_declaration or variable_declaration
|
|
316
|
+
// property_declaration has: binding_pattern_kind("val"), variable_declaration("alias"),
|
|
317
|
+
// "=", and the RHS value (simple_identifier "u")
|
|
318
|
+
// variable_declaration appears directly inside functions and has simple_identifier children
|
|
319
|
+
const extractKotlinPendingAssignment = (node, scopeEnv) => {
|
|
320
|
+
if (node.type === 'property_declaration') {
|
|
321
|
+
// Find the variable name from variable_declaration child
|
|
322
|
+
const varDecl = findChildByType(node, 'variable_declaration');
|
|
323
|
+
if (!varDecl)
|
|
324
|
+
return undefined;
|
|
325
|
+
const nameNode = varDecl.firstNamedChild;
|
|
326
|
+
if (!nameNode || nameNode.type !== 'simple_identifier')
|
|
327
|
+
return undefined;
|
|
328
|
+
const lhs = nameNode.text;
|
|
329
|
+
if (scopeEnv.has(lhs))
|
|
330
|
+
return undefined;
|
|
331
|
+
// Find the RHS: a simple_identifier sibling after the "=" token
|
|
332
|
+
let foundEq = false;
|
|
333
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
334
|
+
const child = node.child(i);
|
|
335
|
+
if (!child)
|
|
336
|
+
continue;
|
|
337
|
+
if (child.type === '=') {
|
|
338
|
+
foundEq = true;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (foundEq && child.type === 'simple_identifier') {
|
|
342
|
+
return { lhs, rhs: child.text };
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
if (node.type === 'variable_declaration') {
|
|
348
|
+
// variable_declaration directly inside functions: simple_identifier children
|
|
349
|
+
const nameNode = findChildByType(node, 'simple_identifier');
|
|
350
|
+
if (!nameNode)
|
|
351
|
+
return undefined;
|
|
352
|
+
const lhs = nameNode.text;
|
|
353
|
+
if (scopeEnv.has(lhs))
|
|
354
|
+
return undefined;
|
|
355
|
+
// Look for RHS simple_identifier after "=" in the parent (property_declaration)
|
|
356
|
+
// variable_declaration itself doesn't contain "=" -- it's in the parent
|
|
357
|
+
const parent = node.parent;
|
|
358
|
+
if (!parent)
|
|
359
|
+
return undefined;
|
|
360
|
+
let foundEq = false;
|
|
361
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
362
|
+
const child = parent.child(i);
|
|
363
|
+
if (!child)
|
|
364
|
+
continue;
|
|
365
|
+
if (child.type === '=') {
|
|
366
|
+
foundEq = true;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (foundEq && child.type === 'simple_identifier') {
|
|
370
|
+
return { lhs, rhs: child.text };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
return undefined;
|
|
376
|
+
};
|
|
377
|
+
export const kotlinTypeConfig = {
|
|
378
|
+
declarationNodeTypes: KOTLIN_DECLARATION_NODE_TYPES,
|
|
379
|
+
forLoopNodeTypes: KOTLIN_FOR_LOOP_NODE_TYPES,
|
|
380
|
+
extractDeclaration: extractKotlinDeclaration,
|
|
381
|
+
extractParameter: extractKotlinParameter,
|
|
382
|
+
extractInitializer: extractKotlinInitializer,
|
|
383
|
+
scanConstructorBinding: scanKotlinConstructorBinding,
|
|
384
|
+
extractForLoopBinding: extractKotlinForLoopBinding,
|
|
385
|
+
extractPendingAssignment: extractKotlinPendingAssignment,
|
|
386
|
+
};
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/type-extractors/php.ts
|
|
2
|
+
/** @file php.ts
|
|
3
|
+
* @description Type extraction for PHP (typed properties, PHPDoc annotations, constructor inference) */
|
|
4
|
+
import { extractSimpleTypeName, extractVarName, extractCalleeName } from './shared.js';
|
|
5
|
+
const DECLARATION_NODE_TYPES = new Set([
|
|
6
|
+
'assignment_expression', // For constructor inference: $x = new User()
|
|
7
|
+
'property_declaration', // PHP 7.4+ typed properties: private UserRepo $repo;
|
|
8
|
+
'method_declaration', // PHPDoc @param on class methods
|
|
9
|
+
'function_definition', // PHPDoc @param on top-level functions
|
|
10
|
+
]);
|
|
11
|
+
// Walk up the AST to find the enclosing class declaration
|
|
12
|
+
const findEnclosingClass = (node) => {
|
|
13
|
+
let current = node.parent;
|
|
14
|
+
while (current) {
|
|
15
|
+
if (current.type === 'class_declaration')
|
|
16
|
+
return current;
|
|
17
|
+
current = current.parent;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
// Resolve PHP self/static/parent to the actual class name
|
|
22
|
+
// self/static -> enclosing class name, parent -> superclass from base_clause
|
|
23
|
+
const resolvePhpKeyword = (keyword, node) => {
|
|
24
|
+
if (keyword === 'self' || keyword === 'static') {
|
|
25
|
+
const cls = findEnclosingClass(node);
|
|
26
|
+
if (!cls)
|
|
27
|
+
return undefined;
|
|
28
|
+
const nameNode = cls.childForFieldName('name');
|
|
29
|
+
return nameNode?.text;
|
|
30
|
+
}
|
|
31
|
+
if (keyword === 'parent') {
|
|
32
|
+
const cls = findEnclosingClass(node);
|
|
33
|
+
if (!cls)
|
|
34
|
+
return undefined;
|
|
35
|
+
// base_clause contains the parent class name
|
|
36
|
+
for (let i = 0; i < cls.namedChildCount; i++) {
|
|
37
|
+
const child = cls.namedChild(i);
|
|
38
|
+
if (child?.type === 'base_clause') {
|
|
39
|
+
const parentName = child.firstNamedChild;
|
|
40
|
+
if (parentName)
|
|
41
|
+
return extractSimpleTypeName(parentName);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
};
|
|
48
|
+
// Normalize a PHP type string to a simple class name identifier
|
|
49
|
+
const normalizePhpType = (raw) => {
|
|
50
|
+
// Strip nullable prefix: ?User -> User
|
|
51
|
+
let type = raw.startsWith('?') ? raw.slice(1) : raw;
|
|
52
|
+
// Strip array suffix: User[] -> User
|
|
53
|
+
type = type.replace(/\[\]$/, '');
|
|
54
|
+
// Strip union with null/false/void: User|null -> User
|
|
55
|
+
const parts = type.split('|').filter(p => p !== 'null' && p !== 'false' && p !== 'void' && p !== 'mixed');
|
|
56
|
+
if (parts.length !== 1)
|
|
57
|
+
return undefined;
|
|
58
|
+
type = parts[0];
|
|
59
|
+
// Strip namespace: \App\Models\User -> User
|
|
60
|
+
const segments = type.split('\\');
|
|
61
|
+
type = segments[segments.length - 1];
|
|
62
|
+
// Skip uninformative types
|
|
63
|
+
if (type === 'mixed' || type === 'void' || type === 'self' || type === 'static' || type === 'object')
|
|
64
|
+
return undefined;
|
|
65
|
+
if (/^\w+$/.test(type))
|
|
66
|
+
return type;
|
|
67
|
+
return undefined;
|
|
68
|
+
};
|
|
69
|
+
// Node types to skip when walking backwards to find doc-comments
|
|
70
|
+
// PHP 8+ attributes (#[Route(...)]) appear as named siblings between PHPDoc and method
|
|
71
|
+
const SKIP_NODE_TYPES = new Set(['attribute_list', 'attribute']);
|
|
72
|
+
// Regex to extract PHPDoc @param annotations: `@param Type $name` (standard order)
|
|
73
|
+
const PHPDOC_PARAM_RE = /@param\s+(\S+)\s+\$(\w+)/g;
|
|
74
|
+
// Alternate PHPDoc order: `@param $name Type` (name first)
|
|
75
|
+
const PHPDOC_PARAM_ALT_RE = /@param\s+\$(\w+)\s+(\S+)/g;
|
|
76
|
+
/**
|
|
77
|
+
* Collect PHPDoc @param type bindings from comment nodes preceding a method/function
|
|
78
|
+
* @returns Map of paramName -> typeName (without $ prefix)
|
|
79
|
+
*/
|
|
80
|
+
const collectPhpDocParams = (methodNode) => {
|
|
81
|
+
const commentTexts = [];
|
|
82
|
+
let sibling = methodNode.previousSibling;
|
|
83
|
+
while (sibling) {
|
|
84
|
+
if (sibling.type === 'comment') {
|
|
85
|
+
commentTexts.unshift(sibling.text);
|
|
86
|
+
}
|
|
87
|
+
else if (sibling.isNamed && !SKIP_NODE_TYPES.has(sibling.type)) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
sibling = sibling.previousSibling;
|
|
91
|
+
}
|
|
92
|
+
if (commentTexts.length === 0)
|
|
93
|
+
return new Map();
|
|
94
|
+
const params = new Map();
|
|
95
|
+
const commentBlock = commentTexts.join('\n');
|
|
96
|
+
PHPDOC_PARAM_RE.lastIndex = 0;
|
|
97
|
+
let match;
|
|
98
|
+
while ((match = PHPDOC_PARAM_RE.exec(commentBlock)) !== null) {
|
|
99
|
+
const typeName = normalizePhpType(match[1]);
|
|
100
|
+
const paramName = match[2]; // without $ prefix
|
|
101
|
+
if (typeName) {
|
|
102
|
+
// Store with $ prefix to match how PHP variables appear in the env
|
|
103
|
+
params.set('$' + paramName, typeName);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Also check alternate PHPDoc order: @param $name Type
|
|
107
|
+
PHPDOC_PARAM_ALT_RE.lastIndex = 0;
|
|
108
|
+
while ((match = PHPDOC_PARAM_ALT_RE.exec(commentBlock)) !== null) {
|
|
109
|
+
const paramName = match[1];
|
|
110
|
+
if (params.has('$' + paramName))
|
|
111
|
+
continue; // standard format takes priority
|
|
112
|
+
const typeName = normalizePhpType(match[2]);
|
|
113
|
+
if (typeName) {
|
|
114
|
+
params.set('$' + paramName, typeName);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return params;
|
|
118
|
+
};
|
|
119
|
+
// PHP: typed class properties (PHP 7.4+): private UserRepo $repo;
|
|
120
|
+
// Also: PHPDoc @param annotations on method/function definitions
|
|
121
|
+
const extractDeclaration = (node, env) => {
|
|
122
|
+
// PHPDoc @param on methods/functions -- pre-populate env with param types
|
|
123
|
+
if (node.type === 'method_declaration' || node.type === 'function_definition') {
|
|
124
|
+
const phpDocParams = collectPhpDocParams(node);
|
|
125
|
+
for (const [paramName, typeName] of phpDocParams) {
|
|
126
|
+
if (!env.has(paramName))
|
|
127
|
+
env.set(paramName, typeName);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (node.type !== 'property_declaration')
|
|
132
|
+
return;
|
|
133
|
+
const typeNode = node.childForFieldName('type');
|
|
134
|
+
if (!typeNode)
|
|
135
|
+
return;
|
|
136
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
137
|
+
if (!typeName)
|
|
138
|
+
return;
|
|
139
|
+
// The variable name is inside property_element > variable_name
|
|
140
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
141
|
+
const child = node.namedChild(i);
|
|
142
|
+
if (child?.type === 'property_element') {
|
|
143
|
+
const varNameNode = child.firstNamedChild; // variable_name
|
|
144
|
+
if (varNameNode) {
|
|
145
|
+
const varName = extractVarName(varNameNode);
|
|
146
|
+
if (varName)
|
|
147
|
+
env.set(varName, typeName);
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
// PHP: $x = new User() -- infer type from object_creation_expression
|
|
154
|
+
const extractInitializer = (node, env, _classNames) => {
|
|
155
|
+
if (node.type !== 'assignment_expression')
|
|
156
|
+
return;
|
|
157
|
+
const left = node.childForFieldName('left');
|
|
158
|
+
const right = node.childForFieldName('right');
|
|
159
|
+
if (!left || !right)
|
|
160
|
+
return;
|
|
161
|
+
if (right.type !== 'object_creation_expression')
|
|
162
|
+
return;
|
|
163
|
+
// The class name is the first named child of object_creation_expression
|
|
164
|
+
// (tree-sitter-php uses 'name' or 'qualified_name' nodes here)
|
|
165
|
+
const ctorType = right.firstNamedChild;
|
|
166
|
+
if (!ctorType)
|
|
167
|
+
return;
|
|
168
|
+
const typeName = extractSimpleTypeName(ctorType);
|
|
169
|
+
if (!typeName)
|
|
170
|
+
return;
|
|
171
|
+
// Resolve PHP self/static/parent to actual class names
|
|
172
|
+
const resolvedType = (typeName === 'self' || typeName === 'static' || typeName === 'parent')
|
|
173
|
+
? resolvePhpKeyword(typeName, node)
|
|
174
|
+
: typeName;
|
|
175
|
+
if (!resolvedType)
|
|
176
|
+
return;
|
|
177
|
+
const varName = extractVarName(left);
|
|
178
|
+
if (varName)
|
|
179
|
+
env.set(varName, resolvedType);
|
|
180
|
+
};
|
|
181
|
+
// PHP: simple_parameter -> type $name
|
|
182
|
+
const extractParameter = (node, env) => {
|
|
183
|
+
let nameNode = null;
|
|
184
|
+
let typeNode = null;
|
|
185
|
+
if (node.type === 'simple_parameter') {
|
|
186
|
+
typeNode = node.childForFieldName('type');
|
|
187
|
+
nameNode = node.childForFieldName('name');
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
|
|
191
|
+
typeNode = node.childForFieldName('type');
|
|
192
|
+
}
|
|
193
|
+
if (!nameNode || !typeNode)
|
|
194
|
+
return;
|
|
195
|
+
const varName = extractVarName(nameNode);
|
|
196
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
197
|
+
if (varName && typeName)
|
|
198
|
+
env.set(varName, typeName);
|
|
199
|
+
};
|
|
200
|
+
// PHP: $x = SomeFactory() or $x = $this->getUser() -- bind variable to call return type
|
|
201
|
+
const scanConstructorBinding = (node) => {
|
|
202
|
+
if (node.type !== 'assignment_expression')
|
|
203
|
+
return undefined;
|
|
204
|
+
const left = node.childForFieldName('left');
|
|
205
|
+
const right = node.childForFieldName('right');
|
|
206
|
+
if (!left || !right)
|
|
207
|
+
return undefined;
|
|
208
|
+
if (left.type !== 'variable_name')
|
|
209
|
+
return undefined;
|
|
210
|
+
// Skip object_creation_expression (new User()) -- handled by extractInitializer
|
|
211
|
+
if (right.type === 'object_creation_expression')
|
|
212
|
+
return undefined;
|
|
213
|
+
// Handle both standalone function calls and method calls ($this->getUser())
|
|
214
|
+
if (right.type === 'function_call_expression') {
|
|
215
|
+
const calleeName = extractCalleeName(right);
|
|
216
|
+
if (!calleeName)
|
|
217
|
+
return undefined;
|
|
218
|
+
return { varName: left.text, calleeName };
|
|
219
|
+
}
|
|
220
|
+
if (right.type === 'member_call_expression') {
|
|
221
|
+
const methodName = right.childForFieldName('name');
|
|
222
|
+
if (!methodName)
|
|
223
|
+
return undefined;
|
|
224
|
+
// When receiver is $this/self/static, qualify with enclosing class for disambiguation
|
|
225
|
+
const receiver = right.childForFieldName('object');
|
|
226
|
+
const receiverText = receiver?.text;
|
|
227
|
+
let receiverClassName;
|
|
228
|
+
if (receiverText === '$this' || receiverText === 'self' || receiverText === 'static') {
|
|
229
|
+
const cls = findEnclosingClass(node);
|
|
230
|
+
const clsName = cls?.childForFieldName('name');
|
|
231
|
+
if (clsName)
|
|
232
|
+
receiverClassName = clsName.text;
|
|
233
|
+
}
|
|
234
|
+
return { varName: left.text, calleeName: methodName.text, receiverClassName };
|
|
235
|
+
}
|
|
236
|
+
return undefined;
|
|
237
|
+
};
|
|
238
|
+
// Regex to extract PHPDoc @return annotations: `@return User`
|
|
239
|
+
const PHPDOC_RETURN_RE = /@return\s+(\S+)/;
|
|
240
|
+
// Extract return type from PHPDoc `@return Type` annotation preceding a method
|
|
241
|
+
const extractReturnType = (node) => {
|
|
242
|
+
let sibling = node.previousSibling;
|
|
243
|
+
while (sibling) {
|
|
244
|
+
if (sibling.type === 'comment') {
|
|
245
|
+
const match = PHPDOC_RETURN_RE.exec(sibling.text);
|
|
246
|
+
if (match)
|
|
247
|
+
return normalizePhpType(match[1]);
|
|
248
|
+
}
|
|
249
|
+
else if (sibling.isNamed && !SKIP_NODE_TYPES.has(sibling.type))
|
|
250
|
+
break;
|
|
251
|
+
sibling = sibling.previousSibling;
|
|
252
|
+
}
|
|
253
|
+
return undefined;
|
|
254
|
+
};
|
|
255
|
+
// PHP: $alias = $user -> assignment_expression with variable_name left/right
|
|
256
|
+
// PHP TypeEnv stores variables WITH $ prefix ($user -> User), so we keep $ in lhs/rhs
|
|
257
|
+
const extractPendingAssignment = (node, scopeEnv) => {
|
|
258
|
+
if (node.type !== 'assignment_expression')
|
|
259
|
+
return undefined;
|
|
260
|
+
const left = node.childForFieldName('left');
|
|
261
|
+
const right = node.childForFieldName('right');
|
|
262
|
+
if (!left || !right)
|
|
263
|
+
return undefined;
|
|
264
|
+
if (left.type !== 'variable_name' || right.type !== 'variable_name')
|
|
265
|
+
return undefined;
|
|
266
|
+
const lhs = left.text;
|
|
267
|
+
const rhs = right.text;
|
|
268
|
+
if (!lhs || !rhs || scopeEnv.has(lhs))
|
|
269
|
+
return undefined;
|
|
270
|
+
return { lhs, rhs };
|
|
271
|
+
};
|
|
272
|
+
export const typeConfig = {
|
|
273
|
+
declarationNodeTypes: DECLARATION_NODE_TYPES,
|
|
274
|
+
extractDeclaration,
|
|
275
|
+
extractParameter,
|
|
276
|
+
extractInitializer,
|
|
277
|
+
scanConstructorBinding,
|
|
278
|
+
extractReturnType,
|
|
279
|
+
extractPendingAssignment,
|
|
280
|
+
};
|