GitNexus/type-resolution-roadmap.md
Gergő Magyar f0132c1077
feat: Phase 6 type resolution — for-loop Tier 1c, pattern matching, container descriptors, 10-language coverage (#318)
* feat: Phase 6 type resolution — pattern matching, for-loop Tier 1c, coverage completion

- Add patternBindingNodeTypes gate to LanguageTypeConfig for 50% perf improvement
- Expand ForLoopExtractor signature with optional declarationTypeNodes + scope
- Add extractElementTypeFromString shared utility for container type parsing
- Python match/case: extractPatternBinding for `case User() as u:` pattern
- C# refactor: move is_pattern_expression from extractDeclaration to extractPatternBinding
- Ruby: add extractPendingAssignment for assignment chain propagation
- TS/JS: add for-loop Tier 1c for `for (const user of users)` with User[] inference
- Python: add for-loop Tier 1c for `for user in users:` with type annotation inference
- Go: add for-loop Tier 1c for `for _, user := range users` with []User inference
- Fix 'Property' as any stale cast in call-processor.ts
- Add dual return-type string length cap (2048 pre-cap, 512 post-cap)
- Add chain call integration tests for C#, Go, Rust, Python, JS, C++
- Add Python match/case integration test fixtures
- 27 new extractElementTypeFromString unit tests
- 3 for-loop edge cases skipped (declarationTypeNodes scope key lookup)

* fix: address code review findings for Phase 6

- Add missing patternBindingNodeTypes to C# typeConfig (perf gate)
- Add 2048-char input length guard to extractElementTypeFromString
- Skip Python match/case integration tests (call extraction needs query updates)

* reorganise

* fix: Phase 1 bug fixes — Go range semantics, typed_parameter, bracket depth

- Go single-var range correctly returns early for slices/maps (index, not element)
- Go single-var range on channels correctly resolves element type
- Added map_type and channel_type to extractGoElementTypeFromTypeNode
- Added isChannelType helper for channel detection before skip decision
- Added 'typed_parameter' to TYPED_PARAMETER_TYPES for Python annotated params
- Fixed bracket depth tracking in extractElementTypeFromString — only match
  selected closeChar at depth 0, return undefined for mismatched brackets
- Un-skipped 3 prematurely skipped tests (TS local const, Python List/Sequence)
- Added tests for map range, single-var range semantics, bracket edge cases

* refactor: Phase 2 architecture — shared helper, required params, decoupled type nodes

- Extract resolveIterableElementType shared helper in shared.ts implementing
  3-strategy fallback (declarationTypeNodes → scopeEnv string → AST walk)
- Refactor TS, Python, Go extractors to use shared helper (eliminates 3x duplication)
- Make ForLoopExtractor params required (aligned with PatternBindingExtractor)
- Update Java, Kotlin, C# extractor signatures to accept required params
- Decouple declarationTypeNodes from scopeEnv — capture raw type annotation
  nodes BEFORE extractDeclaration for container types (User[], []User, List[User])
- Hybrid approach: direct name extraction + keysBefore fallback for multi-declarator
- Document declarationTypeNodes invariant change (superset of scopeEnv)

* feat: Phase 3 partial — Rust for-loop + C# var foreach Tier 1c

- Rust: add extractForLoopBinding with for_expression support
  - Handles &users, &mut users via reference_expression unwrapping
  - extractRustElementTypeFromTypeNode: generic_type, reference_type, slice/array
  - findRustParamElementType: AST walk with reference/mut pattern unwrapping
  - 4 unit tests (Vec<User>, &[User], range expr negative, no-annotation negative)

- C#: upgrade foreach to handle var (implicit_type) via Tier 1c
  - extractCSharpElementTypeFromTypeNode: generic_name, array_type, nullable_type
  - findCSharpParamElementType: AST walk to method_declaration parameters
  - 3 unit tests (var foreach, explicit type regression, no-annotation negative)

* feat: Phase 3 complete — all language gaps + pattern matching

Kotlin Tier 1c:
- Unannotated for-loop resolves via shared helper
- extractKotlinElementTypeFromTypeNode handles type_projection unwrapping
- findKotlinParamElementType walks to function_declaration

Java Tier 1c:
- var foreach resolves via shared helper
- extractJavaElementTypeFromTypeNode handles generic_type, array_type
- findJavaParamElementType walks to method_declaration

TypeScript:
- readonly User[] unwrapped via readonly_type → array_type recursion

C# switch patterns:
- declaration_pattern added to patternBindingNodeTypes
- extractPatternBinding handles standalone declaration_pattern (switch case/expr)

Rust match arms:
- match_arm added to patternBindingNodeTypes
- extractPatternBinding extended with match_arm → match_expression parent traversal

Python:
- as_pattern tries childForFieldName('alias') before positional fallback

Tests: 237 pass (was 224), 13 new tests added

* feat: Phase 4 — known limitation tests, match arm fix, final verification

- Fix Rust match_arm pattern extraction: unwrap match_pattern to get
  tuple_struct_pattern inside (tree-sitter-rust wraps in match_pattern node)
- Add first-writer-wins regression test for match arm scope leakage
- Add 5 documented skip tests for known limitations:
  - TS destructured for-of (tuple destructuring)
  - Python tuple unpacking in for-loops
  - TS instanceof narrowing (block-level scoping)
  - Rust for with .iter() (method call iterable)
  - Ruby block parameters (closure param inference)

Final: 238 passed, 5 skipped (documented limitations), tsc clean

* test: integration tests for all Phase 6 language gaps + fix Rust param pattern field

Integration test fixtures and tests (30 new tests, all with exact match + negative):

Rust for-loop (5 tests):
- for user in &users with Vec<User> → User#save, negative Repo#save
- for repo in &repos with Vec<Repo> → Repo#save, negative User#save

Rust match arm (5 tests):
- match opt { Some(user) => user.save() } → User#save, negative Repo#save
- if let Ok(repo) = res → Repo#save, negative User#save

C# var foreach (5 tests):
- foreach (var user in users) with List<User> → User#Save, negative Repo#Save
- foreach (var repo in repos) with List<Repo> → Repo#Save

C# switch pattern (4 tests):
- is User user → User#Save, case Repo repo → Repo#Save

Kotlin unannotated for (4 tests):
- for (user in users) with List<User> → user.save, negative repo.save

Go map range (3 tests):
- for _, user := range userMap with map[string]User → User#Save, negative

TypeScript readonly (4 tests):
- for (const user of users) with readonly User[] → user.save, negative

Bug fix: type-env.ts parameter branch now falls back to childForFieldName('pattern')
for Rust parameters (Rust uses 'pattern' not 'name' for parameter names)

* test: add assertion bodies to known limitation skip tests

Convert empty skip test stubs to proper tests with parse/buildTypeEnv/expect
assertions following the codebase convention (e.g., call-processor.test.ts:319).
Each skip test now documents the exact expected behavior, so removing .skip
will cause a meaningful failure when the limitation is eventually fixed.

Also clarify Python integration skip tests as call-extraction issues (not
type-env) and Swift integration skips as build-dep issues (self/super
resolution code already exists in type-env.ts).

* feat: resolve 4 known limitation skip tests + method-aware type arg selection

Unskip 4 of 5 type-env known limitations with full integration test coverage:

1. TS destructured for-of: handle array_pattern by binding last named child
   to element type. Fix Map<K,V> to return last generic arg (value type).
2. Python dict.items() loop: handle `call` iterables + `pattern_list` left
   side. Fix dict[K,V] extraction via type_parameter with last-arg heuristic.
   Unwrap `type` wrapper in extractPyElementTypeFromAnnotation.
3. TS instanceof narrowing: add extractPatternBinding for binary_expression
   with positional child access. First-writer-wins (not block-scoped).
4. Rust .iter() for-loops: handle call_expression in for_expression value
   node by extracting receiver from field_expression.

Method-aware type arg resolution:
- Add TypeArgPosition ('first'|'last') to resolveIterableElementType
- .keys()/.keySet()/.Keys → first type arg (key); all else → last (value)
- Thread position through all 3 strategy callbacks in TS/Rust/Python
- Add predefined_type to extractSimpleTypeName for TS primitives (string etc)

New fixtures: rust-iter-for-loop, typescript-destructured-for-of,
typescript-instanceof-narrowing, python-dict-items-loop.
248 unit tests pass (6 new), 1 skip (Ruby block params).

* feat: container descriptor table for generic type arg resolution

Replace simple KEY_METHODS heuristic with CONTAINER_DESCRIPTORS table
that maps 30+ container types across all languages to their type parameter
semantics per access method.

Key improvements:
- Container-aware resolution: HashMap.iter() correctly yields V (arity 2),
  while Vec.iter() yields T (arity 1) — same method, different semantics
- Cross-language coverage: Map/HashMap/BTreeMap/dict/Dict/Dictionary/
  ConcurrentHashMap + List/Vec/Set/HashSet/Queue/Deque/Stack etc.
- Method categorization: keyMethods (keys/keySet/Keys) vs valueMethods
  (values/get/pop/iter/first/last) per container type
- Fallback for unknown containers: still uses method name heuristic,
  so MyCache<K,V>.keys() correctly returns first arg
- Exported getContainerDescriptor() for future heritage-chain lookups

Each language extractor now passes containerTypeName from scopeEnv to
methodToTypeArgPosition for descriptor-aware resolution.

252 unit tests pass (4 new descriptor tests), 1 skip (Ruby).

* feat: method-aware for-loop extractors + integration tests for all languages

Upgrade 4 existing extractors + create 3 new ones for full cross-language
coverage of call_expression iterables and container descriptor resolution:

Upgraded (add call expr iterable + methodToTypeArgPosition):
- Java: method_invocation (data.keySet(), data.values())
- Kotlin: navigation_expression + call_expression (data.keys, data.values())
- C#: member_access_expression + invocation_expression (data.Keys, data.Values)
- Go: TypeArgPosition threading for Go 1.18+ generics

New for-loop extractors:
- C++: for_range_loop with auto& unwrapping, template_type + qualified_identifier
  (std::vector<User>) extraction, explicit vs auto type handling
- PHP: foreach_statement with simple/key-value/by-reference forms, PHPDoc
  @param priority over AST array type
- Ruby: for-in with YARD @param type resolution via comment parsing

Integration test fixtures + tests for all 6 languages:
- java-map-keys-values (Map.values() + List iteration)
- kotlin-map-keys-values (HashMap.values + List iteration)
- csharp-dictionary-keys-values (Dictionary.Values foreach)
- cpp-range-for (auto& + const auto& range-based for)
- php-foreach-loop (foreach with PHPDoc @param User[])
- ruby-for-in-loop (for-in with YARD @param Array<User>)

Bugs fixed during integration testing:
- C++: qualified_identifier (std::vector) not unwrapped to template_type
- PHP: extractParameter overwrote PHPDoc-derived types with bare 'array'

252 unit tests pass, 201 integration tests pass across 6 languages.

* fix: update extractElementTypeFromString tests for last-arg default

TypeArgPosition change (default 'last') broke 5 existing tests expecting
first arg from multi-arg generics. Updated expectations and added explicit
pos='first' tests for key type extraction.

* fix: rename C++ fixture files to correct case for case-sensitive CI

On case-sensitive filesystems (Linux/macOS CI), git tracked both the old
lowercase files (app.cpp, user.h) and the new uppercase files (App.cpp,
User.h) as separate files. The pipeline processed both, causing the old
app.cpp (with explicit User& type) to interfere with the new auto& test.

Removes old lowercase entries and re-adds with uppercase casing to match
the #include directives in the fixture.

* feat: PR #318 review findings — pattern bindings, member access iterables, structured bindings

Address all 7 genuine gaps identified in PR #318 deep code review:

- Kotlin: add extractKotlinPatternBinding for when/is (type_test AST node)
  with allowPatternBindingOverwrite for smart-cast semantics
- Java: add type_pattern branch for Java 17+ switch pattern variables
- TypeScript: explicit object_pattern skip in for-of (no false bindings)
- Cross-language: member access iterables (self.users, this.users, repo.users)
  across all 10 language extractors
- C++: structured_binding_declarator handling in range-for (last-child heuristic)
- Rust: closure_parameter added to TYPED_PARAMETER_TYPES
- PHP: normalizePhpType handles angle-bracket generics (Collection<User>)

Code review fixes applied:
- Remove 4 debug console.log statements (c-cpp.ts, call-processor.ts)
- Hoist KNOWN_CONTAINER_PROPS to module scope (csharp.ts)
- Guard keysBefore allocation behind typeNode check (type-env.ts)
- Add depth limits (50) to 7 recursive type extraction functions
- Add 2048-char length cap to extractSimpleTypeName
- Fix PHP/Ruby missing typeArgPos parameter in resolveIterableElementType

Integration test fixtures: kotlin-when-pattern, java-switch-pattern,
cpp-structured-binding, typescript-member-access-for-loop,
python-member-access-for-loop

* fix: position-indexed when/is bindings, Kotlin param extraction, HashMap.values for-loop

Three root causes for failing Kotlin integration tests:

1. When/is multi-arm resolution: flat scopeEnv stored only the last arm's
   type (last-writer-wins). Added PatternOverrides with AST range indexing
   so each when arm resolves to its narrowed type independently.

2. HashMap.values for-loop: navigation_expression without call_suffix was
   classified as bare property access (iterableName='values' instead of
   'data'). Now tries object-as-iterable + property-as-method first, with
   fallback to property-as-iterable for this.users patterns.

3. Kotlin parameter extraction: tree-sitter-kotlin parameter nodes use
   positional children (simple_identifier, user_type) not named fields
   (name, type). Added fallback to findChildByType in both
   extractKotlinParameter and extractTypeBinding.

Integration tests added for .keys/.values/Set/MutableMap iteration,
3-arm when/is, multi-call within arms, and when+else branch.

* feat: enhance PHP type resolution for generics and member access in foreach loops

* feat: Phase 6.1 type resolution gap closure — container descriptors, recursive_pattern, class fields

Add 13 missing container type descriptors (Collection, MutableMap, Stream, SortedSet, etc.)
to CONTAINER_DESCRIPTORS for correct element type extraction across C#, Kotlin, and Java.

Extend C# pattern binding to handle recursive_pattern (obj is User { Name: "Alice" } u)
in both is-expression and switch expression contexts.

Add TypeScript class field declaration support (public_field_definition) so for-loop
iteration over this.fieldName resolves element types from class field type annotations.
Includes file-scope fallback in resolveIterableElementType and nested member_expression
handling for this.field.method() patterns.

* docs: add type resolution system documentation with roadmap

Covers the full architecture, resolution tiers (0-2), scope model,
language feature matrix, container descriptors, pipeline integration,
and the Phase 7-9 roadmap for cross-scope propagation, field-type
resolution, and return-type-aware binding.

* feat: Phase 6.2 review findings — C# nested member foreach, C++ deref range-for, Java field_access

Close two gaps found during fourth-pass review of PR #318:

- C# foreach (var user in this.data.Values): nested member_access_expression
  now extracts intermediate property name for scopeEnv lookup
- C++ for (auto& user : *ptr): pointer_expression dereference now recognized
  as range-for iterable

Root causes fixed in shared infrastructure:
- extractSimpleTypeName: add template_type (C++) and generic_name (C#)
- extractGenericTypeArgs: add generic_name for consistency
- type-env.ts: unwrap variable_declaration wrapper in field_declaration
  for declarationTypeNodes capture (zero-allocation manual loop)

Additional review findings addressed:
- Java: add field_access handler for this.data.values() in method_invocation
- C++ pointer_expression: document limitation (*identifier only)
- TypeScript: fix stale comment about property_identifier

All 525 tests pass (278 unit + 247 integration).

* perf: optimize type resolution pipeline — worker threshold, skip graph phases, AST pruning

- Skip worker pool creation for small repos (<15 files or <512KB) — saves 100-400ms
- Add skipGraphPhases option to runPipelineFromRepo to skip MRO/community/process phases
- Add conservative SKIP_SUBTREE_TYPES for leaf-only AST nodes (string, comment, number)
- Pre-compute interestingNodeTypes set — single Set.has() replaces 3 checks per node
- Add fastStripNullable — skip full stripNullable for simple identifiers (90%+ case)
- Replace .children?.find() with manual for loops in extractFunctionName (no array alloc)
- Add hookTimeout: 120000 to vitest.config.ts for CI beforeAll hooks

* fix: review findings — remove template_string from SKIP_SUBTREE_TYPES, handle bare nullable keywords

- Remove template_string and concatenated_string from SKIP_SUBTREE_TYPES
  (template literals contain interpolated expressions with typed code)
- Add FAST_NULLABLE_KEYWORDS check to fastStripNullable for behavioral
  parity with stripNullable on bare null/undefined/void/None/nil
- Add explanatory comment on extractPendingAssignment scopeEnv guard

* feat: add type resolution system and roadmap documentation
2026-03-17 17:10:22 +00:00

12 KiB

Type Resolution Roadmap

This roadmap describes the next major capabilities needed to evolve GitNexus's type-resolution layer from a strong receiver-disambiguation aid into a broader static-analysis foundation.

The roadmap assumes the current system already provides:

  • explicit type extraction from declarations and parameters
  • initializer / constructor inference
  • loop element inference for many languages
  • selected pattern binding and narrowing
  • comment-based fallbacks in JS/TS, PHP, and Ruby
  • constrained return-type-aware receiver inference during call processing

The remaining work is about generalisation, deeper structure modelling, and better propagation.


Principles for Future Work

The type system should continue to preserve the qualities that make it practical today:

  • stay conservative
  • prefer explainable inference over clever but brittle inference
  • limit performance overhead during ingestion
  • keep per-language extractors explicit rather than over-generic
  • separate "better receiver resolution" from "compiler-grade typing"

The goal is not to build a compiler. The goal is to support high-value static analysis for call graphs, impact analysis, context gathering, and downstream graph features.


Near-Term Priority: Generalise Existing Inference

The next biggest gain is not inventing a new type system layer. It is expanding the inference the system already performs so more constructs can benefit from it.

Why this is the right next step

Today, return-type-aware inference already exists in constrained form inside call-processor.ts, and loop element inference already handles many identifier-based iterables.

The most valuable next move is to let those signals participate in more places, especially:

  • iterable expressions rather than only iterable identifiers
  • assignment propagation from call results
  • doc-comment-derived file-scope bindings where local scope is insufficient

Phase 7: Cross-Scope and Return-Aware Propagation

Goal

Allow loop inference and assignment inference to see more than the current function-local environment.

Problems this phase addresses

7A. Iterable expressions in Go and similar cases

for _, user := range getUsers() {
    user.Save()
}

The iterable is a call expression, not an identifier with a local binding.

To resolve user, the loop extractor needs access to a return-type source for getUsers().

7B. File-scope or class-scope iterable typing in PHP

foreach ($this->users as $user) {
    $user->save();
}

If $this->users is typed through a class property annotation or file/class-scope doc-comment information, the current local-scope-only path may not be enough.

7C. Broader use of already-known return types

The system can already infer receiver types from uniquely resolved call results in call-processor.ts. That needs to be generalised so TypeEnv can benefit from it too.

Engineering direction

  • extend loop and propagation extractors so they can access more than the current local scope
  • expose file-scope string bindings where needed
  • introduce a shared returnTypeMap or equivalent lookup mechanism
  • keep the interface change coordinated across extractors to avoid partial semantics by language

Expected impact

This phase should unlock:

  • loop inference for iterable-producing call expressions
  • broader propagation from method / function return types
  • fewer missed bindings in real-world code that avoids explicit variable annotations

Risk level

Medium

This work touches extractor interfaces across multiple languages, so the coordination cost is real. However, the conceptual model is an extension of existing behavior rather than a new analysis paradigm.


Phase 8: Field and Property Type Resolution

Goal

Model class / struct fields so chained member access can be resolved more accurately.

Problems this phase addresses

8A. Deep property chains

user.address.city

Today the system may resolve user -> User, but it cannot generally resolve:

  • address -> Address
  • city -> City or scalar type

8B. Chained method targets through field access

user.address.save()

Without field typing, the resolver cannot reliably identify the receiver type of address.

8C. Pattern destructuring that depends on field knowledge

This is especially relevant for:

  • Rust struct-pattern destructuring
  • PHP chained property access
  • richer TypeScript or Python object-based destructuring in future work

Engineering direction

  • parse field / property declarations per class or struct
  • build a field-type map keyed by owning type
  • teach lookup and chain-resolution logic to walk member segments
  • keep this separate from the base variable-binding layer where possible

Expected impact

This is the biggest unlock for richer static analysis because it allows the graph to model more than just top-level receivers.

It would materially improve:

  • chained property resolution
  • member-based call disambiguation
  • deeper context extraction for downstream tooling

Risk level

High

This is the first phase that pushes the system from variable typing into structural object modelling. It will likely require:

  • schema expansion or new internal maps
  • careful handling of inheritance / embedding / language-specific member semantics
  • broader test coverage than earlier phases

Phase 9: Full Return-Type-Aware Variable Binding

Goal

Make return-type-driven inference a first-class input to TypeEnv, not just a downstream verification path.

Problems this phase addresses

9A. Binding variables from call results

const users = repo.getUsers()

Desired binding:

  • users -> List<User>

9B. Looping directly over call results

for (const user of getUsers()) {
    user.save()
}

Desired binding:

  • user -> User

9C. Broader method-chain inference

repo.getUsers().first()

If return types can propagate more systematically, later chain stages become much more resolvable.

Engineering direction

  • expose return types as reusable inference inputs inside TypeEnv
  • distinguish raw textual return types from normalized receiver-usable types
  • make method-call return inference receiver-aware where necessary
  • avoid over-eager propagation when multiple call targets remain ambiguous

Expected impact

This phase would make the type system feel much closer to a static-analysis substrate rather than a set of local heuristics.

It will especially improve codebases that rely heavily on:

  • service-returned collections
  • builder APIs
  • repository methods
  • chain-heavy fluent interfaces

Risk level

Medium to High

The conceptual basis already exists, but generalising it without introducing false bindings requires careful ambiguity rules.


Language-Specific Gaps

Swift

Current support remains relatively minimal.

Missing or weak areas include:

  • for-loop element binding
  • pattern binding
  • assignment-chain propagation
  • broader expression-based inference

Priority: Medium
Reason: It matters for parity, but the biggest global analysis gains are elsewhere.

Go

Key remaining gap:

  • iterable call expressions in range loops

Priority: High
Reason: Go codebases frequently rely on return-value-based iteration patterns.

PHP

Key remaining gaps:

  • file/class-scope iterable propagation
  • chained property access

Priority: High
Reason: PHP heavily benefits from doc-comment-aware field and property modelling.

Rust

Key remaining gap:

  • struct-pattern field destructuring

Priority: Medium
Reason: Important for completeness, but field-type infrastructure is the real prerequisite.

All languages

Shared missing capabilities:

  • field / property type resolution
  • generalised return-type-aware binding in TypeEnv

Priority: Very High
Reason: These are the biggest remaining blockers to deeper static analysis.


1. Generalise existing return and loop inference

This is the best cost-to-value step.

Deliverables:

  • iterable call-expression support
  • wider access to return-type maps
  • file-scope binding visibility where needed

2. Add field / property type maps

This unlocks the next class of analysis depth.

Deliverables:

  • per-type field metadata
  • chained property resolution
  • better destructuring support

3. Promote return types into first-class TypeEnv inputs

This converts existing downstream validation into a broader inference capability.

Deliverables:

  • call-result variable binding
  • loop inference from call results
  • broader chain propagation

4. Broaden branch-sensitive narrowing where low-risk

After the structural work lands, selective branch refinement becomes more valuable and easier to reason about.


What “Production-Grade Static Analysis” Means Here

For GitNexus, production-grade does not mean replacing a language compiler.

A realistic target is:

  • strong receiver-constrained call resolution across common language idioms
  • reliable handling of typed loops, constructor-like initializers, and common patterns
  • useful return-type propagation for service/repository style code
  • enough field/property knowledge to support chained-member analysis
  • conservative behavior under ambiguity
  • predictable performance during indexing

That would be sufficient for:

  • better call graphs
  • more accurate impact analysis
  • stronger context assembly for AI workflows
  • more trustworthy graph traversal features

Suggested Milestone Definitions

Milestone A — Inference Expansion

Success looks like:

  • loop inference works for identifier iterables and common call-expression iterables
  • simple call-result assignments benefit from return types more broadly
  • no major regression in ambiguity handling

Milestone B — Structural Member Typing

Success looks like:

  • field/property maps exist for class-like types
  • chained access can resolve at least one segment beyond the base receiver
  • field-aware member-call resolution works in the most important languages

Milestone C — Static-Analysis Foundation

Success looks like:

  • return-type-aware variable binding is a first-class part of environment construction
  • chains, loops, and assignments share a coherent propagation model
  • downstream graph features can rely on more than local receiver heuristics

Open Questions for Future Design

These should be resolved before or during implementation of the later phases.

  1. Where should field-type metadata live?
    In TypeEnv, in SymbolTable, or in a dedicated side structure?

  2. How should ambiguity be represented?
    Is undefined sufficient, or do later phases need a richer "known ambiguous" state?

  3. How much receiver context should return-type inference require?
    Some methods only become meaningful once the receiver type is already partially known.

  4. How much branch sensitivity is worth the complexity?
    Some narrowing gives clear value; full control-flow typing likely does not.

  5. Should field typing and chain typing be one phase or two?
    Keeping them separate may reduce risk and make regressions easier to isolate.


Summary

The next stage of the type system should focus on generalising what already works before attempting compiler-like sophistication.

The most important path is:

  1. extend return-type and iterable inference
  2. add field/property type knowledge
  3. promote return-type-aware inference into TypeEnv

That path preserves the current strengths of the system while moving GitNexus materially closer to a robust, production-grade static-analysis foundation.