Skip to content

A local declared again in its own scope is offered and called as the one in effect - #438

Merged
RyeMutt merged 1 commit into
developfrom
rye/luau-shadowed-locals
Oct 10, 2026
Merged

RyeMutt merged 1 commit into
developfrom
rye/luau-shadowed-locals

Conversation

@RyeMutt

@RyeMutt RyeMutt commented Oct 10, 2026

Copy link
Copy Markdown
Member

Fixes the intermittent failure of INTEGRATION_TEST_alluaufragment test 2 (about 5 runs in 12 on macOS arm64):

scope3.lsl 29:1 a new line, a name: ~test(kind 1|3, context 2|2)

Cause

Luau keeps every declaration a scope makes among its bindings, an unordered_map keyed by each local's AST node, so ordered by the address the parser put it at. Two places look a local up by name there and take the first the map gives:

  • autocomplete (autocompleteStatement / autocompleteExpression), over the whole script and over a fragment;
  • a fragment's check bringing in the last check's type of each name it uses (cloneTypesFromFragment → linearSearchForBindingPair).

Where one scope declares a name twice, which declaration that is changes from run to run. scope3.lsl, written as SLua, has local function test() and local test = 1 at its top level, so at test = 2 the fragment and the whole script picked independently. Both directions were seen (1|3 and 3|1), so the whole-script answer was random too, not only the fragment's. Signature help over a fragment could likewise show the function a later declaration had shadowed.

Fix

ALLuauShadowed (indra/alscript/luau/alluaushadowed.{h,cpp}), held around Luau::autocomplete and both fragment calls, takes out of each scope of the module the declarations of a name other than the one in effect at the place — the last declared before it, but for one whose local the place is in — and puts them back afterwards.

Where a fragment declares a name again beside the one it brought from the last check, both sit in the fragment's own scope, which Luau makes and reads in the one call; there the fragment declines and the whole script answers.

Luau is slua's, so this is worked around here rather than patched.

Tests

  • New alluaufragment test 6: four completion places and a signature-help call over a redeclared local, over 24 rounds that move the addresses. It failed in every run before the fix, and fails in every run with any one of the three guards taken out.
  • alluaufragment passed 40 runs in a row with the old solver and 16 with the new; the corpus tallies are unchanged (890 of 1069 Tailslide places still answered over a fragment).
  • All 49 alscript CTest tests pass, _newsolver variants included. The newview tests weren't run; none of them asks for a completion or signature help.

Not pinned down: why the flake first showed after 310a8df. Luau's pick is address-dependent either way, so the likeliest reason is that scope3.lsl converts (or converts this way) only with the Tailslide slua carries.

🤖 Generated with Claude Code

…one in effect

Luau keeps every declaration a scope makes among its bindings, an
unordered_map keyed by each local's node, and so ordered by the address
the parser put it at. Its autocomplete, and a fragment's check bringing
in the last check's type of each name the fragment uses, look a local
up by its name there and take the first the map gives. Where a scope
declares a name twice, which one that was changed from run to run:
after `local function test()` and `local test = 1`, `test` was offered
as the function in one run and as the number in the next, over the
whole script and over a fragment alike, and signature help over a
fragment could show the function a later declaration had shadowed.

alluaufragment 2 failed in about a third of runs on scope3.lsl, whose
SLua has both at its top level: the fragment and the whole script
picked differently.

While Luau is asked, ALLuauShadowed now takes out of each scope of the
module the declarations of a name other than the one in effect at the
place -- the last declared before it, but for one whose `local` the
place is in -- and puts them back after. Where a fragment declares a
name again beside the one it brought from the last check, both are in
the fragment's own scope, which Luau makes and reads in the one call;
there the whole script answers instead. Luau is slua's, so this is
worked around here rather than patched.

alluaufragment 6. alluaufragment passes 40 runs in a row, and 16 with
the new solver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fb0f484e-0121-4578-89c9-5b8528bb2c61

📥 Commits

Reviewing files that changed from the base of the PR and between 193bf34 and fbe3349.


📒 Files selected for processing (6)
  • indra/alscript/CMakeLists.txt
  • indra/alscript/luau/alluaucompletion.cpp
  • indra/alscript/luau/alluaufragment.cpp
  • indra/alscript/luau/alluaushadowed.cpp
  • indra/alscript/luau/alluaushadowed.h
  • indra/alscript/tests/alluaufragment_test.cpp

 _________________________________________________________________________________________
< The function of good software is to make the complex appear to be simple. - Grady Booch >
 -----------------------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@RyeMutt
RyeMutt merged commit 0112d48 into develop Oct 10, 2026
16 of 20 checks passed
@RyeMutt
RyeMutt deleted the rye/luau-shadowed-locals branch October 10, 2026 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant