gitnexus 1.4.6 → 1.4.7
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/dist/core/graph/types.d.ts +2 -2
- package/dist/core/ingestion/call-processor.d.ts +7 -1
- package/dist/core/ingestion/call-processor.js +308 -104
- package/dist/core/ingestion/call-routing.d.ts +17 -2
- package/dist/core/ingestion/call-routing.js +21 -0
- package/dist/core/ingestion/parsing-processor.d.ts +2 -1
- package/dist/core/ingestion/parsing-processor.js +32 -6
- package/dist/core/ingestion/pipeline.js +5 -1
- package/dist/core/ingestion/symbol-table.d.ts +13 -3
- package/dist/core/ingestion/symbol-table.js +23 -4
- package/dist/core/ingestion/tree-sitter-queries.d.ts +12 -12
- package/dist/core/ingestion/tree-sitter-queries.js +200 -0
- package/dist/core/ingestion/type-env.js +94 -38
- package/dist/core/ingestion/type-extractors/c-cpp.js +27 -2
- package/dist/core/ingestion/type-extractors/csharp.js +40 -0
- package/dist/core/ingestion/type-extractors/go.js +45 -0
- package/dist/core/ingestion/type-extractors/jvm.js +75 -3
- package/dist/core/ingestion/type-extractors/php.js +31 -4
- package/dist/core/ingestion/type-extractors/python.js +89 -17
- package/dist/core/ingestion/type-extractors/ruby.js +17 -2
- package/dist/core/ingestion/type-extractors/rust.js +37 -3
- package/dist/core/ingestion/type-extractors/shared.d.ts +12 -0
- package/dist/core/ingestion/type-extractors/shared.js +110 -3
- package/dist/core/ingestion/type-extractors/types.d.ts +17 -4
- package/dist/core/ingestion/type-extractors/typescript.js +30 -0
- package/dist/core/ingestion/utils.d.ts +25 -0
- package/dist/core/ingestion/utils.js +160 -1
- package/dist/core/ingestion/workers/parse-worker.d.ts +23 -7
- package/dist/core/ingestion/workers/parse-worker.js +68 -26
- package/dist/core/lbug/lbug-adapter.d.ts +2 -0
- package/dist/core/lbug/lbug-adapter.js +2 -0
- package/dist/core/lbug/schema.d.ts +1 -1
- package/dist/core/lbug/schema.js +1 -1
- package/dist/mcp/core/lbug-adapter.d.ts +22 -0
- package/dist/mcp/core/lbug-adapter.js +167 -23
- package/dist/mcp/local/local-backend.js +3 -3
- package/dist/mcp/resources.js +11 -0
- package/dist/mcp/server.js +26 -4
- package/dist/mcp/tools.js +15 -5
- package/package.json +4 -4
|
@@ -60,6 +60,19 @@ export const TYPESCRIPT_QUERIES = `
|
|
|
60
60
|
(new_expression
|
|
61
61
|
constructor: (identifier) @call.name) @call
|
|
62
62
|
|
|
63
|
+
; Class properties — public_field_definition covers most TS class fields
|
|
64
|
+
(public_field_definition
|
|
65
|
+
name: (property_identifier) @name) @definition.property
|
|
66
|
+
|
|
67
|
+
; Private class fields: #address: Address
|
|
68
|
+
(public_field_definition
|
|
69
|
+
name: (private_property_identifier) @name) @definition.property
|
|
70
|
+
|
|
71
|
+
; Constructor parameter properties: constructor(public address: Address)
|
|
72
|
+
(required_parameter
|
|
73
|
+
(accessibility_modifier)
|
|
74
|
+
pattern: (identifier) @name) @definition.property
|
|
75
|
+
|
|
63
76
|
; Heritage queries - class extends
|
|
64
77
|
(class_declaration
|
|
65
78
|
name: (type_identifier) @heritage.class
|
|
@@ -73,6 +86,20 @@ export const TYPESCRIPT_QUERIES = `
|
|
|
73
86
|
(class_heritage
|
|
74
87
|
(implements_clause
|
|
75
88
|
(type_identifier) @heritage.implements))) @heritage.impl
|
|
89
|
+
|
|
90
|
+
; Write access: obj.field = value
|
|
91
|
+
(assignment_expression
|
|
92
|
+
left: (member_expression
|
|
93
|
+
object: (_) @assignment.receiver
|
|
94
|
+
property: (property_identifier) @assignment.property)
|
|
95
|
+
right: (_)) @assignment
|
|
96
|
+
|
|
97
|
+
; Write access: obj.field += value (compound assignment)
|
|
98
|
+
(augmented_assignment_expression
|
|
99
|
+
left: (member_expression
|
|
100
|
+
object: (_) @assignment.receiver
|
|
101
|
+
property: (property_identifier) @assignment.property)
|
|
102
|
+
right: (_)) @assignment
|
|
76
103
|
`;
|
|
77
104
|
// JavaScript queries - works with tree-sitter-javascript
|
|
78
105
|
export const JAVASCRIPT_QUERIES = `
|
|
@@ -125,12 +152,30 @@ export const JAVASCRIPT_QUERIES = `
|
|
|
125
152
|
(new_expression
|
|
126
153
|
constructor: (identifier) @call.name) @call
|
|
127
154
|
|
|
155
|
+
; Class fields — field_definition captures JS class fields (class User { address = ... })
|
|
156
|
+
(field_definition
|
|
157
|
+
property: (property_identifier) @name) @definition.property
|
|
158
|
+
|
|
128
159
|
; Heritage queries - class extends (JavaScript uses different AST than TypeScript)
|
|
129
160
|
; In tree-sitter-javascript, class_heritage directly contains the parent identifier
|
|
130
161
|
(class_declaration
|
|
131
162
|
name: (identifier) @heritage.class
|
|
132
163
|
(class_heritage
|
|
133
164
|
(identifier) @heritage.extends)) @heritage
|
|
165
|
+
|
|
166
|
+
; Write access: obj.field = value
|
|
167
|
+
(assignment_expression
|
|
168
|
+
left: (member_expression
|
|
169
|
+
object: (_) @assignment.receiver
|
|
170
|
+
property: (property_identifier) @assignment.property)
|
|
171
|
+
right: (_)) @assignment
|
|
172
|
+
|
|
173
|
+
; Write access: obj.field += value (compound assignment)
|
|
174
|
+
(augmented_assignment_expression
|
|
175
|
+
left: (member_expression
|
|
176
|
+
object: (_) @assignment.receiver
|
|
177
|
+
property: (property_identifier) @assignment.property)
|
|
178
|
+
right: (_)) @assignment
|
|
134
179
|
`;
|
|
135
180
|
// Python queries - works with tree-sitter-python
|
|
136
181
|
export const PYTHON_QUERIES = `
|
|
@@ -156,11 +201,33 @@ export const PYTHON_QUERIES = `
|
|
|
156
201
|
function: (attribute
|
|
157
202
|
attribute: (identifier) @call.name)) @call
|
|
158
203
|
|
|
204
|
+
; Class attribute type annotations — PEP 526: address: Address or address: Address = Address()
|
|
205
|
+
; Both bare annotations (address: Address) and annotated assignments (name: str = "test")
|
|
206
|
+
; are parsed as (assignment left: ... type: ...) in tree-sitter-python.
|
|
207
|
+
(expression_statement
|
|
208
|
+
(assignment
|
|
209
|
+
left: (identifier) @name
|
|
210
|
+
type: (type)) @definition.property)
|
|
211
|
+
|
|
159
212
|
; Heritage queries - Python class inheritance
|
|
160
213
|
(class_definition
|
|
161
214
|
name: (identifier) @heritage.class
|
|
162
215
|
superclasses: (argument_list
|
|
163
216
|
(identifier) @heritage.extends)) @heritage
|
|
217
|
+
|
|
218
|
+
; Write access: obj.field = value
|
|
219
|
+
(assignment
|
|
220
|
+
left: (attribute
|
|
221
|
+
object: (_) @assignment.receiver
|
|
222
|
+
attribute: (identifier) @assignment.property)
|
|
223
|
+
right: (_)) @assignment
|
|
224
|
+
|
|
225
|
+
; Write access: obj.field += value (compound assignment)
|
|
226
|
+
(augmented_assignment
|
|
227
|
+
left: (attribute
|
|
228
|
+
object: (_) @assignment.receiver
|
|
229
|
+
attribute: (identifier) @assignment.property)
|
|
230
|
+
right: (_)) @assignment
|
|
164
231
|
`;
|
|
165
232
|
// Java queries - works with tree-sitter-java
|
|
166
233
|
export const JAVA_QUERIES = `
|
|
@@ -174,6 +241,11 @@ export const JAVA_QUERIES = `
|
|
|
174
241
|
(method_declaration name: (identifier) @name) @definition.method
|
|
175
242
|
(constructor_declaration name: (identifier) @name) @definition.constructor
|
|
176
243
|
|
|
244
|
+
; Fields — typed field declarations inside class bodies
|
|
245
|
+
(field_declaration
|
|
246
|
+
declarator: (variable_declarator
|
|
247
|
+
name: (identifier) @name)) @definition.property
|
|
248
|
+
|
|
177
249
|
; Imports - capture any import declaration child as source
|
|
178
250
|
(import_declaration (_) @import.source) @import
|
|
179
251
|
|
|
@@ -191,6 +263,13 @@ export const JAVA_QUERIES = `
|
|
|
191
263
|
; Heritage - implements interfaces
|
|
192
264
|
(class_declaration name: (identifier) @heritage.class
|
|
193
265
|
(super_interfaces (type_list (type_identifier) @heritage.implements))) @heritage.impl
|
|
266
|
+
|
|
267
|
+
; Write access: obj.field = value
|
|
268
|
+
(assignment_expression
|
|
269
|
+
left: (field_access
|
|
270
|
+
object: (_) @assignment.receiver
|
|
271
|
+
field: (identifier) @assignment.property)
|
|
272
|
+
right: (_)) @assignment
|
|
194
273
|
`;
|
|
195
274
|
// C queries - works with tree-sitter-c
|
|
196
275
|
export const C_QUERIES = `
|
|
@@ -236,6 +315,11 @@ export const GO_QUERIES = `
|
|
|
236
315
|
(import_declaration (import_spec path: (interpreted_string_literal) @import.source)) @import
|
|
237
316
|
(import_declaration (import_spec_list (import_spec path: (interpreted_string_literal) @import.source))) @import
|
|
238
317
|
|
|
318
|
+
; Struct fields — named field declarations inside struct types
|
|
319
|
+
(field_declaration_list
|
|
320
|
+
(field_declaration
|
|
321
|
+
name: (field_identifier) @name) @definition.property)
|
|
322
|
+
|
|
239
323
|
; Struct embedding (anonymous fields = inheritance)
|
|
240
324
|
(type_declaration
|
|
241
325
|
(type_spec
|
|
@@ -251,6 +335,14 @@ export const GO_QUERIES = `
|
|
|
251
335
|
|
|
252
336
|
; Struct literal construction: User{Name: "Alice"}
|
|
253
337
|
(composite_literal type: (type_identifier) @call.name) @call
|
|
338
|
+
|
|
339
|
+
; Write access: obj.field = value
|
|
340
|
+
(assignment_statement
|
|
341
|
+
left: (expression_list
|
|
342
|
+
(selector_expression
|
|
343
|
+
operand: (_) @assignment.receiver
|
|
344
|
+
field: (field_identifier) @assignment.property))
|
|
345
|
+
right: (_)) @assignment
|
|
254
346
|
`;
|
|
255
347
|
// C++ queries - works with tree-sitter-cpp
|
|
256
348
|
export const CPP_QUERIES = `
|
|
@@ -291,6 +383,21 @@ export const CPP_QUERIES = `
|
|
|
291
383
|
(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function
|
|
292
384
|
(declaration declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function
|
|
293
385
|
|
|
386
|
+
; Class/struct data member fields (Address address; int count;)
|
|
387
|
+
; Uses field_identifier to exclude method declarations (which use function_declarator)
|
|
388
|
+
(field_declaration
|
|
389
|
+
declarator: (field_identifier) @name) @definition.property
|
|
390
|
+
|
|
391
|
+
; Pointer member fields (Address* address;)
|
|
392
|
+
(field_declaration
|
|
393
|
+
declarator: (pointer_declarator
|
|
394
|
+
declarator: (field_identifier) @name)) @definition.property
|
|
395
|
+
|
|
396
|
+
; Reference member fields (Address& address;)
|
|
397
|
+
(field_declaration
|
|
398
|
+
declarator: (reference_declarator
|
|
399
|
+
(field_identifier) @name)) @definition.property
|
|
400
|
+
|
|
294
401
|
; Inline class method declarations (inside class body, no body: void Foo();)
|
|
295
402
|
(field_declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.method
|
|
296
403
|
|
|
@@ -321,6 +428,14 @@ export const CPP_QUERIES = `
|
|
|
321
428
|
(base_class_clause (type_identifier) @heritage.extends)) @heritage
|
|
322
429
|
(class_specifier name: (type_identifier) @heritage.class
|
|
323
430
|
(base_class_clause (access_specifier) (type_identifier) @heritage.extends)) @heritage
|
|
431
|
+
|
|
432
|
+
; Write access: obj.field = value
|
|
433
|
+
(assignment_expression
|
|
434
|
+
left: (field_expression
|
|
435
|
+
argument: (_) @assignment.receiver
|
|
436
|
+
field: (field_identifier) @assignment.property)
|
|
437
|
+
right: (_)) @assignment
|
|
438
|
+
|
|
324
439
|
`;
|
|
325
440
|
// C# queries - works with tree-sitter-c-sharp
|
|
326
441
|
export const CSHARP_QUERIES = `
|
|
@@ -374,6 +489,13 @@ export const CSHARP_QUERIES = `
|
|
|
374
489
|
(base_list (identifier) @heritage.extends)) @heritage
|
|
375
490
|
(class_declaration name: (identifier) @heritage.class
|
|
376
491
|
(base_list (generic_name (identifier) @heritage.extends))) @heritage
|
|
492
|
+
|
|
493
|
+
; Write access: obj.field = value
|
|
494
|
+
(assignment_expression
|
|
495
|
+
left: (member_access_expression
|
|
496
|
+
expression: (_) @assignment.receiver
|
|
497
|
+
name: (identifier) @assignment.property)
|
|
498
|
+
right: (_)) @assignment
|
|
377
499
|
`;
|
|
378
500
|
// Rust queries - works with tree-sitter-rust
|
|
379
501
|
export const RUST_QUERIES = `
|
|
@@ -404,11 +526,30 @@ export const RUST_QUERIES = `
|
|
|
404
526
|
; Struct literal construction: User { name: value }
|
|
405
527
|
(struct_expression name: (type_identifier) @call.name) @call
|
|
406
528
|
|
|
529
|
+
; Struct fields — named field declarations inside struct bodies
|
|
530
|
+
(field_declaration_list
|
|
531
|
+
(field_declaration
|
|
532
|
+
name: (field_identifier) @name) @definition.property)
|
|
533
|
+
|
|
407
534
|
; Heritage (trait implementation) — all combinations of concrete/generic trait × concrete/generic type
|
|
408
535
|
(impl_item trait: (type_identifier) @heritage.trait type: (type_identifier) @heritage.class) @heritage
|
|
409
536
|
(impl_item trait: (generic_type type: (type_identifier) @heritage.trait) type: (type_identifier) @heritage.class) @heritage
|
|
410
537
|
(impl_item trait: (type_identifier) @heritage.trait type: (generic_type type: (type_identifier) @heritage.class)) @heritage
|
|
411
538
|
(impl_item trait: (generic_type type: (type_identifier) @heritage.trait) type: (generic_type type: (type_identifier) @heritage.class)) @heritage
|
|
539
|
+
|
|
540
|
+
; Write access: obj.field = value
|
|
541
|
+
(assignment_expression
|
|
542
|
+
left: (field_expression
|
|
543
|
+
value: (_) @assignment.receiver
|
|
544
|
+
field: (field_identifier) @assignment.property)
|
|
545
|
+
right: (_)) @assignment
|
|
546
|
+
|
|
547
|
+
; Write access: obj.field += value (compound assignment)
|
|
548
|
+
(compound_assignment_expr
|
|
549
|
+
left: (field_expression
|
|
550
|
+
value: (_) @assignment.receiver
|
|
551
|
+
field: (field_identifier) @assignment.property)
|
|
552
|
+
right: (_)) @assignment
|
|
412
553
|
`;
|
|
413
554
|
// PHP queries - works with tree-sitter-php (php_only grammar)
|
|
414
555
|
export const PHP_QUERIES = `
|
|
@@ -446,6 +587,13 @@ export const PHP_QUERIES = `
|
|
|
446
587
|
(variable_name
|
|
447
588
|
(name) @name))) @definition.property
|
|
448
589
|
|
|
590
|
+
; Constructor property promotion (PHP 8.0+: public Address $address in __construct)
|
|
591
|
+
(method_declaration
|
|
592
|
+
parameters: (formal_parameters
|
|
593
|
+
(property_promotion_parameter
|
|
594
|
+
name: (variable_name
|
|
595
|
+
(name) @name)))) @definition.property
|
|
596
|
+
|
|
449
597
|
; ── Imports: use statements ──────────────────────────────────────────────────
|
|
450
598
|
; Simple: use App\\Models\\User;
|
|
451
599
|
(namespace_use_declaration
|
|
@@ -490,6 +638,20 @@ export const PHP_QUERIES = `
|
|
|
490
638
|
body: (declaration_list
|
|
491
639
|
(use_declaration
|
|
492
640
|
[(name) (qualified_name)] @heritage.trait))) @heritage
|
|
641
|
+
|
|
642
|
+
; Write access: $obj->field = value
|
|
643
|
+
(assignment_expression
|
|
644
|
+
left: (member_access_expression
|
|
645
|
+
object: (_) @assignment.receiver
|
|
646
|
+
name: (name) @assignment.property)
|
|
647
|
+
right: (_)) @assignment
|
|
648
|
+
|
|
649
|
+
; Write access: ClassName::$field = value (static property)
|
|
650
|
+
(assignment_expression
|
|
651
|
+
left: (scoped_property_access_expression
|
|
652
|
+
scope: (_) @assignment.receiver
|
|
653
|
+
name: (variable_name (name) @assignment.property))
|
|
654
|
+
right: (_)) @assignment
|
|
493
655
|
`;
|
|
494
656
|
// Ruby queries - works with tree-sitter-ruby
|
|
495
657
|
// NOTE: Ruby uses `call` for require, include, extend, prepend, attr_* etc.
|
|
@@ -534,6 +696,20 @@ export const RUBY_QUERIES = `
|
|
|
534
696
|
name: (constant) @heritage.class
|
|
535
697
|
superclass: (superclass
|
|
536
698
|
(constant) @heritage.extends)) @heritage
|
|
699
|
+
|
|
700
|
+
; Write access: obj.field = value (Ruby setter — syntactically a method call to field=)
|
|
701
|
+
(assignment
|
|
702
|
+
left: (call
|
|
703
|
+
receiver: (_) @assignment.receiver
|
|
704
|
+
method: (identifier) @assignment.property)
|
|
705
|
+
right: (_)) @assignment
|
|
706
|
+
|
|
707
|
+
; Write access: obj.field += value (compound assignment — operator_assignment node, not assignment)
|
|
708
|
+
(operator_assignment
|
|
709
|
+
left: (call
|
|
710
|
+
receiver: (_) @assignment.receiver
|
|
711
|
+
method: (identifier) @assignment.property)
|
|
712
|
+
right: (_)) @assignment
|
|
537
713
|
`;
|
|
538
714
|
// Kotlin queries - works with tree-sitter-kotlin (fwcd/tree-sitter-kotlin)
|
|
539
715
|
// Based on official tags.scm; functions use simple_identifier, classes use type_identifier
|
|
@@ -569,6 +745,12 @@ export const KOTLIN_QUERIES = `
|
|
|
569
745
|
(variable_declaration
|
|
570
746
|
(simple_identifier) @name)) @definition.property
|
|
571
747
|
|
|
748
|
+
; Primary constructor val/var parameters (data class, value class, regular class)
|
|
749
|
+
; binding_pattern_kind contains "val" or "var" — without it, the param is not a property
|
|
750
|
+
(class_parameter
|
|
751
|
+
(binding_pattern_kind)
|
|
752
|
+
(simple_identifier) @name) @definition.property
|
|
753
|
+
|
|
572
754
|
; ── Enum entries ─────────────────────────────────────────────────────────
|
|
573
755
|
(enum_entry
|
|
574
756
|
(simple_identifier) @name) @definition.enum
|
|
@@ -613,6 +795,15 @@ export const KOTLIN_QUERIES = `
|
|
|
613
795
|
(delegation_specifier
|
|
614
796
|
(constructor_invocation
|
|
615
797
|
(user_type (type_identifier) @heritage.extends)))) @heritage
|
|
798
|
+
|
|
799
|
+
; Write access: obj.field = value
|
|
800
|
+
(assignment
|
|
801
|
+
(directly_assignable_expression
|
|
802
|
+
(_) @assignment.receiver
|
|
803
|
+
(navigation_suffix
|
|
804
|
+
(simple_identifier) @assignment.property))
|
|
805
|
+
(_)) @assignment
|
|
806
|
+
|
|
616
807
|
`;
|
|
617
808
|
// Swift queries - works with tree-sitter-swift
|
|
618
809
|
export const SWIFT_QUERIES = `
|
|
@@ -670,6 +861,15 @@ export const SWIFT_QUERIES = `
|
|
|
670
861
|
; Extensions wrap the name in user_type unlike class/struct/enum declarations
|
|
671
862
|
(class_declaration "extension" name: (user_type (type_identifier) @heritage.class)
|
|
672
863
|
(inheritance_specifier inherits_from: (user_type (type_identifier) @heritage.extends))) @heritage
|
|
864
|
+
|
|
865
|
+
; Write access: obj.field = value
|
|
866
|
+
(assignment
|
|
867
|
+
(directly_assignable_expression
|
|
868
|
+
(_) @assignment.receiver
|
|
869
|
+
(navigation_suffix
|
|
870
|
+
(simple_identifier) @assignment.property))
|
|
871
|
+
(_)) @assignment
|
|
872
|
+
|
|
673
873
|
`;
|
|
674
874
|
export const LANGUAGE_QUERIES = {
|
|
675
875
|
[SupportedLanguages.TypeScript]: TYPESCRIPT_QUERIES,
|
|
@@ -296,6 +296,47 @@ const SKIP_SUBTREE_TYPES = new Set([
|
|
|
296
296
|
// Regex
|
|
297
297
|
'regex', 'regex_pattern',
|
|
298
298
|
]);
|
|
299
|
+
const CLASS_LIKE_TYPES = new Set(['Class', 'Struct', 'Interface']);
|
|
300
|
+
/** Resolve a field's declared type given a receiver variable and field name.
|
|
301
|
+
* Uses SymbolTable to find the class nodeId for the receiver's type, then
|
|
302
|
+
* looks up the field via the eagerly-populated fieldByOwner index. */
|
|
303
|
+
const resolveFieldType = (receiver, field, scopeEnv, symbolTable) => {
|
|
304
|
+
if (!symbolTable)
|
|
305
|
+
return undefined;
|
|
306
|
+
const receiverType = scopeEnv.get(receiver);
|
|
307
|
+
if (!receiverType)
|
|
308
|
+
return undefined;
|
|
309
|
+
const classDefs = symbolTable.lookupFuzzy(receiverType)
|
|
310
|
+
.filter(d => CLASS_LIKE_TYPES.has(d.type));
|
|
311
|
+
if (classDefs.length !== 1)
|
|
312
|
+
return undefined;
|
|
313
|
+
const fieldDef = symbolTable.lookupFieldByOwner(classDefs[0].nodeId, field);
|
|
314
|
+
if (!fieldDef?.declaredType)
|
|
315
|
+
return undefined;
|
|
316
|
+
return extractReturnTypeName(fieldDef.declaredType);
|
|
317
|
+
};
|
|
318
|
+
/** Resolve a method's return type given a receiver variable and method name.
|
|
319
|
+
* Uses SymbolTable to find class nodeIds for the receiver's type, then
|
|
320
|
+
* looks up the method via lookupFuzzyCallable filtered by ownerId. */
|
|
321
|
+
const resolveMethodReturnType = (receiver, method, scopeEnv, symbolTable) => {
|
|
322
|
+
if (!symbolTable)
|
|
323
|
+
return undefined;
|
|
324
|
+
const receiverType = scopeEnv.get(receiver);
|
|
325
|
+
if (!receiverType)
|
|
326
|
+
return undefined;
|
|
327
|
+
const classDefs = symbolTable.lookupFuzzy(receiverType)
|
|
328
|
+
.filter(d => CLASS_LIKE_TYPES.has(d.type));
|
|
329
|
+
if (classDefs.length === 0)
|
|
330
|
+
return undefined;
|
|
331
|
+
const classNodeIds = new Set(classDefs.map(d => d.nodeId));
|
|
332
|
+
const methods = symbolTable.lookupFuzzyCallable(method)
|
|
333
|
+
.filter(d => d.ownerId && classNodeIds.has(d.ownerId));
|
|
334
|
+
if (methods.length !== 1)
|
|
335
|
+
return undefined;
|
|
336
|
+
if (!methods[0].returnType)
|
|
337
|
+
return undefined;
|
|
338
|
+
return extractReturnTypeName(methods[0].returnType);
|
|
339
|
+
};
|
|
299
340
|
export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
300
341
|
const env = new Map();
|
|
301
342
|
const patternOverrides = new Map();
|
|
@@ -336,12 +377,10 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
336
377
|
TYPED_PARAMETER_TYPES.forEach(t => interestingNodeTypes.add(t));
|
|
337
378
|
config.declarationNodeTypes.forEach(t => interestingNodeTypes.add(t));
|
|
338
379
|
config.forLoopNodeTypes?.forEach(t => interestingNodeTypes.add(t));
|
|
339
|
-
// Tier 2:
|
|
340
|
-
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
// function return types at TypeEnv build time. See PendingAssignment in types.ts.
|
|
344
|
-
const pendingCallResults = [];
|
|
380
|
+
// Tier 2: unified fixpoint propagation — collects copy, callResult, fieldAccess, and
|
|
381
|
+
// methodCallResult items during walk(), then iterates until no new bindings are produced.
|
|
382
|
+
// Handles arbitrary-depth mixed chains: callResult → fieldAccess → methodCallResult → copy.
|
|
383
|
+
const pendingItems = [];
|
|
345
384
|
// Maps `scope\0varName` → the type annotation AST node from the original declaration.
|
|
346
385
|
// Allows pattern extractors to navigate back to the declaration's generic type arguments
|
|
347
386
|
// (e.g., to extract T from Result<T, E> for `if let Ok(x) = res`).
|
|
@@ -371,7 +410,9 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
371
410
|
let typeNode = node.childForFieldName('type');
|
|
372
411
|
if (typeNode) {
|
|
373
412
|
const nameNode = node.childForFieldName('name')
|
|
374
|
-
?? node.childForFieldName('pattern')
|
|
413
|
+
?? node.childForFieldName('pattern')
|
|
414
|
+
// Python typed_parameter: name is a positional child (identifier), not a named field
|
|
415
|
+
?? (node.firstNamedChild?.type === 'identifier' ? node.firstNamedChild : null);
|
|
375
416
|
if (nameNode) {
|
|
376
417
|
const varName = extractVarName(nameNode);
|
|
377
418
|
if (varName && !declarationTypeNodes.has(`${scope}\0${varName}`)) {
|
|
@@ -549,12 +590,7 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
549
590
|
if (scopeEnv) {
|
|
550
591
|
const pending = config.extractPendingAssignment(node, scopeEnv);
|
|
551
592
|
if (pending) {
|
|
552
|
-
|
|
553
|
-
pendingCopies.push({ scope, lhs: pending.lhs, rhs: pending.rhs });
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
pendingCallResults.push({ scope, lhs: pending.lhs, callee: pending.callee });
|
|
557
|
-
}
|
|
593
|
+
pendingItems.push({ scope, ...pending });
|
|
558
594
|
}
|
|
559
595
|
}
|
|
560
596
|
}
|
|
@@ -577,31 +613,51 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
577
613
|
}
|
|
578
614
|
};
|
|
579
615
|
walk(tree.rootNode, FILE_SCOPE);
|
|
580
|
-
//
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
//
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
616
|
+
// Unified fixpoint propagation: iterate over ALL pending items (copy, callResult,
|
|
617
|
+
// fieldAccess, methodCallResult) until no new bindings are produced.
|
|
618
|
+
// Handles arbitrary-depth mixed chains:
|
|
619
|
+
// const user = getUser(); // callResult → User
|
|
620
|
+
// const addr = user.address; // fieldAccess → Address (depends on user)
|
|
621
|
+
// const city = addr.getCity(); // methodCallResult → City (depends on addr)
|
|
622
|
+
// const alias = city; // copy → City (depends on city)
|
|
623
|
+
// Data flow: SymbolTable (immutable) + scopeEnv → resolve → scopeEnv.
|
|
624
|
+
// Termination: finite entries, each bound at most once (first-writer-wins), max 10 iterations.
|
|
625
|
+
const MAX_FIXPOINT_ITERATIONS = 10;
|
|
626
|
+
const resolved = new Set();
|
|
627
|
+
for (let iter = 0; iter < MAX_FIXPOINT_ITERATIONS; iter++) {
|
|
628
|
+
let changed = false;
|
|
629
|
+
for (let i = 0; i < pendingItems.length; i++) {
|
|
630
|
+
if (resolved.has(i))
|
|
631
|
+
continue;
|
|
632
|
+
const item = pendingItems[i];
|
|
633
|
+
const scopeEnv = env.get(item.scope);
|
|
634
|
+
if (!scopeEnv || scopeEnv.has(item.lhs)) {
|
|
635
|
+
resolved.add(i);
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
let typeName;
|
|
639
|
+
switch (item.kind) {
|
|
640
|
+
case 'callResult':
|
|
641
|
+
typeName = returnTypeLookup.lookupReturnType(item.callee);
|
|
642
|
+
break;
|
|
643
|
+
case 'copy':
|
|
644
|
+
typeName = scopeEnv.get(item.rhs) ?? env.get(FILE_SCOPE)?.get(item.rhs);
|
|
645
|
+
break;
|
|
646
|
+
case 'fieldAccess':
|
|
647
|
+
typeName = resolveFieldType(item.receiver, item.field, scopeEnv, symbolTable);
|
|
648
|
+
break;
|
|
649
|
+
case 'methodCallResult':
|
|
650
|
+
typeName = resolveMethodReturnType(item.receiver, item.method, scopeEnv, symbolTable);
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
if (typeName) {
|
|
654
|
+
scopeEnv.set(item.lhs, typeName);
|
|
655
|
+
resolved.add(i);
|
|
656
|
+
changed = true;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (!changed)
|
|
660
|
+
break;
|
|
605
661
|
}
|
|
606
662
|
return {
|
|
607
663
|
lookup: (varName, callNode) => lookupInEnv(env, varName, callNode, patternOverrides),
|
|
@@ -186,7 +186,7 @@ const extractPendingAssignment = (node, scopeEnv) => {
|
|
|
186
186
|
if (!declarator || declarator.type !== 'init_declarator')
|
|
187
187
|
return undefined;
|
|
188
188
|
const value = declarator.childForFieldName('value');
|
|
189
|
-
if (!value
|
|
189
|
+
if (!value)
|
|
190
190
|
return undefined;
|
|
191
191
|
const nameNode = declarator.childForFieldName('declarator');
|
|
192
192
|
if (!nameNode)
|
|
@@ -198,7 +198,32 @@ const extractPendingAssignment = (node, scopeEnv) => {
|
|
|
198
198
|
const lhs = extractVarName(finalName);
|
|
199
199
|
if (!lhs || scopeEnv.has(lhs))
|
|
200
200
|
return undefined;
|
|
201
|
-
|
|
201
|
+
if (value.type === 'identifier')
|
|
202
|
+
return { kind: 'copy', lhs, rhs: value.text };
|
|
203
|
+
// field_expression RHS → fieldAccess (a.field)
|
|
204
|
+
if (value.type === 'field_expression') {
|
|
205
|
+
const obj = value.firstNamedChild;
|
|
206
|
+
const field = value.lastNamedChild;
|
|
207
|
+
if (obj?.type === 'identifier' && field?.type === 'field_identifier') {
|
|
208
|
+
return { kind: 'fieldAccess', lhs, receiver: obj.text, field: field.text };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// call_expression RHS
|
|
212
|
+
if (value.type === 'call_expression') {
|
|
213
|
+
const funcNode = value.childForFieldName('function');
|
|
214
|
+
if (funcNode?.type === 'identifier') {
|
|
215
|
+
return { kind: 'callResult', lhs, callee: funcNode.text };
|
|
216
|
+
}
|
|
217
|
+
// method call with receiver: call_expression → function: field_expression
|
|
218
|
+
if (funcNode?.type === 'field_expression') {
|
|
219
|
+
const obj = funcNode.firstNamedChild;
|
|
220
|
+
const field = funcNode.lastNamedChild;
|
|
221
|
+
if (obj?.type === 'identifier' && field?.type === 'field_identifier') {
|
|
222
|
+
return { kind: 'methodCallResult', lhs, receiver: obj.text, method: field.text };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return undefined;
|
|
202
227
|
};
|
|
203
228
|
// --- For-loop Tier 1c ---
|
|
204
229
|
const FOR_LOOP_NODE_TYPES = new Set(['for_range_loop']);
|
|
@@ -367,6 +367,46 @@ const extractPendingAssignment = (node, scopeEnv) => {
|
|
|
367
367
|
if (valueNode && valueNode !== nameNode && (valueNode.type === 'identifier' || valueNode.type === 'simple_identifier')) {
|
|
368
368
|
return { kind: 'copy', lhs, rhs: valueNode.text };
|
|
369
369
|
}
|
|
370
|
+
// member_access_expression RHS → fieldAccess (a.Field)
|
|
371
|
+
if (valueNode?.type === 'member_access_expression') {
|
|
372
|
+
const expr = valueNode.childForFieldName('expression');
|
|
373
|
+
const name = valueNode.childForFieldName('name');
|
|
374
|
+
if (expr?.type === 'identifier' && name?.type === 'identifier') {
|
|
375
|
+
return { kind: 'fieldAccess', lhs, receiver: expr.text, field: name.text };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// invocation_expression RHS
|
|
379
|
+
if (valueNode?.type === 'invocation_expression') {
|
|
380
|
+
const funcNode = valueNode.firstNamedChild;
|
|
381
|
+
if (funcNode?.type === 'identifier_name' || funcNode?.type === 'identifier') {
|
|
382
|
+
return { kind: 'callResult', lhs, callee: funcNode.text };
|
|
383
|
+
}
|
|
384
|
+
// method call with receiver → methodCallResult: a.GetC()
|
|
385
|
+
if (funcNode?.type === 'member_access_expression') {
|
|
386
|
+
const expr = funcNode.childForFieldName('expression');
|
|
387
|
+
const name = funcNode.childForFieldName('name');
|
|
388
|
+
if (expr?.type === 'identifier' && name?.type === 'identifier') {
|
|
389
|
+
return { kind: 'methodCallResult', lhs, receiver: expr.text, method: name.text };
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// await_expression → unwrap and check inner
|
|
394
|
+
if (valueNode?.type === 'await_expression') {
|
|
395
|
+
const inner = valueNode.firstNamedChild;
|
|
396
|
+
if (inner?.type === 'invocation_expression') {
|
|
397
|
+
const funcNode = inner.firstNamedChild;
|
|
398
|
+
if (funcNode?.type === 'identifier_name' || funcNode?.type === 'identifier') {
|
|
399
|
+
return { kind: 'callResult', lhs, callee: funcNode.text };
|
|
400
|
+
}
|
|
401
|
+
if (funcNode?.type === 'member_access_expression') {
|
|
402
|
+
const expr = funcNode.childForFieldName('expression');
|
|
403
|
+
const name = funcNode.childForFieldName('name');
|
|
404
|
+
if (expr?.type === 'identifier' && name?.type === 'identifier') {
|
|
405
|
+
return { kind: 'methodCallResult', lhs, receiver: expr.text, method: name.text };
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
370
410
|
}
|
|
371
411
|
return undefined;
|
|
372
412
|
};
|
|
@@ -419,6 +419,29 @@ const extractPendingAssignment = (node, scopeEnv) => {
|
|
|
419
419
|
return undefined;
|
|
420
420
|
if (rhsNode.type === 'identifier')
|
|
421
421
|
return { kind: 'copy', lhs, rhs: rhsNode.text };
|
|
422
|
+
// selector_expression RHS → fieldAccess (a.field)
|
|
423
|
+
if (rhsNode.type === 'selector_expression') {
|
|
424
|
+
const operand = rhsNode.childForFieldName('operand');
|
|
425
|
+
const field = rhsNode.childForFieldName('field');
|
|
426
|
+
if (operand?.type === 'identifier' && field) {
|
|
427
|
+
return { kind: 'fieldAccess', lhs, receiver: operand.text, field: field.text };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// call_expression RHS
|
|
431
|
+
if (rhsNode.type === 'call_expression') {
|
|
432
|
+
const funcNode = rhsNode.childForFieldName('function');
|
|
433
|
+
if (funcNode?.type === 'identifier') {
|
|
434
|
+
return { kind: 'callResult', lhs, callee: funcNode.text };
|
|
435
|
+
}
|
|
436
|
+
// method call with receiver: call_expression → function: selector_expression
|
|
437
|
+
if (funcNode?.type === 'selector_expression') {
|
|
438
|
+
const operand = funcNode.childForFieldName('operand');
|
|
439
|
+
const field = funcNode.childForFieldName('field');
|
|
440
|
+
if (operand?.type === 'identifier' && field) {
|
|
441
|
+
return { kind: 'methodCallResult', lhs, receiver: operand.text, method: field.text };
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
422
445
|
return undefined;
|
|
423
446
|
}
|
|
424
447
|
if (node.type === 'var_spec' || node.type === 'var_declaration') {
|
|
@@ -452,6 +475,28 @@ const extractPendingAssignment = (node, scopeEnv) => {
|
|
|
452
475
|
const rhsNode = exprList?.firstNamedChild;
|
|
453
476
|
if (rhsNode?.type === 'identifier')
|
|
454
477
|
return { kind: 'copy', lhs, rhs: rhsNode.text };
|
|
478
|
+
// selector_expression RHS → fieldAccess
|
|
479
|
+
if (rhsNode?.type === 'selector_expression') {
|
|
480
|
+
const operand = rhsNode.childForFieldName('operand');
|
|
481
|
+
const field = rhsNode.childForFieldName('field');
|
|
482
|
+
if (operand?.type === 'identifier' && field) {
|
|
483
|
+
return { kind: 'fieldAccess', lhs, receiver: operand.text, field: field.text };
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
// call_expression RHS
|
|
487
|
+
if (rhsNode?.type === 'call_expression') {
|
|
488
|
+
const funcNode = rhsNode.childForFieldName('function');
|
|
489
|
+
if (funcNode?.type === 'identifier') {
|
|
490
|
+
return { kind: 'callResult', lhs, callee: funcNode.text };
|
|
491
|
+
}
|
|
492
|
+
if (funcNode?.type === 'selector_expression') {
|
|
493
|
+
const operand = funcNode.childForFieldName('operand');
|
|
494
|
+
const field = funcNode.childForFieldName('field');
|
|
495
|
+
if (operand?.type === 'identifier' && field) {
|
|
496
|
+
return { kind: 'methodCallResult', lhs, receiver: operand.text, method: field.text };
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
455
500
|
}
|
|
456
501
|
}
|
|
457
502
|
return undefined;
|