gitnexus 1.4.7 → 1.4.9
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 +29 -1
- package/dist/cli/ai-context.d.ts +1 -1
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +54 -21
- package/dist/cli/index-repo.d.ts +15 -0
- package/dist/cli/index-repo.js +115 -0
- package/dist/cli/index.js +13 -3
- package/dist/cli/setup.js +90 -10
- package/dist/cli/wiki.d.ts +4 -0
- package/dist/cli/wiki.js +174 -53
- package/dist/config/supported-languages.d.ts +33 -1
- package/dist/config/supported-languages.js +32 -0
- package/dist/core/embeddings/embedder.d.ts +6 -1
- package/dist/core/embeddings/embedder.js +65 -5
- package/dist/core/embeddings/embedding-pipeline.js +11 -9
- package/dist/core/embeddings/http-client.d.ts +31 -0
- package/dist/core/embeddings/http-client.js +179 -0
- package/dist/core/embeddings/index.d.ts +1 -0
- package/dist/core/embeddings/index.js +1 -0
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/graph.js +9 -1
- package/dist/core/graph/types.d.ts +11 -2
- package/dist/core/ingestion/call-processor.d.ts +66 -2
- package/dist/core/ingestion/call-processor.js +650 -30
- package/dist/core/ingestion/call-routing.d.ts +9 -18
- package/dist/core/ingestion/call-routing.js +0 -19
- package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
- package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
- package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
- package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
- package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
- package/dist/core/ingestion/cobol-processor.d.ts +54 -0
- package/dist/core/ingestion/cobol-processor.js +1186 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
- package/dist/core/ingestion/entry-point-scoring.js +52 -28
- package/dist/core/ingestion/export-detection.d.ts +47 -8
- package/dist/core/ingestion/export-detection.js +29 -50
- package/dist/core/ingestion/field-extractor.d.ts +29 -0
- package/dist/core/ingestion/field-extractor.js +25 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
- package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
- package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
- package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
- package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
- package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
- package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
- package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
- package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
- package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
- package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
- package/dist/core/ingestion/field-extractors/generic.js +111 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
- package/dist/core/ingestion/field-extractors/typescript.js +291 -0
- package/dist/core/ingestion/field-types.d.ts +59 -0
- package/dist/core/ingestion/field-types.js +2 -0
- package/dist/core/ingestion/framework-detection.d.ts +97 -2
- package/dist/core/ingestion/framework-detection.js +114 -14
- package/dist/core/ingestion/heritage-processor.js +62 -66
- package/dist/core/ingestion/import-processor.d.ts +9 -10
- package/dist/core/ingestion/import-processor.js +150 -196
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
- package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/dart.js +44 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +10 -1
- package/dist/core/ingestion/import-resolvers/jvm.js +159 -0
- package/dist/core/ingestion/import-resolvers/php.d.ts +25 -0
- package/dist/core/ingestion/import-resolvers/php.js +80 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
- package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/swift.js +23 -0
- package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
- package/dist/core/ingestion/import-resolvers/types.js +6 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +2 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +7 -0
- package/dist/core/ingestion/language-config.d.ts +6 -0
- package/dist/core/ingestion/language-config.js +13 -0
- package/dist/core/ingestion/language-provider.d.ts +121 -0
- package/dist/core/ingestion/language-provider.js +24 -0
- package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
- package/dist/core/ingestion/languages/c-cpp.js +71 -0
- package/dist/core/ingestion/languages/cobol.d.ts +1 -0
- package/dist/core/ingestion/languages/cobol.js +26 -0
- package/dist/core/ingestion/languages/csharp.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp.js +49 -0
- package/dist/core/ingestion/languages/dart.d.ts +12 -0
- package/dist/core/ingestion/languages/dart.js +58 -0
- package/dist/core/ingestion/languages/go.d.ts +11 -0
- package/dist/core/ingestion/languages/go.js +28 -0
- package/dist/core/ingestion/languages/index.d.ts +38 -0
- package/dist/core/ingestion/languages/index.js +63 -0
- package/dist/core/ingestion/languages/java.d.ts +9 -0
- package/dist/core/ingestion/languages/java.js +29 -0
- package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
- package/dist/core/ingestion/languages/kotlin.js +53 -0
- package/dist/core/ingestion/languages/php.d.ts +8 -0
- package/dist/core/ingestion/languages/php.js +145 -0
- package/dist/core/ingestion/languages/python.d.ts +12 -0
- package/dist/core/ingestion/languages/python.js +39 -0
- package/dist/core/ingestion/languages/ruby.d.ts +9 -0
- package/dist/core/ingestion/languages/ruby.js +44 -0
- package/dist/core/ingestion/languages/rust.d.ts +12 -0
- package/dist/core/ingestion/languages/rust.js +44 -0
- package/dist/core/ingestion/languages/swift.d.ts +12 -0
- package/dist/core/ingestion/languages/swift.js +133 -0
- package/dist/core/ingestion/languages/typescript.d.ts +10 -0
- package/dist/core/ingestion/languages/typescript.js +60 -0
- package/dist/core/ingestion/markdown-processor.d.ts +17 -0
- package/dist/core/ingestion/markdown-processor.js +124 -0
- package/dist/core/ingestion/mro-processor.js +22 -18
- package/dist/core/ingestion/named-binding-processor.d.ts +18 -0
- package/dist/core/ingestion/named-binding-processor.js +42 -0
- package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/csharp.js +37 -0
- package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/java.js +29 -0
- package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
- package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/php.js +61 -0
- package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/python.js +49 -0
- package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/rust.js +64 -0
- package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
- package/dist/core/ingestion/named-bindings/types.js +6 -0
- package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/typescript.js +58 -0
- package/dist/core/ingestion/parsing-processor.d.ts +6 -2
- package/dist/core/ingestion/parsing-processor.js +125 -85
- package/dist/core/ingestion/pipeline.d.ts +10 -0
- package/dist/core/ingestion/pipeline.js +1235 -317
- package/dist/core/ingestion/resolution-context.d.ts +5 -0
- package/dist/core/ingestion/resolution-context.js +8 -5
- package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
- package/dist/core/ingestion/route-extractors/expo.js +36 -0
- package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
- package/dist/core/ingestion/route-extractors/middleware.js +143 -0
- package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
- package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
- package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
- package/dist/core/ingestion/route-extractors/php.js +21 -0
- package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
- package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
- package/dist/core/ingestion/symbol-table.d.ts +16 -0
- package/dist/core/ingestion/symbol-table.js +20 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +10 -9
- package/dist/core/ingestion/tree-sitter-queries.js +274 -11
- package/dist/core/ingestion/type-env.d.ts +42 -18
- package/dist/core/ingestion/type-env.js +481 -106
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
- package/dist/core/ingestion/type-extractors/csharp.js +149 -16
- package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
- package/dist/core/ingestion/type-extractors/dart.js +371 -0
- package/dist/core/ingestion/type-extractors/jvm.js +169 -66
- package/dist/core/ingestion/type-extractors/rust.js +35 -1
- package/dist/core/ingestion/type-extractors/shared.d.ts +1 -15
- package/dist/core/ingestion/type-extractors/shared.js +14 -112
- package/dist/core/ingestion/type-extractors/swift.js +338 -7
- package/dist/core/ingestion/type-extractors/types.d.ts +40 -8
- package/dist/core/ingestion/type-extractors/typescript.js +141 -9
- package/dist/core/ingestion/utils/ast-helpers.d.ts +83 -0
- package/dist/core/ingestion/utils/ast-helpers.js +817 -0
- package/dist/core/ingestion/utils/call-analysis.d.ts +73 -0
- package/dist/core/ingestion/utils/call-analysis.js +527 -0
- package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
- package/dist/core/ingestion/utils/event-loop.js +5 -0
- package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
- package/dist/core/ingestion/utils/language-detection.js +70 -0
- package/dist/core/ingestion/utils/verbose.d.ts +1 -0
- package/dist/core/ingestion/utils/verbose.js +7 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +55 -5
- package/dist/core/ingestion/workers/parse-worker.js +415 -225
- package/dist/core/lbug/csv-generator.js +51 -1
- package/dist/core/lbug/lbug-adapter.d.ts +10 -0
- package/dist/core/lbug/lbug-adapter.js +75 -4
- package/dist/core/lbug/schema.d.ts +8 -4
- package/dist/core/lbug/schema.js +65 -4
- package/dist/core/tree-sitter/parser-loader.js +7 -1
- package/dist/core/wiki/cursor-client.d.ts +31 -0
- package/dist/core/wiki/cursor-client.js +127 -0
- package/dist/core/wiki/generator.d.ts +28 -9
- package/dist/core/wiki/generator.js +115 -18
- package/dist/core/wiki/graph-queries.d.ts +4 -0
- package/dist/core/wiki/graph-queries.js +7 -1
- package/dist/core/wiki/llm-client.d.ts +2 -0
- package/dist/core/wiki/llm-client.js +8 -4
- package/dist/core/wiki/prompts.d.ts +3 -3
- package/dist/core/wiki/prompts.js +6 -0
- package/dist/mcp/core/embedder.js +11 -3
- package/dist/mcp/core/lbug-adapter.d.ts +5 -0
- package/dist/mcp/core/lbug-adapter.js +23 -2
- package/dist/mcp/local/local-backend.d.ts +38 -5
- package/dist/mcp/local/local-backend.js +804 -63
- package/dist/mcp/resources.js +2 -0
- package/dist/mcp/tools.js +73 -4
- package/dist/server/api.d.ts +19 -1
- package/dist/server/api.js +66 -6
- package/dist/storage/git.d.ts +12 -0
- package/dist/storage/git.js +21 -0
- package/dist/storage/repo-manager.d.ts +3 -0
- package/package.json +25 -16
- package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
- package/dist/core/ingestion/named-binding-extraction.js +0 -363
- package/dist/core/ingestion/resolvers/index.d.ts +0 -18
- package/dist/core/ingestion/resolvers/index.js +0 -13
- package/dist/core/ingestion/resolvers/jvm.js +0 -87
- package/dist/core/ingestion/resolvers/php.d.ts +0 -15
- package/dist/core/ingestion/resolvers/php.js +0 -35
- package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
- package/dist/core/ingestion/type-extractors/index.js +0 -31
- package/dist/core/ingestion/utils.d.ts +0 -138
- package/dist/core/ingestion/utils.js +0 -1290
- package/scripts/patch-tree-sitter-swift.cjs +0 -74
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findChild } from '../utils/ast-helpers.js';
|
|
2
|
+
import { extractSimpleTypeName, extractVarName, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString } from './shared.js';
|
|
2
3
|
// ── Java ──────────────────────────────────────────────────────────────────
|
|
3
4
|
const JAVA_DECLARATION_NODE_TYPES = new Set([
|
|
4
5
|
'local_variable_declaration',
|
|
@@ -78,7 +79,7 @@ const scanJavaConstructorBinding = (node) => {
|
|
|
78
79
|
return undefined;
|
|
79
80
|
if (typeNode.text !== 'var')
|
|
80
81
|
return undefined;
|
|
81
|
-
const declarator =
|
|
82
|
+
const declarator = findChild(node, 'variable_declarator');
|
|
82
83
|
if (!declarator)
|
|
83
84
|
return undefined;
|
|
84
85
|
const nameNode = declarator.childForFieldName('name');
|
|
@@ -293,6 +294,39 @@ const extractJavaPatternBinding = (node) => {
|
|
|
293
294
|
return undefined;
|
|
294
295
|
return { varName, typeName };
|
|
295
296
|
};
|
|
297
|
+
/** Infer the type of a literal AST node for Java/Kotlin overload disambiguation. */
|
|
298
|
+
const inferJvmLiteralType = (node) => {
|
|
299
|
+
switch (node.type) {
|
|
300
|
+
case 'decimal_integer_literal':
|
|
301
|
+
case 'integer_literal':
|
|
302
|
+
case 'hex_integer_literal':
|
|
303
|
+
case 'octal_integer_literal':
|
|
304
|
+
case 'binary_integer_literal':
|
|
305
|
+
// Check for long suffix
|
|
306
|
+
if (node.text.endsWith('L') || node.text.endsWith('l'))
|
|
307
|
+
return 'long';
|
|
308
|
+
return 'int';
|
|
309
|
+
case 'decimal_floating_point_literal':
|
|
310
|
+
case 'real_literal':
|
|
311
|
+
if (node.text.endsWith('f') || node.text.endsWith('F'))
|
|
312
|
+
return 'float';
|
|
313
|
+
return 'double';
|
|
314
|
+
case 'string_literal':
|
|
315
|
+
case 'line_string_literal':
|
|
316
|
+
case 'multi_line_string_literal':
|
|
317
|
+
return 'String';
|
|
318
|
+
case 'character_literal':
|
|
319
|
+
return 'char';
|
|
320
|
+
case 'true':
|
|
321
|
+
case 'false':
|
|
322
|
+
case 'boolean_literal':
|
|
323
|
+
return 'boolean';
|
|
324
|
+
case 'null_literal':
|
|
325
|
+
return 'null';
|
|
326
|
+
default:
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
296
330
|
export const javaTypeConfig = {
|
|
297
331
|
declarationNodeTypes: JAVA_DECLARATION_NODE_TYPES,
|
|
298
332
|
forLoopNodeTypes: JAVA_FOR_LOOP_NODE_TYPES,
|
|
@@ -304,6 +338,7 @@ export const javaTypeConfig = {
|
|
|
304
338
|
extractForLoopBinding: extractJavaForLoopBinding,
|
|
305
339
|
extractPendingAssignment: extractJavaPendingAssignment,
|
|
306
340
|
extractPatternBinding: extractJavaPatternBinding,
|
|
341
|
+
inferLiteralType: inferJvmLiteralType,
|
|
307
342
|
};
|
|
308
343
|
// ── Kotlin ────────────────────────────────────────────────────────────────
|
|
309
344
|
const KOTLIN_DECLARATION_NODE_TYPES = new Set([
|
|
@@ -314,10 +349,11 @@ const KOTLIN_DECLARATION_NODE_TYPES = new Set([
|
|
|
314
349
|
const extractKotlinDeclaration = (node, env) => {
|
|
315
350
|
if (node.type === 'property_declaration') {
|
|
316
351
|
// Kotlin property_declaration: name/type are inside a variable_declaration child
|
|
317
|
-
const varDecl =
|
|
352
|
+
const varDecl = findChild(node, 'variable_declaration');
|
|
318
353
|
if (varDecl) {
|
|
319
|
-
const nameNode =
|
|
320
|
-
const typeNode =
|
|
354
|
+
const nameNode = findChild(varDecl, 'simple_identifier');
|
|
355
|
+
const typeNode = findChild(varDecl, 'user_type')
|
|
356
|
+
?? findChild(varDecl, 'nullable_type');
|
|
321
357
|
if (!nameNode || !typeNode)
|
|
322
358
|
return;
|
|
323
359
|
const varName = extractVarName(nameNode);
|
|
@@ -328,9 +364,9 @@ const extractKotlinDeclaration = (node, env) => {
|
|
|
328
364
|
}
|
|
329
365
|
// Fallback: try direct fields
|
|
330
366
|
const nameNode = node.childForFieldName('name')
|
|
331
|
-
??
|
|
367
|
+
?? findChild(node, 'simple_identifier');
|
|
332
368
|
const typeNode = node.childForFieldName('type')
|
|
333
|
-
??
|
|
369
|
+
?? findChild(node, 'user_type');
|
|
334
370
|
if (!nameNode || !typeNode)
|
|
335
371
|
return;
|
|
336
372
|
const varName = extractVarName(nameNode);
|
|
@@ -340,8 +376,8 @@ const extractKotlinDeclaration = (node, env) => {
|
|
|
340
376
|
}
|
|
341
377
|
else if (node.type === 'variable_declaration') {
|
|
342
378
|
// variable_declaration directly inside functions
|
|
343
|
-
const nameNode =
|
|
344
|
-
const typeNode =
|
|
379
|
+
const nameNode = findChild(node, 'simple_identifier');
|
|
380
|
+
const typeNode = findChild(node, 'user_type');
|
|
345
381
|
if (nameNode && typeNode) {
|
|
346
382
|
const varName = extractVarName(nameNode);
|
|
347
383
|
const typeName = extractSimpleTypeName(typeNode);
|
|
@@ -353,7 +389,7 @@ const extractKotlinDeclaration = (node, env) => {
|
|
|
353
389
|
/** Kotlin: parameter / formal_parameter → type name.
|
|
354
390
|
* Kotlin's tree-sitter grammar uses positional children (simple_identifier, user_type)
|
|
355
391
|
* rather than named fields (name, type) on `parameter` nodes, so we fall back to
|
|
356
|
-
*
|
|
392
|
+
* findChild when childForFieldName returns null. */
|
|
357
393
|
const extractKotlinParameter = (node, env) => {
|
|
358
394
|
let nameNode = null;
|
|
359
395
|
let typeNode = null;
|
|
@@ -367,9 +403,10 @@ const extractKotlinParameter = (node, env) => {
|
|
|
367
403
|
}
|
|
368
404
|
// Fallback: Kotlin `parameter` nodes use positional children, not named fields
|
|
369
405
|
if (!nameNode)
|
|
370
|
-
nameNode =
|
|
406
|
+
nameNode = findChild(node, 'simple_identifier');
|
|
371
407
|
if (!typeNode)
|
|
372
|
-
typeNode =
|
|
408
|
+
typeNode = findChild(node, 'user_type')
|
|
409
|
+
?? findChild(node, 'nullable_type');
|
|
373
410
|
if (!nameNode || !typeNode)
|
|
374
411
|
return;
|
|
375
412
|
const varName = extractVarName(nameNode);
|
|
@@ -377,48 +414,61 @@ const extractKotlinParameter = (node, env) => {
|
|
|
377
414
|
if (varName && typeName)
|
|
378
415
|
env.set(varName, typeName);
|
|
379
416
|
};
|
|
380
|
-
/**
|
|
381
|
-
*
|
|
382
|
-
|
|
383
|
-
const extractKotlinInitializer = (node, env, classNames) => {
|
|
417
|
+
/** Find the constructor callee name in a Kotlin property_declaration's initializer.
|
|
418
|
+
* Returns the class name if the callee is a verified class constructor, undefined otherwise. */
|
|
419
|
+
const findKotlinConstructorCallee = (node, classNames) => {
|
|
384
420
|
if (node.type !== 'property_declaration')
|
|
385
|
-
return;
|
|
386
|
-
// Skip if there's an explicit type annotation — Tier 0 already handled it
|
|
387
|
-
const varDecl = findChildByType(node, 'variable_declaration');
|
|
388
|
-
if (varDecl && findChildByType(varDecl, 'user_type'))
|
|
389
|
-
return;
|
|
390
|
-
// Get the initializer value — the call_expression after '='
|
|
421
|
+
return undefined;
|
|
391
422
|
const value = node.childForFieldName('value')
|
|
392
|
-
??
|
|
423
|
+
?? findChild(node, 'call_expression');
|
|
393
424
|
if (!value || value.type !== 'call_expression')
|
|
394
|
-
return;
|
|
395
|
-
// The callee is the first child of call_expression (simple_identifier for direct calls)
|
|
425
|
+
return undefined;
|
|
396
426
|
const callee = value.firstNamedChild;
|
|
397
427
|
if (!callee || callee.type !== 'simple_identifier')
|
|
398
|
-
return;
|
|
428
|
+
return undefined;
|
|
399
429
|
const calleeName = callee.text;
|
|
400
430
|
if (!calleeName || !classNames.has(calleeName))
|
|
431
|
+
return undefined;
|
|
432
|
+
return calleeName;
|
|
433
|
+
};
|
|
434
|
+
/** Kotlin: val user = User() — infer type from call_expression when callee is a known class.
|
|
435
|
+
* Kotlin constructors are syntactically identical to function calls, so we verify
|
|
436
|
+
* against classNames (which may include cross-file SymbolTable lookups). */
|
|
437
|
+
const extractKotlinInitializer = (node, env, classNames) => {
|
|
438
|
+
// Skip if there's an explicit type annotation — Tier 0 already handled it
|
|
439
|
+
const varDecl = findChild(node, 'variable_declaration');
|
|
440
|
+
if (varDecl && findChild(varDecl, 'user_type'))
|
|
441
|
+
return;
|
|
442
|
+
const calleeName = findKotlinConstructorCallee(node, classNames);
|
|
443
|
+
if (!calleeName)
|
|
401
444
|
return;
|
|
402
445
|
// Extract the variable name from the variable_declaration inside property_declaration
|
|
403
446
|
const nameNode = varDecl
|
|
404
|
-
?
|
|
405
|
-
:
|
|
447
|
+
? findChild(varDecl, 'simple_identifier')
|
|
448
|
+
: findChild(node, 'simple_identifier');
|
|
406
449
|
if (!nameNode)
|
|
407
450
|
return;
|
|
408
451
|
const varName = extractVarName(nameNode);
|
|
409
452
|
if (varName)
|
|
410
453
|
env.set(varName, calleeName);
|
|
411
454
|
};
|
|
455
|
+
/** Kotlin: detect constructor type from call_expression in typed declarations.
|
|
456
|
+
* Unlike extractKotlinInitializer (which SKIPS typed declarations), this detects
|
|
457
|
+
* the constructor type EVEN when a type annotation exists, enabling virtual dispatch
|
|
458
|
+
* for patterns like `val a: Animal = Dog()`. */
|
|
459
|
+
const detectKotlinConstructorType = (node, classNames) => {
|
|
460
|
+
return findKotlinConstructorCallee(node, classNames);
|
|
461
|
+
};
|
|
412
462
|
/** Kotlin: val x = User(...) — constructor binding for property_declaration with call_expression */
|
|
413
463
|
const scanKotlinConstructorBinding = (node) => {
|
|
414
464
|
if (node.type !== 'property_declaration')
|
|
415
465
|
return undefined;
|
|
416
|
-
const varDecl =
|
|
466
|
+
const varDecl = findChild(node, 'variable_declaration');
|
|
417
467
|
if (!varDecl)
|
|
418
468
|
return undefined;
|
|
419
|
-
if (
|
|
469
|
+
if (findChild(varDecl, 'user_type'))
|
|
420
470
|
return undefined;
|
|
421
|
-
const callExpr =
|
|
471
|
+
const callExpr = findChild(node, 'call_expression');
|
|
422
472
|
if (!callExpr)
|
|
423
473
|
return undefined;
|
|
424
474
|
const callee = callExpr.firstNamedChild;
|
|
@@ -440,7 +490,7 @@ const scanKotlinConstructorBinding = (node) => {
|
|
|
440
490
|
}
|
|
441
491
|
if (!calleeName)
|
|
442
492
|
return undefined;
|
|
443
|
-
const nameNode =
|
|
493
|
+
const nameNode = findChild(varDecl, 'simple_identifier');
|
|
444
494
|
if (!nameNode)
|
|
445
495
|
return undefined;
|
|
446
496
|
return { varName: nameNode.text, calleeName };
|
|
@@ -453,7 +503,7 @@ const KOTLIN_FOR_LOOP_NODE_TYPES = new Set([
|
|
|
453
503
|
* Handles the type_projection wrapper that Kotlin uses for generic type arguments. */
|
|
454
504
|
const extractKotlinElementTypeFromTypeNode = (typeNode, pos = 'last') => {
|
|
455
505
|
if (typeNode.type === 'user_type') {
|
|
456
|
-
const argsNode =
|
|
506
|
+
const argsNode = findChild(typeNode, 'type_arguments');
|
|
457
507
|
if (argsNode && argsNode.namedChildCount >= 1) {
|
|
458
508
|
const targetArg = pos === 'first'
|
|
459
509
|
? argsNode.namedChild(0)
|
|
@@ -476,16 +526,16 @@ const findKotlinParamElementType = (iterableName, startNode, pos = 'last') => {
|
|
|
476
526
|
let current = startNode.parent;
|
|
477
527
|
while (current) {
|
|
478
528
|
if (current.type === 'function_declaration') {
|
|
479
|
-
const paramsNode =
|
|
529
|
+
const paramsNode = findChild(current, 'function_value_parameters');
|
|
480
530
|
if (paramsNode) {
|
|
481
531
|
for (let i = 0; i < paramsNode.namedChildCount; i++) {
|
|
482
532
|
const param = paramsNode.namedChild(i);
|
|
483
533
|
if (!param || param.type !== 'parameter')
|
|
484
534
|
continue;
|
|
485
|
-
const nameNode =
|
|
535
|
+
const nameNode = findChild(param, 'simple_identifier');
|
|
486
536
|
if (nameNode?.text !== iterableName)
|
|
487
537
|
continue;
|
|
488
|
-
const typeNode =
|
|
538
|
+
const typeNode = findChild(param, 'user_type');
|
|
489
539
|
if (typeNode)
|
|
490
540
|
return extractKotlinElementTypeFromTypeNode(typeNode, pos);
|
|
491
541
|
}
|
|
@@ -500,17 +550,17 @@ const findKotlinParamElementType = (iterableName, startNode, pos = 'last') => {
|
|
|
500
550
|
* Tier 1c: for `for (user in users)` without annotation, resolves from iterable. */
|
|
501
551
|
const extractKotlinForLoopBinding = (node, ctx) => {
|
|
502
552
|
const { scopeEnv, declarationTypeNodes, scope, returnTypeLookup } = ctx;
|
|
503
|
-
const varDecl =
|
|
553
|
+
const varDecl = findChild(node, 'variable_declaration');
|
|
504
554
|
if (!varDecl)
|
|
505
555
|
return;
|
|
506
|
-
const nameNode =
|
|
556
|
+
const nameNode = findChild(varDecl, 'simple_identifier');
|
|
507
557
|
if (!nameNode)
|
|
508
558
|
return;
|
|
509
559
|
const varName = extractVarName(nameNode);
|
|
510
560
|
if (!varName)
|
|
511
561
|
return;
|
|
512
562
|
// Explicit type annotation (existing behavior): for (user: User in users)
|
|
513
|
-
const typeNode =
|
|
563
|
+
const typeNode = findChild(varDecl, 'user_type');
|
|
514
564
|
if (typeNode) {
|
|
515
565
|
const typeName = extractSimpleTypeName(typeNode);
|
|
516
566
|
if (typeName)
|
|
@@ -540,9 +590,9 @@ const extractKotlinForLoopBinding = (node, ctx) => {
|
|
|
540
590
|
if (child.type === 'navigation_expression') {
|
|
541
591
|
// data.keys → navigation_expression > simple_identifier(data) + navigation_suffix > simple_identifier(keys)
|
|
542
592
|
const obj = child.firstNamedChild;
|
|
543
|
-
const suffix =
|
|
544
|
-
const prop = suffix ?
|
|
545
|
-
const hasCallSuffix = suffix ?
|
|
593
|
+
const suffix = findChild(child, 'navigation_suffix');
|
|
594
|
+
const prop = suffix ? findChild(suffix, 'simple_identifier') : null;
|
|
595
|
+
const hasCallSuffix = suffix ? findChild(suffix, 'call_suffix') !== null : false;
|
|
546
596
|
// Always try object as iterable + property as method first (handles data.values, data.keys).
|
|
547
597
|
// For bare property access without call_suffix, also save property as fallback
|
|
548
598
|
// (handles this.users, repo.items where the property IS the iterable).
|
|
@@ -562,9 +612,9 @@ const extractKotlinForLoopBinding = (node, ctx) => {
|
|
|
562
612
|
const obj = callee.firstNamedChild;
|
|
563
613
|
if (obj?.type === 'simple_identifier')
|
|
564
614
|
iterableName = obj.text;
|
|
565
|
-
const suffix =
|
|
615
|
+
const suffix = findChild(callee, 'navigation_suffix');
|
|
566
616
|
if (suffix) {
|
|
567
|
-
const prop =
|
|
617
|
+
const prop = findChild(suffix, 'simple_identifier');
|
|
568
618
|
if (prop)
|
|
569
619
|
methodName = prop.text;
|
|
570
620
|
}
|
|
@@ -606,7 +656,7 @@ const extractKotlinForLoopBinding = (node, ctx) => {
|
|
|
606
656
|
const extractKotlinPendingAssignment = (node, scopeEnv) => {
|
|
607
657
|
if (node.type === 'property_declaration') {
|
|
608
658
|
// Find the variable name from variable_declaration child
|
|
609
|
-
const varDecl =
|
|
659
|
+
const varDecl = findChild(node, 'variable_declaration');
|
|
610
660
|
if (!varDecl)
|
|
611
661
|
return undefined;
|
|
612
662
|
const nameNode = varDecl.firstNamedChild;
|
|
@@ -658,7 +708,7 @@ const extractKotlinPendingAssignment = (node, scopeEnv) => {
|
|
|
658
708
|
}
|
|
659
709
|
if (node.type === 'variable_declaration') {
|
|
660
710
|
// variable_declaration directly inside functions: simple_identifier children
|
|
661
|
-
const nameNode =
|
|
711
|
+
const nameNode = findChild(node, 'simple_identifier');
|
|
662
712
|
if (!nameNode)
|
|
663
713
|
return undefined;
|
|
664
714
|
const lhs = nameNode.text;
|
|
@@ -717,32 +767,83 @@ const findAncestorByType = (node, type) => {
|
|
|
717
767
|
}
|
|
718
768
|
return undefined;
|
|
719
769
|
};
|
|
720
|
-
const extractKotlinPatternBinding = (node) => {
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
770
|
+
const extractKotlinPatternBinding = (node, scopeEnv, declarationTypeNodes, scope) => {
|
|
771
|
+
// Kotlin when/is smart casts (existing behavior)
|
|
772
|
+
if (node.type === 'type_test') {
|
|
773
|
+
const typeNode = node.lastNamedChild;
|
|
774
|
+
if (!typeNode)
|
|
775
|
+
return undefined;
|
|
776
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
777
|
+
if (!typeName)
|
|
778
|
+
return undefined;
|
|
779
|
+
const whenExpr = findAncestorByType(node, 'when_expression');
|
|
780
|
+
if (!whenExpr)
|
|
781
|
+
return undefined;
|
|
782
|
+
const whenSubject = whenExpr.namedChild(0);
|
|
783
|
+
const subject = whenSubject?.firstNamedChild ?? whenSubject;
|
|
784
|
+
if (!subject)
|
|
785
|
+
return undefined;
|
|
786
|
+
const varName = extractVarName(subject);
|
|
787
|
+
if (!varName)
|
|
788
|
+
return undefined;
|
|
789
|
+
return { varName, typeName };
|
|
790
|
+
}
|
|
791
|
+
// Null-check narrowing: if (x != null) { ... }
|
|
792
|
+
// Kotlin AST: equality_expression > simple_identifier, "!=" [anon], "null" [anon]
|
|
793
|
+
// Note: `null` is an anonymous node in tree-sitter-kotlin, not `null_literal`.
|
|
794
|
+
if (node.type === 'equality_expression') {
|
|
795
|
+
const op = node.children.find(c => !c.isNamed && c.text === '!=');
|
|
796
|
+
if (!op)
|
|
797
|
+
return undefined;
|
|
798
|
+
// `null` is anonymous in Kotlin grammar — use positional child scan
|
|
799
|
+
let varNode;
|
|
800
|
+
let hasNull = false;
|
|
801
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
802
|
+
const c = node.child(i);
|
|
803
|
+
if (!c)
|
|
804
|
+
continue;
|
|
805
|
+
if (c.type === 'simple_identifier')
|
|
806
|
+
varNode = c;
|
|
807
|
+
if (!c.isNamed && c.text === 'null')
|
|
808
|
+
hasNull = true;
|
|
809
|
+
}
|
|
810
|
+
if (!varNode || !hasNull)
|
|
811
|
+
return undefined;
|
|
812
|
+
const varName = varNode.text;
|
|
813
|
+
const resolvedType = scopeEnv.get(varName);
|
|
814
|
+
if (!resolvedType)
|
|
815
|
+
return undefined;
|
|
816
|
+
// Check if the original declaration type was nullable (ends with ?)
|
|
817
|
+
const declTypeNode = declarationTypeNodes.get(`${scope}\0${varName}`);
|
|
818
|
+
if (!declTypeNode)
|
|
819
|
+
return undefined;
|
|
820
|
+
const declText = declTypeNode.text;
|
|
821
|
+
if (!declText.includes('?') && !declText.includes('null'))
|
|
822
|
+
return undefined;
|
|
823
|
+
// Find the if-body: walk up to if_expression, then find control_structure_body
|
|
824
|
+
const ifExpr = findAncestorByType(node, 'if_expression');
|
|
825
|
+
if (!ifExpr)
|
|
826
|
+
return undefined;
|
|
827
|
+
// The consequence is the first control_structure_body child
|
|
828
|
+
for (let i = 0; i < ifExpr.childCount; i++) {
|
|
829
|
+
const child = ifExpr.child(i);
|
|
830
|
+
if (child?.type === 'control_structure_body') {
|
|
831
|
+
return {
|
|
832
|
+
varName,
|
|
833
|
+
typeName: resolvedType,
|
|
834
|
+
narrowingRange: { startIndex: child.startIndex, endIndex: child.endIndex },
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
}
|
|
738
838
|
return undefined;
|
|
739
|
-
|
|
839
|
+
}
|
|
840
|
+
return undefined;
|
|
740
841
|
};
|
|
741
842
|
export const kotlinTypeConfig = {
|
|
742
843
|
allowPatternBindingOverwrite: true,
|
|
743
844
|
declarationNodeTypes: KOTLIN_DECLARATION_NODE_TYPES,
|
|
744
845
|
forLoopNodeTypes: KOTLIN_FOR_LOOP_NODE_TYPES,
|
|
745
|
-
patternBindingNodeTypes: new Set(['type_test']),
|
|
846
|
+
patternBindingNodeTypes: new Set(['type_test', 'equality_expression']),
|
|
746
847
|
extractDeclaration: extractKotlinDeclaration,
|
|
747
848
|
extractParameter: extractKotlinParameter,
|
|
748
849
|
extractInitializer: extractKotlinInitializer,
|
|
@@ -750,4 +851,6 @@ export const kotlinTypeConfig = {
|
|
|
750
851
|
extractForLoopBinding: extractKotlinForLoopBinding,
|
|
751
852
|
extractPendingAssignment: extractKotlinPendingAssignment,
|
|
752
853
|
extractPatternBinding: extractKotlinPatternBinding,
|
|
854
|
+
inferLiteralType: inferJvmLiteralType,
|
|
855
|
+
detectConstructorType: detectKotlinConstructorType,
|
|
753
856
|
};
|
|
@@ -205,7 +205,8 @@ const scanConstructorBinding = (node) => {
|
|
|
205
205
|
return undefined;
|
|
206
206
|
return { varName: patternNode.text, calleeName };
|
|
207
207
|
};
|
|
208
|
-
/** Rust: let alias = u; → let_declaration with pattern + value fields
|
|
208
|
+
/** Rust: let alias = u; → let_declaration with pattern + value fields.
|
|
209
|
+
* Also handles struct destructuring: `let Point { x, y } = p` → N fieldAccess items. */
|
|
209
210
|
const extractPendingAssignment = (node, scopeEnv) => {
|
|
210
211
|
if (node.type !== 'let_declaration')
|
|
211
212
|
return undefined;
|
|
@@ -213,6 +214,39 @@ const extractPendingAssignment = (node, scopeEnv) => {
|
|
|
213
214
|
const value = node.childForFieldName('value');
|
|
214
215
|
if (!pattern || !value)
|
|
215
216
|
return undefined;
|
|
217
|
+
// Struct pattern destructuring: `let Point { x, y } = receiver`
|
|
218
|
+
// struct_pattern has a type child (struct name) and field_pattern children
|
|
219
|
+
if (pattern.type === 'struct_pattern' && value.type === 'identifier') {
|
|
220
|
+
const receiver = value.text;
|
|
221
|
+
const items = [];
|
|
222
|
+
for (let j = 0; j < pattern.namedChildCount; j++) {
|
|
223
|
+
const field = pattern.namedChild(j);
|
|
224
|
+
if (!field)
|
|
225
|
+
continue;
|
|
226
|
+
if (field.type === 'field_pattern') {
|
|
227
|
+
// `Point { x: local_x }` → field_pattern with name + pattern children
|
|
228
|
+
const nameNode = field.childForFieldName('name');
|
|
229
|
+
const patNode = field.childForFieldName('pattern');
|
|
230
|
+
if (nameNode && patNode) {
|
|
231
|
+
const fieldName = nameNode.text;
|
|
232
|
+
const varName = extractVarName(patNode);
|
|
233
|
+
if (varName && !scopeEnv.has(varName)) {
|
|
234
|
+
items.push({ kind: 'fieldAccess', lhs: varName, receiver, field: fieldName });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else if (nameNode) {
|
|
238
|
+
// Shorthand: `Point { x }` → field_pattern with only name (varName = fieldName)
|
|
239
|
+
const varName = nameNode.text;
|
|
240
|
+
if (!scopeEnv.has(varName)) {
|
|
241
|
+
items.push({ kind: 'fieldAccess', lhs: varName, receiver, field: varName });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (items.length > 0)
|
|
247
|
+
return items;
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
216
250
|
const lhs = extractVarName(pattern);
|
|
217
251
|
if (!lhs || scopeEnv.has(lhs))
|
|
218
252
|
return undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SyntaxNode } from '../utils.js';
|
|
1
|
+
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
|
2
2
|
/** Which type argument to extract from a multi-arg generic container.
|
|
3
3
|
* - 'first': key type (e.g., K from Map<K,V>) — used for .keys(), .keySet()
|
|
4
4
|
* - 'last': value type (e.g., V from Map<K,V>) — used for .values(), .items(), .iter() */
|
|
@@ -108,8 +108,6 @@ export declare const unwrapAwait: (node: SyntaxNode | null) => SyntaxNode | null
|
|
|
108
108
|
* Navigates to the 'function' field (or first named child) and extracts a simple type name.
|
|
109
109
|
*/
|
|
110
110
|
export declare const extractCalleeName: (callNode: SyntaxNode) => string | undefined;
|
|
111
|
-
/** Find the first named child with the given node type */
|
|
112
|
-
export declare const findChildByType: (node: SyntaxNode, type: string) => SyntaxNode | null;
|
|
113
111
|
/**
|
|
114
112
|
* Extract element type from a container type string.
|
|
115
113
|
* Uses bracket-balanced parsing (no regex) for generic argument extraction.
|
|
@@ -130,16 +128,4 @@ export declare const findChildByType: (node: SyntaxNode, type: string) => Syntax
|
|
|
130
128
|
*/
|
|
131
129
|
export declare function extractElementTypeFromString(typeStr: string, pos?: TypeArgPosition): string | undefined;
|
|
132
130
|
export declare const extractReturnTypeName: (raw: string, depth?: number) => string | undefined;
|
|
133
|
-
/**
|
|
134
|
-
* Extract the declared type of a property/field from its AST definition node.
|
|
135
|
-
* Handles cross-language patterns:
|
|
136
|
-
* - TypeScript: `name: Type` → type_annotation child
|
|
137
|
-
* - Java: `Type name` → type child on field_declaration
|
|
138
|
-
* - C#: `Type Name { get; set; }` → type child on property_declaration
|
|
139
|
-
* - Go: `Name Type` → type child on field_declaration
|
|
140
|
-
* - Kotlin: `var name: Type` → variable_declaration child with type field
|
|
141
|
-
*
|
|
142
|
-
* Returns the normalized type name, or undefined if no type can be extracted.
|
|
143
|
-
*/
|
|
144
|
-
export declare const extractPropertyDeclaredType: (definitionNode: SyntaxNode | null) => string | undefined;
|
|
145
131
|
export {};
|
|
@@ -237,7 +237,11 @@ export const extractSimpleTypeName = (typeNode, depth = 0) => {
|
|
|
237
237
|
}
|
|
238
238
|
// Primitive/predefined types: string, int, float, bool, number, unknown, any
|
|
239
239
|
// PHP: primitive_type; TS/JS: predefined_type
|
|
240
|
-
|
|
240
|
+
// Java: integral_type (int/long/short/byte), floating_point_type (float/double),
|
|
241
|
+
// boolean_type (boolean), void_type (void)
|
|
242
|
+
if (typeNode.type === 'primitive_type' || typeNode.type === 'predefined_type'
|
|
243
|
+
|| typeNode.type === 'integral_type' || typeNode.type === 'floating_point_type'
|
|
244
|
+
|| typeNode.type === 'boolean_type' || typeNode.type === 'void_type') {
|
|
241
245
|
return typeNode.text;
|
|
242
246
|
}
|
|
243
247
|
// PHP named_type / optional_type
|
|
@@ -274,6 +278,12 @@ export const extractVarName = (node) => {
|
|
|
274
278
|
if (inner)
|
|
275
279
|
return extractVarName(inner);
|
|
276
280
|
}
|
|
281
|
+
// Swift: pattern node wraps a simple_identifier
|
|
282
|
+
if (node.type === 'pattern') {
|
|
283
|
+
const inner = node.firstNamedChild;
|
|
284
|
+
if (inner)
|
|
285
|
+
return extractVarName(inner);
|
|
286
|
+
}
|
|
277
287
|
return undefined;
|
|
278
288
|
};
|
|
279
289
|
/** Node types for function/method parameters with type annotations */
|
|
@@ -454,15 +464,6 @@ export const extractCalleeName = (callNode) => {
|
|
|
454
464
|
return undefined;
|
|
455
465
|
return extractSimpleTypeName(func);
|
|
456
466
|
};
|
|
457
|
-
/** Find the first named child with the given node type */
|
|
458
|
-
export const findChildByType = (node, type) => {
|
|
459
|
-
for (let i = 0; i < node.namedChildCount; i++) {
|
|
460
|
-
const child = node.namedChild(i);
|
|
461
|
-
if (child?.type === type)
|
|
462
|
-
return child;
|
|
463
|
-
}
|
|
464
|
-
return null;
|
|
465
|
-
};
|
|
466
467
|
// Internal helper: extract the first comma-separated argument from a string,
|
|
467
468
|
// respecting nested angle-bracket and square-bracket depth.
|
|
468
469
|
function extractFirstArg(args) {
|
|
@@ -706,105 +707,6 @@ export const extractReturnTypeName = (raw, depth = 0) => {
|
|
|
706
707
|
return undefined;
|
|
707
708
|
return text;
|
|
708
709
|
};
|
|
709
|
-
//
|
|
710
|
-
//
|
|
711
|
-
|
|
712
|
-
* Extract the declared type of a property/field from its AST definition node.
|
|
713
|
-
* Handles cross-language patterns:
|
|
714
|
-
* - TypeScript: `name: Type` → type_annotation child
|
|
715
|
-
* - Java: `Type name` → type child on field_declaration
|
|
716
|
-
* - C#: `Type Name { get; set; }` → type child on property_declaration
|
|
717
|
-
* - Go: `Name Type` → type child on field_declaration
|
|
718
|
-
* - Kotlin: `var name: Type` → variable_declaration child with type field
|
|
719
|
-
*
|
|
720
|
-
* Returns the normalized type name, or undefined if no type can be extracted.
|
|
721
|
-
*/
|
|
722
|
-
export const extractPropertyDeclaredType = (definitionNode) => {
|
|
723
|
-
if (!definitionNode)
|
|
724
|
-
return undefined;
|
|
725
|
-
// Strategy 1: Look for a `type` or `type_annotation` named field
|
|
726
|
-
const typeNode = definitionNode.childForFieldName?.('type');
|
|
727
|
-
if (typeNode) {
|
|
728
|
-
const typeName = extractSimpleTypeName(typeNode);
|
|
729
|
-
if (typeName)
|
|
730
|
-
return typeName;
|
|
731
|
-
// Fallback: use the raw text (for complex types like User[] or List<User>)
|
|
732
|
-
const text = typeNode.text?.trim();
|
|
733
|
-
if (text && text.length < 100)
|
|
734
|
-
return text;
|
|
735
|
-
}
|
|
736
|
-
// Strategy 2: Walk children looking for type_annotation (TypeScript pattern)
|
|
737
|
-
for (let i = 0; i < definitionNode.childCount; i++) {
|
|
738
|
-
const child = definitionNode.child(i);
|
|
739
|
-
if (!child)
|
|
740
|
-
continue;
|
|
741
|
-
if (child.type === 'type_annotation') {
|
|
742
|
-
// Type annotation has the actual type as a child
|
|
743
|
-
for (let j = 0; j < child.childCount; j++) {
|
|
744
|
-
const typeChild = child.child(j);
|
|
745
|
-
if (typeChild && typeChild.type !== ':') {
|
|
746
|
-
const typeName = extractSimpleTypeName(typeChild);
|
|
747
|
-
if (typeName)
|
|
748
|
-
return typeName;
|
|
749
|
-
const text = typeChild.text?.trim();
|
|
750
|
-
if (text && text.length < 100)
|
|
751
|
-
return text;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
// Strategy 3: For Java field_declaration, the type is a sibling of variable_declarator
|
|
757
|
-
// AST: (field_declaration type: (type_identifier) declarator: (variable_declarator ...))
|
|
758
|
-
const parentDecl = definitionNode.parent;
|
|
759
|
-
if (parentDecl) {
|
|
760
|
-
const parentType = parentDecl.childForFieldName?.('type');
|
|
761
|
-
if (parentType) {
|
|
762
|
-
const typeName = extractSimpleTypeName(parentType);
|
|
763
|
-
if (typeName)
|
|
764
|
-
return typeName;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
// Strategy 4: Kotlin property_declaration — type is nested inside variable_declaration child
|
|
768
|
-
// AST: (property_declaration (variable_declaration (simple_identifier) ":" (user_type (type_identifier))))
|
|
769
|
-
// Kotlin's variable_declaration has NO named 'type' field — children are all positional.
|
|
770
|
-
for (let i = 0; i < definitionNode.childCount; i++) {
|
|
771
|
-
const child = definitionNode.child(i);
|
|
772
|
-
if (child?.type === 'variable_declaration') {
|
|
773
|
-
// Try named field first (works for other languages sharing this strategy)
|
|
774
|
-
const varType = child.childForFieldName?.('type');
|
|
775
|
-
if (varType) {
|
|
776
|
-
const typeName = extractSimpleTypeName(varType);
|
|
777
|
-
if (typeName)
|
|
778
|
-
return typeName;
|
|
779
|
-
const text = varType.text?.trim();
|
|
780
|
-
if (text && text.length < 100)
|
|
781
|
-
return text;
|
|
782
|
-
}
|
|
783
|
-
// Fallback: walk unnamed children for user_type / type_identifier (Kotlin)
|
|
784
|
-
for (let j = 0; j < child.namedChildCount; j++) {
|
|
785
|
-
const varChild = child.namedChild(j);
|
|
786
|
-
if (varChild && (varChild.type === 'user_type' || varChild.type === 'type_identifier'
|
|
787
|
-
|| varChild.type === 'nullable_type' || varChild.type === 'generic_type')) {
|
|
788
|
-
const typeName = extractSimpleTypeName(varChild);
|
|
789
|
-
if (typeName)
|
|
790
|
-
return typeName;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
// Strategy 5: PHP @var PHPDoc — look for preceding comment with @var Type
|
|
796
|
-
// Handles pre-PHP-7.4 code: /** @var Address */ public $address;
|
|
797
|
-
const prevSibling = definitionNode.previousNamedSibling ?? definitionNode.parent?.previousNamedSibling;
|
|
798
|
-
if (prevSibling?.type === 'comment') {
|
|
799
|
-
const commentText = prevSibling.text;
|
|
800
|
-
const varMatch = commentText?.match(/@var\s+([A-Z][\w\\]*)/);
|
|
801
|
-
if (varMatch) {
|
|
802
|
-
// Strip namespace prefix: \App\Models\User → User
|
|
803
|
-
const raw = varMatch[1];
|
|
804
|
-
const base = raw.includes('\\') ? raw.split('\\').pop() : raw;
|
|
805
|
-
if (base && /^[A-Z]\w*$/.test(base))
|
|
806
|
-
return base;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
return undefined;
|
|
810
|
-
};
|
|
710
|
+
// extractPropertyDeclaredType removed — all 14 languages register a FieldExtractor
|
|
711
|
+
// via defineLanguage() which is the single source of truth for Property metadata
|
|
712
|
+
// (declaredType, visibility, isStatic, isReadonly).
|