gitnexus 1.4.9 → 1.5.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 +6 -5
- package/dist/cli/ai-context.d.ts +4 -1
- package/dist/cli/ai-context.js +19 -11
- package/dist/cli/analyze.d.ts +6 -0
- package/dist/cli/analyze.js +105 -251
- package/dist/cli/eval-server.js +20 -11
- package/dist/cli/index-repo.js +20 -22
- package/dist/cli/index.js +8 -7
- package/dist/cli/mcp.js +1 -1
- package/dist/cli/serve.js +29 -1
- package/dist/cli/setup.js +9 -9
- package/dist/cli/skill-gen.js +15 -9
- package/dist/cli/wiki.d.ts +2 -0
- package/dist/cli/wiki.js +141 -26
- package/dist/config/ignore-service.js +102 -22
- package/dist/config/supported-languages.d.ts +8 -42
- package/dist/config/supported-languages.js +8 -43
- package/dist/core/augmentation/engine.js +19 -7
- package/dist/core/embeddings/embedder.js +19 -15
- package/dist/core/embeddings/embedding-pipeline.js +6 -6
- package/dist/core/embeddings/http-client.js +3 -3
- package/dist/core/embeddings/text-generator.js +9 -24
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/embeddings/types.js +1 -7
- package/dist/core/graph/graph.js +6 -2
- package/dist/core/graph/types.d.ts +9 -59
- package/dist/core/ingestion/ast-cache.js +3 -3
- package/dist/core/ingestion/call-processor.d.ts +20 -2
- package/dist/core/ingestion/call-processor.js +347 -144
- package/dist/core/ingestion/call-routing.js +10 -4
- package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +10 -0
- package/dist/core/ingestion/call-sites/extract-language-call-site.js +22 -0
- package/dist/core/ingestion/call-sites/java.d.ts +9 -0
- package/dist/core/ingestion/call-sites/java.js +30 -0
- package/dist/core/ingestion/cluster-enricher.js +6 -8
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +10 -3
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +287 -81
- package/dist/core/ingestion/cobol/jcl-parser.js +1 -1
- package/dist/core/ingestion/cobol/jcl-processor.js +1 -1
- package/dist/core/ingestion/cobol-processor.js +102 -56
- package/dist/core/ingestion/community-processor.js +21 -15
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -1
- package/dist/core/ingestion/entry-point-scoring.js +5 -6
- package/dist/core/ingestion/export-detection.js +32 -9
- package/dist/core/ingestion/field-extractor.d.ts +1 -1
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +8 -12
- package/dist/core/ingestion/field-extractors/configs/csharp.js +45 -2
- package/dist/core/ingestion/field-extractors/configs/dart.js +5 -3
- package/dist/core/ingestion/field-extractors/configs/go.js +3 -7
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +14 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +7 -7
- package/dist/core/ingestion/field-extractors/configs/php.js +9 -11
- package/dist/core/ingestion/field-extractors/configs/python.js +1 -1
- package/dist/core/ingestion/field-extractors/configs/ruby.js +4 -3
- package/dist/core/ingestion/field-extractors/configs/rust.js +2 -5
- package/dist/core/ingestion/field-extractors/configs/swift.js +9 -7
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +2 -6
- package/dist/core/ingestion/field-extractors/generic.d.ts +5 -2
- package/dist/core/ingestion/field-extractors/generic.js +6 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +1 -1
- package/dist/core/ingestion/field-extractors/typescript.js +1 -1
- package/dist/core/ingestion/field-types.d.ts +4 -2
- package/dist/core/ingestion/filesystem-walker.js +3 -3
- package/dist/core/ingestion/framework-detection.d.ts +1 -1
- package/dist/core/ingestion/framework-detection.js +355 -85
- package/dist/core/ingestion/heritage-processor.d.ts +24 -0
- package/dist/core/ingestion/heritage-processor.js +99 -8
- package/dist/core/ingestion/import-processor.js +44 -15
- package/dist/core/ingestion/import-resolvers/csharp.js +7 -3
- package/dist/core/ingestion/import-resolvers/dart.js +1 -1
- package/dist/core/ingestion/import-resolvers/go.js +4 -2
- package/dist/core/ingestion/import-resolvers/jvm.js +4 -4
- package/dist/core/ingestion/import-resolvers/php.js +4 -4
- package/dist/core/ingestion/import-resolvers/python.js +1 -1
- package/dist/core/ingestion/import-resolvers/rust.js +9 -3
- package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
- package/dist/core/ingestion/import-resolvers/standard.js +6 -5
- package/dist/core/ingestion/import-resolvers/swift.js +2 -1
- package/dist/core/ingestion/import-resolvers/utils.js +26 -7
- package/dist/core/ingestion/language-config.js +5 -4
- package/dist/core/ingestion/language-provider.d.ts +7 -2
- package/dist/core/ingestion/languages/c-cpp.js +106 -21
- package/dist/core/ingestion/languages/cobol.js +1 -1
- package/dist/core/ingestion/languages/csharp.js +96 -19
- package/dist/core/ingestion/languages/dart.js +23 -7
- package/dist/core/ingestion/languages/go.js +1 -1
- package/dist/core/ingestion/languages/index.d.ts +1 -1
- package/dist/core/ingestion/languages/index.js +2 -3
- package/dist/core/ingestion/languages/java.js +4 -1
- package/dist/core/ingestion/languages/kotlin.js +60 -13
- package/dist/core/ingestion/languages/php.js +102 -25
- package/dist/core/ingestion/languages/python.js +28 -5
- package/dist/core/ingestion/languages/ruby.js +56 -14
- package/dist/core/ingestion/languages/rust.js +55 -11
- package/dist/core/ingestion/languages/swift.js +112 -27
- package/dist/core/ingestion/languages/typescript.js +95 -19
- package/dist/core/ingestion/markdown-processor.js +5 -5
- package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +283 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +326 -0
- package/dist/core/ingestion/method-extractors/generic.d.ts +5 -0
- package/dist/core/ingestion/method-extractors/generic.js +137 -0
- package/dist/core/ingestion/method-types.d.ts +61 -0
- package/dist/core/ingestion/method-types.js +2 -0
- package/dist/core/ingestion/mro-processor.d.ts +1 -1
- package/dist/core/ingestion/mro-processor.js +12 -8
- package/dist/core/ingestion/named-binding-processor.js +2 -2
- package/dist/core/ingestion/named-bindings/rust.js +3 -1
- package/dist/core/ingestion/parsing-processor.js +74 -24
- package/dist/core/ingestion/pipeline.d.ts +2 -1
- package/dist/core/ingestion/pipeline.js +208 -102
- package/dist/core/ingestion/process-processor.js +12 -10
- package/dist/core/ingestion/resolution-context.js +3 -3
- package/dist/core/ingestion/route-extractors/middleware.js +31 -7
- package/dist/core/ingestion/route-extractors/php.js +2 -1
- package/dist/core/ingestion/route-extractors/response-shapes.js +8 -4
- package/dist/core/ingestion/structure-processor.d.ts +1 -1
- package/dist/core/ingestion/structure-processor.js +4 -4
- package/dist/core/ingestion/symbol-table.d.ts +1 -1
- package/dist/core/ingestion/symbol-table.js +22 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
- package/dist/core/ingestion/tree-sitter-queries.js +1 -1
- package/dist/core/ingestion/type-env.d.ts +2 -2
- package/dist/core/ingestion/type-env.js +75 -50
- package/dist/core/ingestion/type-extractors/c-cpp.js +33 -30
- package/dist/core/ingestion/type-extractors/csharp.js +24 -14
- package/dist/core/ingestion/type-extractors/dart.js +6 -8
- package/dist/core/ingestion/type-extractors/go.js +7 -6
- package/dist/core/ingestion/type-extractors/jvm.js +10 -21
- package/dist/core/ingestion/type-extractors/php.js +26 -13
- package/dist/core/ingestion/type-extractors/python.js +11 -15
- package/dist/core/ingestion/type-extractors/ruby.js +8 -3
- package/dist/core/ingestion/type-extractors/rust.js +6 -8
- package/dist/core/ingestion/type-extractors/shared.js +134 -50
- package/dist/core/ingestion/type-extractors/swift.js +16 -13
- package/dist/core/ingestion/type-extractors/typescript.js +23 -15
- package/dist/core/ingestion/utils/ast-helpers.d.ts +8 -8
- package/dist/core/ingestion/utils/ast-helpers.js +72 -35
- package/dist/core/ingestion/utils/call-analysis.d.ts +2 -0
- package/dist/core/ingestion/utils/call-analysis.js +96 -49
- package/dist/core/ingestion/utils/event-loop.js +1 -1
- package/dist/core/ingestion/workers/parse-worker.d.ts +7 -2
- package/dist/core/ingestion/workers/parse-worker.js +364 -84
- package/dist/core/ingestion/workers/worker-pool.js +5 -10
- package/dist/core/lbug/csv-generator.js +54 -15
- package/dist/core/lbug/lbug-adapter.d.ts +5 -0
- package/dist/core/lbug/lbug-adapter.js +86 -23
- package/dist/core/lbug/schema.d.ts +3 -6
- package/dist/core/lbug/schema.js +6 -30
- package/dist/core/run-analyze.d.ts +49 -0
- package/dist/core/run-analyze.js +257 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +1 -1
- package/dist/core/tree-sitter/parser-loader.js +1 -1
- package/dist/core/wiki/cursor-client.js +2 -7
- package/dist/core/wiki/generator.js +38 -23
- package/dist/core/wiki/graph-queries.js +10 -10
- package/dist/core/wiki/html-viewer.js +7 -3
- package/dist/core/wiki/llm-client.d.ts +23 -2
- package/dist/core/wiki/llm-client.js +96 -26
- package/dist/core/wiki/prompts.js +7 -6
- package/dist/mcp/core/embedder.js +1 -1
- package/dist/mcp/core/lbug-adapter.d.ts +4 -1
- package/dist/mcp/core/lbug-adapter.js +17 -7
- package/dist/mcp/local/local-backend.js +247 -95
- package/dist/mcp/resources.js +14 -6
- package/dist/mcp/server.js +13 -5
- package/dist/mcp/staleness.js +5 -1
- package/dist/mcp/tools.js +100 -23
- package/dist/server/analyze-job.d.ts +53 -0
- package/dist/server/analyze-job.js +146 -0
- package/dist/server/analyze-worker.d.ts +13 -0
- package/dist/server/analyze-worker.js +59 -0
- package/dist/server/api.js +795 -44
- package/dist/server/git-clone.d.ts +25 -0
- package/dist/server/git-clone.js +91 -0
- package/dist/storage/git.js +1 -3
- package/dist/storage/repo-manager.d.ts +5 -2
- package/dist/storage/repo-manager.js +4 -4
- package/dist/types/pipeline.d.ts +1 -21
- package/dist/types/pipeline.js +1 -18
- package/hooks/claude/gitnexus-hook.cjs +52 -22
- package/package.json +13 -13
- package/dist/core/ingestion/utils/language-detection.d.ts +0 -9
- package/dist/core/ingestion/utils/language-detection.js +0 -70
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { findChild } from '../utils/ast-helpers.js';
|
|
2
|
-
import { extractSimpleTypeName, extractVarName, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString } from './shared.js';
|
|
2
|
+
import { extractSimpleTypeName, extractVarName, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString, } from './shared.js';
|
|
3
3
|
// ── Java ──────────────────────────────────────────────────────────────────
|
|
4
4
|
const JAVA_DECLARATION_NODE_TYPES = new Set([
|
|
5
5
|
'local_variable_declaration',
|
|
@@ -95,9 +95,7 @@ const scanJavaConstructorBinding = (node) => {
|
|
|
95
95
|
return undefined;
|
|
96
96
|
return { varName: nameNode.text, calleeName: methodName.text };
|
|
97
97
|
};
|
|
98
|
-
const JAVA_FOR_LOOP_NODE_TYPES = new Set([
|
|
99
|
-
'enhanced_for_statement',
|
|
100
|
-
]);
|
|
98
|
+
const JAVA_FOR_LOOP_NODE_TYPES = new Set(['enhanced_for_statement']);
|
|
101
99
|
/** Extract element type from a Java type annotation AST node.
|
|
102
100
|
* Handles generic_type (List<User>), array_type (User[]). */
|
|
103
101
|
const extractJavaElementTypeFromTypeNode = (typeNode, pos = 'last') => {
|
|
@@ -352,8 +350,7 @@ const extractKotlinDeclaration = (node, env) => {
|
|
|
352
350
|
const varDecl = findChild(node, 'variable_declaration');
|
|
353
351
|
if (varDecl) {
|
|
354
352
|
const nameNode = findChild(varDecl, 'simple_identifier');
|
|
355
|
-
const typeNode = findChild(varDecl, 'user_type')
|
|
356
|
-
?? findChild(varDecl, 'nullable_type');
|
|
353
|
+
const typeNode = findChild(varDecl, 'user_type') ?? findChild(varDecl, 'nullable_type');
|
|
357
354
|
if (!nameNode || !typeNode)
|
|
358
355
|
return;
|
|
359
356
|
const varName = extractVarName(nameNode);
|
|
@@ -363,10 +360,8 @@ const extractKotlinDeclaration = (node, env) => {
|
|
|
363
360
|
return;
|
|
364
361
|
}
|
|
365
362
|
// Fallback: try direct fields
|
|
366
|
-
const nameNode = node.childForFieldName('name')
|
|
367
|
-
|
|
368
|
-
const typeNode = node.childForFieldName('type')
|
|
369
|
-
?? findChild(node, 'user_type');
|
|
363
|
+
const nameNode = node.childForFieldName('name') ?? findChild(node, 'simple_identifier');
|
|
364
|
+
const typeNode = node.childForFieldName('type') ?? findChild(node, 'user_type');
|
|
370
365
|
if (!nameNode || !typeNode)
|
|
371
366
|
return;
|
|
372
367
|
const varName = extractVarName(nameNode);
|
|
@@ -405,8 +400,7 @@ const extractKotlinParameter = (node, env) => {
|
|
|
405
400
|
if (!nameNode)
|
|
406
401
|
nameNode = findChild(node, 'simple_identifier');
|
|
407
402
|
if (!typeNode)
|
|
408
|
-
typeNode = findChild(node, 'user_type')
|
|
409
|
-
?? findChild(node, 'nullable_type');
|
|
403
|
+
typeNode = findChild(node, 'user_type') ?? findChild(node, 'nullable_type');
|
|
410
404
|
if (!nameNode || !typeNode)
|
|
411
405
|
return;
|
|
412
406
|
const varName = extractVarName(nameNode);
|
|
@@ -419,8 +413,7 @@ const extractKotlinParameter = (node, env) => {
|
|
|
419
413
|
const findKotlinConstructorCallee = (node, classNames) => {
|
|
420
414
|
if (node.type !== 'property_declaration')
|
|
421
415
|
return undefined;
|
|
422
|
-
const value = node.childForFieldName('value')
|
|
423
|
-
?? findChild(node, 'call_expression');
|
|
416
|
+
const value = node.childForFieldName('value') ?? findChild(node, 'call_expression');
|
|
424
417
|
if (!value || value.type !== 'call_expression')
|
|
425
418
|
return undefined;
|
|
426
419
|
const callee = value.firstNamedChild;
|
|
@@ -495,9 +488,7 @@ const scanKotlinConstructorBinding = (node) => {
|
|
|
495
488
|
return undefined;
|
|
496
489
|
return { varName: nameNode.text, calleeName };
|
|
497
490
|
};
|
|
498
|
-
const KOTLIN_FOR_LOOP_NODE_TYPES = new Set([
|
|
499
|
-
'for_statement',
|
|
500
|
-
]);
|
|
491
|
+
const KOTLIN_FOR_LOOP_NODE_TYPES = new Set(['for_statement']);
|
|
501
492
|
/** Extract element type from a Kotlin type annotation AST node (user_type wrapping generic).
|
|
502
493
|
* Kotlin: user_type → [type_identifier, type_arguments → [type_projection → user_type]]
|
|
503
494
|
* Handles the type_projection wrapper that Kotlin uses for generic type arguments. */
|
|
@@ -511,9 +502,7 @@ const extractKotlinElementTypeFromTypeNode = (typeNode, pos = 'last') => {
|
|
|
511
502
|
if (!targetArg)
|
|
512
503
|
return undefined;
|
|
513
504
|
// Kotlin wraps type args in type_projection — unwrap to get the inner type
|
|
514
|
-
const inner = targetArg.type === 'type_projection'
|
|
515
|
-
? targetArg.firstNamedChild
|
|
516
|
-
: targetArg;
|
|
505
|
+
const inner = targetArg.type === 'type_projection' ? targetArg.firstNamedChild : targetArg;
|
|
517
506
|
if (inner)
|
|
518
507
|
return extractSimpleTypeName(inner);
|
|
519
508
|
}
|
|
@@ -792,7 +781,7 @@ const extractKotlinPatternBinding = (node, scopeEnv, declarationTypeNodes, scope
|
|
|
792
781
|
// Kotlin AST: equality_expression > simple_identifier, "!=" [anon], "null" [anon]
|
|
793
782
|
// Note: `null` is an anonymous node in tree-sitter-kotlin, not `null_literal`.
|
|
794
783
|
if (node.type === 'equality_expression') {
|
|
795
|
-
const op = node.children.find(c => !c.isNamed && c.text === '!=');
|
|
784
|
+
const op = node.children.find((c) => !c.isNamed && c.text === '!=');
|
|
796
785
|
if (!op)
|
|
797
786
|
return undefined;
|
|
798
787
|
// `null` is anonymous in Kotlin grammar — use positional child scan
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractSimpleTypeName, extractVarName, extractCalleeName, resolveIterableElementType, extractElementTypeFromString } from './shared.js';
|
|
1
|
+
import { extractSimpleTypeName, extractVarName, extractCalleeName, resolveIterableElementType, extractElementTypeFromString, } from './shared.js';
|
|
2
2
|
const DECLARATION_NODE_TYPES = new Set([
|
|
3
3
|
'assignment_expression', // For constructor inference: $x = new User()
|
|
4
4
|
'property_declaration', // PHP 7.4+ typed properties: private UserRepo $repo;
|
|
@@ -51,7 +51,9 @@ const normalizePhpType = (raw) => {
|
|
|
51
51
|
// Strip array suffix: User[] → User
|
|
52
52
|
type = type.replace(/\[\]$/, '');
|
|
53
53
|
// Strip union with null/false/void: User|null → User
|
|
54
|
-
const parts = type
|
|
54
|
+
const parts = type
|
|
55
|
+
.split('|')
|
|
56
|
+
.filter((p) => p !== 'null' && p !== 'false' && p !== 'void' && p !== 'mixed');
|
|
55
57
|
if (parts.length !== 1)
|
|
56
58
|
return undefined;
|
|
57
59
|
type = parts[0];
|
|
@@ -59,7 +61,11 @@ const normalizePhpType = (raw) => {
|
|
|
59
61
|
const segments = type.split('\\');
|
|
60
62
|
type = segments[segments.length - 1];
|
|
61
63
|
// Skip uninformative types
|
|
62
|
-
if (type === 'mixed' ||
|
|
64
|
+
if (type === 'mixed' ||
|
|
65
|
+
type === 'void' ||
|
|
66
|
+
type === 'self' ||
|
|
67
|
+
type === 'static' ||
|
|
68
|
+
type === 'object')
|
|
63
69
|
return undefined;
|
|
64
70
|
// Extract element type from generic: Collection<User> → User
|
|
65
71
|
// PHPDoc generics encode the element type in angle brackets. Since PHP's Strategy B
|
|
@@ -124,8 +130,8 @@ const extractClassPropertyElementType = (propDecl) => {
|
|
|
124
130
|
* both fail to find the type.
|
|
125
131
|
*/
|
|
126
132
|
const findClassPropertyElementType = (propName, classNode) => {
|
|
127
|
-
const declList = classNode.childForFieldName('body')
|
|
128
|
-
|
|
133
|
+
const declList = classNode.childForFieldName('body') ??
|
|
134
|
+
(classNode.namedChild(classNode.namedChildCount - 1)?.type === 'declaration_list'
|
|
129
135
|
? classNode.namedChild(classNode.namedChildCount - 1)
|
|
130
136
|
: null); // fallback: last named child, only if it's a declaration_list
|
|
131
137
|
if (!declList)
|
|
@@ -245,7 +251,7 @@ const extractInitializer = (node, env, _classNames) => {
|
|
|
245
251
|
if (!typeName)
|
|
246
252
|
return;
|
|
247
253
|
// Resolve PHP self/static/parent to actual class names
|
|
248
|
-
const resolvedType =
|
|
254
|
+
const resolvedType = typeName === 'self' || typeName === 'static' || typeName === 'parent'
|
|
249
255
|
? resolvePhpKeyword(typeName, node)
|
|
250
256
|
: typeName;
|
|
251
257
|
if (!resolvedType)
|
|
@@ -331,7 +337,9 @@ const normalizePhpReturnType = (raw) => {
|
|
|
331
337
|
// Strip nullable prefix: ?User[] → User[]
|
|
332
338
|
let type = raw.startsWith('?') ? raw.slice(1) : raw;
|
|
333
339
|
// Strip union with null/false/void: User[]|null → User[]
|
|
334
|
-
const parts = type
|
|
340
|
+
const parts = type
|
|
341
|
+
.split('|')
|
|
342
|
+
.filter((p) => p !== 'null' && p !== 'false' && p !== 'void' && p !== 'mixed');
|
|
335
343
|
if (parts.length !== 1)
|
|
336
344
|
return undefined;
|
|
337
345
|
type = parts[0];
|
|
@@ -339,7 +347,12 @@ const normalizePhpReturnType = (raw) => {
|
|
|
339
347
|
const segments = type.split('\\');
|
|
340
348
|
type = segments[segments.length - 1];
|
|
341
349
|
// Skip uninformative types
|
|
342
|
-
if (type === 'mixed' ||
|
|
350
|
+
if (type === 'mixed' ||
|
|
351
|
+
type === 'void' ||
|
|
352
|
+
type === 'self' ||
|
|
353
|
+
type === 'static' ||
|
|
354
|
+
type === 'object' ||
|
|
355
|
+
type === 'array')
|
|
343
356
|
return undefined;
|
|
344
357
|
if (/^\w+(\[\])?$/.test(type) || /^\w+\s*</.test(type))
|
|
345
358
|
return type;
|
|
@@ -408,9 +421,7 @@ const extractPendingAssignment = (node, scopeEnv) => {
|
|
|
408
421
|
}
|
|
409
422
|
return undefined;
|
|
410
423
|
};
|
|
411
|
-
const FOR_LOOP_NODE_TYPES = new Set([
|
|
412
|
-
'foreach_statement',
|
|
413
|
-
]);
|
|
424
|
+
const FOR_LOOP_NODE_TYPES = new Set(['foreach_statement']);
|
|
414
425
|
/** Extract element type from a PHP type annotation AST node.
|
|
415
426
|
* PHP has limited AST-level container types — `array` is a primitive_type with no generic args.
|
|
416
427
|
* Named types (e.g., `Collection`) are returned as-is (container descriptor lookup handles them). */
|
|
@@ -482,11 +493,13 @@ const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope, re
|
|
|
482
493
|
if (!lastChild)
|
|
483
494
|
return;
|
|
484
495
|
// Handle by_ref: foreach ($arr as $k => &$v)
|
|
485
|
-
loopVarNode =
|
|
496
|
+
loopVarNode =
|
|
497
|
+
lastChild.type === 'by_ref' ? (lastChild.firstNamedChild ?? lastChild) : lastChild;
|
|
486
498
|
}
|
|
487
499
|
else {
|
|
488
500
|
// Simple: foreach ($users as $user) or foreach ($users as &$user)
|
|
489
|
-
loopVarNode =
|
|
501
|
+
loopVarNode =
|
|
502
|
+
valueOrPair.type === 'by_ref' ? (valueOrPair.firstNamedChild ?? valueOrPair) : valueOrPair;
|
|
490
503
|
}
|
|
491
504
|
const varName = extractVarName(loopVarNode);
|
|
492
505
|
if (!varName)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractSimpleTypeName, extractVarName, extractElementTypeFromString, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition } from './shared.js';
|
|
1
|
+
import { extractSimpleTypeName, extractVarName, extractElementTypeFromString, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, } from './shared.js';
|
|
2
2
|
const DECLARATION_NODE_TYPES = new Set([
|
|
3
3
|
'assignment',
|
|
4
4
|
'named_expression',
|
|
@@ -147,13 +147,9 @@ const scanConstructorBinding = (node) => {
|
|
|
147
147
|
return undefined;
|
|
148
148
|
return { varName: left.text, calleeName };
|
|
149
149
|
};
|
|
150
|
-
const FOR_LOOP_NODE_TYPES = new Set([
|
|
151
|
-
'for_statement',
|
|
152
|
-
]);
|
|
150
|
+
const FOR_LOOP_NODE_TYPES = new Set(['for_statement']);
|
|
153
151
|
/** Python function/method node types that carry a parameters list. */
|
|
154
|
-
const PY_FUNCTION_NODE_TYPES = new Set([
|
|
155
|
-
'function_definition', 'decorated_definition',
|
|
156
|
-
]);
|
|
152
|
+
const PY_FUNCTION_NODE_TYPES = new Set(['function_definition', 'decorated_definition']);
|
|
157
153
|
/**
|
|
158
154
|
* Extract element type from a Python type annotation AST node.
|
|
159
155
|
* Handles:
|
|
@@ -215,13 +211,13 @@ const findPyParamElementType = (iterableName, startNode, pos = 'last') => {
|
|
|
215
211
|
continue;
|
|
216
212
|
// Try named `name` field first (parameter node), then first identifier child
|
|
217
213
|
// (typed_parameter node may store name as first positional child)
|
|
218
|
-
const nameNode = param.childForFieldName('name')
|
|
219
|
-
|
|
214
|
+
const nameNode = param.childForFieldName('name') ??
|
|
215
|
+
(param.firstNamedChild?.type === 'identifier' ? param.firstNamedChild : null);
|
|
220
216
|
if (nameNode?.text !== iterableName)
|
|
221
217
|
continue;
|
|
222
218
|
// Try `type` field, then last named child (typed_parameter stores type last)
|
|
223
|
-
const typeAnnotation = param.childForFieldName('type')
|
|
224
|
-
|
|
219
|
+
const typeAnnotation = param.childForFieldName('type') ??
|
|
220
|
+
(param.namedChildCount >= 2 ? param.namedChild(param.namedChildCount - 1) : null);
|
|
225
221
|
if (typeAnnotation && typeAnnotation !== nameNode) {
|
|
226
222
|
return extractPyElementTypeFromAnnotation(typeAnnotation, pos);
|
|
227
223
|
}
|
|
@@ -245,7 +241,7 @@ const extractMethodCall = (callNode) => {
|
|
|
245
241
|
if (obj?.type !== 'identifier')
|
|
246
242
|
return undefined;
|
|
247
243
|
const method = fn.lastNamedChild;
|
|
248
|
-
const methodName =
|
|
244
|
+
const methodName = method?.type === 'identifier' && method !== obj ? method.text : undefined;
|
|
249
245
|
return { iterableName: obj.text, methodName };
|
|
250
246
|
};
|
|
251
247
|
/**
|
|
@@ -428,8 +424,7 @@ const extractPatternBinding = (node, scopeEnv) => {
|
|
|
428
424
|
if (node.namedChildCount < 2)
|
|
429
425
|
return undefined;
|
|
430
426
|
const patternChild = node.namedChild(0);
|
|
431
|
-
const varNameNode = node.childForFieldName('alias')
|
|
432
|
-
?? node.namedChild(node.namedChildCount - 1);
|
|
427
|
+
const varNameNode = node.childForFieldName('alias') ?? node.namedChild(node.namedChildCount - 1);
|
|
433
428
|
if (!patternChild || !varNameNode)
|
|
434
429
|
return undefined;
|
|
435
430
|
if (varNameNode.type !== 'identifier')
|
|
@@ -456,7 +451,8 @@ const extractPatternBinding = (node, scopeEnv) => {
|
|
|
456
451
|
return undefined;
|
|
457
452
|
// class_pattern children: dotted_name (the class name) + optional keyword_pattern args.
|
|
458
453
|
const classNameNode = classPattern.firstNamedChild;
|
|
459
|
-
if (!classNameNode ||
|
|
454
|
+
if (!classNameNode ||
|
|
455
|
+
(classNameNode.type !== 'dotted_name' && classNameNode.type !== 'identifier'))
|
|
460
456
|
return undefined;
|
|
461
457
|
const typeName = classNameNode.text;
|
|
462
458
|
if (!typeName)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractRubyConstructorAssignment, extractSimpleTypeName, extractElementTypeFromString, extractVarName, resolveIterableElementType } from './shared.js';
|
|
1
|
+
import { extractRubyConstructorAssignment, extractSimpleTypeName, extractElementTypeFromString, extractVarName, resolveIterableElementType, } from './shared.js';
|
|
2
2
|
/**
|
|
3
3
|
* Ruby type extractor — YARD annotation parsing.
|
|
4
4
|
*
|
|
@@ -51,7 +51,7 @@ const extractYardTypeName = (yardType) => {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
parts.push(trimmed.slice(start).trim());
|
|
54
|
-
const filtered = parts.filter(p => p !== '' && p !== 'nil');
|
|
54
|
+
const filtered = parts.filter((p) => p !== '' && p !== 'nil');
|
|
55
55
|
if (filtered.length !== 1)
|
|
56
56
|
return undefined; // ambiguous union
|
|
57
57
|
const typePart = filtered[0];
|
|
@@ -386,7 +386,12 @@ const extractPendingAssignment = (node, scopeEnv) => {
|
|
|
386
386
|
}
|
|
387
387
|
if (receiverNode?.type === 'identifier' && methodNode?.type === 'identifier') {
|
|
388
388
|
// With receiver → methodCallResult (a.method)
|
|
389
|
-
return {
|
|
389
|
+
return {
|
|
390
|
+
kind: 'methodCallResult',
|
|
391
|
+
lhs: varName,
|
|
392
|
+
receiver: receiverNode.text,
|
|
393
|
+
method: methodNode.text,
|
|
394
|
+
};
|
|
390
395
|
}
|
|
391
396
|
}
|
|
392
397
|
return undefined;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { extractSimpleTypeName, extractVarName, hasTypeAnnotation, unwrapAwait, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString } from './shared.js';
|
|
2
|
-
const DECLARATION_NODE_TYPES = new Set([
|
|
3
|
-
'let_declaration',
|
|
4
|
-
'let_condition',
|
|
5
|
-
]);
|
|
1
|
+
import { extractSimpleTypeName, extractVarName, hasTypeAnnotation, unwrapAwait, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString, } from './shared.js';
|
|
2
|
+
const DECLARATION_NODE_TYPES = new Set(['let_declaration', 'let_condition']);
|
|
6
3
|
/** Walk up the AST to find the enclosing impl block and extract the implementing type name. */
|
|
7
4
|
const findEnclosingImplType = (node) => {
|
|
8
5
|
let current = node.parent;
|
|
@@ -308,9 +305,10 @@ const extractPatternBinding = (node, scopeEnv, declarationTypeNodes, scope) => {
|
|
|
308
305
|
// match_arm → pattern field is match_pattern wrapping the actual pattern
|
|
309
306
|
const matchPatternNode = node.childForFieldName('pattern');
|
|
310
307
|
// Unwrap match_pattern to get the tuple_struct_pattern inside
|
|
311
|
-
patternNode =
|
|
312
|
-
|
|
313
|
-
|
|
308
|
+
patternNode =
|
|
309
|
+
matchPatternNode?.type === 'match_pattern'
|
|
310
|
+
? matchPatternNode.firstNamedChild
|
|
311
|
+
: matchPatternNode;
|
|
314
312
|
// source variable is in the parent match_expression's 'value' field
|
|
315
313
|
const matchExpr = node.parent?.parent; // match_arm → match_block → match_expression
|
|
316
314
|
if (matchExpr?.type === 'match_expression') {
|
|
@@ -8,8 +8,18 @@ const CSHARP_KEY_METHODS = new Set(['Keys']);
|
|
|
8
8
|
const STD_VALUE_METHODS = new Set(['values', 'get', 'pop', 'remove']);
|
|
9
9
|
const CSHARP_VALUE_METHODS = new Set(['Values', 'TryGetValue']);
|
|
10
10
|
const SINGLE_ELEMENT_METHODS = new Set([
|
|
11
|
-
'iter',
|
|
12
|
-
'
|
|
11
|
+
'iter',
|
|
12
|
+
'into_iter',
|
|
13
|
+
'iterator',
|
|
14
|
+
'get',
|
|
15
|
+
'first',
|
|
16
|
+
'last',
|
|
17
|
+
'pop',
|
|
18
|
+
'peek',
|
|
19
|
+
'poll',
|
|
20
|
+
'find',
|
|
21
|
+
'filter',
|
|
22
|
+
'map',
|
|
13
23
|
]);
|
|
14
24
|
const CONTAINER_DESCRIPTORS = new Map([
|
|
15
25
|
// --- Map / Dict types (arity 2: key + value) ---
|
|
@@ -22,11 +32,20 @@ const CONTAINER_DESCRIPTORS = new Map([
|
|
|
22
32
|
['dict', { arity: 2, keyMethods: STD_KEY_METHODS, valueMethods: STD_VALUE_METHODS }],
|
|
23
33
|
['Dict', { arity: 2, keyMethods: STD_KEY_METHODS, valueMethods: STD_VALUE_METHODS }],
|
|
24
34
|
['Dictionary', { arity: 2, keyMethods: CSHARP_KEY_METHODS, valueMethods: CSHARP_VALUE_METHODS }],
|
|
25
|
-
[
|
|
35
|
+
[
|
|
36
|
+
'SortedDictionary',
|
|
37
|
+
{ arity: 2, keyMethods: CSHARP_KEY_METHODS, valueMethods: CSHARP_VALUE_METHODS },
|
|
38
|
+
],
|
|
26
39
|
['Record', { arity: 2, keyMethods: STD_KEY_METHODS, valueMethods: STD_VALUE_METHODS }],
|
|
27
40
|
['OrderedDict', { arity: 2, keyMethods: STD_KEY_METHODS, valueMethods: STD_VALUE_METHODS }],
|
|
28
|
-
[
|
|
29
|
-
|
|
41
|
+
[
|
|
42
|
+
'ConcurrentHashMap',
|
|
43
|
+
{ arity: 2, keyMethods: JAVA_KEY_METHODS, valueMethods: STD_VALUE_METHODS },
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
'ConcurrentDictionary',
|
|
47
|
+
{ arity: 2, keyMethods: CSHARP_KEY_METHODS, valueMethods: CSHARP_VALUE_METHODS },
|
|
48
|
+
],
|
|
30
49
|
// --- Single-element containers (arity 1) ---
|
|
31
50
|
['Array', { arity: 1, keyMethods: NO_KEYS, valueMethods: SINGLE_ELEMENT_METHODS }],
|
|
32
51
|
['List', { arity: 1, keyMethods: NO_KEYS, valueMethods: SINGLE_ELEMENT_METHODS }],
|
|
@@ -110,8 +129,8 @@ export function getContainerDescriptor(typeName) {
|
|
|
110
129
|
*/
|
|
111
130
|
export function resolveIterableElementType(iterableName, node, scopeEnv, declarationTypeNodes, scope, extractFromTypeNode, findParamElementType, typeArgPos = 'last') {
|
|
112
131
|
// Strategy 1: declarationTypeNodes AST node (check current scope, then file scope)
|
|
113
|
-
const typeNode = declarationTypeNodes.get(`${scope}\0${iterableName}`)
|
|
114
|
-
|
|
132
|
+
const typeNode = declarationTypeNodes.get(`${scope}\0${iterableName}`) ??
|
|
133
|
+
(scope !== '' ? declarationTypeNodes.get(`\0${iterableName}`) : undefined);
|
|
115
134
|
if (typeNode) {
|
|
116
135
|
const t = extractFromTypeNode(typeNode, typeArgPos);
|
|
117
136
|
if (t)
|
|
@@ -148,23 +167,32 @@ export const extractSimpleTypeName = (typeNode, depth = 0) => {
|
|
|
148
167
|
if (depth > 50 || typeNode.text.length > 2048)
|
|
149
168
|
return undefined;
|
|
150
169
|
// Direct type identifier (includes Ruby 'constant' for class names)
|
|
151
|
-
if (typeNode.type === 'type_identifier' ||
|
|
152
|
-
|
|
170
|
+
if (typeNode.type === 'type_identifier' ||
|
|
171
|
+
typeNode.type === 'identifier' ||
|
|
172
|
+
typeNode.type === 'simple_identifier' ||
|
|
173
|
+
typeNode.type === 'constant') {
|
|
153
174
|
return typeNode.text;
|
|
154
175
|
}
|
|
155
176
|
// Qualified/scoped names: take the last segment (e.g., models.User → User, Models::User → User)
|
|
156
|
-
if (typeNode.type === 'scoped_identifier' ||
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
177
|
+
if (typeNode.type === 'scoped_identifier' ||
|
|
178
|
+
typeNode.type === 'qualified_identifier' ||
|
|
179
|
+
typeNode.type === 'scoped_type_identifier' ||
|
|
180
|
+
typeNode.type === 'qualified_name' ||
|
|
181
|
+
typeNode.type === 'qualified_type' ||
|
|
182
|
+
typeNode.type === 'member_expression' ||
|
|
183
|
+
typeNode.type === 'member_access_expression' ||
|
|
184
|
+
typeNode.type === 'attribute' ||
|
|
185
|
+
typeNode.type === 'scope_resolution' ||
|
|
186
|
+
typeNode.type === 'selector_expression') {
|
|
163
187
|
const last = typeNode.lastNamedChild;
|
|
164
|
-
if (last &&
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
188
|
+
if (last &&
|
|
189
|
+
(last.type === 'type_identifier' ||
|
|
190
|
+
last.type === 'identifier' ||
|
|
191
|
+
last.type === 'simple_identifier' ||
|
|
192
|
+
last.type === 'name' ||
|
|
193
|
+
last.type === 'constant' ||
|
|
194
|
+
last.type === 'property_identifier' ||
|
|
195
|
+
last.type === 'field_identifier')) {
|
|
168
196
|
return last.text;
|
|
169
197
|
}
|
|
170
198
|
}
|
|
@@ -176,11 +204,12 @@ export const extractSimpleTypeName = (typeNode, depth = 0) => {
|
|
|
176
204
|
}
|
|
177
205
|
// Generic types: extract the base type (e.g., List<User> → List)
|
|
178
206
|
// For nullable wrappers (Optional<User>, Option<User>), unwrap to inner type.
|
|
179
|
-
if (typeNode.type === 'generic_type' ||
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
207
|
+
if (typeNode.type === 'generic_type' ||
|
|
208
|
+
typeNode.type === 'parameterized_type' ||
|
|
209
|
+
typeNode.type === 'generic_name') {
|
|
210
|
+
const base = typeNode.childForFieldName('name') ??
|
|
211
|
+
typeNode.childForFieldName('type') ??
|
|
212
|
+
typeNode.firstNamedChild;
|
|
184
213
|
if (!base)
|
|
185
214
|
return undefined;
|
|
186
215
|
const baseName = extractSimpleTypeName(base, depth + 1);
|
|
@@ -218,8 +247,9 @@ export const extractSimpleTypeName = (typeNode, depth = 0) => {
|
|
|
218
247
|
}
|
|
219
248
|
}
|
|
220
249
|
// Type annotations that wrap the actual type (TS/Python: `: Foo`, Kotlin: user_type)
|
|
221
|
-
if (typeNode.type === 'type_annotation' ||
|
|
222
|
-
|
|
250
|
+
if (typeNode.type === 'type_annotation' ||
|
|
251
|
+
typeNode.type === 'type' ||
|
|
252
|
+
typeNode.type === 'user_type') {
|
|
223
253
|
const inner = typeNode.firstNamedChild;
|
|
224
254
|
if (inner)
|
|
225
255
|
return extractSimpleTypeName(inner, depth + 1);
|
|
@@ -239,9 +269,12 @@ export const extractSimpleTypeName = (typeNode, depth = 0) => {
|
|
|
239
269
|
// PHP: primitive_type; TS/JS: predefined_type
|
|
240
270
|
// Java: integral_type (int/long/short/byte), floating_point_type (float/double),
|
|
241
271
|
// boolean_type (boolean), void_type (void)
|
|
242
|
-
if (typeNode.type === 'primitive_type' ||
|
|
243
|
-
|
|
244
|
-
|
|
272
|
+
if (typeNode.type === 'primitive_type' ||
|
|
273
|
+
typeNode.type === 'predefined_type' ||
|
|
274
|
+
typeNode.type === 'integral_type' ||
|
|
275
|
+
typeNode.type === 'floating_point_type' ||
|
|
276
|
+
typeNode.type === 'boolean_type' ||
|
|
277
|
+
typeNode.type === 'void_type') {
|
|
245
278
|
return typeNode.text;
|
|
246
279
|
}
|
|
247
280
|
// PHP named_type / optional_type
|
|
@@ -261,9 +294,12 @@ export const extractSimpleTypeName = (typeNode, depth = 0) => {
|
|
|
261
294
|
* Returns the simple identifier text, or undefined for destructuring/complex patterns.
|
|
262
295
|
*/
|
|
263
296
|
export const extractVarName = (node) => {
|
|
264
|
-
if (node.type === 'identifier' ||
|
|
265
|
-
|
|
266
|
-
|
|
297
|
+
if (node.type === 'identifier' ||
|
|
298
|
+
node.type === 'simple_identifier' ||
|
|
299
|
+
node.type === 'variable_name' ||
|
|
300
|
+
node.type === 'name' ||
|
|
301
|
+
node.type === 'constant' ||
|
|
302
|
+
node.type === 'property_identifier') {
|
|
267
303
|
return node.text;
|
|
268
304
|
}
|
|
269
305
|
// variable_declarator (Java/C#): has a 'name' field
|
|
@@ -320,17 +356,20 @@ export const extractGenericTypeArgs = (typeNode, depth = 0) => {
|
|
|
320
356
|
if (depth > 50)
|
|
321
357
|
return [];
|
|
322
358
|
// Unwrap wrapper nodes that may sit above the generic_type
|
|
323
|
-
if (typeNode.type === 'type_annotation' ||
|
|
324
|
-
|
|
325
|
-
|
|
359
|
+
if (typeNode.type === 'type_annotation' ||
|
|
360
|
+
typeNode.type === 'type' ||
|
|
361
|
+
typeNode.type === 'user_type' ||
|
|
362
|
+
typeNode.type === 'nullable_type' ||
|
|
363
|
+
typeNode.type === 'optional_type') {
|
|
326
364
|
const inner = typeNode.firstNamedChild;
|
|
327
365
|
if (inner)
|
|
328
366
|
return extractGenericTypeArgs(inner, depth + 1);
|
|
329
367
|
return [];
|
|
330
368
|
}
|
|
331
369
|
// Only process generic/parameterized type nodes (includes C#'s generic_name)
|
|
332
|
-
if (typeNode.type !== 'generic_type' &&
|
|
333
|
-
|
|
370
|
+
if (typeNode.type !== 'generic_type' &&
|
|
371
|
+
typeNode.type !== 'parameterized_type' &&
|
|
372
|
+
typeNode.type !== 'generic_name') {
|
|
334
373
|
return [];
|
|
335
374
|
}
|
|
336
375
|
// Find the type_arguments / type_argument_list child
|
|
@@ -438,7 +477,10 @@ export const stripNullable = (typeName) => {
|
|
|
438
477
|
text = text.slice(0, -1).trim();
|
|
439
478
|
// Strip union with null/undefined/None/nil/void
|
|
440
479
|
if (text.includes('|')) {
|
|
441
|
-
const parts = text
|
|
480
|
+
const parts = text
|
|
481
|
+
.split('|')
|
|
482
|
+
.map((p) => p.trim())
|
|
483
|
+
.filter((p) => p !== '' && !NULLABLE_KEYWORDS.has(p));
|
|
442
484
|
if (parts.length === 1)
|
|
443
485
|
return parts[0];
|
|
444
486
|
return undefined; // genuine union or all-nullable — refuse
|
|
@@ -581,10 +623,35 @@ export function extractElementTypeFromString(typeStr, pos = 'last') {
|
|
|
581
623
|
// Extracts the base user-defined type name.
|
|
582
624
|
/** Primitive / built-in types that should NOT produce a receiver binding. */
|
|
583
625
|
const PRIMITIVE_TYPES = new Set([
|
|
584
|
-
'string',
|
|
585
|
-
'
|
|
586
|
-
'
|
|
587
|
-
'
|
|
626
|
+
'string',
|
|
627
|
+
'number',
|
|
628
|
+
'boolean',
|
|
629
|
+
'void',
|
|
630
|
+
'int',
|
|
631
|
+
'float',
|
|
632
|
+
'double',
|
|
633
|
+
'long',
|
|
634
|
+
'short',
|
|
635
|
+
'byte',
|
|
636
|
+
'char',
|
|
637
|
+
'bool',
|
|
638
|
+
'str',
|
|
639
|
+
'i8',
|
|
640
|
+
'i16',
|
|
641
|
+
'i32',
|
|
642
|
+
'i64',
|
|
643
|
+
'u8',
|
|
644
|
+
'u16',
|
|
645
|
+
'u32',
|
|
646
|
+
'u64',
|
|
647
|
+
'f32',
|
|
648
|
+
'f64',
|
|
649
|
+
'usize',
|
|
650
|
+
'isize',
|
|
651
|
+
'undefined',
|
|
652
|
+
'null',
|
|
653
|
+
'None',
|
|
654
|
+
'nil',
|
|
588
655
|
]);
|
|
589
656
|
/**
|
|
590
657
|
* Extract a simple type name from raw return-type text.
|
|
@@ -600,13 +667,27 @@ const PRIMITIVE_TYPES = new Set([
|
|
|
600
667
|
* Returns undefined for complex types or primitives.
|
|
601
668
|
*/
|
|
602
669
|
const WRAPPER_GENERICS = new Set([
|
|
603
|
-
'Promise',
|
|
604
|
-
'
|
|
605
|
-
'
|
|
670
|
+
'Promise',
|
|
671
|
+
'Observable',
|
|
672
|
+
'Future',
|
|
673
|
+
'CompletableFuture',
|
|
674
|
+
'Task',
|
|
675
|
+
'ValueTask', // async wrappers
|
|
676
|
+
'Option',
|
|
677
|
+
'Some',
|
|
678
|
+
'Optional',
|
|
679
|
+
'Maybe', // nullable wrappers
|
|
680
|
+
'Result',
|
|
681
|
+
'Either', // result wrappers
|
|
606
682
|
// Rust smart pointers (Deref to inner type)
|
|
607
|
-
'Rc',
|
|
608
|
-
'
|
|
609
|
-
'
|
|
683
|
+
'Rc',
|
|
684
|
+
'Arc',
|
|
685
|
+
'Weak', // pointer types
|
|
686
|
+
'MutexGuard',
|
|
687
|
+
'RwLockReadGuard',
|
|
688
|
+
'RwLockWriteGuard', // guard types
|
|
689
|
+
'Ref',
|
|
690
|
+
'RefMut', // RefCell guards
|
|
610
691
|
'Cow', // copy-on-write
|
|
611
692
|
// Containers (List, Array, Vec, Set, etc.) are intentionally excluded —
|
|
612
693
|
// methods are called on the container, not the element type.
|
|
@@ -668,7 +749,10 @@ export const extractReturnTypeName = (raw, depth = 0) => {
|
|
|
668
749
|
text = text.replace(/\?$/, '');
|
|
669
750
|
// Handle union types: "User | null" → "User"
|
|
670
751
|
if (text.includes('|')) {
|
|
671
|
-
const parts = text
|
|
752
|
+
const parts = text
|
|
753
|
+
.split('|')
|
|
754
|
+
.map((p) => p.trim())
|
|
755
|
+
.filter((p) => p !== 'null' && p !== 'undefined' && p !== 'void' && p !== 'None' && p !== 'nil');
|
|
672
756
|
if (parts.length === 1)
|
|
673
757
|
text = parts[0];
|
|
674
758
|
else
|