Adds the two cases the deep-dive flagged: a built-in-typed primary-ctor param
(int count) must not fabricate a referenced node, and the branch's struct_declaration
claim is locked by a struct primary-ctor test. Also adds the CHANGELOG entry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A C# 12 primary constructor puts its parameters on the class/record declaration
itself, which the extractor never walked: class Holder(IDep dep) emitted no
references edge Holder->IDep, and because dep was never registered in the class
receiver table, a dep.Method() call inside the class was dropped too. Scan the
declaration's parameter_list, register each param name->type, and emit the
param type reference — mirroring the field/property handlers. Built-in and
type-parameter types are not fabricated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Track C# receiver types per lexical declaration scope (byte ranges) and
resolve by the call's position, instead of a method-wide flat table that
poisoned a name on any None-typed binding. A typed static local-function
parameter now keeps resolving even when an out var reuses the name in the
enclosing body. Fixes a regression from #2346; #2299 cross-method
independence and field-conflict poisoning are unchanged.
Thanks @JensD-git for the bisect and repro.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- C# receivers declared inline via out-var / is / case / switch-arm patterns
are now typed into the per-method table, so their member calls resolve (#2346).
- partial class halves across files now merge to one class node (new
_merge_csharp_partial_class_nodes pass, mirroring the Swift-extension merge),
so cross-half member calls resolve instead of splitting the class (#2332).
- Kotlin anonymous-object (object : Foo {}) members now get nodes, contains/
implements edges, and their calls resolve (#2347).
All in-corpus only, never a wrong edge.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The C# receiver-type table was per-FILE and poisoned a name on any
conflicting/untypable rebind anywhere in the file, so `var item = items[i]`
in one method silently deleted the true calls edge in another method where
the same name was a typed parameter (~2.3% of calls lost, per the reporter).
Ported C# to a per-method table mirroring the Java resolver (per-class field
scope + method params/locals, method-local poisoning only) and retired the
file-wide table. The namespace resolver, ambiguity bail, and inherits-chain
guards are unchanged, so the no-wrong-edge bar holds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adapted from #1620 by @TheFedaikin, reworked onto v8 as a focused change
(without the module split or the references-fallback behavior change).
Builds on the shipped #1609 resolver: instead of bailing when a receiver's
class name is ambiguous corpus-wide, the declared type is resolved with a
shared CsharpNameResolver (same-namespace, using-directive, and alias aware)
against the caller's namespace/scope, falling back to the unique bare match
only when scoping is non-decisive. Adds base./this.field receivers and
inherited-member lookup through the inherits chain (an out-of-corpus base
poisons the lookup, so no wrong edge). The per-file type table now poisons a
name on any conflicting rebinding, killing the wrong-edge class where a local
shadows a field of a different type. C#-gated; never emits a wrong edge.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The reported bug — a method chained directly onto a `new X(...)` expression
(no intermediate variable) producing no calls edge — is already fixed on v8:
`new Merger(ctx).Combine(...)` emits calls -> Merger.Combine. Add a regression
test so the fluent new-expression receiver stays covered.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
C# had no member-call resolver (unlike Swift/Python/Ruby/TS/C++/ObjC), so
`recv.Method()` fell back to a bare method-name match against label_to_nid —
which, under ambiguity, silently mis-bound `_server.Save()` to an unrelated
`Cache.Save()`. That's a WRONG edge, not just a missing one, and it left
delegation-heavy C# call graphs (wrappers, service layers) blind across typed
member/param boundaries.
Mirrors the C++ #1547 pattern:
- capture the member_access_expression receiver (simple identifier or `this`)
into member_receiver and set is_member_call in the C# invocation branch;
- defer ALL C# member calls with a receiver to the resolver (tgt_nid = None) so
the bare in-file match can't fire, and emit a raw_call tagged lang="csharp";
- _csharp_member_type_table: file-wide name -> Type from fields, properties,
parameters, and locals (incl. `var v = new T()`), first-binding-wins;
- _resolve_csharp_member_calls: `this` -> enclosing class (EXTRACTED),
capitalized -> the named type (EXTRACTED), else the receiver's table type
(INFERRED), each gated by the single-definition guard; no method on the type
-> no edge. Registered for .cs.
Verified: the ambiguous `_server.Save()` now resolves to Server.Save and NOT
Cache.Save; field/param/local/this/Type.static/cross-file all resolve; dynamic
receiver and absent-method emit nothing; unqualified calls unregressed. 8 new
tests, full suite 2841.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>