Summary
When assigning the result of string.gsub(...) back to a for-loop control variable (line = ...), VSCode Lua Debug fails to hit breakpoints inside the loop body—even though the logic clearly runs. Rewriting the result to a different local/global variable (cleaned = ...) restores normal breakpoint behavior.
Environment
Minimal Reproducible Code:
function func(lines)
for line in lines do
-- Breakpoint here will NOT be hit:
line = string.gsub(line, '#.*', '')
end
end
Replace that line with:
-- Breakpoint here WILL be hit:
cleaned = string.gsub(line, '#.*', '')
And the breakpoint reliably hits.
Bytecode Comparison (luac -l -p)
Problematic case: line = string.gsub(...)
...
9 [4] CALL 6 4 2
10 [4] MOVE 5 6 ; line = result of gsub
...
Working case: cleaned = string.gsub(...)
...
9 [4] CALL 6 4 2
10 [4] SETTABUP 0 0 6 ; _ENV["cleaned"] = result
...
In the broken version, line is the loop control variable from TFORCALL. Assigning to it uses MOVE instead of SETLOCAL, and this appears to prevent the debug hook from attaching a breakpoint on that line.
Suspected Cause
The assignment line = ... is treated specially due to its relation to the for iterator. Lua internally may optimize or generate different metadata for it, and the debug extension seems to skip that line for hook registration.
Expected Behavior
Breakpoints should be respected regardless of whether the assignment target is a loop control variable or not, especially if the line clearly contains a side-effecting function call like string.gsub.
Summary
When assigning the result of
string.gsub(...)back to a for-loop control variable (line = ...), VSCode Lua Debug fails to hit breakpoints inside the loop body—even though the logic clearly runs. Rewriting the result to a different local/global variable (cleaned = ...) restores normal breakpoint behavior.Environment
jit.off(true, true))Minimal Reproducible Code:
Replace that line with:
And the breakpoint reliably hits.
Bytecode Comparison (luac -l -p)
Problematic case:
line = string.gsub(...)Working case:
cleaned = string.gsub(...)In the broken version,
lineis the loop control variable fromTFORCALL. Assigning to it usesMOVEinstead ofSETLOCAL, and this appears to prevent the debug hook from attaching a breakpoint on that line.Suspected Cause
The assignment
line = ...is treated specially due to its relation to theforiterator. Lua internally may optimize or generate different metadata for it, and the debug extension seems to skip that line for hook registration.Expected Behavior
Breakpoints should be respected regardless of whether the assignment target is a loop control variable or not, especially if the line clearly contains a side-effecting function call like
string.gsub.