gitnexus 1.4.5 → 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/cli/eval-server.js +13 -5
- package/dist/cli/index.js +0 -0
- package/dist/cli/tool.d.ts +3 -2
- package/dist/cli/tool.js +48 -13
- package/dist/core/graph/types.d.ts +2 -2
- package/dist/core/ingestion/call-processor.d.ts +7 -2
- package/dist/core/ingestion/call-processor.js +308 -235
- 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 +37 -8
- package/dist/core/ingestion/pipeline.js +5 -1
- package/dist/core/ingestion/symbol-table.d.ts +19 -3
- package/dist/core/ingestion/symbol-table.js +41 -2
- 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 +126 -18
- package/dist/core/ingestion/type-extractors/c-cpp.js +28 -3
- package/dist/core/ingestion/type-extractors/csharp.js +61 -7
- package/dist/core/ingestion/type-extractors/go.js +86 -10
- package/dist/core/ingestion/type-extractors/jvm.js +122 -23
- package/dist/core/ingestion/type-extractors/php.js +172 -7
- package/dist/core/ingestion/type-extractors/python.js +107 -21
- package/dist/core/ingestion/type-extractors/ruby.js +18 -3
- package/dist/core/ingestion/type-extractors/rust.js +61 -14
- package/dist/core/ingestion/type-extractors/shared.d.ts +13 -0
- package/dist/core/ingestion/type-extractors/shared.js +243 -4
- package/dist/core/ingestion/type-extractors/types.d.ts +57 -12
- package/dist/core/ingestion/type-extractors/typescript.js +52 -8
- 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 +73 -28
- 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.d.ts +1 -0
- package/dist/mcp/local/local-backend.js +25 -3
- package/dist/mcp/resources.js +11 -0
- package/dist/mcp/server.js +26 -4
- package/dist/mcp/tools.js +15 -5
- package/hooks/claude/gitnexus-hook.cjs +0 -0
- package/hooks/claude/pre-tool-use.sh +0 -0
- package/hooks/claude/session-start.sh +0 -0
- package/package.json +6 -5
- package/scripts/patch-tree-sitter-swift.cjs +0 -0
|
@@ -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,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FUNCTION_NODE_TYPES, extractFunctionName, CLASS_CONTAINER_TYPES } from './utils.js';
|
|
1
|
+
import { FUNCTION_NODE_TYPES, extractFunctionName, CLASS_CONTAINER_TYPES, isBuiltInOrNoise } from './utils.js';
|
|
2
2
|
import { typeConfigs, TYPED_PARAMETER_TYPES } from './type-extractors/index.js';
|
|
3
|
-
import { extractSimpleTypeName, extractVarName, stripNullable } from './type-extractors/shared.js';
|
|
3
|
+
import { extractSimpleTypeName, extractVarName, stripNullable, extractReturnTypeName } from './type-extractors/shared.js';
|
|
4
4
|
/** File-level scope key */
|
|
5
5
|
const FILE_SCOPE = '';
|
|
6
6
|
/** Fallback for languages where class names aren't in a 'name' field (e.g. Kotlin uses type_identifier). */
|
|
@@ -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();
|
|
@@ -303,13 +344,43 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
303
344
|
const classNames = createClassNameLookup(localClassNames, symbolTable);
|
|
304
345
|
const config = typeConfigs[language];
|
|
305
346
|
const bindings = [];
|
|
347
|
+
// Build ReturnTypeLookup from optional SymbolTable.
|
|
348
|
+
// Conservative: returns undefined when callee is ambiguous (0 or 2+ matches).
|
|
349
|
+
const returnTypeLookup = {
|
|
350
|
+
lookupReturnType(callee) {
|
|
351
|
+
if (!symbolTable)
|
|
352
|
+
return undefined;
|
|
353
|
+
if (isBuiltInOrNoise(callee))
|
|
354
|
+
return undefined;
|
|
355
|
+
const callables = symbolTable.lookupFuzzyCallable(callee);
|
|
356
|
+
if (callables.length !== 1)
|
|
357
|
+
return undefined;
|
|
358
|
+
const rawReturn = callables[0].returnType;
|
|
359
|
+
if (!rawReturn)
|
|
360
|
+
return undefined;
|
|
361
|
+
return extractReturnTypeName(rawReturn);
|
|
362
|
+
},
|
|
363
|
+
lookupRawReturnType(callee) {
|
|
364
|
+
if (!symbolTable)
|
|
365
|
+
return undefined;
|
|
366
|
+
if (isBuiltInOrNoise(callee))
|
|
367
|
+
return undefined;
|
|
368
|
+
const callables = symbolTable.lookupFuzzyCallable(callee);
|
|
369
|
+
if (callables.length !== 1)
|
|
370
|
+
return undefined;
|
|
371
|
+
return callables[0].returnType;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
306
374
|
// Pre-compute combined set of node types that need extractTypeBinding.
|
|
307
375
|
// Single Set.has() replaces 3 separate checks per node in walk().
|
|
308
376
|
const interestingNodeTypes = new Set();
|
|
309
377
|
TYPED_PARAMETER_TYPES.forEach(t => interestingNodeTypes.add(t));
|
|
310
378
|
config.declarationNodeTypes.forEach(t => interestingNodeTypes.add(t));
|
|
311
379
|
config.forLoopNodeTypes?.forEach(t => interestingNodeTypes.add(t));
|
|
312
|
-
|
|
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 = [];
|
|
313
384
|
// Maps `scope\0varName` → the type annotation AST node from the original declaration.
|
|
314
385
|
// Allows pattern extractors to navigate back to the declaration's generic type arguments
|
|
315
386
|
// (e.g., to extract T from Result<T, E> for `if let Ok(x) = res`).
|
|
@@ -339,7 +410,9 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
339
410
|
let typeNode = node.childForFieldName('type');
|
|
340
411
|
if (typeNode) {
|
|
341
412
|
const nameNode = node.childForFieldName('name')
|
|
342
|
-
?? 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);
|
|
343
416
|
if (nameNode) {
|
|
344
417
|
const varName = extractVarName(nameNode);
|
|
345
418
|
if (varName && !declarationTypeNodes.has(`${scope}\0${varName}`)) {
|
|
@@ -376,7 +449,10 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
376
449
|
// For-each loop variable bindings (Java/C#/Kotlin): explicit element types in the AST.
|
|
377
450
|
// Checked before declarationNodeTypes — loop variables are not declarations.
|
|
378
451
|
if (config.forLoopNodeTypes?.has(node.type)) {
|
|
379
|
-
config.extractForLoopBinding
|
|
452
|
+
if (config.extractForLoopBinding) {
|
|
453
|
+
const forLoopCtx = { scopeEnv, declarationTypeNodes, scope, returnTypeLookup };
|
|
454
|
+
config.extractForLoopBinding(node, forLoopCtx);
|
|
455
|
+
}
|
|
380
456
|
return;
|
|
381
457
|
}
|
|
382
458
|
if (config.declarationNodeTypes.has(node.type)) {
|
|
@@ -514,7 +590,7 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
514
590
|
if (scopeEnv) {
|
|
515
591
|
const pending = config.extractPendingAssignment(node, scopeEnv);
|
|
516
592
|
if (pending) {
|
|
517
|
-
|
|
593
|
+
pendingItems.push({ scope, ...pending });
|
|
518
594
|
}
|
|
519
595
|
}
|
|
520
596
|
}
|
|
@@ -537,19 +613,51 @@ export const buildTypeEnv = (tree, language, symbolTable) => {
|
|
|
537
613
|
}
|
|
538
614
|
};
|
|
539
615
|
walk(tree.rootNode, FILE_SCOPE);
|
|
540
|
-
//
|
|
541
|
-
//
|
|
542
|
-
//
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
+
}
|
|
552
658
|
}
|
|
659
|
+
if (!changed)
|
|
660
|
+
break;
|
|
553
661
|
}
|
|
554
662
|
return {
|
|
555
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']);
|
|
@@ -291,7 +316,7 @@ const findCppParamElementType = (iterableName, startNode, pos = 'last') => {
|
|
|
291
316
|
/** C++: for (auto& user : users) — extract loop variable binding.
|
|
292
317
|
* Handles explicit types (for (User& user : users)) and auto (for (auto& user : users)).
|
|
293
318
|
* For auto, resolves element type from the iterable's container type. */
|
|
294
|
-
const extractForLoopBinding = (node, scopeEnv, declarationTypeNodes, scope) => {
|
|
319
|
+
const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope }) => {
|
|
295
320
|
if (node.type !== 'for_range_loop')
|
|
296
321
|
return;
|
|
297
322
|
const typeNode = node.childForFieldName('type');
|