Repository navigation
fix(flow): track available function signatures through assignments - #1356
Merged
CppCXY merged 1 commit intoNov 4, 2026
Merged
Conversation
Signature help must show the function signatures available at each call.
These describe the parameters and return values. The function definition
provides the main signature. An overload annotation adds another one.
Assignments, casts and branches can change which signatures apply.
Two functions can have the same main signature and different overloads.
The analyzer must track which function is used, even when its type stays
the same:
---@PARAM value number
---@overload fun(value: string): string
local function accept(value)
return value
end
---@PARAM value number
local function replacement(value)
return value
end
accept = (accept) -- keep the string overload
accept = replacement -- remove the string overload
The existing code reads the current function type and then adds the
signatures from the original declaration. This can show an old overload
after a replacement or a cast.
Reuse the code that already follows assignments, casts and branches. Add
a mode that keeps only the original signatures that still apply.
Assigning the function to itself keeps its signatures. Other assignments
remove them. Casts that replace a type remove them. Casts that remove a
type remove only matching signatures. Combine results from branches that
can run.
Use this mode to collect the available function signatures. Later
changes can use the same support to check function annotations and
arguments passed through pcall. A separate signature tracker would need
another copy of the assignment, loop and branch rules, and another
cache.
The changes to the shared code are needed for these reasons:
- Use one work stack for pending assignments, casts and branches. Keep
the existing assignment type checks in one helper as the stack
changes.
- Save results at each step, instead of only where a check starts.
Repeated calls can then reuse earlier work. Keep later casts out of
earlier saved results.
- Check shared earlier code before checking each branch. Long chains of
branches can then reuse saved results without deeply nested Rust
calls.
- Identify saved results by the check mode, original function and full
signature list. Results for different generic type arguments, or lists
with and without the main signature, must stay separate. Keep the
existing rules for loops and unfinished checks.
- Check the current return types before using a function call to rule
out a branch. The types must be fully known. A generic return can
still be false or nil until the call arguments set its type. Such a
return cannot rule out a branch. The function must be local and have
no replacement assignments or declared overloads. Its original return
annotation can describe an earlier function value. Save the result of
checking its assignments so each condition does not scan them all
again.
Use signatures from the current value for replacement functions and
casts that add types. Keep the original main signature if it can still
be used on another branch. Keep the existing generic type information.
Lua tests check signature-help text, inferred types and the amount of
work for repeated calls and long branch chains. Tests and their support
add 637 lines. The 425 net new lines in flow.rs support the new mode,
the shared work stack, saved results and the checks on function return
types.
Normal type inference keeps its current function type information. Rules
for which annotation takes priority, argument checks through pcall, and
wider changes to function-call inference remain separate work.
mareklangiewicz
added a commit
to mareklangiewicz/emmylua-analyzer-rust
that referenced
this pull request
Nov 4, 2026
Upstream replaced TraceOptions with DeclTracePurpose. The fork's capture
read moves onto it as `ReadValue { capture }`. A field, not a new variant,
so every upstream ReadValue use had to be looked at: a variant would have
left `matches!(purpose, ReadValue)` false for captured reads.
Fork behaviour kept: unknown_assign_keeps_kind, a nil base for an
uninitialized local (now decl_position_type) and the memo for guards
collected before a node.
Two changes the merge needed, both from upstream's new tests:
- live_decl_overloads appended a local's overloads whatever casts ran
before the read. overload_candidates_for_decl now goes through
original_decl_signatures_at, which replaces the fork's check for any
earlier assignment, so an assignment on one branch no longer drops
overloads on the other.
- Incoming casts now apply to the memoized state as guards do. They used
to skip the memo, and 64 branches with casts ran past the 5 s limit in
test_deep_branch_casts_merge_surviving_overloads. Correlated narrowings
still take the uncached walk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NxUUNMvTbsNag6WkLaLu7
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.
Signature help can show old overloads after a function is replaced by an assignment or a cast. This change keeps only the original signatures that can still apply at the call.
Two functions can have the same main signature and different overloads. The analyzer must track which function is used, even when its type stays the same:
Reuse the existing code that follows assignments, casts and branches. Add a mode that keeps only the original signatures that still apply. Self-assignment keeps them. Other assignments and replacement casts remove them. Removal casts remove only matching signatures. Combine signatures from branches that can run.
The shared flow code needs these changes:
falseornilcannot provide this proof. Save the check of the function's assignments so each condition does not scan them again.Keep the existing generic type information and the main signature from each branch where the original function still applies. Normal type inference keeps its current function type information.
This shared support also lets later annotation and
pcallchecks use the same assignment and branch rules. A separate signature tracker would need another copy of those rules and another cache. Annotation priority, argument checks throughpcall, and wider changes to function-call inference remain separate work.