Scenario contact: @ilonatommy
Scenario
A for loop that renders a button per item, with each handler referring to the loop counter, compiles cleanly and then does the wrong thing: every button acts on the last item, because the counter is one variable shared by every iteration. This validates that the build points the pattern out before it reaches a user.
Minimum build
.NET 11 Preview 7 or later.
Configurations to cover
These diagnostics are produced by the compiler, so they do not vary by render mode. The configuration below is the one needed to reproduce the runtime behavior the warning predicts, not a matrix to work through.
Also exercise
Setup
| Id |
Title |
Code fix |
BL0014 |
For loop iterator used inside a closure |
No |
What to build
A component with a for loop rendering a list, covering each shape the analyzer is meant to flag:
- A button whose
@onclick lambda uses the loop counter.
- A lambda that also takes event args and still uses the counter,
@(e => Handle(e, i)).
- An
<input @bind="items[i]" />, where the counter is used as an indexer in a two-way binding.
- A child component taking an
EventCallback, <MyComponent OnClick="@(() => Handle(i))" />.
- A child component whose
ChildContent renders the counter, <MyComponent>Item @i</MyComponent>.
Then the shapes that are correct and must stay quiet:
- The same loop with
var index = i; copied inside the body.
- The same list rendered with
foreach.
foreach (var i in Enumerable.Range(0, items.Length)).
Show which item each control acted on, so the wrong behavior is visible rather than inferred.
Things to try
- Building at the command line and in an IDE, and comparing what each reports.
- Running the app and clicking every button in the flagged loop.
- Typing into the
@bind inputs and checking which item each one wrote to.
- Copying the counter to a local inside the loop body, then rebuilding and clicking again.
- Rewriting the loop as
foreach, then rebuilding.
- Using the counter in the loop body but not inside a closure, to see whether it is flagged.
Expected behavior
The pattern is flagged in every shape where it misbehaves, and the shapes that work are left alone.
Must hold
- All five flagged shapes produce a
BL0014 warning naming the iterator variable: the @onclick lambda, the lambda with event args, the @bind indexer, the EventCallback, and the ChildContent.
- Clicking the buttons in the flagged loop reproduces the behavior the message predicts: they act on the final value rather than their own.
- Typing into a flagged
@bind input writes to the wrong item, matching what the warning predicts.
- The copied-local loop, the
foreach loop, and the foreach over Enumerable.Range produce no warning and behave correctly.
- Using the counter outside a closure produces no warning.
- The warnings appear both at the command line and in the IDE, at the same lines.
Expected differences between configurations
- None. The warning is produced at compile time and is identical in every configuration.
Documentation to use
What to report
Report results using the format described in the validation testing manual. Include link to a repository with the test app.
Scenario contact: @ilonatommy
Scenario
A
forloop that renders a button per item, with each handler referring to the loop counter, compiles cleanly and then does the wrong thing: every button acts on the last item, because the counter is one variable shared by every iteration. This validates that the build points the pattern out before it reaches a user.Minimum build
.NET 11 Preview 7 or later.
Configurations to cover
These diagnostics are produced by the compiler, so they do not vary by render mode. The configuration below is the one needed to reproduce the runtime behavior the warning predicts, not a matrix to work through.
Also exercise
Setup
BL0014What to build
A component with a
forloop rendering a list, covering each shape the analyzer is meant to flag:@onclicklambda uses the loop counter.@(e => Handle(e, i)).<input @bind="items[i]" />, where the counter is used as an indexer in a two-way binding.EventCallback,<MyComponent OnClick="@(() => Handle(i))" />.ChildContentrenders the counter,<MyComponent>Item @i</MyComponent>.Then the shapes that are correct and must stay quiet:
var index = i;copied inside the body.foreach.foreach (var i in Enumerable.Range(0, items.Length)).Show which item each control acted on, so the wrong behavior is visible rather than inferred.
Things to try
@bindinputs and checking which item each one wrote to.foreach, then rebuilding.Expected behavior
The pattern is flagged in every shape where it misbehaves, and the shapes that work are left alone.
Must hold
BL0014warning naming the iterator variable: the@onclicklambda, the lambda with event args, the@bindindexer, theEventCallback, and theChildContent.@bindinput writes to the wrong item, matching what the warning predicts.foreachloop, and theforeachoverEnumerable.Rangeproduce no warning and behave correctly.Expected differences between configurations
Documentation to use
What to report
Report results using the format described in the validation testing manual. Include link to a repository with the test app.