A C# method called through a static factory or fluent chain — `Foo.Create().Bar()`, `JObject.Parse(s).Property(...)`, `Instant.FromUtc(...).InZone(zone)` — lost the receiver's type, so the chained method didn't resolve and the call was invisible to callers/impact/trace. Ports the #645/#608 mechanism to C# (additive, like Java #751): - Part 1: capture C# return types in the extractor, reading the `returns` field (`static Foo Create()` -> `Foo`); predefined/array/generic/nullable/namespaced types are normalized or skipped. - Part 2: encode a chained `member_access_expression` receiver (`Foo.Create(args).Bar()`) as `inner().Bar` with normalized empty parens, so factory calls that take arguments still split. Non-chained member calls keep their existing `recv.Method` text. - Part 3: resolve via the shared matchDottedCallChain (now Java/Kotlin/C#), validated by resolveMethodOnType so a wrong inference yields NO edge. Known limitation (safe): C# extension-method chains don't resolve, since the method lives on the extension class, not the receiver's type — no edge, never a wrong one. Validated: synthetic decoy + args + absent-method safety tests; full suite green; real-repo A/B on Newtonsoft.Json (945 .cs: +3, 0 lost) and nodatime (488 .cs: +73, 0 lost) — node count identical (no explosion), 0 edges lost, precision spot-checked verbatim (Instant.FromUtc().InZone(), Offset.FromHoursAndMinutes().Plus(), OffsetDateTimePattern.CreateWithInvariantCulture().WithTwoDigitYearMax()). EXTRACTION_VERSION 7 -> 8. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3e04650850
commit
aa07dc59d4
@@ -583,8 +583,8 @@ export function matchPhpCallChain(
|
||||
* (its declared return type); the outer method is then resolved and VALIDATED on
|
||||
* it (resolveMethodOnType requires `Type::method` to exist), so a wrong inference
|
||||
* yields no edge rather than a wrong one (e.g. a same-named `bar()` on an
|
||||
* unrelated class is never matched). Shared by the JVM dot-notation languages
|
||||
* (Java, Kotlin) — same receiver shape, same `Class::method` qualified names.
|
||||
* unrelated class is never matched). Shared by the dot-notation languages
|
||||
* (Java, Kotlin, C#) — same receiver shape, same `Class::method` qualified names.
|
||||
*/
|
||||
export function matchDottedCallChain(
|
||||
ref: UnresolvedRef,
|
||||
@@ -1062,11 +1062,11 @@ export function matchReference(
|
||||
if (result) return result;
|
||||
}
|
||||
|
||||
// 1d. JVM (Java / Kotlin) chained static-factory / fluent call —
|
||||
// 1d. Dotted chained static-factory / fluent call (Java / Kotlin / C#) —
|
||||
// `Foo.getInstance().bar()` encoded as `Foo.getInstance().bar` (#645/#608
|
||||
// mechanism). Resolve bar's class from getInstance's declared return type, then
|
||||
// validate the method on it.
|
||||
if (ref.language === 'java' || ref.language === 'kotlin') {
|
||||
if (ref.language === 'java' || ref.language === 'kotlin' || ref.language === 'csharp') {
|
||||
result = matchDottedCallChain(ref, context);
|
||||
if (result) return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user