Skip to content

fix(completion): offer using-for extensions on values, not just non-contract types - #233

Draft
rifuki wants to merge 1 commit into
asyncswap:mainfrom
rifuki:fix/completion-using-for-on-values
Draft

rifuki wants to merge 1 commit into
asyncswap:mainfrom
rifuki:fix/completion-using-for-on-values

Conversation

@rifuki

@rifuki rifuki commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

First slice of #225 — the using SafeERC20 for IERC20 case, where extensions never showed up on a variable declared as IERC20.

The suppression behind it was deliberate. The comment explains it: typing Lock. should give Lock's own members, not everything from using Pool for *. That's right. But the check asks whether the type is a contract, and IERC20. and a paymentToken declared as IERC20 carry the same typeIdentifier — so values got suppressed along with type names.

That distinction can't come from the type. It has to come from where the receiver was resolved, which already knows: a name_to_type hit is a variable, a name_to_node_id hit is a contract name. It was just being dropped before completions_for_type saw it. This threads a ReceiverKind down instead. Casts, mapping indexes and chained members count as values regardless of the name in front, so IERC20(addr). gets extensions too. Public signatures are unchanged.

One extra hop for the cast case: a contract name with no reverse entry in type_to_node falls back to the synthetic __node_id_N marker, which can never match a real key like t_contract$_IHooks_$1840, so lookup_using_for now also matches on node id.

Tests use the poolmanager.json fixture, which already has using Hooks for IHooks and a hooks variable typed IHooks. hasPermission and isValidHookAddress only exist on the library, never on the interface, so they tell the two apart — I deliberately don't assert on names like beforeSwap, since IHooks declares those itself. hooks. and IHooks(addr). both fail on main; IHooks. not offering them already worked and is now locked in.

cargo test 662 passed, clippy and fmt clean.

This is extracted and rebased out of my draft #226. The nested-enum and live-edit parts of #225 are separate and not here. #226 shows a stale diff that predates #224 — I'll split the rest out and close it.

…ontract types

`using SafeERC20 for IERC20` produced no completions on a variable declared as
`IERC20`. The suppression that caused it was deliberate but keyed on the wrong
thing:

    let is_contract_name = resolved_node_id
        .map(|nid| cache.contract_kinds.contains_key(&nid))

The intent, per the comment above it, was that typing a *type name* — `Lock.` —
should offer Lock's own members rather than functions from `using Pool for *`.
That is right. But the check asks "is this type a contract?", and `IERC20.` and
a `paymentToken` declared as `IERC20` carry the identical typeIdentifier, so the
answer is yes for both. Values lost their extensions along with type names.

The distinction cannot be recovered from the type. It has to come from where
the receiver was resolved, which already knows: `name_to_type` means a variable,
`name_to_node_id` means a contract/library/interface name. That was simply
discarded before reaching `completions_for_type`.

Threads a `ReceiverKind` down from the resolvers instead. Plain access keeps
whatever the name resolved to; a cast (`IERC20(addr).`), a mapping index, and
any member reached by walking a chain are values regardless of the name in
front, so they get extensions too.

One extra hop was needed for the cast case. A contract name with no reverse
entry in `type_to_node` resolves to the synthetic `__node_id_N` marker, which
can never match a real using-for key like `t_contract$_IHooks_$1840`, so
`lookup_using_for` now falls back to matching on the node id.

Covers repro 1 of asyncswap#225. The nested-enum and live-edit repros in that issue are
separate and not addressed here.

Tests use the poolmanager.json fixture, which carries `using Hooks for IHooks`
and a `hooks` variable declared as `IHooks`. `hasPermission` and
`isValidHookAddress` exist only on the library, never on the interface, so they
tell the two sources apart:

  hooks.          must offer them   — failed on main
  IHooks(addr).   must offer them   — failed on main
  IHooks.         must not          — already correct, now locked in
@rifuki
rifuki force-pushed the fix/completion-using-for-on-values branch from e48c50f to b4f3ea9 Compare August 25, 2026 09:59
@rifuki
rifuki marked this pull request as draft August 25, 2026 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant