Resolve consecutive WithoutEndTag tag helpers as siblings - #13211
Closed
chsienki wants to merge 1 commit into
Closed
Resolve consecutive WithoutEndTag tag helpers as siblings#13211chsienki wants to merge 1 commit into
chsienki wants to merge 1 commit into
Conversation
When a StartTagOnly (TagStructure.WithoutEndTag) tag helper is bound, its body children -- which the HTML parser nested underneath it because the tag was left unclosed (`<a><b><c>` parses as a > b > c) -- are promoted to be siblings of the tag helper. The element walker iterates children in reverse, so it never revisits these newly inserted positions; the promoted siblings were left as unresolved elements and later unwrapped to literal markup. As a result only the first of a run of consecutive WithoutEndTag helpers bound, and the rest were emitted as plain text. Resolve the promoted siblings in place after the promotion, mirroring the re-resolution already performed on the ConvertToPlainElement path. Resolution runs in reverse because resolving a promoted StartTagOnly sibling can itself insert further siblings after it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e4a482bf-ffc0-4a98-a673-863b5e84c6d8
Member
|
Wrong repo :P |
Member
Author
picard facepalm.gif |
Member
Author
|
Reopened against the correct repo: dotnet/roslyn#84771 (Razor now lives in dotnet/roslyn). |
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.
Summary
Consecutive tag helpers declared with
TagStructure.WithoutEndTagand written without a self-closing slash (e.g.<meta-description><meta-keywords><head-custom>) only bind the first helper. The rest are emitted as literal, unprocessed markup.This is a regression from the deferred tag helper lowering work (#12957), which moved tag helper resolution after IR lowering. It was reported downstream as dotnet/razor#13212, where a GrandNode app rendered a blank page (
Uncaught ReferenceError: Vue is not defined) because the<head>script-registration tag helpers silently stopped running and the Vue bundle was never emitted.Root cause
The HTML parser nests consecutive unclosed tags, so
<a><b><c>parses asa > b > c. WhenDefaultTagHelperResolutionPhase.ResolveElementbindsaas aStartTagOnlyhelper, it promotesa's parser-nested body children to be siblings inserted aftera. But the element walkerResolveElementsiterates children in reverse, so it has already moved past that position and never resolves the promoted siblings. They remainUnresolvedElementIntermediateNodes and are later unwrapped to literal HTML.The
ConvertToPlainElementpath already re-resolves its promoted siblings; the directResolveElementStartTagOnlypath was missing the equivalent step.Fix
After promoting the
StartTagOnlyelement's children to siblings, resolve them in place (in reverse, since resolving a promotedStartTagOnlysibling can itself insert further siblings). This mirrors the existing pattern inConvertToPlainElementAndResolve.Testing
Two new integration tests in
TagHelpersIntegrationTest:ConsecutiveWithoutEndTagTagHelpers_AllBind— three consecutiveWithoutEndTaghelpers all bind.MixedNestedStartTagOnlyAndHtmlTagHelpers_AllResolveCorrectly— a tangled mix of a nestable helper, consecutiveWithoutEndTaghelpers, a normal helper, and real HTML (<section>,<div>); asserts every helper binds in document order and real markup is preserved.Both fail without the fix (only the first helper binds) and pass with it. The existing tag helper suites remain green (Language
~TagHelper: 953 pass; MVC extensions codegen/baseline: 178 pass).