dotnet / dotnet/aspnetcore

[Validation] Analyzer diagnostics for loop iterators captured in closures

Open
#68,490 3 comments 0 reactions 2 assignees Claimed by @ilonatommy View on GitHub
area-blazor Validation validation-scenario
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 10h
Merged PRs (30d)
281

Description

**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

* Blazor Web App
* [ ] Static SSR
* [x] Interactive Server
* [ ] Interactive WebAssembly
* [ ] Interactive Auto
* [ ] Standalone WebAssembly
* [ ] Hybrid (MAUI)

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

* [ ] Published output
* [ ] An existing .NET 10 app upgraded to .NET 11
* [ ] Trimming or ahead-of-time compilation
* [ ] More than one server instance, or a proxy in front
* [ ] Hot Reload
* [x] An IDE as well as the command line
* [ ] Container

## 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 ``, where the counter is used as an indexer in a two-way binding.
* A child component taking an `EventCallback`, ``.
* A child component whose `ChildContent` renders the counter, `Item @i`.

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

* [ASP.NET Core Blazor event handling](https://learn.microsoft.com/aspnet/core/blazor/components/event-handling?view=aspnetcore-11.0)

## What to report

Report results using the format described in the [validation testing manual](https://github.com/dotnet/aspnetcore/issues/68479). Include link to a repository with the test app.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.