/** * Resolve a compound-receiver expression's TYPE — `user.address.save()`, * `svc.get_user().save()`, `c.greet().save()` — to the class def of * the value the receiver expression produces. * * Three shapes (parsed C-family-style): * - bare identifier `name` — look up via typeBinding chain * - dotted `obj.field[.field]…` — walk fields via class-scope typeBindings * - call `expr.method()` — recurse into expr, find method's return-type * typeBinding on its class, resolve to a class * * **Field-fallback heuristic** (Phase-9C "unified fixpoint"): when the * receiver class has no `methodName`, walk its fields and try the * lookup on each field's type. Useful for dynamically-typed languages * (Python). Strictly-typed languages should pass * `fieldFallbackOnMethodLookup: false` via `ScopeResolver`. * * Generic for any C-family language (`.` member access, `()` call * syntax). Languages with non-C-family syntax (Ruby blocks, COBOL) * either don't trigger the call branch or skip this pass entirely. */ import type { ScopeId, SymbolDefinition } from '../../../../_shared/index.js'; import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js'; import type { WorkspaceResolutionIndex } from '../workspace-index.js'; interface ResolveCompoundReceiverOptions { /** When true (default), if method lookup fails on the receiver's * class, walk its fields and try the lookup on each field's class. * Phase-9C "unified fixpoint" — Python-shaped heuristic. */ readonly fieldFallback?: boolean; /** Language-specific accessor unwrap — `data.Values` on a * Dictionary-typed receiver yields V (C#), etc. Returns the * element type's simple name, or `undefined` to let the regular * field-walk handle the access. */ readonly unwrapCollectionAccessor?: (receiverType: string, accessor: string) => string | undefined; /** Walk up from the class scope to ancestor (Module) scopes when * looking up a method's return-type typeBinding. Only enable for * languages that hoist return-type bindings to Module scope (C#); * otherwise we risk picking up unrelated module-level bindings. */ readonly hoistTypeBindingsToModule?: boolean; /** Strip C-style cast expressions from the receiver text before * resolving it (`stripCastWrappers`). Default `false` — the text * reaches the resolver untouched and no cast logic runs. See the * `ScopeResolver` contract toggle of the same name for the * classifier grammar and per-language opt-in rules. */ readonly stripReceiverCastExpressions?: boolean; } export declare function resolveCompoundReceiverClass(receiverText: string, inScope: ScopeId, scopes: ScopeResolutionIndexes, index: WorkspaceResolutionIndex, options?: ResolveCompoundReceiverOptions, depth?: number): SymbolDefinition | undefined; /** * Peel C-style cast layers off a receiver-position expression: * `((Target)((Other)expr))` → `workingText` `expr`, `castType` * `Target`. Pure text scan — no scope or index access — consumed by * `resolveCompoundReceiverClass` when a language opts in via * `stripReceiverCastExpressions`. Track the outermost meaningful cast * type: the cast narrows the receiver's declared type, so the caller * resolves the CAST type, not the underlying expression's type. * * Each peeled paren group with a non-empty trailing expression (a * cast candidate) is classified three ways: * (a) simple identifier (`SIMPLE_CAST_TYPE_RE`) → cast type * captured (outermost capture wins; later simple groups are * noise casts, as in decompiler output like * `((Target)((Object)expr))`); * (b) type-shaped but unparseable here — dotted / generic / array * (`UNPARSEABLE_CAST_TYPE_RE`) → this IS a cast, but its type * cannot be looked up: report `unresolvableCast: true` so the * caller resolves nothing rather than falling through to the * pre-cast expression's own declared type (the pre-#2353 safe * no-op for these shapes); * (c) anything else → not a cast: stop scanning and return the * text peeled so far for the normal resolver. * A paren group with an EMPTY remainder is never a cast candidate — * `((…))` / `(foo)` is a redundant-paren unwrap: unwrap and re-scan * without capturing anything. * * Known limitation: the paren scan is not string-literal-aware — a * `)` inside a quoted call argument (e.g. `((T)f(")")).g`) mis-scans * the group boundary. Such shapes classify as not-a-cast and fall * through safely to the normal resolver. */ export declare function stripCastWrappers(text: string): { workingText: string; castType: string | undefined; unresolvableCast: boolean; }; export {};