-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Open
Labels
analyzerIndicates an issue which is related to analyzer experienceIndicates an issue which is related to analyzer experiencearea-blazorIncludes: Blazor, Razor ComponentsIncludes: Blazor, Razor Components
Milestone
Description
Summary
Add a Roslyn analyzer to detect and warn when a for loop iterator variable is captured in a lambda expression, EventCallback, two-way binding (@bind), or other closure context within Razor components. This is a common pitfall that leads to unexpected runtime behavior because all closures reference the same variable, which holds the final value after the loop completes.
Cases Where the Analyzer SHOULD Warn ⚠️
1. Lambda expressions in event handlers capturing for loop variable
@for (var i = 0; i < items.Length; i++)
{
<button @onclick="@(() => SelectItem(i))">Item @i</button>
}2. Two-way binding with @bind using loop variable as indexer
@for (var i = 0; i < stringArray.Length; i++)
{
<input type="text" @bind="stringArray[i]" />
}3. EventCallback parameters with captured loop variable
@for (var i = 0; i < 5; i++)
{
<MyComponent OnClick="@(() => HandleClick(i))" />
}4. RenderFragment/ChildContent capturing loop variable
@for (var i = 0; i < 5; i++)
{
<MyComponent>Count: @i</MyComponent>
}5. Lambda with event args still capturing loop variable
@for (var i = 1; i < 4; i++)
{
<button @onclick="@(e => UpdateHeading(e, i))">Button #@i</button>
}Cases Where the Analyzer Should NOT Warn ✅
1. Local variable copy within the loop body
@for (var i = 0; i < items.Length; i++)
{
var index = i;
<button @onclick="@(() => SelectItem(index))">Item @index</button>
}2. Using foreach (iteration variable is scoped per iteration in C# 5.0+)
@foreach (var item in items)
{
<button @onclick="@(() => SelectItem(item))">@item.Name</button>
}3. Using foreach with Enumerable.Range
@foreach (var i in Enumerable.Range(0, items.Length))
{
<button @onclick="@(() => SelectItem(i))">Item @i</button>
}4. Using Index() LINQ method (.NET 9+) for index and value
@foreach (var (index, item) in items.Index())
{
<button @onclick="@(() => SelectItem(index))">@item.Name</button>
}5. Using Select with index overload
@foreach (var entry in items.Select((item, index) => (item, index)))
{
<button @onclick="@(() => SelectItem(entry.index))">@entry.item.Name</button>
}6. Direct method reference (no lambda/closure)
@for (var i = 0; i < items.Length; i++)
{
<button @onclick="HandleClick">Item @i</button>
}7. Loop variable used only in non-closure contexts (display only)
@for (var i = 0; i < items.Length; i++)
{
<span>Item @i</span> // No closure, just rendering
}boukenka, MariovanZeist and rogihee
Metadata
Metadata
Assignees
Labels
analyzerIndicates an issue which is related to analyzer experienceIndicates an issue which is related to analyzer experiencearea-blazorIncludes: Blazor, Razor ComponentsIncludes: Blazor, Razor Components