File: Language\Intermediate\UnresolvedAttributeIntermediateNode.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace Microsoft.AspNetCore.Razor.Language.Intermediate;
 
/// <summary>
/// An intermediate node representing an attribute whose final form (plain HTML attribute
/// vs tag helper bound attribute) is not yet determined. Pre-lowered fallback forms are
/// stored so the resolution phase does not need syntax tree access.
/// </summary>
internal sealed class UnresolvedAttributeIntermediateNode : IntermediateNode
{
    /// <summary>The attribute name (e.g., "Value", "@bind-Value", "@onclick").</summary>
    public string AttributeName { get; set; } = string.Empty;
 
    /// <summary>Whether this is a minimized attribute (no value).</summary>
    public bool IsMinimized { get; set; }
 
    /// <summary>The raw value content of the attribute (for all-literal values only).</summary>
    public string? ValueContent { get; set; }
 
    /// <summary>
    /// Source span covering the attribute value content (between quotes).
    /// Computed from syntax tree positions during lowering so the resolution phase
    /// can use it without needing syntax tree access.
    /// </summary>
    public SourceSpan? ValueSourceSpan { get; set; }
 
    /// <summary>
    /// The attribute structure (quote style). Pre-computed during initial lowering
    /// so the resolution phase does not need to inspect syntax.
    /// </summary>
    public AttributeStructure AttributeStructure { get; set; }
 
    /// <summary>
    /// Source span of the attribute name only (not the whole attribute).
    /// Pre-computed during initial lowering for tag helper property OriginalAttributeSpan.
    /// </summary>
    public SourceSpan? AttributeNameSpan { get; set; }
 
    /// <summary>
    /// Pre-lowered form of this attribute for when the element IS a tag helper but this
    /// attribute is unbound (doesn't match any tag helper property). The tag helper runtime
    /// represents unbound HTML attributes as <see cref="TagHelperHtmlAttributeIntermediateNode"/>
    /// wrapping a structured <see cref="HtmlAttributeIntermediateNode"/> with merged value tokens.
    /// Produced during lowering via <c>VisitAttributeValue</c>, which merges adjacent literal
    /// tokens into a single <see cref="HtmlContentIntermediateNode"/>.
    /// </summary>
    public IntermediateNode? AsTagHelperAttribute { get; set; }
 
    /// <summary>
    /// Pre-lowered form of this attribute for when the element is NOT a tag helper and needs
    /// to be unwrapped back to plain HTML markup. Plain HTML elements represent attributes with
    /// individual value tokens preserving the original parse structure. Produced during lowering
    /// via <c>LowerAttributeAsHtml</c>, which preserves individual tokens without merging.
    /// Used by <c>ConvertToMarkupElement</c> and <c>UnwrapElement</c> to restore the element
    /// to its non-tag-helper form.
    /// </summary>
    public IntermediateNode? AsMarkupAttribute { get; set; }
 
    /// <summary>
    /// The <see cref="HtmlAttributeIntermediateNode"/> child containing unresolved attribute
    /// value children. Set during lowering to avoid linear scans in the resolution phase.
    /// </summary>
    public HtmlAttributeIntermediateNode? HtmlAttributeNode { get; set; }
 
    public override IntermediateNodeCollection Children { get => field ??= []; }
 
    public override void Accept(IntermediateNodeVisitor visitor)
    {
        visitor.VisitDefault(this);
    }
 
    public override void FormatNode(IntermediateNodeFormatter formatter)
    {
        formatter.WriteContent(AttributeName);
        formatter.WriteProperty(nameof(AttributeName), AttributeName);
        formatter.WriteProperty(nameof(IsMinimized), IsMinimized.ToString());
    }
}