Add analyzer to prevent local functions accessing RenderTreeBuilder from parent scope #63826
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #53269
Problem
Local functions defined within
@{ ... }
blocks in Razor components can captureRenderTreeBuilder
instances from their parent scope, leading to incorrect rendering behavior. This pattern appears to work but actually corrupts the rendering output instead of properly writing to child component render fragments.The issue occurs because C# scoping rules cause the local function to capture the
RenderTreeBuilder
from the parent context rather than using the builder that should be passed to theRenderFragment
.Solution
This PR adds a new analyzer diagnostic ASP0029 that detects local functions which access
RenderTreeBuilder
methods from captured variables in their parent scope.New Diagnostic: ASP0029
Detection Logic
The analyzer intelligently identifies problematic patterns while allowing safe alternatives:
✅ Allowed (Safe Patterns):
RenderTreeBuilder
as a parameterRenderTreeBuilder
at all❌ Detected (Problematic Patterns):
RenderTreeBuilder
from captured variablesExample
Before (causes runtime issues):
After (recommended approaches):
Testing
Added comprehensive test coverage with 7 test cases covering:
All existing RenderTreeBuilder analyzer tests continue to pass.
Impact
This change helps developers avoid a subtle but problematic pattern that can cause rendering corruption in Blazor applications. The analyzer provides clear, actionable feedback at compile time rather than allowing runtime failures.
Original prompt
This section details on the original issue you should resolve
<issue_title>Prevent use of local functions inside markup</issue_title>
<issue_description>[Edit by @SteveSandersonMS] This issue was originally reported by @verdie-g as follows below the line. On investigation the problem is that C# has added a new syntax that doesn't work in Razor.
The Razor compiler allows arbitrary C# code within
@{ ... }
blocks. Unfortunately this means it allows the use of local functions in a way that confuses the parsing logic, causing it to use the wrong__builder
instance. Example:Here, the child content of
FluentTreeItem
should be compiled as aRenderFragment
that acts on whateverRenderTreeBuilder
is passed in. But because of C# scoping rules, theRenderFragment
actually acts on the__builder
captured from its parent context, so it is simply corrupting the output instead of doing something useful.Possible solutions:
@{ ... }
specifically. However that's probably impractical because Razor doesn't parse the contents of@{ ... }
.RenderTreeBuilder
is invoked. For example if the runtime set an "rendering in progress" flag on it before it starts rendering and synchronously unsets that flag at the end of rendering, then it would have caught this case because child components are rendered afterwards (not recursively), so when the child is rendered it would see it's trying to write to a builder that does not have the "rendering in progress" flag set.NullReferenceException
instead of corrupt output. Obviously that's not super easy to understand but avoids any perf cost.Is there an existing issue for this?
Describe the bug
I'm rendering a blazor wasm component using a recursive C# method and while it's working fine using C# only (
OpenComponent
,AddAttribute
, etc.), it fails when returning HTML from that recursive method.Expected Behavior
I'm expecting a tree structure to be built and clicking on a line should expand its children but it seems like the children are not rendered and an error is thrown on click.
Steps To Reproduce
I was not able to reproduce the issue without the library fluentui-blazor.
dotnet new install Microsoft.FluentUI.AspNetCore.Templates dotnet new fluentblazorwasm --name aspnetcore-issue-53269 cd aspnetcore-issue-53269
Then replace
Home.razor
withClick on the item generated and check the error in the console.
It could be an error with the library (initially reported here microsoft/fluentui-blazor#1289) but this code works fine:
Exceptions (if any)