fix(resolve): enum name/ordinal go-to-def + whole-receiver-as-T generic return substitution - #318
Merged
Merged
Conversation
Hessesian
added a commit
that referenced
this pull request
Sep 14, 2026
Triaged all 10 findings from the automated review (1 posted inline,
9 suppressed but listed in the review body). Fixed 6, each with a
red-before-fix-verified regression test:
- companion_applies was gated on nested.is_empty(), so Outer.Inner.member()
never reached Inner's own companion object even though anchors_for had
already normalized the anchor down to Inner itself.
- The last-resort JAR-extension fallback in resolve_qualified keyed on the
qualifier's literal ROOT spelling ("Outer") instead of the leaf
("Inner") — extension_by_receiver keys by the receiver's own simple name.
- supertype_extension was skipped whenever inherited_members was non-empty,
with no regard for arity — the same bug own_type_extension was already
fixed for a few lines above, on its sibling field.
- The Java-getter synthetic-property retry checked the RECEIVER's own
declaring-file language, rejecting a getter genuinely inherited from a
Java ancestor whenever the receiver's own class is Kotlin-declared. Now
checks each getter candidate's own declaring file.
- resolve_from_class_hierarchy_scoped's per-ancestor closure fell through to
an unscoped find_name_in_uri whenever the container-tagged lookup came up
empty — but an empty result there usually just means "this ancestor has
no member of this name" (normal while walking up), not "no container
metadata exists". Reintroduced the exact cross-container leak this file's
own container-tag scoping exists to prevent.
- The denylist prefix check only matched a DESCENDANT package
(trailing-dot starts_with), never the bare prefix package itself.
Also fixed: default_kotlin_import_tie_break now gated on the origin file's
own language — it was applying Kotlin's default-import set even from a
Java origin, which can never implicitly import kotlin.*.
Investigated but NOT changed (documented, not silently dropped):
- package.rs's coarse whole-JAR package fallback (Copilot flagged it as
imprecise for a multi-package JAR) — tried removing it, but that broke
three real existing tests that specifically depend on it for JARs with no
per-symbol package table. Reverted; the imprecision risk is real but this
codebase's own tests show the fallback is the tested, intentional
trade-off, not an oversight.
- The line-scan fallback in find_name_in_uri_after_line isn't
container-bounded even when container_name is known (only matters for
un-indexed names, e.g. constructor params, colliding with a same-named
sibling elsewhere in a degenerate JAR-stub file) — a correct fix needs the
container's own end-line threaded through, which isn't available at every
call site; left for its own investigation.
- The Java-getter retry doesn't filter candidates to zero-arg overloads —
needs arity data threaded through member_tiers's Vec<Location>, which
currently loses it; left for its own investigation.
Verified: cargo test (1933 passed), cargo clippy -D warnings clean, and a
full resolution-accuracy scan against the real Moneta corpus shows no new
anomalies in the Gap/FilteredCandidate lists (a small aggregate recall
delta vs. the prior measurement is fully explained by this branch not yet
containing PR #318's separate enum-intrinsics fix).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF
Hessesian
added a commit
that referenced
this pull request
Sep 14, 2026
Triaged all 10 findings from the automated review (1 posted inline,
9 suppressed but listed in the review body). Fixed 6, each with a
red-before-fix-verified regression test:
- companion_applies was gated on nested.is_empty(), so Outer.Inner.member()
never reached Inner's own companion object even though anchors_for had
already normalized the anchor down to Inner itself.
- The last-resort JAR-extension fallback in resolve_qualified keyed on the
qualifier's literal ROOT spelling ("Outer") instead of the leaf
("Inner") — extension_by_receiver keys by the receiver's own simple name.
- supertype_extension was skipped whenever inherited_members was non-empty,
with no regard for arity — the same bug own_type_extension was already
fixed for a few lines above, on its sibling field.
- The Java-getter synthetic-property retry checked the RECEIVER's own
declaring-file language, rejecting a getter genuinely inherited from a
Java ancestor whenever the receiver's own class is Kotlin-declared. Now
checks each getter candidate's own declaring file.
- resolve_from_class_hierarchy_scoped's per-ancestor closure fell through to
an unscoped find_name_in_uri whenever the container-tagged lookup came up
empty — but an empty result there usually just means "this ancestor has
no member of this name" (normal while walking up), not "no container
metadata exists". Reintroduced the exact cross-container leak this file's
own container-tag scoping exists to prevent.
- The denylist prefix check only matched a DESCENDANT package
(trailing-dot starts_with), never the bare prefix package itself.
Also fixed: default_kotlin_import_tie_break now gated on the origin file's
own language — it was applying Kotlin's default-import set even from a
Java origin, which can never implicitly import kotlin.*.
Investigated but NOT changed (documented, not silently dropped):
- package.rs's coarse whole-JAR package fallback (Copilot flagged it as
imprecise for a multi-package JAR) — tried removing it, but that broke
three real existing tests that specifically depend on it for JARs with no
per-symbol package table. Reverted; the imprecision risk is real but this
codebase's own tests show the fallback is the tested, intentional
trade-off, not an oversight.
- The line-scan fallback in find_name_in_uri_after_line isn't
container-bounded even when container_name is known (only matters for
un-indexed names, e.g. constructor params, colliding with a same-named
sibling elsewhere in a degenerate JAR-stub file) — a correct fix needs the
container's own end-line threaded through, which isn't available at every
call site; left for its own investigation.
- The Java-getter retry doesn't filter candidates to zero-arg overloads —
needs arity data threaded through member_tiers's Vec<Location>, which
currently loses it; left for its own investigation.
Verified: cargo test (1933 passed), cargo clippy -D warnings clean, and a
full resolution-accuracy scan against the real Moneta corpus shows no new
anomalies in the Gap/FilteredCandidate lists (a small aggregate recall
delta vs. the prior measurement is fully explained by this branch not yet
containing PR #318's separate enum-intrinsics fix).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF
Hessesian
added a commit
that referenced
this pull request
Sep 14, 2026
…easured gap fixes (#317) * fix(resolve): stop leaking a sibling class's member across a JAR's synthetic file boundary Real bug behind navController.navigate(route = "...") never resolving on a real Android corpus: androidx.navigation's NavController.navigate is a real overloaded JVM member (not an extension), and NavHostController extends it without declaring its own navigate. Both classes compile into one synthetic per-JAR FileData. Two compounding bugs in the JAR-derived member lookup: 1. find_name_in_uri_after_line's final fallback matched the closest same-named symbol "at or after" a container's line with no container check at all -- for a JAR packing several classes into one file, this mis-attributed NavController's own navigate overloads to NavHostController purely by file position, corrupting resolve_qualified into treating an inherited member as an own, wrong-arity one. 2. resolve_from_class_hierarchy_scoped's ancestor walk used the single- result, arity-blind find_name_in_uri per ancestor instead of the container- and overload-aware find_all_names_scoped_to_container, so even reaching the right ancestor only ever returned one arbitrary overload. find_name_in_uri_after_line now takes an optional container name and only trusts a symbol's real container tag over position; a new find_all_names_with_container_in_uri helper lets the hierarchy walk look up every overload scoped to the ancestor's class name directly, without first needing to resolve that ancestor's own declaration Location. Verified on the real Moneta corpus: navigate goes from 220-224 occurrences in the resolution-accuracy Gap top-20 to zero. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * fix(resolve): resolve a Java getter as the Kotlin synthetic property it exposes Kotlin's Java-interop synthetic-property rule: `obj.foo` retries as `obj.getFoo()` when `foo` names no real member and the declaring file is Java. Implements the read-only, getFoo-only slice of that rule inside resolve_qualified's uppercase-branch member tier, after the real member/ inherited-member lookups (now unified behind member_or_inherited_member) return empty and before the supertype-extension tier. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * fix(resolve): match the JAR-extension probe's fallback by declaration, not name alone, and de-duplicate the star-import package list and default-import constant resolve_qualified's item-1 JAR-extension fallback materialized its Location by finding the first symbol in the declaring file whose bare name matched, not the actual extension declaration -- so a same-named, unrelated symbol elsewhere in the file could win, returning the wrong range. Fixed by matching through extension_declaration_matches (receiver + container), the same helper the sibling in-scope lookup already uses. The fallback stays deliberately fail-open (no extension_is_in_scope check) -- that behavior is load-bearing for resolve_extension_fn_on_uppercase_qualifier and is now guarded by its own regression test. Also de-duplicates two small helpers that straddle the resolve.rs/ package_scope.rs boundary planned for the next task: a single star_import_packages() replaces three copies of the same star-import filter, and is_default_import_package() now reads KOTLIN_DEFAULT_IMPORT_PACKAGES instead of carrying its own identical 10-entry list. Corpus measurement (resolution-accuracy /home/ocel/Work/Moneta/android): member recall 90.8% (150626/165876), no regression from the most recent known baseline (90.3%, 149718/165795) -- expected, since item 1 is a goto-definition precision fix the benchmark doesn't score and items 2-3 are behavior-preserving deduplications. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * refactor(resolver): split resolve.rs into a spine and nine named siblings Pure mechanical move, zero behavior change: resolve.rs shrinks from 2753 to 686 lines. Moved by resolution-order taxonomy into container.rs, package.rs, tie_break.rs, package_scope.rs, imports.rs, extension.rs, scope_check.rs, qualified.rs, and platform_types.rs, per the task-2 brief's module boundary table (moved innermost-first, compiling and testing after each module). Includes the brief's one documented exception: corrects the wrong comment in resolve_qualified (now in qualified.rs) claiming an "exact-key extension" had already been ruled out before the supertype-extension fallback — it hadn't; member_or_inherited_member's inherited-member half is a pure member lookup. 1910 tests pass before and after (0 failed, 3 ignored), clippy and fmt clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * refactor(resolve): give resolve_qualified one parse, one anchor, and one precedence ladder `resolve_qualified` implemented the same three-stage sequence twice -- parse the qualifier, normalize it to a receiver anchor, run a member/extension precedence ladder -- once for an uppercase (`Outer.Inner`) root and once for a lowercase (`variable.field`) root. Because the sequence was written twice with no shared boundary, the two ladders had drifted: a tier that exists on one branch and not the other, and an anchor re-derived per branch so an extension probe could stay keyed on the ROOT while the anchor had already moved to the leaf. The four stages are now named and separately testable: parse_qualifier -> QualifierRoot (no Indexer, no IO) anchors_for -> ReceiverAnchor (qualifier -> receiver, nothing else) candidates_on -> QualifiedCandidates (the four tiers) companion_member_on (a different question, kept separate) `ReceiverAnchor` carries the leaf type's full declaration `Location` -- file and range -- because `find_all_names_scoped_to_container` scopes its member search by the container's declaration range, not merely its file. It also carries the LEAF type's simple name, which makes "probe keyed on the root while the anchor has moved to `Inner`" unrepresentable rather than merely fixed. Deliberate, named behaviour correction (not a regression): the two branches did not merely differ in which tiers existed, they checked them in a DIFFERENT ORDER. The uppercase branch probed the own-type extension FIRST and returned early, before its member tier was ever computed; the lowercase branch ran members first and reached its extension tier last. One ladder can encode only one order, and the lowercase branch's is the correct one -- Kotlin resolves a real member over a same-named extension. The uppercase branch's extension-first check was the outlier and is corrected, not averaged. `resolve_qualified_uppercase_receiver_own_member_now_wins_over_own_type_extension` is the test for exactly this, and is red against the previous code. `resolve_imported_extension_preferred_over_member` encoded the old order and is inverted and renamed to `resolve_member_preferred_over_imported_extension`. Precedence is carried by candidate ORDER, not by refusing to compute a tier: a same-named member does not always satisfy the call's arity, so the own-type extension is appended below a winning member rather than dropped, matching what the supertype-extension tier has always done. Measured: short-circuiting it instead cost 791 `IMockProvider.loadJSONFromAssets` call sites (a 1-arg extension shadowed by a 2-arg member), which fell out of resolution into the ambiguous bucket. Moneta corpus (`resolution-accuracy`, before -> after): member recall 90.9% (150924/166021) -> 90.9% (150553/165558) Gap (member) 8214 -> 8121 FilteredCandidate 6883 -> 6884 No new name in either the Gap or FilteredCandidate top-20; remaining per-name movement is single-digit and within the tool's run-to-run variance, with `collectEmit` (-13) and `collectLatest` (-9) leaving the ambiguous bucket. Tests: 1957 -> 1964 (7 added, 0 failures). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * test(resolve): lock in the own-type extension tier's coverage post-Task-2b Task 2b's decomposition of resolve_qualified (parse_qualifier -> anchors_for -> candidates_on) already populates own_type_extension unconditionally, for both TypePath and ValuePath roots and every nesting depth, since both families normalize through the same ReceiverAnchor before the ladder runs. Task 3's planned ~20-line fix (jar-promotion-latency-budget-plan) is fully absorbed: all six cases from its brief (lowercase-root wrong-arity member, uppercase-root regression guard, member-wins-on-arity-match, supertype fallback still reached, and both finding-11 nested-type-qualifier exits) are green against today's code with no production change. These tests are the proof. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * fix(resolver): stop dropping the hierarchy walk's budget at the ambiguity-safe tail `supertype_targets` threads its own `sidecar_budget: &mut usize` into the per-hop `ensure_jar_definitions_for` promotion, but its two calls into `resolve_symbol_hierarchy_ambiguity_safe` (the nested-type "outer" resolution and the final leaf-name tail) never received it. That function's `resolve_chain` tail (`ResolveIo::HierarchyAmbiguitySafe`) reads via `indexer.lookup_definitions`, which internally promotes with a hardcoded zero budget -- so a supertype name resolvable only through this ambiguity- safe path, and living in a not-yet-materialized JAR, silently fell through to a zero-budget promotion even mid-walk with real budget remaining. Adds `sidecar_budget: &mut usize` to `resolve_symbol_hierarchy_ambiguity_safe` and spends it on a pre-promotion of `name` before falling into `resolve_chain`, threaded through from both `supertype_targets` call sites. Plain `&mut usize`, matching every other budget site in this codebase -- independent of the still-parked `LatencyClass`/`JarPromotionBudget` work. New test: hierarchy_walk_shares_its_budget_with_the_ambiguity_safe_tail, modeled on the promotion-counting harness at tests.rs:7568. Verified red against pre-fix code, green after. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * test(resolve): split a two-assertion test to satisfy the no-"and" naming rule Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * fix(resolve): a missing nested-type segment must yield no anchor, not fall back to the root Copilot review finding on #317: `Outer.Missing.member` -- `Outer` resolves but `Missing` names no real nested type of it -- fell through to the declaration-less anchor fallback keyed on `root` ("Outer") whenever the nested walk produced zero anchors, regardless of why it was empty. That silently resolved `member` against `Outer` itself, as if `.Missing` had never been written. The fallback is now reserved for a genuinely unresolved root (no indexed declaration at all); a resolved root whose nested segment failed yields no anchors. Also fixes a Copilot finding on #315: a decoy-overload test comment said `navigate(Int)` but the fixture encodes a 0-arg overload via `param_counts: (0, 0)` -- text now matches what's actually asserted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF * fix(resolve): address PR #317 Copilot review findings Triaged all 10 findings from the automated review (1 posted inline, 9 suppressed but listed in the review body). Fixed 6, each with a red-before-fix-verified regression test: - companion_applies was gated on nested.is_empty(), so Outer.Inner.member() never reached Inner's own companion object even though anchors_for had already normalized the anchor down to Inner itself. - The last-resort JAR-extension fallback in resolve_qualified keyed on the qualifier's literal ROOT spelling ("Outer") instead of the leaf ("Inner") — extension_by_receiver keys by the receiver's own simple name. - supertype_extension was skipped whenever inherited_members was non-empty, with no regard for arity — the same bug own_type_extension was already fixed for a few lines above, on its sibling field. - The Java-getter synthetic-property retry checked the RECEIVER's own declaring-file language, rejecting a getter genuinely inherited from a Java ancestor whenever the receiver's own class is Kotlin-declared. Now checks each getter candidate's own declaring file. - resolve_from_class_hierarchy_scoped's per-ancestor closure fell through to an unscoped find_name_in_uri whenever the container-tagged lookup came up empty — but an empty result there usually just means "this ancestor has no member of this name" (normal while walking up), not "no container metadata exists". Reintroduced the exact cross-container leak this file's own container-tag scoping exists to prevent. - The denylist prefix check only matched a DESCENDANT package (trailing-dot starts_with), never the bare prefix package itself. Also fixed: default_kotlin_import_tie_break now gated on the origin file's own language — it was applying Kotlin's default-import set even from a Java origin, which can never implicitly import kotlin.*. Investigated but NOT changed (documented, not silently dropped): - package.rs's coarse whole-JAR package fallback (Copilot flagged it as imprecise for a multi-package JAR) — tried removing it, but that broke three real existing tests that specifically depend on it for JARs with no per-symbol package table. Reverted; the imprecision risk is real but this codebase's own tests show the fallback is the tested, intentional trade-off, not an oversight. - The line-scan fallback in find_name_in_uri_after_line isn't container-bounded even when container_name is known (only matters for un-indexed names, e.g. constructor params, colliding with a same-named sibling elsewhere in a degenerate JAR-stub file) — a correct fix needs the container's own end-line threaded through, which isn't available at every call site; left for its own investigation. - The Java-getter retry doesn't filter candidates to zero-arg overloads — needs arity data threaded through member_tiers's Vec<Location>, which currently loses it; left for its own investigation. Verified: cargo test (1933 passed), cargo clippy -D warnings clean, and a full resolution-accuracy scan against the real Moneta corpus shows no new anomalies in the Gap/FilteredCandidate lists (a small aggregate recall delta vs. the prior measurement is fully explained by this branch not yet containing PR #318's separate enum-intrinsics fix). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Hessesian
changed the base branch from
fix/extension-supertype-variable-receiver
to
main
September 14, 2026 08:57
…eceiver-as-T in generic returns Two root causes found while scouting the "name"/"title"/"text" corpus Gap entries the user flagged as real (not import-filtering noise): - `.name`/`.ordinal` are compiler-generated kotlin.Enum instance members, same class of gap as `.entries`/`values()`/`valueOf()` (already handled). A stale doc comment claimed they were "already handled" by the indexer's synthetic_enum_field, but that only feeds type inference, never a real go-to-def Location -- qualified resolution dead-ended. Extended synthesize_enum_members to also synthesize name/ordinal, the same way entries/values/valueOf already are. Confirmed on the real Moneta corpus: "name" drops out of the member-ref Gap list entirely (was #182 e.g. at InsuranceExtensions.kt:136, `it.name.equals(...)` over `.entries`). - `StrategyOutcome::finalize`'s ReceiverDerived arm only substituted a type param NESTED inside the receiver's own type argument (`List<T>`'s `T`), via build_type_arg_subst. It missed the shape where the extension's own type parameter IS the whole receiver (`fun <T> T?.required(field): T`) -- effective_type ("String") declares no class type params, so raw_return ("T") stayed unsubstituted. Filled the gap with build_ext_fn_type_subst (an existing helper already used by a different call path), without overriding any key the existing substitution already resolved. CACHE_VERSION bumped: the parser's synthesized-symbol set changed shape. Note: the second fix is real and covered by a unit test, but does not by itself move the "title"/"text" corpus Gap counts -- corpus verification after the fix showed chain.rs's receiver_based_method never reaches this substitution for required()'s real declaration shape (a member extension on a bare generic type parameter receiver): find_method_return_type_for_type returns None before substitution ever runs, because extension_by_receiver keys extensions by literal receiver text ("T"), with no wildcard match for a concrete receiver ("String"). That is a separate, deeper registry gap, not this fix's scope -- left for its own investigation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF
…aming rule Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF
Hessesian
force-pushed
the
fix/enum-intrinsics-and-generic-return-subst
branch
from
September 14, 2026 09:02
22d3fa9 to
3ecfc69
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Resolver correctness issues and unsafe callable metadata lookup remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Fixes enum name/ordinal go-to-definition and whole-receiver generic return substitution.
Changes:
- Synthesizes enum
nameandordinalmembers. - Adds receiver-based generic substitution and regression tests.
- Bumps the cache version to 32.
File summaries
| File | Summary |
|---|---|
src/resolver/tests.rs |
Tests enum member resolution; coverage should use instance access and reject Flavor.name. |
src/parser.rs |
Adds synthetic enum members; current exposure can affect invalid qualified and unqualified lookups. |
src/indexer/infer/mod_tests.rs |
Adds whole-receiver generic substitution coverage. |
src/indexer/infer/chain.rs |
Applies receiver substitutions; callable metadata lookup may select the wrong declaration and adds unnecessary hot-path work. |
src/indexer/cache.rs |
Bumps the cache version. |
Review details
Suppressed comments (3)
src/indexer/infer/chain.rs:619
- This lookup is unconditional for every
ReceiverDerivedcall, including methods whoseraw_returnis already concrete. It repeats a name scan and can invokeensure_jar_definitions_foron the chain-inference hot path, adding avoidable work to ordinary calls; gate it on an unresolved generic return or reuse metadata from method resolution.
if let Some(info) = ctx.deps.find_fun_callable_info(ctx.fn_name, ctx.uri) {
if !info.extension_receiver_type.is_empty() && !info.type_params.is_empty() {
src/parser.rs:555
name/ordinalare instance-only members, but synthesizing them withcontainer: Some(cls.name)makes them direct members of the enum's type scope.resolve_qualifiedtreats aTypePathsuch asFlavor.nameas companion-only (seesrc/resolver/qualified.rs:92-105), so this causes invalid class-qualified accesses to resolve. Keep these symbols available from value/receiver lookups without exposing them from the type-root candidate tier, or model them throughkotlin.Enum; add a negativeFlavor.nameregression test.
symbols.push(SymbolEntry {
name: "name".to_owned(),
kind: SymbolKind::PROPERTY,
visibility: cls.visibility,
range: cls.selection_range,
src/resolver/tests.rs:1152
Flavoris passed as a type qualifier here, so this exercisesFlavor.name, not the intended instance access (flavor.name/it.name). Perresolve_qualified, type-qualified lookup must not return instance members; with the new symbols this assertion passes only by introducing that invalid behavior and therefore cannot prove the Moneta regression. Test through a value or lambda receiver and separately assert thatFlavor.nameremains unresolved.
assert!(
!resolve_symbol(&idx, "name", Some("Flavor"), &uri).is_empty(),
"Flavor.name (kotlin.Enum.name) did not resolve"
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+618
to
+622
| if let Some(info) = ctx.deps.find_fun_callable_info(ctx.fn_name, ctx.uri) { | ||
| if !info.extension_receiver_type.is_empty() && !info.type_params.is_empty() { | ||
| let ext_subst = build_ext_fn_type_subst( | ||
| &info.extension_receiver_type, | ||
| &receiver_type, |
Comment on lines
+551
to
+555
| symbols.push(SymbolEntry { | ||
| name: "name".to_owned(), | ||
| kind: SymbolKind::PROPERTY, | ||
| visibility: cls.visibility, | ||
| range: cls.selection_range, |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two scouted fixes from the "name"/"title"/"text" Gap investigation the user pushed back on (correctly flagging real value, not import-filtering noise). Stacked on #317.
.name/.ordinalresolution: these are compiler-generatedkotlin.Enuminstance members, same class of gap as.entries/values()/valueOf()whichsynthesize_enum_membersalready handled. Its own doc comment claimed.name/.ordinalwere "a separate, already-handled concern" via the indexer'ssynthetic_enum_field— that helper only feeds type inference (hover/inlay chain propagation), never a real go-to-defLocation, so qualified resolution dead-ended. Extended the same synthesis site. Confirmed on the real Moneta corpus:namedrops out of the member-ref Gap list entirely (was fix: recover annotation-mis-parsed interfaces + strip suspend before receiver type #182, e.g.Insurance.EType.entries.firstOrNull { it.name.equals(...) }).Generic return-type substitution for whole-receiver-as-
T:StrategyOutcome::finalize'sReceiverDerivedarm only substituted a type param nested inside the receiver's own type argument (List<T>'sT, viabuild_type_arg_subst). It missed the shape where the extension's own type parameter IS the whole receiver (fun <T> T?.required(field): T) — filled the gap usingbuild_ext_fn_type_subst, an existing helper already used by a different call path, without overriding any key the existing substitution already resolved. Covered by a unit test using the realresolve_call_expr_typeentry point (not just the substitution helper in isolation).CACHE_VERSIONbumped (31→32): the parser's synthesized-symbol set changed shape.Known limitation (documented, not fixed here)
The second fix is real and correctly tested, but corpus verification showed it does not by itself move the
title/textGap counts. Root cause:chain.rs'sreceiver_based_methodnever reaches the new substitution forrequired()'s actual real-world declaration shape (a member extension on a bare generic type parameter receiver,fun <T : Any> T?.required(...): Tinsideclass NullableScope) —find_method_return_type_for_typereturnsNonebefore substitution ever runs, becauseextension_by_receiverkeys extensions by literal receiver text ("T"), with no wildcard match against a concrete receiver ("String"). That's a separate, deeper extension-registry gap affecting both type inference and (likely) other consumers — left for its own scoped investigation rather than folded in here.Test plan
cargo test— 1930 passed, 0 failedcargo clippy --all-targets -- -D warnings— cleanresolution-accuracy: overall member-ref recall 91.1% (flat/slightly up vs. prior 90.8-91.0%),namegone from the Gap top-20🤖 Generated with Claude Code
https://claude.ai/code/session_01L7ZonwYUh94VsuQphiHykF