gitnexus 1.4.8 → 1.4.10
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 +7 -0
- package/dist/cli/index-repo.d.ts +15 -0
- package/dist/cli/index-repo.js +115 -0
- package/dist/cli/index.js +11 -2
- package/dist/cli/setup.js +12 -9
- package/dist/cli/wiki.d.ts +4 -0
- package/dist/cli/wiki.js +174 -53
- package/dist/config/supported-languages.d.ts +7 -5
- package/dist/config/supported-languages.js +6 -4
- package/dist/core/graph/graph.js +9 -1
- package/dist/core/graph/types.d.ts +10 -2
- package/dist/core/ingestion/call-processor.d.ts +18 -1
- package/dist/core/ingestion/call-processor.js +297 -38
- package/dist/core/ingestion/call-routing.d.ts +3 -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 +18 -4
- 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 +87 -0
- package/dist/core/ingestion/framework-detection.js +65 -2
- package/dist/core/ingestion/heritage-processor.js +15 -17
- package/dist/core/ingestion/import-processor.d.ts +9 -10
- package/dist/core/ingestion/import-processor.js +59 -14
- 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 +9 -1
- package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.js +56 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/php.d.ts +6 -10
- package/dist/core/ingestion/{resolvers → import-resolvers}/php.js +7 -2
- 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 +0 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +0 -9
- package/dist/core/ingestion/language-config.d.ts +4 -1
- 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/mro-processor.js +14 -15
- package/dist/core/ingestion/{named-binding-extraction.d.ts → named-binding-processor.d.ts} +0 -9
- 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 +5 -1
- package/dist/core/ingestion/parsing-processor.js +115 -16
- package/dist/core/ingestion/pipeline.js +925 -424
- package/dist/core/ingestion/resolution-context.js +1 -1
- 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/tree-sitter-queries.d.ts +8 -7
- package/dist/core/ingestion/tree-sitter-queries.js +231 -9
- package/dist/core/ingestion/type-env.d.ts +14 -17
- package/dist/core/ingestion/type-env.js +66 -14
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +1 -1
- package/dist/core/ingestion/type-extractors/csharp.js +1 -1
- 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 +1 -1
- package/dist/core/ingestion/type-extractors/shared.d.ts +1 -13
- package/dist/core/ingestion/type-extractors/shared.js +9 -102
- package/dist/core/ingestion/type-extractors/swift.js +334 -4
- package/dist/core/ingestion/type-extractors/types.d.ts +3 -1
- package/dist/core/ingestion/{ast-helpers.d.ts → utils/ast-helpers.d.ts} +16 -13
- package/dist/core/ingestion/{ast-helpers.js → utils/ast-helpers.js} +111 -32
- package/dist/core/ingestion/{call-analysis.js → utils/call-analysis.js} +37 -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 +43 -2
- package/dist/core/ingestion/workers/parse-worker.js +361 -150
- package/dist/core/lbug/csv-generator.js +34 -1
- package/dist/core/lbug/lbug-adapter.js +6 -0
- package/dist/core/lbug/schema.d.ts +5 -3
- package/dist/core/lbug/schema.js +39 -2
- 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/lbug-adapter.d.ts +5 -0
- package/dist/mcp/core/lbug-adapter.js +11 -1
- package/dist/mcp/local/local-backend.d.ts +16 -5
- package/dist/mcp/local/local-backend.js +711 -74
- package/dist/mcp/tools.js +71 -2
- package/dist/storage/repo-manager.d.ts +3 -0
- package/package.json +14 -14
- package/dist/core/ingestion/import-resolution.d.ts +0 -101
- package/dist/core/ingestion/import-resolution.js +0 -251
- package/dist/core/ingestion/named-binding-extraction.js +0 -373
- package/dist/core/ingestion/resolvers/index.d.ts +0 -18
- package/dist/core/ingestion/resolvers/index.js +0 -13
- 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 -20
- package/dist/core/ingestion/utils.js +0 -242
- package/scripts/patch-tree-sitter-swift.cjs +0 -74
- /package/dist/core/ingestion/{call-analysis.d.ts → utils/call-analysis.d.ts} +0 -0
|
@@ -278,6 +278,12 @@ export const extractVarName = (node) => {
|
|
|
278
278
|
if (inner)
|
|
279
279
|
return extractVarName(inner);
|
|
280
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
|
+
}
|
|
281
287
|
return undefined;
|
|
282
288
|
};
|
|
283
289
|
/** Node types for function/method parameters with type annotations */
|
|
@@ -701,105 +707,6 @@ export const extractReturnTypeName = (raw, depth = 0) => {
|
|
|
701
707
|
return undefined;
|
|
702
708
|
return text;
|
|
703
709
|
};
|
|
704
|
-
//
|
|
705
|
-
//
|
|
706
|
-
|
|
707
|
-
* Extract the declared type of a property/field from its AST definition node.
|
|
708
|
-
* Handles cross-language patterns:
|
|
709
|
-
* - TypeScript: `name: Type` → type_annotation child
|
|
710
|
-
* - Java: `Type name` → type child on field_declaration
|
|
711
|
-
* - C#: `Type Name { get; set; }` → type child on property_declaration
|
|
712
|
-
* - Go: `Name Type` → type child on field_declaration
|
|
713
|
-
* - Kotlin: `var name: Type` → variable_declaration child with type field
|
|
714
|
-
*
|
|
715
|
-
* Returns the normalized type name, or undefined if no type can be extracted.
|
|
716
|
-
*/
|
|
717
|
-
export const extractPropertyDeclaredType = (definitionNode) => {
|
|
718
|
-
if (!definitionNode)
|
|
719
|
-
return undefined;
|
|
720
|
-
// Strategy 1: Look for a `type` or `type_annotation` named field
|
|
721
|
-
const typeNode = definitionNode.childForFieldName?.('type');
|
|
722
|
-
if (typeNode) {
|
|
723
|
-
const typeName = extractSimpleTypeName(typeNode);
|
|
724
|
-
if (typeName)
|
|
725
|
-
return typeName;
|
|
726
|
-
// Fallback: use the raw text (for complex types like User[] or List<User>)
|
|
727
|
-
const text = typeNode.text?.trim();
|
|
728
|
-
if (text && text.length < 100)
|
|
729
|
-
return text;
|
|
730
|
-
}
|
|
731
|
-
// Strategy 2: Walk children looking for type_annotation (TypeScript pattern)
|
|
732
|
-
for (let i = 0; i < definitionNode.childCount; i++) {
|
|
733
|
-
const child = definitionNode.child(i);
|
|
734
|
-
if (!child)
|
|
735
|
-
continue;
|
|
736
|
-
if (child.type === 'type_annotation') {
|
|
737
|
-
// Type annotation has the actual type as a child
|
|
738
|
-
for (let j = 0; j < child.childCount; j++) {
|
|
739
|
-
const typeChild = child.child(j);
|
|
740
|
-
if (typeChild && typeChild.type !== ':') {
|
|
741
|
-
const typeName = extractSimpleTypeName(typeChild);
|
|
742
|
-
if (typeName)
|
|
743
|
-
return typeName;
|
|
744
|
-
const text = typeChild.text?.trim();
|
|
745
|
-
if (text && text.length < 100)
|
|
746
|
-
return text;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
// Strategy 3: For Java field_declaration, the type is a sibling of variable_declarator
|
|
752
|
-
// AST: (field_declaration type: (type_identifier) declarator: (variable_declarator ...))
|
|
753
|
-
const parentDecl = definitionNode.parent;
|
|
754
|
-
if (parentDecl) {
|
|
755
|
-
const parentType = parentDecl.childForFieldName?.('type');
|
|
756
|
-
if (parentType) {
|
|
757
|
-
const typeName = extractSimpleTypeName(parentType);
|
|
758
|
-
if (typeName)
|
|
759
|
-
return typeName;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
// Strategy 4: Kotlin property_declaration — type is nested inside variable_declaration child
|
|
763
|
-
// AST: (property_declaration (variable_declaration (simple_identifier) ":" (user_type (type_identifier))))
|
|
764
|
-
// Kotlin's variable_declaration has NO named 'type' field — children are all positional.
|
|
765
|
-
for (let i = 0; i < definitionNode.childCount; i++) {
|
|
766
|
-
const child = definitionNode.child(i);
|
|
767
|
-
if (child?.type === 'variable_declaration') {
|
|
768
|
-
// Try named field first (works for other languages sharing this strategy)
|
|
769
|
-
const varType = child.childForFieldName?.('type');
|
|
770
|
-
if (varType) {
|
|
771
|
-
const typeName = extractSimpleTypeName(varType);
|
|
772
|
-
if (typeName)
|
|
773
|
-
return typeName;
|
|
774
|
-
const text = varType.text?.trim();
|
|
775
|
-
if (text && text.length < 100)
|
|
776
|
-
return text;
|
|
777
|
-
}
|
|
778
|
-
// Fallback: walk unnamed children for user_type / type_identifier (Kotlin)
|
|
779
|
-
for (let j = 0; j < child.namedChildCount; j++) {
|
|
780
|
-
const varChild = child.namedChild(j);
|
|
781
|
-
if (varChild && (varChild.type === 'user_type' || varChild.type === 'type_identifier'
|
|
782
|
-
|| varChild.type === 'nullable_type' || varChild.type === 'generic_type')) {
|
|
783
|
-
const typeName = extractSimpleTypeName(varChild);
|
|
784
|
-
if (typeName)
|
|
785
|
-
return typeName;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
// Strategy 5: PHP @var PHPDoc — look for preceding comment with @var Type
|
|
791
|
-
// Handles pre-PHP-7.4 code: /** @var Address */ public $address;
|
|
792
|
-
const prevSibling = definitionNode.previousNamedSibling ?? definitionNode.parent?.previousNamedSibling;
|
|
793
|
-
if (prevSibling?.type === 'comment') {
|
|
794
|
-
const commentText = prevSibling.text;
|
|
795
|
-
const varMatch = commentText?.match(/@var\s+([A-Z][\w\\]*)/);
|
|
796
|
-
if (varMatch) {
|
|
797
|
-
// Strip namespace prefix: \App\Models\User → User
|
|
798
|
-
const raw = varMatch[1];
|
|
799
|
-
const base = raw.includes('\\') ? raw.split('\\').pop() : raw;
|
|
800
|
-
if (base && /^[A-Z]\w*$/.test(base))
|
|
801
|
-
return base;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
return undefined;
|
|
805
|
-
};
|
|
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).
|
|
@@ -1,8 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { findChild } from '../utils/ast-helpers.js';
|
|
2
|
+
import { extractSimpleTypeName, extractVarName, hasTypeAnnotation, extractElementTypeFromString, resolveIterableElementType } from './shared.js';
|
|
3
3
|
const DECLARATION_NODE_TYPES = new Set([
|
|
4
4
|
'property_declaration',
|
|
5
|
+
'if_statement',
|
|
6
|
+
'guard_statement',
|
|
5
7
|
]);
|
|
8
|
+
const FOR_LOOP_NODE_TYPES = new Set([
|
|
9
|
+
'for_statement',
|
|
10
|
+
]);
|
|
11
|
+
/**
|
|
12
|
+
* Unwrap Swift `await_expression` and `try_expression` nodes to find the inner
|
|
13
|
+
* call_expression or other value node. `try` nodes contain a `try_operator` child
|
|
14
|
+
* that must be skipped.
|
|
15
|
+
*/
|
|
16
|
+
function unwrapSwiftExpression(node) {
|
|
17
|
+
if (node.type === 'await_expression' || node.type === 'try_expression') {
|
|
18
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
19
|
+
const child = node.namedChild(i);
|
|
20
|
+
if (child && child.type !== 'try_operator')
|
|
21
|
+
return unwrapSwiftExpression(child);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return node;
|
|
25
|
+
}
|
|
6
26
|
/** Swift: let x: Foo = ... */
|
|
7
27
|
const extractDeclaration = (node, env) => {
|
|
8
28
|
// Swift property_declaration has pattern and type_annotation
|
|
@@ -53,8 +73,21 @@ const extractInitializer = (node, env, classNames) => {
|
|
|
53
73
|
const varName = extractVarName(pattern) ?? pattern.text;
|
|
54
74
|
if (!varName || env.has(varName))
|
|
55
75
|
return;
|
|
56
|
-
// Find call_expression in the value
|
|
57
|
-
|
|
76
|
+
// Find call_expression in the value (unwrap await/try)
|
|
77
|
+
let callExpr = findChild(node, 'call_expression');
|
|
78
|
+
if (!callExpr) {
|
|
79
|
+
// Check for await_expression or try_expression wrapping a call_expression
|
|
80
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
81
|
+
const child = node.namedChild(i);
|
|
82
|
+
if (child && (child.type === 'await_expression' || child.type === 'try_expression')) {
|
|
83
|
+
const unwrapped = unwrapSwiftExpression(child);
|
|
84
|
+
if (unwrapped.type === 'call_expression') {
|
|
85
|
+
callExpr = unwrapped;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
58
91
|
if (!callExpr)
|
|
59
92
|
return;
|
|
60
93
|
const callee = callExpr.firstNamedChild;
|
|
@@ -99,6 +132,14 @@ const scanConstructorBinding = (node) => {
|
|
|
99
132
|
callExpr = child;
|
|
100
133
|
break;
|
|
101
134
|
}
|
|
135
|
+
// Unwrap await/try to find inner call_expression
|
|
136
|
+
if (child && (child.type === 'await_expression' || child.type === 'try_expression')) {
|
|
137
|
+
const unwrapped = unwrapSwiftExpression(child);
|
|
138
|
+
if (unwrapped.type === 'call_expression') {
|
|
139
|
+
callExpr = unwrapped;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
102
143
|
}
|
|
103
144
|
if (!callExpr)
|
|
104
145
|
return undefined;
|
|
@@ -129,10 +170,299 @@ const scanConstructorBinding = (node) => {
|
|
|
129
170
|
}
|
|
130
171
|
return undefined;
|
|
131
172
|
};
|
|
173
|
+
/**
|
|
174
|
+
* Extract the variable name from an if_statement or guard_statement with optional binding.
|
|
175
|
+
* Pattern: `if let varName = expr` / `guard let varName = expr`
|
|
176
|
+
* AST: if_statement/guard_statement contains value_binding_pattern, then simple_identifier (varName),
|
|
177
|
+
* then call_expression/simple_identifier/navigation_expression (value).
|
|
178
|
+
*/
|
|
179
|
+
function extractIfGuardBinding(node, scopeEnv) {
|
|
180
|
+
// Find value_binding_pattern to confirm this is an optional binding
|
|
181
|
+
let hasValueBinding = false;
|
|
182
|
+
let varName;
|
|
183
|
+
let valueNode = null;
|
|
184
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
185
|
+
const child = node.namedChild(i);
|
|
186
|
+
if (!child)
|
|
187
|
+
continue;
|
|
188
|
+
if (child.type === 'value_binding_pattern') {
|
|
189
|
+
hasValueBinding = true;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (hasValueBinding && !varName && child.type === 'simple_identifier') {
|
|
193
|
+
varName = child.text;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (varName && !valueNode) {
|
|
197
|
+
// Skip type annotations and binding operators
|
|
198
|
+
if (child.type === 'type_annotation')
|
|
199
|
+
continue;
|
|
200
|
+
valueNode = child;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (!hasValueBinding || !varName || !valueNode || scopeEnv.has(varName))
|
|
205
|
+
return undefined;
|
|
206
|
+
// Unwrap await/try
|
|
207
|
+
valueNode = unwrapSwiftExpression(valueNode);
|
|
208
|
+
// simple_identifier → copy
|
|
209
|
+
if (valueNode.type === 'simple_identifier') {
|
|
210
|
+
return { kind: 'copy', lhs: varName, rhs: valueNode.text };
|
|
211
|
+
}
|
|
212
|
+
// navigation_expression → fieldAccess
|
|
213
|
+
if (valueNode.type === 'navigation_expression') {
|
|
214
|
+
const receiver = valueNode.firstNamedChild;
|
|
215
|
+
const suffix = valueNode.lastNamedChild;
|
|
216
|
+
if (receiver?.type === 'simple_identifier' && suffix?.type === 'navigation_suffix') {
|
|
217
|
+
const field = suffix.lastNamedChild;
|
|
218
|
+
if (field?.type === 'simple_identifier') {
|
|
219
|
+
return { kind: 'fieldAccess', lhs: varName, receiver: receiver.text, field: field.text };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
// call_expression → callResult or methodCallResult
|
|
225
|
+
if (valueNode.type === 'call_expression') {
|
|
226
|
+
const callee = valueNode.firstNamedChild;
|
|
227
|
+
if (!callee)
|
|
228
|
+
return undefined;
|
|
229
|
+
if (callee.type === 'simple_identifier') {
|
|
230
|
+
return { kind: 'callResult', lhs: varName, callee: callee.text };
|
|
231
|
+
}
|
|
232
|
+
if (callee.type === 'navigation_expression') {
|
|
233
|
+
const receiver = callee.firstNamedChild;
|
|
234
|
+
const suffix = callee.lastNamedChild;
|
|
235
|
+
if (receiver?.type === 'simple_identifier' && suffix?.type === 'navigation_suffix') {
|
|
236
|
+
const method = suffix.lastNamedChild;
|
|
237
|
+
if (method?.type === 'simple_identifier') {
|
|
238
|
+
return { kind: 'methodCallResult', lhs: varName, receiver: receiver.text, method: method.text };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Swift: extract pending assignments for Tier 2 return-type propagation.
|
|
247
|
+
* Handles:
|
|
248
|
+
* let user = getUser() → callResult
|
|
249
|
+
* let result = user.save() → methodCallResult
|
|
250
|
+
* let name = user.name → fieldAccess
|
|
251
|
+
* let copy = user → copy
|
|
252
|
+
* let user = await getUser() → callResult (unwrapped)
|
|
253
|
+
* let user = try getUser() → callResult (unwrapped)
|
|
254
|
+
* if let user = getUser() → callResult (optional binding)
|
|
255
|
+
* guard let user = getUser() → callResult (optional binding)
|
|
256
|
+
*/
|
|
257
|
+
const extractPendingAssignment = (node, scopeEnv) => {
|
|
258
|
+
// Handle if_statement and guard_statement optional bindings
|
|
259
|
+
if (node.type === 'if_statement' || node.type === 'guard_statement') {
|
|
260
|
+
return extractIfGuardBinding(node, scopeEnv);
|
|
261
|
+
}
|
|
262
|
+
if (node.type !== 'property_declaration')
|
|
263
|
+
return undefined;
|
|
264
|
+
// Skip if type annotation exists — extractDeclaration handles it
|
|
265
|
+
if (hasTypeAnnotation(node))
|
|
266
|
+
return undefined;
|
|
267
|
+
// Find the variable name from the pattern child
|
|
268
|
+
let lhs;
|
|
269
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
270
|
+
const child = node.namedChild(i);
|
|
271
|
+
if (child?.type === 'pattern') {
|
|
272
|
+
lhs = child.text;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (!lhs || scopeEnv.has(lhs))
|
|
277
|
+
return undefined;
|
|
278
|
+
// Find the value expression (last meaningful named child after pattern)
|
|
279
|
+
let valueNode = null;
|
|
280
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
281
|
+
const child = node.namedChild(i);
|
|
282
|
+
if (!child)
|
|
283
|
+
continue;
|
|
284
|
+
if (child.type === 'pattern' || child.type === 'value_binding_pattern' || child.type === 'type_annotation')
|
|
285
|
+
continue;
|
|
286
|
+
valueNode = child;
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
if (!valueNode)
|
|
290
|
+
return undefined;
|
|
291
|
+
// Unwrap await/try expressions (Feature 2)
|
|
292
|
+
valueNode = unwrapSwiftExpression(valueNode);
|
|
293
|
+
// let copy = user → copy
|
|
294
|
+
if (valueNode.type === 'simple_identifier') {
|
|
295
|
+
return { kind: 'copy', lhs, rhs: valueNode.text };
|
|
296
|
+
}
|
|
297
|
+
// let name = user.name → fieldAccess
|
|
298
|
+
if (valueNode.type === 'navigation_expression') {
|
|
299
|
+
const receiver = valueNode.firstNamedChild;
|
|
300
|
+
const suffix = valueNode.lastNamedChild;
|
|
301
|
+
if (receiver?.type === 'simple_identifier' && suffix?.type === 'navigation_suffix') {
|
|
302
|
+
const field = suffix.lastNamedChild;
|
|
303
|
+
if (field?.type === 'simple_identifier') {
|
|
304
|
+
return { kind: 'fieldAccess', lhs, receiver: receiver.text, field: field.text };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
// Call expressions
|
|
310
|
+
if (valueNode.type === 'call_expression') {
|
|
311
|
+
const callee = valueNode.firstNamedChild;
|
|
312
|
+
if (!callee)
|
|
313
|
+
return undefined;
|
|
314
|
+
// let user = getUser() → callResult
|
|
315
|
+
if (callee.type === 'simple_identifier') {
|
|
316
|
+
return { kind: 'callResult', lhs, callee: callee.text };
|
|
317
|
+
}
|
|
318
|
+
// let result = user.save() → methodCallResult
|
|
319
|
+
if (callee.type === 'navigation_expression') {
|
|
320
|
+
const receiver = callee.firstNamedChild;
|
|
321
|
+
const suffix = callee.lastNamedChild;
|
|
322
|
+
if (receiver?.type === 'simple_identifier' && suffix?.type === 'navigation_suffix') {
|
|
323
|
+
const method = suffix.lastNamedChild;
|
|
324
|
+
if (method?.type === 'simple_identifier') {
|
|
325
|
+
return { kind: 'methodCallResult', lhs, receiver: receiver.text, method: method.text };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return undefined;
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* Swift: extract loop variable type binding from `for item in collection`.
|
|
334
|
+
* AST: for_statement with pattern > simple_identifier (loop var) and
|
|
335
|
+
* a simple_identifier/call_expression (collection).
|
|
336
|
+
*/
|
|
337
|
+
const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope, returnTypeLookup }) => {
|
|
338
|
+
if (node.type !== 'for_statement')
|
|
339
|
+
return;
|
|
340
|
+
// Find the loop variable from the pattern child
|
|
341
|
+
let loopVarName;
|
|
342
|
+
let iterableNode = null;
|
|
343
|
+
// for_statement children: pattern (loop var), then the iterable expression, then the body
|
|
344
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
345
|
+
const child = node.namedChild(i);
|
|
346
|
+
if (!child)
|
|
347
|
+
continue;
|
|
348
|
+
if (child.type === 'pattern' || child.type === 'simple_identifier') {
|
|
349
|
+
if (!loopVarName) {
|
|
350
|
+
// Extract a simple identifier from the pattern. Skip non-trivial patterns
|
|
351
|
+
// (e.g. tuple destructuring `for (a, b) in ...`) to avoid polluting scopeEnv.
|
|
352
|
+
const varName = extractVarName(child) ?? (child.type === 'simple_identifier' ? child.text : undefined);
|
|
353
|
+
if (!varName)
|
|
354
|
+
return; // Non-simple pattern — bail out
|
|
355
|
+
loopVarName = varName;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// After we found the loop var, the next expression-like node is the iterable
|
|
360
|
+
if (loopVarName && !iterableNode) {
|
|
361
|
+
if (child.type === 'simple_identifier' || child.type === 'call_expression' ||
|
|
362
|
+
child.type === 'navigation_expression') {
|
|
363
|
+
iterableNode = child;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (!loopVarName || !iterableNode)
|
|
369
|
+
return;
|
|
370
|
+
let iterableName;
|
|
371
|
+
let callExprElementType;
|
|
372
|
+
if (iterableNode.type === 'simple_identifier') {
|
|
373
|
+
iterableName = iterableNode.text;
|
|
374
|
+
}
|
|
375
|
+
else if (iterableNode.type === 'navigation_expression') {
|
|
376
|
+
// collection.property
|
|
377
|
+
const suffix = iterableNode.lastNamedChild;
|
|
378
|
+
if (suffix?.type === 'navigation_suffix') {
|
|
379
|
+
const prop = suffix.lastNamedChild;
|
|
380
|
+
if (prop?.type === 'simple_identifier')
|
|
381
|
+
iterableName = prop.text;
|
|
382
|
+
}
|
|
383
|
+
else if (suffix?.type === 'simple_identifier') {
|
|
384
|
+
iterableName = suffix.text;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
else if (iterableNode.type === 'call_expression') {
|
|
388
|
+
// getItems() or collection.values()
|
|
389
|
+
const fn = iterableNode.firstNamedChild;
|
|
390
|
+
let callee;
|
|
391
|
+
if (fn?.type === 'simple_identifier') {
|
|
392
|
+
callee = fn.text;
|
|
393
|
+
}
|
|
394
|
+
else if (fn?.type === 'navigation_expression') {
|
|
395
|
+
const obj = fn.firstNamedChild;
|
|
396
|
+
const suffix = fn.lastNamedChild;
|
|
397
|
+
if (obj?.type === 'simple_identifier')
|
|
398
|
+
iterableName = obj.text;
|
|
399
|
+
if (suffix?.type === 'navigation_suffix') {
|
|
400
|
+
const m = suffix.lastNamedChild;
|
|
401
|
+
if (m?.type === 'simple_identifier')
|
|
402
|
+
callee = m.text;
|
|
403
|
+
}
|
|
404
|
+
else if (suffix?.type === 'simple_identifier') {
|
|
405
|
+
callee = suffix.text;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (callee) {
|
|
409
|
+
const rawReturn = returnTypeLookup.lookupRawReturnType(callee);
|
|
410
|
+
if (rawReturn)
|
|
411
|
+
callExprElementType = extractElementTypeFromString(rawReturn);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (!iterableName && !callExprElementType)
|
|
415
|
+
return;
|
|
416
|
+
let elementType;
|
|
417
|
+
if (callExprElementType) {
|
|
418
|
+
elementType = callExprElementType;
|
|
419
|
+
}
|
|
420
|
+
else if (iterableName) {
|
|
421
|
+
// Try to resolve element type from the iterable's declared type
|
|
422
|
+
elementType = resolveIterableElementType(iterableName, node, scopeEnv, declarationTypeNodes, scope, extractSwiftElementTypeFromTypeNode);
|
|
423
|
+
}
|
|
424
|
+
if (elementType && !scopeEnv.has(loopVarName)) {
|
|
425
|
+
scopeEnv.set(loopVarName, elementType);
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
/**
|
|
429
|
+
* Extract element type from a Swift type annotation AST node.
|
|
430
|
+
* Handles: [User] (array sugar), Array<User>, Set<User>, etc.
|
|
431
|
+
*/
|
|
432
|
+
function extractSwiftElementTypeFromTypeNode(typeNode) {
|
|
433
|
+
// Swift array sugar: [User] — parsed as array_type > user_type > type_identifier
|
|
434
|
+
if (typeNode.type === 'array_type') {
|
|
435
|
+
const inner = typeNode.firstNamedChild;
|
|
436
|
+
if (inner)
|
|
437
|
+
return extractSimpleTypeName(inner);
|
|
438
|
+
}
|
|
439
|
+
// Generic type: Array<User>, Set<User>
|
|
440
|
+
if (typeNode.type === 'user_type') {
|
|
441
|
+
// Check for generic args: user_type > type_identifier + type_arguments
|
|
442
|
+
for (let i = 0; i < typeNode.namedChildCount; i++) {
|
|
443
|
+
const child = typeNode.namedChild(i);
|
|
444
|
+
if (child?.type === 'type_arguments') {
|
|
445
|
+
const lastArg = child.lastNamedChild;
|
|
446
|
+
if (lastArg)
|
|
447
|
+
return extractSimpleTypeName(lastArg);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// type_annotation wrapping
|
|
452
|
+
if (typeNode.type === 'type_annotation') {
|
|
453
|
+
const inner = typeNode.firstNamedChild;
|
|
454
|
+
if (inner)
|
|
455
|
+
return extractSwiftElementTypeFromTypeNode(inner);
|
|
456
|
+
}
|
|
457
|
+
return undefined;
|
|
458
|
+
}
|
|
132
459
|
export const typeConfig = {
|
|
133
460
|
declarationNodeTypes: DECLARATION_NODE_TYPES,
|
|
461
|
+
forLoopNodeTypes: FOR_LOOP_NODE_TYPES,
|
|
134
462
|
extractDeclaration,
|
|
135
463
|
extractParameter,
|
|
136
464
|
extractInitializer,
|
|
137
465
|
scanConstructorBinding,
|
|
466
|
+
extractPendingAssignment,
|
|
467
|
+
extractForLoopBinding,
|
|
138
468
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SyntaxNode } from '../utils.js';
|
|
1
|
+
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
|
2
2
|
/** Extracts type bindings from a declaration node into the env map */
|
|
3
3
|
export type TypeBindingExtractor = (node: SyntaxNode, env: Map<string, string>) => void;
|
|
4
4
|
/** Extracts type bindings from a parameter node into the env map */
|
|
@@ -75,6 +75,8 @@ export type PendingAssignment = {
|
|
|
75
75
|
kind: 'callResult';
|
|
76
76
|
lhs: string;
|
|
77
77
|
callee: string;
|
|
78
|
+
calleeFqn?: string;
|
|
79
|
+
line?: number;
|
|
78
80
|
} | {
|
|
79
81
|
kind: 'fieldAccess';
|
|
80
82
|
lhs: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type Parser from 'tree-sitter';
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
2
|
+
import type { NodeLabel } from '../../graph/types.js';
|
|
3
|
+
import type { LanguageProvider } from '../language-provider.js';
|
|
4
4
|
/** Tree-sitter AST node. Re-exported for use across ingestion modules. */
|
|
5
5
|
export type SyntaxNode = Parser.SyntaxNode;
|
|
6
6
|
/**
|
|
@@ -29,20 +29,13 @@ export declare const CONTAINER_TYPE_TO_LABEL: Record<string, string>;
|
|
|
29
29
|
export declare function isKotlinClassMethod(captureNode: {
|
|
30
30
|
parent?: any;
|
|
31
31
|
} | null | undefined): boolean;
|
|
32
|
-
/**
|
|
33
|
-
* C/C++: check if a Function capture is inside a class/struct body.
|
|
34
|
-
* If true, the function is already captured by @definition.method and should be skipped
|
|
35
|
-
* to prevent double-indexing in globalIndex.
|
|
36
|
-
*/
|
|
37
|
-
export declare function isCppDuplicateClassFunction(functionNode: {
|
|
38
|
-
parent?: any;
|
|
39
|
-
} | null | undefined, nodeLabel: string, language: SupportedLanguages): boolean;
|
|
40
32
|
/**
|
|
41
33
|
* Determine the graph node label from a tree-sitter capture map.
|
|
42
|
-
* Handles language-specific reclassification
|
|
34
|
+
* Handles language-specific reclassification via the provider's labelOverride hook
|
|
35
|
+
* (e.g. C/C++ duplicate skipping, Kotlin Method promotion).
|
|
43
36
|
* Returns null if the capture should be skipped (import, call, C/C++ duplicate, missing name).
|
|
44
37
|
*/
|
|
45
|
-
export declare function getLabelFromCaptures(captureMap: Record<string, any>,
|
|
38
|
+
export declare function getLabelFromCaptures(captureMap: Record<string, any>, provider: LanguageProvider): NodeLabel | null;
|
|
46
39
|
/** Walk up AST to find enclosing class/struct/interface/impl, return its generateId or null.
|
|
47
40
|
* For Go method_declaration nodes, extracts receiver type (e.g. `func (u *User) Save()` → User struct). */
|
|
48
41
|
export declare const findEnclosingClassId: (node: any, filePath: string) => string | null;
|
|
@@ -57,7 +50,7 @@ export declare const findSiblingChild: (parent: any, siblingType: string, childT
|
|
|
57
50
|
*/
|
|
58
51
|
export declare const extractFunctionName: (node: SyntaxNode) => {
|
|
59
52
|
funcName: string | null;
|
|
60
|
-
label:
|
|
53
|
+
label: NodeLabel;
|
|
61
54
|
};
|
|
62
55
|
export interface MethodSignature {
|
|
63
56
|
parameterCount: number | undefined;
|
|
@@ -78,3 +71,13 @@ export declare const CALL_ARGUMENT_LIST_TYPES: Set<string>;
|
|
|
78
71
|
* Works across languages by looking for common AST patterns.
|
|
79
72
|
*/
|
|
80
73
|
export declare const extractMethodSignature: (node: SyntaxNode | null | undefined) => MethodSignature;
|
|
74
|
+
/** Walk an AST node depth-first, returning the first descendant with the given type. */
|
|
75
|
+
export declare function findDescendant(node: any, type: string): any;
|
|
76
|
+
/** Extract the text content from a string or encapsed_string AST node. */
|
|
77
|
+
export declare function extractStringContent(node: any): string | null;
|
|
78
|
+
/** Check if a C/C++ function_definition is inside a class or struct body.
|
|
79
|
+
* Used by the C/C++ labelOverride to skip duplicate function captures
|
|
80
|
+
* that are already covered by definition.method queries. */
|
|
81
|
+
export declare function isCppInsideClassOrStruct(functionNode: SyntaxNode): boolean;
|
|
82
|
+
/** Find the first direct named child of a tree-sitter node matching the given type. */
|
|
83
|
+
export declare function findChild(node: SyntaxNode, type: string): SyntaxNode | null;
|