Avoid _ collision in bind-*:after property accessor#83572
Draft
chsienki wants to merge 3 commits intodotnet:mainfrom
Draft
Avoid _ collision in bind-*:after property accessor#83572chsienki wants to merge 3 commits intodotnet:mainfrom
chsienki wants to merge 3 commits intodotnet:mainfrom
Conversation
Repros DevCom 10961132 / dotnet/razor regression introduced by PR dotnet/razor#11214 ("[Fuse] bind-Value:attribute support", merged 2024-11-18). That PR added a design-time `_ = nameof(...)` discard line for @bind-x:set/event/after directive parameters so the IDE has a target for FAR/Hover. The discard is emitted unconditionally as the literal token `_`, which collides with user-named `_` lambda parameters introduced by `Context="_"` on a templated parent component: <OuterComponent Context="_"> // _ : FilterContext <MyComponent @bind-Value="ParentValue" @bind-Value:after="OnAfter" /> </OuterComponent> The generated code becomes (paraphrased): AddAttribute(1, "ChildContent", (RenderFragment<FilterContext>)((_) => (__builder2) => { // ... bind:after lowering ... _ = nameof(MyComponent.Value); // assignment to _ : FilterContext })); Roslyn parses `_ = nameof(...)` as an assignment to the in-scope `_` parameter rather than a discard, producing CS0029 (cannot convert string to FilterContext) plus cascading CS1662s on the wrapping template lambdas. This commit only adds the failing test and baselines. The fix to WriteDesignTimePropertyAccessor (wrapping the discard in a `{ }` block scope) follows in a separate commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Emit `var (_, _) = (nameof(...), 0);` instead of `_ = nameof(...);` in
WriteDesignTimePropertyAccessor so that `_` is unambiguously a discard
pattern.
A bare `_ = ...` resolves to any outer `_` lambda parameter (introduced
e.g. by `Context="_"` on a templated parent component), turning the
discard into a typed assignment that fails with CS0029 / cascading
CS1662 errors. Wrapping the discard in `{ }` doesn't help because C#
block scope doesn't shadow outer parameters of the same name; using
`var (_, _)` deconstruction does, because positional discards in a
deconstruction are always discards regardless of any in-scope `_`.
Fixes the BindToComponent_WithAfter_InsideTemplateWithDiscardContext
test added in the previous commit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mechanical baseline regeneration to reflect the new `var (_, _) = (nameof(...), 0);` discard pattern emitted by WriteDesignTimePropertyAccessor in the previous commit. No source changes; only `.codegen.cs` and `.mappings.txt` files for the existing BindTo* tests that already covered @bind-x:set, @bind-x:event, and @bind-x:after lowering. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thank you so very much for attending to this! 🙏🙏🙏 |
davidwengier
approved these changes
May 6, 2026
Member
davidwengier
left a comment
There was a problem hiding this comment.
LGTM.
Unless you have any ideas for allowing the razor generator to be specially blessed so it, and it alone, can use unspeakable identifiers 😁
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Issue:
When using a
bind-MyValue:after="..."tag helper, the emitted code is transformed into a callback onComponent.MyValueChanged. As such there is no expression to emit the#linemapping forMyValuemeaning things like FAR, GTD can't work on it.To make these work, PR dotnet/razor#11214 introduced a
_ = nameof(Component.MyValue);expression to code gen that can have a#linemapping applied to it.When passing child content to a
RenderFragment<T>the caller can optionally provide aContextvariable name for disambiguation between multiple render fragment contexts (see Templated Components). If a user chooses_as the context name, the generated lambda will have a parameter named_. C# backwards compatibility rules mean that when a lambda has a single parameter named_, that is in actually an accessible parameter named_and not a discard. Any discards in that scope are invalid as they refer to the parameter.Combining these two things: if you set a render fragment context to
_and then use thebind-*:aftertag helper inside the child content, we emit an assignment to_, but that_is the lambda parameter, not a discard, and C# emits an error saying it can't assign a string to whatever the type ofTis in the render fragment.Fix:
Instead of a raw discard, emit
var (_, _) = (nameof(...), 0);instead. Positional discards invar (_, _) =deconstruction always discard, even with an in-scope _ parameter, and the compiler will still remove this line altogether as unused code.This is the only place in the compiler that emits a bare
_as an expression, so we don't need to fix it anywhere else.Microsoft Reviewers: Open in CodeFlow