@zuvia-software-solutions/code-mapper 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +215 -0
- package/dist/cli/ai-context.d.ts +19 -0
- package/dist/cli/ai-context.js +168 -0
- package/dist/cli/analyze.d.ts +7 -0
- package/dist/cli/analyze.js +325 -0
- package/dist/cli/augment.d.ts +7 -0
- package/dist/cli/augment.js +27 -0
- package/dist/cli/clean.d.ts +5 -0
- package/dist/cli/clean.js +56 -0
- package/dist/cli/eval-server.d.ts +25 -0
- package/dist/cli/eval-server.js +365 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +102 -0
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +19 -0
- package/dist/cli/list.d.ts +2 -0
- package/dist/cli/list.js +27 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +35 -0
- package/dist/cli/refresh.d.ts +12 -0
- package/dist/cli/refresh.js +165 -0
- package/dist/cli/serve.d.ts +5 -0
- package/dist/cli/serve.js +8 -0
- package/dist/cli/setup.d.ts +6 -0
- package/dist/cli/setup.js +218 -0
- package/dist/cli/status.d.ts +2 -0
- package/dist/cli/status.js +33 -0
- package/dist/cli/tool.d.ts +28 -0
- package/dist/cli/tool.js +87 -0
- package/dist/config/ignore-service.d.ts +32 -0
- package/dist/config/ignore-service.js +282 -0
- package/dist/config/supported-languages.d.ts +23 -0
- package/dist/config/supported-languages.js +52 -0
- package/dist/core/augmentation/engine.d.ts +22 -0
- package/dist/core/augmentation/engine.js +232 -0
- package/dist/core/embeddings/embedder.d.ts +35 -0
- package/dist/core/embeddings/embedder.js +171 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
- package/dist/core/embeddings/embedding-pipeline.js +402 -0
- package/dist/core/embeddings/index.d.ts +5 -0
- package/dist/core/embeddings/index.js +6 -0
- package/dist/core/embeddings/text-generator.d.ts +20 -0
- package/dist/core/embeddings/text-generator.js +159 -0
- package/dist/core/embeddings/types.d.ts +60 -0
- package/dist/core/embeddings/types.js +23 -0
- package/dist/core/graph/graph.d.ts +4 -0
- package/dist/core/graph/graph.js +65 -0
- package/dist/core/graph/types.d.ts +69 -0
- package/dist/core/graph/types.js +3 -0
- package/dist/core/incremental/child-process.d.ts +8 -0
- package/dist/core/incremental/child-process.js +649 -0
- package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
- package/dist/core/incremental/refresh-coordinator.js +147 -0
- package/dist/core/incremental/types.d.ts +78 -0
- package/dist/core/incremental/types.js +153 -0
- package/dist/core/incremental/watcher.d.ts +63 -0
- package/dist/core/incremental/watcher.js +338 -0
- package/dist/core/ingestion/ast-cache.d.ts +12 -0
- package/dist/core/ingestion/ast-cache.js +34 -0
- package/dist/core/ingestion/call-processor.d.ts +34 -0
- package/dist/core/ingestion/call-processor.js +937 -0
- package/dist/core/ingestion/call-routing.d.ts +40 -0
- package/dist/core/ingestion/call-routing.js +97 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
- package/dist/core/ingestion/cluster-enricher.js +151 -0
- package/dist/core/ingestion/community-processor.d.ts +26 -0
- package/dist/core/ingestion/community-processor.js +272 -0
- package/dist/core/ingestion/constants.d.ts +5 -0
- package/dist/core/ingestion/constants.js +8 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
- package/dist/core/ingestion/entry-point-scoring.js +317 -0
- package/dist/core/ingestion/export-detection.d.ts +11 -0
- package/dist/core/ingestion/export-detection.js +203 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
- package/dist/core/ingestion/filesystem-walker.js +64 -0
- package/dist/core/ingestion/framework-detection.d.ts +42 -0
- package/dist/core/ingestion/framework-detection.js +405 -0
- package/dist/core/ingestion/heritage-processor.d.ts +15 -0
- package/dist/core/ingestion/heritage-processor.js +237 -0
- package/dist/core/ingestion/import-processor.d.ts +31 -0
- package/dist/core/ingestion/import-processor.js +416 -0
- package/dist/core/ingestion/language-config.d.ts +32 -0
- package/dist/core/ingestion/language-config.js +161 -0
- package/dist/core/ingestion/mro-processor.d.ts +32 -0
- package/dist/core/ingestion/mro-processor.js +343 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
- package/dist/core/ingestion/named-binding-extraction.js +343 -0
- package/dist/core/ingestion/parsing-processor.d.ts +20 -0
- package/dist/core/ingestion/parsing-processor.js +282 -0
- package/dist/core/ingestion/pipeline.d.ts +3 -0
- package/dist/core/ingestion/pipeline.js +416 -0
- package/dist/core/ingestion/process-processor.d.ts +42 -0
- package/dist/core/ingestion/process-processor.js +357 -0
- package/dist/core/ingestion/resolution-context.d.ts +40 -0
- package/dist/core/ingestion/resolution-context.js +171 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
- package/dist/core/ingestion/resolvers/csharp.js +101 -0
- package/dist/core/ingestion/resolvers/go.d.ts +8 -0
- package/dist/core/ingestion/resolvers/go.js +33 -0
- package/dist/core/ingestion/resolvers/index.d.ts +14 -0
- package/dist/core/ingestion/resolvers/index.js +10 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
- package/dist/core/ingestion/resolvers/jvm.js +74 -0
- package/dist/core/ingestion/resolvers/php.d.ts +7 -0
- package/dist/core/ingestion/resolvers/php.js +30 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
- package/dist/core/ingestion/resolvers/ruby.js +13 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
- package/dist/core/ingestion/resolvers/rust.js +62 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
- package/dist/core/ingestion/resolvers/standard.js +144 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
- package/dist/core/ingestion/resolvers/utils.js +113 -0
- package/dist/core/ingestion/structure-processor.d.ts +4 -0
- package/dist/core/ingestion/structure-processor.js +39 -0
- package/dist/core/ingestion/symbol-table.d.ts +34 -0
- package/dist/core/ingestion/symbol-table.js +48 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
- package/dist/core/ingestion/tree-sitter-queries.js +691 -0
- package/dist/core/ingestion/type-env.d.ts +52 -0
- package/dist/core/ingestion/type-env.js +349 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/csharp.js +224 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/go.js +261 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
- package/dist/core/ingestion/type-extractors/index.js +30 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/jvm.js +386 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/php.js +280 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/python.js +175 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
- package/dist/core/ingestion/type-extractors/ruby.js +218 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/rust.js +290 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
- package/dist/core/ingestion/type-extractors/shared.js +322 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/swift.js +140 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
- package/dist/core/ingestion/type-extractors/types.js +4 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/typescript.js +227 -0
- package/dist/core/ingestion/utils.d.ts +73 -0
- package/dist/core/ingestion/utils.js +992 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
- package/dist/core/ingestion/workers/parse-worker.js +1055 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
- package/dist/core/ingestion/workers/worker-pool.js +123 -0
- package/dist/core/lbug/csv-generator.d.ts +28 -0
- package/dist/core/lbug/csv-generator.js +355 -0
- package/dist/core/lbug/lbug-adapter.d.ts +96 -0
- package/dist/core/lbug/lbug-adapter.js +753 -0
- package/dist/core/lbug/schema.d.ts +46 -0
- package/dist/core/lbug/schema.js +402 -0
- package/dist/core/search/bm25-index.d.ts +20 -0
- package/dist/core/search/bm25-index.js +123 -0
- package/dist/core/search/hybrid-search.d.ts +32 -0
- package/dist/core/search/hybrid-search.js +131 -0
- package/dist/core/search/query-cache.d.ts +18 -0
- package/dist/core/search/query-cache.js +47 -0
- package/dist/core/search/query-expansion.d.ts +19 -0
- package/dist/core/search/query-expansion.js +75 -0
- package/dist/core/search/reranker.d.ts +29 -0
- package/dist/core/search/reranker.js +122 -0
- package/dist/core/search/types.d.ts +154 -0
- package/dist/core/search/types.js +51 -0
- package/dist/core/semantic/tsgo-service.d.ts +67 -0
- package/dist/core/semantic/tsgo-service.js +355 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
- package/dist/core/tree-sitter/parser-loader.js +71 -0
- package/dist/lib/memory-guard.d.ts +35 -0
- package/dist/lib/memory-guard.js +70 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.js +6 -0
- package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
- package/dist/mcp/compatible-stdio-transport.js +209 -0
- package/dist/mcp/core/embedder.d.ts +24 -0
- package/dist/mcp/core/embedder.js +168 -0
- package/dist/mcp/core/lbug-adapter.d.ts +29 -0
- package/dist/mcp/core/lbug-adapter.js +330 -0
- package/dist/mcp/local/local-backend.d.ts +188 -0
- package/dist/mcp/local/local-backend.js +2759 -0
- package/dist/mcp/resources.d.ts +22 -0
- package/dist/mcp/resources.js +379 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +217 -0
- package/dist/mcp/staleness.d.ts +10 -0
- package/dist/mcp/staleness.js +25 -0
- package/dist/mcp/tools.d.ts +21 -0
- package/dist/mcp/tools.js +202 -0
- package/dist/server/api.d.ts +5 -0
- package/dist/server/api.js +340 -0
- package/dist/server/mcp-http.d.ts +7 -0
- package/dist/server/mcp-http.js +95 -0
- package/dist/storage/git.d.ts +6 -0
- package/dist/storage/git.js +35 -0
- package/dist/storage/repo-manager.d.ts +87 -0
- package/dist/storage/repo-manager.js +249 -0
- package/dist/types/pipeline.d.ts +35 -0
- package/dist/types/pipeline.js +20 -0
- package/hooks/claude/code-mapper-hook.cjs +238 -0
- package/hooks/claude/pre-tool-use.sh +79 -0
- package/hooks/claude/session-start.sh +42 -0
- package/models/mlx-embedder.py +185 -0
- package/package.json +100 -0
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/type-extractors/shared.ts
|
|
2
|
+
/** @file shared.ts
|
|
3
|
+
* @description Shared utilities for type extraction across all languages */
|
|
4
|
+
// Nullable wrapper types that unwrap to their inner type for receiver resolution
|
|
5
|
+
// e.g. Optional<User> -> "User", Option<User> -> "User"
|
|
6
|
+
// Only nullable wrappers -- NOT containers (List, Vec) or async wrappers (Promise, Future)
|
|
7
|
+
// See call-processor.ts WRAPPER_GENERICS for the full set used in return-type inference
|
|
8
|
+
const NULLABLE_WRAPPER_TYPES = new Set([
|
|
9
|
+
'Optional', // Java
|
|
10
|
+
'Option', // Rust, Scala
|
|
11
|
+
'Maybe', // Haskell-style, Kotlin Arrow
|
|
12
|
+
]);
|
|
13
|
+
/**
|
|
14
|
+
* Extract the simple type name from a type AST node
|
|
15
|
+
*
|
|
16
|
+
* Handles generic types (List<User> -> List), qualified names (models.User -> User),
|
|
17
|
+
* and nullable types (User? -> User). Returns undefined for complex types
|
|
18
|
+
*/
|
|
19
|
+
export const extractSimpleTypeName = (typeNode) => {
|
|
20
|
+
// Direct type identifier (includes Ruby 'constant' for class names)
|
|
21
|
+
if (typeNode.type === 'type_identifier' || typeNode.type === 'identifier'
|
|
22
|
+
|| typeNode.type === 'simple_identifier' || typeNode.type === 'constant') {
|
|
23
|
+
return typeNode.text;
|
|
24
|
+
}
|
|
25
|
+
// Qualified/scoped names: take the last segment (e.g. models.User -> User, Models::User -> User)
|
|
26
|
+
if (typeNode.type === 'scoped_identifier' || typeNode.type === 'qualified_identifier'
|
|
27
|
+
|| typeNode.type === 'scoped_type_identifier' || typeNode.type === 'qualified_name'
|
|
28
|
+
|| typeNode.type === 'qualified_type'
|
|
29
|
+
|| typeNode.type === 'member_expression' || typeNode.type === 'member_access_expression'
|
|
30
|
+
|| typeNode.type === 'attribute'
|
|
31
|
+
|| typeNode.type === 'scope_resolution'
|
|
32
|
+
|| typeNode.type === 'selector_expression') {
|
|
33
|
+
const last = typeNode.lastNamedChild;
|
|
34
|
+
if (last && (last.type === 'type_identifier' || last.type === 'identifier'
|
|
35
|
+
|| last.type === 'simple_identifier' || last.type === 'name'
|
|
36
|
+
|| last.type === 'constant' || last.type === 'property_identifier'
|
|
37
|
+
|| last.type === 'field_identifier')) {
|
|
38
|
+
return last.text;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Generic types: extract the base type (e.g. List<User> -> List)
|
|
42
|
+
// For nullable wrappers (Optional<User>, Option<User>), unwrap to inner type
|
|
43
|
+
if (typeNode.type === 'generic_type' || typeNode.type === 'parameterized_type') {
|
|
44
|
+
const base = typeNode.childForFieldName('name')
|
|
45
|
+
?? typeNode.childForFieldName('type')
|
|
46
|
+
?? typeNode.firstNamedChild;
|
|
47
|
+
if (!base)
|
|
48
|
+
return undefined;
|
|
49
|
+
const baseName = extractSimpleTypeName(base);
|
|
50
|
+
// Unwrap known nullable wrappers: Optional<User> -> User, Option<User> -> User
|
|
51
|
+
if (baseName && NULLABLE_WRAPPER_TYPES.has(baseName)) {
|
|
52
|
+
const args = extractGenericTypeArgs(typeNode);
|
|
53
|
+
if (args.length >= 1)
|
|
54
|
+
return args[0];
|
|
55
|
+
}
|
|
56
|
+
return baseName;
|
|
57
|
+
}
|
|
58
|
+
// Nullable types (Kotlin User?, C# User?)
|
|
59
|
+
if (typeNode.type === 'nullable_type') {
|
|
60
|
+
const inner = typeNode.firstNamedChild;
|
|
61
|
+
if (inner)
|
|
62
|
+
return extractSimpleTypeName(inner);
|
|
63
|
+
}
|
|
64
|
+
// Nullable union types (TS/JS: User | null, User | undefined, User | null | undefined)
|
|
65
|
+
// Extract the single non-null/undefined type from the union
|
|
66
|
+
if (typeNode.type === 'union_type') {
|
|
67
|
+
const nonNullTypes = [];
|
|
68
|
+
for (let i = 0; i < typeNode.namedChildCount; i++) {
|
|
69
|
+
const child = typeNode.namedChild(i);
|
|
70
|
+
if (!child)
|
|
71
|
+
continue;
|
|
72
|
+
// Skip null/undefined/void literal types
|
|
73
|
+
const text = child.text;
|
|
74
|
+
if (text === 'null' || text === 'undefined' || text === 'void')
|
|
75
|
+
continue;
|
|
76
|
+
nonNullTypes.push(child);
|
|
77
|
+
}
|
|
78
|
+
// Only unwrap if exactly one meaningful type remains
|
|
79
|
+
if (nonNullTypes.length === 1) {
|
|
80
|
+
return extractSimpleTypeName(nonNullTypes[0]);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Type annotations that wrap the actual type (TS/Python: `: Foo`, Kotlin: user_type)
|
|
84
|
+
if (typeNode.type === 'type_annotation' || typeNode.type === 'type'
|
|
85
|
+
|| typeNode.type === 'user_type') {
|
|
86
|
+
const inner = typeNode.firstNamedChild;
|
|
87
|
+
if (inner)
|
|
88
|
+
return extractSimpleTypeName(inner);
|
|
89
|
+
}
|
|
90
|
+
// Pointer/reference types (C++, Rust): User*, &User, &mut User
|
|
91
|
+
if (typeNode.type === 'pointer_type' || typeNode.type === 'reference_type') {
|
|
92
|
+
const inner = typeNode.firstNamedChild;
|
|
93
|
+
if (inner)
|
|
94
|
+
return extractSimpleTypeName(inner);
|
|
95
|
+
}
|
|
96
|
+
// PHP primitive_type (string, int, float, bool)
|
|
97
|
+
if (typeNode.type === 'primitive_type') {
|
|
98
|
+
return typeNode.text;
|
|
99
|
+
}
|
|
100
|
+
// PHP named_type / optional_type
|
|
101
|
+
if (typeNode.type === 'named_type' || typeNode.type === 'optional_type') {
|
|
102
|
+
const inner = typeNode.childForFieldName('name') ?? typeNode.firstNamedChild;
|
|
103
|
+
if (inner)
|
|
104
|
+
return extractSimpleTypeName(inner);
|
|
105
|
+
}
|
|
106
|
+
// Name node (PHP)
|
|
107
|
+
if (typeNode.type === 'name') {
|
|
108
|
+
return typeNode.text;
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Extract variable name from a declarator or pattern node
|
|
114
|
+
*
|
|
115
|
+
* Returns the simple identifier text, or undefined for destructuring/complex patterns
|
|
116
|
+
*/
|
|
117
|
+
export const extractVarName = (node) => {
|
|
118
|
+
if (node.type === 'identifier' || node.type === 'simple_identifier'
|
|
119
|
+
|| node.type === 'variable_name' || node.type === 'name'
|
|
120
|
+
|| node.type === 'constant') {
|
|
121
|
+
return node.text;
|
|
122
|
+
}
|
|
123
|
+
// variable_declarator (Java/C#): has a 'name' field
|
|
124
|
+
if (node.type === 'variable_declarator') {
|
|
125
|
+
const nameChild = node.childForFieldName('name');
|
|
126
|
+
if (nameChild)
|
|
127
|
+
return extractVarName(nameChild);
|
|
128
|
+
}
|
|
129
|
+
// Rust: let mut x = ... -- mut_pattern wraps an identifier
|
|
130
|
+
if (node.type === 'mut_pattern') {
|
|
131
|
+
const inner = node.firstNamedChild;
|
|
132
|
+
if (inner)
|
|
133
|
+
return extractVarName(inner);
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
};
|
|
137
|
+
/** Node types for function/method parameters with type annotations */
|
|
138
|
+
export const TYPED_PARAMETER_TYPES = new Set([
|
|
139
|
+
'required_parameter', // TS: (x: Foo)
|
|
140
|
+
'optional_parameter', // TS: (x?: Foo)
|
|
141
|
+
'formal_parameter', // Java/Kotlin
|
|
142
|
+
'parameter', // C#/Rust/Go/Python/Swift
|
|
143
|
+
'parameter_declaration', // C/C++ void f(Type name)
|
|
144
|
+
'simple_parameter', // PHP function(Foo $x)
|
|
145
|
+
'property_promotion_parameter', // PHP 8.0+ constructor promotion: __construct(private Foo $x)
|
|
146
|
+
]);
|
|
147
|
+
/**
|
|
148
|
+
* Extract type arguments from a generic type node
|
|
149
|
+
*
|
|
150
|
+
* e.g. List<User, String> -> ['User', 'String'], Vec<User> -> ['User']
|
|
151
|
+
* Used by extractSimpleTypeName to unwrap nullable wrappers (Optional<User> -> User)
|
|
152
|
+
*
|
|
153
|
+
* Handles language-specific AST structures:
|
|
154
|
+
* - TS/Java/Rust/Go: generic_type > type_arguments > type nodes
|
|
155
|
+
* - C#: generic_type > type_argument_list > type nodes
|
|
156
|
+
* - Kotlin: generic_type > type_arguments > type_projection > type nodes
|
|
157
|
+
*
|
|
158
|
+
* Note: Go slices/maps use slice_type/map_type, not generic_type -- those are
|
|
159
|
+
* NOT handled here; use language-specific extractors for Go container types
|
|
160
|
+
*
|
|
161
|
+
* @param typeNode A generic_type or parameterized_type AST node (returns [] for non-generic types)
|
|
162
|
+
* @returns Array of resolved type argument names (unresolvable arguments are omitted)
|
|
163
|
+
*/
|
|
164
|
+
export const extractGenericTypeArgs = (typeNode) => {
|
|
165
|
+
// Unwrap wrapper nodes that may sit above the generic_type
|
|
166
|
+
if (typeNode.type === 'type_annotation' || typeNode.type === 'type'
|
|
167
|
+
|| typeNode.type === 'user_type' || typeNode.type === 'nullable_type'
|
|
168
|
+
|| typeNode.type === 'optional_type') {
|
|
169
|
+
const inner = typeNode.firstNamedChild;
|
|
170
|
+
if (inner)
|
|
171
|
+
return extractGenericTypeArgs(inner);
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
// Only process generic/parameterized type nodes
|
|
175
|
+
if (typeNode.type !== 'generic_type' && typeNode.type !== 'parameterized_type') {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
// Find the type_arguments / type_argument_list child
|
|
179
|
+
let argsNode = null;
|
|
180
|
+
for (let i = 0; i < typeNode.namedChildCount; i++) {
|
|
181
|
+
const child = typeNode.namedChild(i);
|
|
182
|
+
if (child && (child.type === 'type_arguments' || child.type === 'type_argument_list')) {
|
|
183
|
+
argsNode = child;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!argsNode)
|
|
188
|
+
return [];
|
|
189
|
+
const result = [];
|
|
190
|
+
for (let i = 0; i < argsNode.namedChildCount; i++) {
|
|
191
|
+
let argNode = argsNode.namedChild(i);
|
|
192
|
+
if (!argNode)
|
|
193
|
+
continue;
|
|
194
|
+
// Kotlin: type_arguments > type_projection > user_type > type_identifier
|
|
195
|
+
if (argNode.type === 'type_projection') {
|
|
196
|
+
argNode = argNode.firstNamedChild;
|
|
197
|
+
if (!argNode)
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const name = extractSimpleTypeName(argNode);
|
|
201
|
+
if (name)
|
|
202
|
+
result.push(name);
|
|
203
|
+
}
|
|
204
|
+
return result;
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Match Ruby constructor assignment: `user = User.new` or `service = Models::User.new`
|
|
208
|
+
*
|
|
209
|
+
* Handles both simple constants and scope_resolution (namespaced) receivers
|
|
210
|
+
* @returns `{ varName, calleeName }` or undefined if not a Ruby constructor assignment
|
|
211
|
+
*/
|
|
212
|
+
export const extractRubyConstructorAssignment = (node) => {
|
|
213
|
+
if (node.type !== 'assignment')
|
|
214
|
+
return undefined;
|
|
215
|
+
const left = node.childForFieldName('left');
|
|
216
|
+
const right = node.childForFieldName('right');
|
|
217
|
+
if (!left || !right)
|
|
218
|
+
return undefined;
|
|
219
|
+
if (left.type !== 'identifier' && left.type !== 'constant')
|
|
220
|
+
return undefined;
|
|
221
|
+
if (right.type !== 'call')
|
|
222
|
+
return undefined;
|
|
223
|
+
const method = right.childForFieldName('method');
|
|
224
|
+
if (!method || method.text !== 'new')
|
|
225
|
+
return undefined;
|
|
226
|
+
const receiver = right.childForFieldName('receiver');
|
|
227
|
+
if (!receiver)
|
|
228
|
+
return undefined;
|
|
229
|
+
let calleeName;
|
|
230
|
+
if (receiver.type === 'constant') {
|
|
231
|
+
calleeName = receiver.text;
|
|
232
|
+
}
|
|
233
|
+
else if (receiver.type === 'scope_resolution') {
|
|
234
|
+
// Models::User -> extract last segment "User"
|
|
235
|
+
const last = receiver.lastNamedChild;
|
|
236
|
+
if (!last || last.type !== 'constant')
|
|
237
|
+
return undefined;
|
|
238
|
+
calleeName = last.text;
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
return { varName: left.text, calleeName };
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* Check if an AST node has an explicit type annotation
|
|
247
|
+
*
|
|
248
|
+
* Checks both named fields ('type') and child nodes ('type_annotation')
|
|
249
|
+
* Used by constructor binding scanners to skip annotated declarations
|
|
250
|
+
*/
|
|
251
|
+
export const hasTypeAnnotation = (node) => {
|
|
252
|
+
if (node.childForFieldName('type'))
|
|
253
|
+
return true;
|
|
254
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
255
|
+
if (node.child(i)?.type === 'type_annotation')
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
};
|
|
260
|
+
// Bare nullable keywords that should not produce a receiver binding
|
|
261
|
+
const NULLABLE_KEYWORDS = new Set(['null', 'undefined', 'void', 'None', 'nil']);
|
|
262
|
+
/**
|
|
263
|
+
* Strip nullable wrappers from a type name string
|
|
264
|
+
*
|
|
265
|
+
* Used by both lookupInEnv (TypeEnv annotations) and extractReturnTypeName
|
|
266
|
+
* (return-type text) to normalize types before receiver lookup
|
|
267
|
+
*
|
|
268
|
+
* "User | null" -> "User"
|
|
269
|
+
* "User | undefined" -> "User"
|
|
270
|
+
* "User | null | undefined" -> "User"
|
|
271
|
+
* "User?" -> "User"
|
|
272
|
+
* "User | Repo" -> undefined (genuine union -- refuse)
|
|
273
|
+
* "null" -> undefined
|
|
274
|
+
*/
|
|
275
|
+
export const stripNullable = (typeName) => {
|
|
276
|
+
let text = typeName.trim();
|
|
277
|
+
if (!text)
|
|
278
|
+
return undefined;
|
|
279
|
+
if (NULLABLE_KEYWORDS.has(text))
|
|
280
|
+
return undefined;
|
|
281
|
+
// Strip nullable suffix: User? -> User
|
|
282
|
+
if (text.endsWith('?'))
|
|
283
|
+
text = text.slice(0, -1).trim();
|
|
284
|
+
// Strip union with null/undefined/None/nil/void
|
|
285
|
+
if (text.includes('|')) {
|
|
286
|
+
const parts = text.split('|').map(p => p.trim()).filter(p => p !== '' && !NULLABLE_KEYWORDS.has(p));
|
|
287
|
+
if (parts.length === 1)
|
|
288
|
+
return parts[0];
|
|
289
|
+
return undefined; // genuine union or all-nullable -- refuse
|
|
290
|
+
}
|
|
291
|
+
return text || undefined;
|
|
292
|
+
};
|
|
293
|
+
/**
|
|
294
|
+
* Unwrap an await_expression to get the inner value
|
|
295
|
+
*
|
|
296
|
+
* Returns the node itself if not an await_expression, or null if input is null
|
|
297
|
+
*/
|
|
298
|
+
export const unwrapAwait = (node) => {
|
|
299
|
+
if (!node)
|
|
300
|
+
return null;
|
|
301
|
+
return node.type === 'await_expression' ? node.firstNamedChild : node;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Extract the callee name from a call_expression node
|
|
305
|
+
*
|
|
306
|
+
* Navigates to the 'function' field (or first named child) and extracts a simple type name
|
|
307
|
+
*/
|
|
308
|
+
export const extractCalleeName = (callNode) => {
|
|
309
|
+
const func = callNode.childForFieldName('function') ?? callNode.firstNamedChild;
|
|
310
|
+
if (!func)
|
|
311
|
+
return undefined;
|
|
312
|
+
return extractSimpleTypeName(func);
|
|
313
|
+
};
|
|
314
|
+
/** Find the first named child with the given node type */
|
|
315
|
+
export const findChildByType = (node, type) => {
|
|
316
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
317
|
+
const child = node.namedChild(i);
|
|
318
|
+
if (child?.type === type)
|
|
319
|
+
return child;
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/type-extractors/swift.ts
|
|
2
|
+
/** @file swift.ts
|
|
3
|
+
* @description Type extraction for Swift (property declarations, initializer inference) */
|
|
4
|
+
import { extractSimpleTypeName, extractVarName, findChildByType, hasTypeAnnotation } from './shared.js';
|
|
5
|
+
const DECLARATION_NODE_TYPES = new Set([
|
|
6
|
+
'property_declaration',
|
|
7
|
+
]);
|
|
8
|
+
// Swift: let x: Foo = ...
|
|
9
|
+
const extractDeclaration = (node, env) => {
|
|
10
|
+
// Swift property_declaration has pattern and type_annotation
|
|
11
|
+
const pattern = node.childForFieldName('pattern')
|
|
12
|
+
?? findChildByType(node, 'pattern');
|
|
13
|
+
const typeAnnotation = node.childForFieldName('type')
|
|
14
|
+
?? findChildByType(node, 'type_annotation');
|
|
15
|
+
if (!pattern || !typeAnnotation)
|
|
16
|
+
return;
|
|
17
|
+
const varName = extractVarName(pattern) ?? pattern.text;
|
|
18
|
+
const typeName = extractSimpleTypeName(typeAnnotation);
|
|
19
|
+
if (varName && typeName)
|
|
20
|
+
env.set(varName, typeName);
|
|
21
|
+
};
|
|
22
|
+
// Swift: parameter -> name: type
|
|
23
|
+
const extractParameter = (node, env) => {
|
|
24
|
+
let nameNode = null;
|
|
25
|
+
let typeNode = null;
|
|
26
|
+
if (node.type === 'parameter') {
|
|
27
|
+
nameNode = node.childForFieldName('name')
|
|
28
|
+
?? node.childForFieldName('internal_name');
|
|
29
|
+
typeNode = node.childForFieldName('type');
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
|
|
33
|
+
typeNode = node.childForFieldName('type');
|
|
34
|
+
}
|
|
35
|
+
if (!nameNode || !typeNode)
|
|
36
|
+
return;
|
|
37
|
+
const varName = extractVarName(nameNode);
|
|
38
|
+
const typeName = extractSimpleTypeName(typeNode);
|
|
39
|
+
if (varName && typeName)
|
|
40
|
+
env.set(varName, typeName);
|
|
41
|
+
};
|
|
42
|
+
// Swift: let user = User(name: "alice") -- infer type from call when callee is a known class
|
|
43
|
+
// Swift initializers are syntactically identical to function calls, so we verify
|
|
44
|
+
// against classNames (which may include cross-file SymbolTable lookups)
|
|
45
|
+
const extractInitializer = (node, env, classNames) => {
|
|
46
|
+
if (node.type !== 'property_declaration')
|
|
47
|
+
return;
|
|
48
|
+
// Skip if has type annotation -- extractDeclaration handled it
|
|
49
|
+
if (node.childForFieldName('type') || findChildByType(node, 'type_annotation'))
|
|
50
|
+
return;
|
|
51
|
+
// Find pattern (variable name)
|
|
52
|
+
const pattern = node.childForFieldName('pattern') ?? findChildByType(node, 'pattern');
|
|
53
|
+
if (!pattern)
|
|
54
|
+
return;
|
|
55
|
+
const varName = extractVarName(pattern) ?? pattern.text;
|
|
56
|
+
if (!varName || env.has(varName))
|
|
57
|
+
return;
|
|
58
|
+
// Find call_expression in the value
|
|
59
|
+
const callExpr = findChildByType(node, 'call_expression');
|
|
60
|
+
if (!callExpr)
|
|
61
|
+
return;
|
|
62
|
+
const callee = callExpr.firstNamedChild;
|
|
63
|
+
if (!callee)
|
|
64
|
+
return;
|
|
65
|
+
// Direct call: User(name: "alice")
|
|
66
|
+
if (callee.type === 'simple_identifier') {
|
|
67
|
+
const calleeName = callee.text;
|
|
68
|
+
if (calleeName && classNames.has(calleeName)) {
|
|
69
|
+
env.set(varName, calleeName);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Explicit init: User.init(name: "alice") -- navigation_expression with .init suffix
|
|
74
|
+
if (callee.type === 'navigation_expression') {
|
|
75
|
+
const receiver = callee.firstNamedChild;
|
|
76
|
+
const suffix = callee.lastNamedChild;
|
|
77
|
+
if (receiver?.type === 'simple_identifier' && suffix?.text === 'init') {
|
|
78
|
+
const calleeName = receiver.text;
|
|
79
|
+
if (calleeName && classNames.has(calleeName)) {
|
|
80
|
+
env.set(varName, calleeName);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
// Swift: let user = User(name: "alice") -- scan property_declaration for constructor binding
|
|
86
|
+
const scanConstructorBinding = (node) => {
|
|
87
|
+
if (node.type !== 'property_declaration')
|
|
88
|
+
return undefined;
|
|
89
|
+
if (hasTypeAnnotation(node))
|
|
90
|
+
return undefined;
|
|
91
|
+
const pattern = node.childForFieldName('pattern');
|
|
92
|
+
if (!pattern)
|
|
93
|
+
return undefined;
|
|
94
|
+
const varName = pattern.text;
|
|
95
|
+
if (!varName)
|
|
96
|
+
return undefined;
|
|
97
|
+
let callExpr = null;
|
|
98
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
99
|
+
const child = node.namedChild(i);
|
|
100
|
+
if (child?.type === 'call_expression') {
|
|
101
|
+
callExpr = child;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!callExpr)
|
|
106
|
+
return undefined;
|
|
107
|
+
const callee = callExpr.firstNamedChild;
|
|
108
|
+
if (!callee)
|
|
109
|
+
return undefined;
|
|
110
|
+
if (callee.type === 'simple_identifier') {
|
|
111
|
+
return { varName, calleeName: callee.text };
|
|
112
|
+
}
|
|
113
|
+
if (callee.type === 'navigation_expression') {
|
|
114
|
+
const receiver = callee.firstNamedChild;
|
|
115
|
+
const suffix = callee.lastNamedChild;
|
|
116
|
+
if (receiver?.type === 'simple_identifier' && suffix?.text === 'init') {
|
|
117
|
+
return { varName, calleeName: receiver.text };
|
|
118
|
+
}
|
|
119
|
+
// General qualified call: service.getUser() -> extract method name
|
|
120
|
+
// tree-sitter-swift may wrap the identifier in navigation_suffix, so
|
|
121
|
+
// check both direct simple_identifier and navigation_suffix > simple_identifier
|
|
122
|
+
if (suffix?.type === 'simple_identifier') {
|
|
123
|
+
return { varName, calleeName: suffix.text };
|
|
124
|
+
}
|
|
125
|
+
if (suffix?.type === 'navigation_suffix') {
|
|
126
|
+
const inner = suffix.lastNamedChild;
|
|
127
|
+
if (inner?.type === 'simple_identifier') {
|
|
128
|
+
return { varName, calleeName: inner.text };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
};
|
|
134
|
+
export const typeConfig = {
|
|
135
|
+
declarationNodeTypes: DECLARATION_NODE_TYPES,
|
|
136
|
+
extractDeclaration,
|
|
137
|
+
extractParameter,
|
|
138
|
+
extractInitializer,
|
|
139
|
+
scanConstructorBinding,
|
|
140
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/** @file types.ts
|
|
2
|
+
* @description Type definitions for per-language type extraction used by TypeEnv */
|
|
3
|
+
import type { SyntaxNode } from '../utils.js';
|
|
4
|
+
/** Extracts type bindings from a declaration node into the env map */
|
|
5
|
+
export type TypeBindingExtractor = (node: SyntaxNode, env: Map<string, string>) => void;
|
|
6
|
+
/** Extracts type bindings from a parameter node into the env map */
|
|
7
|
+
export type ParameterExtractor = (node: SyntaxNode, env: Map<string, string>) => void;
|
|
8
|
+
/** Minimal interface for checking whether a name is a known class/struct -- only `.has()` is needed */
|
|
9
|
+
export type ClassNameLookup = {
|
|
10
|
+
has(name: string): boolean;
|
|
11
|
+
};
|
|
12
|
+
/** Extracts type bindings from a constructor-call initializer, with access to known class names */
|
|
13
|
+
export type InitializerExtractor = (node: SyntaxNode, env: Map<string, string>, classNames: ClassNameLookup) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Scans an AST node for untyped `var = callee()` patterns for return-type inference
|
|
16
|
+
* @returns `{ varName, calleeName }` if matched, undefined otherwise
|
|
17
|
+
* `receiverClassName` is an optional hint for method calls on known receivers
|
|
18
|
+
* (e.g. `$this->getUser()` in PHP provides the enclosing class name)
|
|
19
|
+
*/
|
|
20
|
+
export type ConstructorBindingScanner = (node: SyntaxNode) => {
|
|
21
|
+
varName: string;
|
|
22
|
+
calleeName: string;
|
|
23
|
+
receiverClassName?: string;
|
|
24
|
+
} | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Extracts a return type string from a method/function definition node
|
|
27
|
+
*
|
|
28
|
+
* Used for languages where return types live in comments (e.g. YARD `@return [Type]`)
|
|
29
|
+
* rather than AST fields. Returns undefined if no return type can be determined
|
|
30
|
+
*/
|
|
31
|
+
export type ReturnTypeExtractor = (node: SyntaxNode) => string | undefined;
|
|
32
|
+
/** Extracts loop variable type binding from a for-each statement */
|
|
33
|
+
export type ForLoopExtractor = (node: SyntaxNode, scopeEnv: Map<string, string>) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Extracts a plain-identifier assignment for Tier 2 propagation
|
|
36
|
+
*
|
|
37
|
+
* For `const b = a`, returns `{ lhs: 'b', rhs: 'a' }` when the LHS has no resolved type
|
|
38
|
+
* Returns undefined if the node is not a plain identifier assignment
|
|
39
|
+
*/
|
|
40
|
+
export type PendingAssignmentExtractor = (node: SyntaxNode, scopeEnv: ReadonlyMap<string, string>) => {
|
|
41
|
+
lhs: string;
|
|
42
|
+
rhs: string;
|
|
43
|
+
} | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Extracts a typed variable binding from a pattern-matching construct
|
|
46
|
+
*
|
|
47
|
+
* Returns `{ varName, typeName }` for patterns that introduce new variables
|
|
48
|
+
* Examples: `if let Some(user) = opt` (Rust), `x instanceof User user` (Java)
|
|
49
|
+
* Conservative: returns undefined when the source variable's type is unknown
|
|
50
|
+
*
|
|
51
|
+
* @param scopeEnv Read-only view of already-resolved type bindings in the current scope
|
|
52
|
+
* @param declarationTypeNodes Maps `scope\0varName` to the original declaration's type
|
|
53
|
+
* annotation AST node, allowing generic type argument extraction (e.g. T from Result<T,E>)
|
|
54
|
+
* that is stripped during normal TypeEnv extraction
|
|
55
|
+
* @param scope Current scope key (e.g. `"process@42"`) for declarationTypeNodes lookups
|
|
56
|
+
*/
|
|
57
|
+
export type PatternBindingExtractor = (node: SyntaxNode, scopeEnv: ReadonlyMap<string, string>, declarationTypeNodes: ReadonlyMap<string, SyntaxNode>, scope: string) => {
|
|
58
|
+
varName: string;
|
|
59
|
+
typeName: string;
|
|
60
|
+
} | undefined;
|
|
61
|
+
/** Per-language type extraction configuration */
|
|
62
|
+
export interface LanguageTypeConfig {
|
|
63
|
+
/** Node types that represent typed declarations for this language */
|
|
64
|
+
declarationNodeTypes: ReadonlySet<string>;
|
|
65
|
+
/** AST node types for for-each/for-in statements with explicit element types */
|
|
66
|
+
forLoopNodeTypes?: ReadonlySet<string>;
|
|
67
|
+
/** Extract a (varName -> typeName) binding from a declaration node */
|
|
68
|
+
extractDeclaration: TypeBindingExtractor;
|
|
69
|
+
/** Extract a (varName -> typeName) binding from a parameter node */
|
|
70
|
+
extractParameter: ParameterExtractor;
|
|
71
|
+
/**
|
|
72
|
+
* Extract a (varName -> typeName) binding from a constructor-call initializer
|
|
73
|
+
*
|
|
74
|
+
* Called as fallback when extractDeclaration produces no binding for a declaration node
|
|
75
|
+
* Only for languages with syntactic constructor markers (new, composite_literal, ::new)
|
|
76
|
+
* Receives classNames -- the set of class/struct names visible in the current file's AST
|
|
77
|
+
*/
|
|
78
|
+
extractInitializer?: InitializerExtractor;
|
|
79
|
+
/**
|
|
80
|
+
* Scan for untyped `var = callee()` assignments for return-type inference
|
|
81
|
+
*
|
|
82
|
+
* Called on every AST node during buildTypeEnv walk; returns undefined for non-matches
|
|
83
|
+
* The callee binding is unverified -- the caller must confirm against the SymbolTable
|
|
84
|
+
*/
|
|
85
|
+
scanConstructorBinding?: ConstructorBindingScanner;
|
|
86
|
+
/**
|
|
87
|
+
* Extract return type from comment-based annotations (e.g. YARD `@return [Type]`)
|
|
88
|
+
*
|
|
89
|
+
* Called as fallback when extractMethodSignature finds no AST-based return type
|
|
90
|
+
*/
|
|
91
|
+
extractReturnType?: ReturnTypeExtractor;
|
|
92
|
+
/** Extract loop variable -> type binding from a for-each AST node */
|
|
93
|
+
extractForLoopBinding?: ForLoopExtractor;
|
|
94
|
+
/**
|
|
95
|
+
* Extract plain-identifier assignment (e.g. `const b = a`) for Tier 2 chain propagation
|
|
96
|
+
*
|
|
97
|
+
* Called on declaration/assignment nodes; returns {lhs, rhs} when the RHS is a bare
|
|
98
|
+
* identifier and the LHS has no resolved type yet. Language-specific because AST shapes
|
|
99
|
+
* differ widely
|
|
100
|
+
*/
|
|
101
|
+
extractPendingAssignment?: PendingAssignmentExtractor;
|
|
102
|
+
/**
|
|
103
|
+
* Extract a typed variable binding from a pattern-matching construct
|
|
104
|
+
*
|
|
105
|
+
* Called on every AST node; returns { varName, typeName } when the node introduces a new
|
|
106
|
+
* typed variable via pattern matching (e.g. `if let Some(x) = opt`, `x instanceof T t`)
|
|
107
|
+
* Receives the current scope's resolved bindings (read-only) to look up the source
|
|
108
|
+
* variable's type. Returns undefined for non-matching nodes or unknown source types
|
|
109
|
+
*/
|
|
110
|
+
extractPatternBinding?: PatternBindingExtractor;
|
|
111
|
+
}
|