Finding
src/lint.c:1836-1845 explains why W015 is scoped to function-name clobbering only, and hands the general case off:
The general local-discipline lint belongs to the scope-aware name-resolution pass (#404), which can do the dataflow to tell benign reuse from a real clobber; #396 tracks that.
Both referenced issues are closed (#404 "Lint/LSP: scope-aware name-resolution pass", #396 "Lint: fence the two load-bearing traps"). Nothing open tracks the general fence, so the comment reads as "covered elsewhere" while pointing at nothing.
The gap that remains open
llms.txt:63-64 calls missing local "the single most common scope bug". Neither shape is currently flagged at v0.38.0 / 078e759:
total is 100
define helper as:
total is 0 # no `local` -> clobbers the module binding
for i in range of n:
total is total + i
return total
print of (str of (helper of 4))
print of ("caller total is now " + (str of total))
define f(flag) as:
if flag == 1:
local t is 1
else:
t is 2 # sibling branch, no `local` -> writes outward
return t
print of (str of (f of 0))
Both: no issues found.
The second is the more interesting one, because it is the trap llms.txt:66-68 documents explicitly ("each sibling if/elif/else branch needs its own local for a first assignment") and it has a statically decidable signature: a name is local-declared on one branch of an if/elif/else and bare-assigned on a sibling. That does not need full dataflow, and it does not have W015's noise problem — the presence of the local on the sibling branch is direct evidence the author intended a local.
Not arguing with W015's scoping
The rationale at lint.c:1836-1845 is evidence-backed (155 benign firings for the broad reading) and I think it is right — a blanket outer-mutation warning would be noise. This is only about the tracker being dangling and about the narrower, higher-precision subcase above being available without the full pass.
Suggested scope
- Reopen or supersede the tracking issue so
lint.c:1844's reference resolves.
- Land the sibling-branch check as its own W-code — high precision, no dataflow required.
- Leave the general case where it is until the scope-aware pass exists.
Related: #396, #404, #373, W015/W016 in src/lint.c.
Finding
src/lint.c:1836-1845explains why W015 is scoped to function-name clobbering only, and hands the general case off:Both referenced issues are closed (#404 "Lint/LSP: scope-aware name-resolution pass", #396 "Lint: fence the two load-bearing traps"). Nothing open tracks the general fence, so the comment reads as "covered elsewhere" while pointing at nothing.
The gap that remains open
llms.txt:63-64calls missinglocal"the single most common scope bug". Neither shape is currently flagged at v0.38.0 /078e759:Both:
no issues found.The second is the more interesting one, because it is the trap
llms.txt:66-68documents explicitly ("each siblingif/elif/elsebranch needs its ownlocalfor a first assignment") and it has a statically decidable signature: a name islocal-declared on one branch of an if/elif/else and bare-assigned on a sibling. That does not need full dataflow, and it does not have W015's noise problem — the presence of thelocalon the sibling branch is direct evidence the author intended a local.Not arguing with W015's scoping
The rationale at
lint.c:1836-1845is evidence-backed (155 benign firings for the broad reading) and I think it is right — a blanket outer-mutation warning would be noise. This is only about the tracker being dangling and about the narrower, higher-precision subcase above being available without the full pass.Suggested scope
lint.c:1844's reference resolves.Related: #396, #404, #373, W015/W016 in
src/lint.c.