File: EmbeddedLanguages\StackFrame\StackFrameNodeDefinitions.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.StackFrame;
 
using StackFrameNodeOrToken = EmbeddedSyntaxNodeOrToken<StackFrameKind, StackFrameNode>;
using StackFrameToken = EmbeddedSyntaxToken<StackFrameKind>;
 
internal abstract class StackFrameNode : EmbeddedSyntaxNode<StackFrameKind, StackFrameNode>
{
    protected StackFrameNode(StackFrameKind kind) : base(kind)
    {
    }
 
    public abstract void Accept(IStackFrameNodeVisitor visitor);
}
 
internal abstract class StackFrameDeclarationNode : StackFrameNode
{
    protected StackFrameDeclarationNode(StackFrameKind kind) : base(kind)
    {
    }
}
 
internal sealed class StackFrameMethodDeclarationNode(
    StackFrameQualifiedNameNode memberAccessExpression,
    StackFrameTypeArgumentList? typeArguments,
    StackFrameParameterList argumentList) : StackFrameDeclarationNode(StackFrameKind.MethodDeclaration)
{
    public readonly StackFrameQualifiedNameNode MemberAccessExpression = memberAccessExpression;
    public readonly StackFrameTypeArgumentList? TypeArguments = typeArguments;
    public readonly StackFrameParameterList ArgumentList = argumentList;
 
    internal override int ChildCount => 3;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
         => index switch
         {
             0 => MemberAccessExpression,
             1 => TypeArguments,
             2 => ArgumentList,
             _ => throw new InvalidOperationException(),
         };
}
 
/// <summary>
/// Base class for all type nodes
/// </summary>
internal abstract class StackFrameTypeNode : StackFrameNode
{
    protected StackFrameTypeNode(StackFrameKind kind) : base(kind)
    {
    }
}
 
/// <summary>
/// Base class for all name nodes
/// </summary>
/// <remarks>
/// All of these are <see cref="StackFrameTypeNode" />. If a node requires an identifier or name that 
/// is not a type then it should use <see cref="StackFrameToken"/> with <see cref="StackFrameKind.IdentifierToken"/>
/// directly.
/// </remarks>
internal abstract class StackFrameNameNode : StackFrameTypeNode
{
    protected StackFrameNameNode(StackFrameKind kind) : base(kind)
    {
    }
}
 
/// <summary>
/// Base class for <see cref="StackFrameIdentifierNameNode"/> and <see cref="StackFrameGenericNameNode"/>
/// </summary>
internal abstract class StackFrameSimpleNameNode : StackFrameNameNode
{
    public readonly StackFrameToken Identifier;
 
    protected StackFrameSimpleNameNode(StackFrameToken identifier, StackFrameKind kind) : base(kind)
    {
        Debug.Assert(identifier.Kind is StackFrameKind.IdentifierToken or StackFrameKind.ConstructorToken);
        Identifier = identifier;
    }
}
 
/// <summary>
/// Represents a qualified name, such as "MyClass.MyMethod"
/// </summary>
internal sealed class StackFrameQualifiedNameNode : StackFrameNameNode
{
    public readonly StackFrameNameNode Left;
    public readonly StackFrameToken DotToken;
    public readonly StackFrameSimpleNameNode Right;
 
    public StackFrameQualifiedNameNode(StackFrameNameNode left, StackFrameToken dotToken, StackFrameSimpleNameNode right) : base(StackFrameKind.MemberAccess)
    {
        Debug.Assert(dotToken.Kind == StackFrameKind.DotToken);
        Debug.Assert(left.Kind is not StackFrameKind.Constructor);
 
        Left = left;
        DotToken = dotToken;
        Right = right;
    }
 
    internal override int ChildCount => 3;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
        => index switch
        {
            0 => Left,
            1 => DotToken,
            2 => Right,
            _ => throw new InvalidOperationException()
        };
}
 
/// <summary>
/// The simplest identifier node, which wraps a <see cref="StackFrameKind.IdentifierToken" />
/// </summary>
internal sealed class StackFrameIdentifierNameNode(StackFrameToken identifier) : StackFrameSimpleNameNode(identifier, StackFrameKind.TypeIdentifier)
{
    internal override int ChildCount => 1;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
        => index switch
        {
            0 => Identifier,
            _ => throw new InvalidOperationException()
        };
}
 
internal sealed class StackFrameConstructorNode(StackFrameToken constructor) : StackFrameSimpleNameNode(constructor, StackFrameKind.Constructor)
{
    internal override int ChildCount => 1;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
        => index switch
        {
            0 => Identifier,
            _ => throw new InvalidOperationException()
        };
}
 
/// <summary>
/// An identifier with an arity, such as "MyNamespace.MyClass`1" 
/// </summary>
internal sealed class StackFrameGenericNameNode : StackFrameSimpleNameNode
{
    /// <summary>
    /// The "`" token in arity identifiers. Must be <see cref="StackFrameKind.GraveAccentToken"/>
    /// </summary>
    public readonly StackFrameToken GraveAccentToken;
 
    public readonly StackFrameToken NumberToken;
 
    internal override int ChildCount => 3;
 
    public StackFrameGenericNameNode(StackFrameToken identifier, StackFrameToken graveAccentToken, StackFrameToken numberToken)
        : base(identifier, StackFrameKind.GenericTypeIdentifier)
    {
        Debug.Assert(graveAccentToken.Kind == StackFrameKind.GraveAccentToken);
        Debug.Assert(numberToken.Kind == StackFrameKind.NumberToken);
 
        GraveAccentToken = graveAccentToken;
        NumberToken = numberToken;
    }
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
        => index switch
        {
            0 => Identifier,
            1 => GraveAccentToken,
            2 => NumberToken,
            _ => throw new InvalidOperationException()
        };
}
 
internal abstract class StackFrameGeneratedNameNode : StackFrameSimpleNameNode
{
    protected StackFrameGeneratedNameNode(StackFrameToken identifier, StackFrameKind kind) : base(identifier, kind)
    {
    }
}
 
/// <summary>
/// Generated methods follow the pattern Namespace.ClassName.&gt;MethodName$&lt;(), where 
/// the "$" is optional.
/// </summary>
internal sealed class StackFrameGeneratedMethodNameNode : StackFrameGeneratedNameNode
{
    public readonly StackFrameToken LessThanToken;
    public readonly StackFrameToken GreaterThanToken;
    public readonly StackFrameToken? DollarToken;
 
    internal override int ChildCount => 4;
 
    public StackFrameGeneratedMethodNameNode(StackFrameToken lessThanToken, StackFrameToken identifier, StackFrameToken greaterThanToken, StackFrameToken? dollarToken)
        : base(identifier, StackFrameKind.GeneratedIdentifier)
    {
        Debug.Assert(lessThanToken.Kind == StackFrameKind.LessThanToken);
        Debug.Assert(greaterThanToken.Kind == StackFrameKind.GreaterThanToken);
        Debug.Assert(!dollarToken.HasValue || dollarToken.Value.Kind == StackFrameKind.DollarToken);
 
        LessThanToken = lessThanToken;
        GreaterThanToken = greaterThanToken;
        DollarToken = dollarToken;
    }
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
        => index switch
        {
            0 => LessThanToken,
            1 => Identifier,
            2 => GreaterThanToken,
            3 => DollarToken.HasValue ? DollarToken.Value : null,
            _ => throw new InvalidOperationException()
        };
}
 
/// <summary>
/// Local method names are identifiers for local functions. They follow the pattern
/// <code>
/// Namespace.ClassName.&gt;ContainingMember&lt;g__LocalMethodName|0_0()
///                     ^----------------------^-------------------------- EncapsulatingMethod
///                                             ^-^----------------------- GeneratedNameSeparator
///                                                ^--------------^------- Identifier 
///                                                                ^------ PipeToken
///                                                                 ^--^-- Suffix
/// </code>                                                               
/// </summary>
internal sealed class StackFrameLocalMethodNameNode : StackFrameGeneratedNameNode
{
    internal readonly StackFrameGeneratedMethodNameNode EncapsulatingMethod;
    internal readonly StackFrameToken GeneratedNameSeparator;
    internal readonly StackFrameToken PipeToken;
    internal readonly StackFrameToken Suffix;
 
    internal override int ChildCount => 5;
 
    public StackFrameLocalMethodNameNode(
        StackFrameGeneratedMethodNameNode encapsulatngMethod,
        StackFrameToken generatedNameSeparator,
        StackFrameToken identifier,
        StackFrameToken pipeToken,
        StackFrameToken suffix)
        : base(identifier, StackFrameKind.LocalMethodIdentifier)
    {
        Debug.Assert(generatedNameSeparator.Kind == StackFrameKind.GeneratedNameSeparatorToken);
        Debug.Assert(identifier.Kind == StackFrameKind.IdentifierToken);
        Debug.Assert(pipeToken.Kind == StackFrameKind.PipeToken);
        Debug.Assert(suffix.Kind == StackFrameKind.GeneratedNameSuffixToken);
 
        EncapsulatingMethod = encapsulatngMethod;
        GeneratedNameSeparator = generatedNameSeparator;
        PipeToken = pipeToken;
        Suffix = suffix;
    }
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
    => index switch
    {
        0 => EncapsulatingMethod,
        1 => GeneratedNameSeparator,
        2 => Identifier,
        3 => PipeToken,
        4 => Suffix,
        _ => throw new InvalidOperationException()
    };
}
 
/// <summary>
/// Represents an array type declaration, such as string[,][]
/// </summary>
internal sealed class StackFrameArrayTypeNode : StackFrameTypeNode
{
    /// <summary>
    /// The type identifier without the array indicators.
    /// <code>
    /// string[][]
    /// ^----^
    /// </code>
    /// </summary>
    public readonly StackFrameNameNode TypeIdentifier;
 
    /// <summary>
    /// Each unique array identifier for the type
    /// <code>
    /// string[,][]
    ///        ^---  First array rank specifier  = "[,]"
    ///           ^- Second array rank specifier = "[]" 
    /// </code>
    /// </summary>
    public ImmutableArray<StackFrameArrayRankSpecifier> ArrayRankSpecifiers;
 
    public StackFrameArrayTypeNode(StackFrameNameNode typeIdentifier, ImmutableArray<StackFrameArrayRankSpecifier> arrayRankSpecifiers) : base(StackFrameKind.ArrayTypeExpression)
    {
        Debug.Assert(!arrayRankSpecifiers.IsDefaultOrEmpty);
        TypeIdentifier = typeIdentifier;
        ArrayRankSpecifiers = arrayRankSpecifiers;
    }
 
    internal override int ChildCount => 1 + ArrayRankSpecifiers.Length;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
        => index switch
        {
            0 => TypeIdentifier,
            _ => ArrayRankSpecifiers[index - 1]
        };
}
 
internal sealed class StackFrameArrayRankSpecifier : StackFrameNode
{
    public readonly StackFrameToken OpenBracket;
    public readonly StackFrameToken CloseBracket;
    public readonly ImmutableArray<StackFrameToken> CommaTokens;
 
    public StackFrameArrayRankSpecifier(StackFrameToken openBracket, StackFrameToken closeBracket, ImmutableArray<StackFrameToken> commaTokens)
        : base(StackFrameKind.ArrayExpression)
    {
        Debug.Assert(!commaTokens.IsDefault);
        Debug.Assert(openBracket.Kind == StackFrameKind.OpenBracketToken);
        Debug.Assert(closeBracket.Kind == StackFrameKind.CloseBracketToken);
        Debug.Assert(commaTokens.All(static t => t.Kind == StackFrameKind.CommaToken));
 
        OpenBracket = openBracket;
        CloseBracket = closeBracket;
        CommaTokens = commaTokens;
    }
 
    internal override int ChildCount => 2 + CommaTokens.Length;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
    {
        if (index == 0)
        {
            return OpenBracket;
        }
 
        if (index == ChildCount - 1)
        {
            return CloseBracket;
        }
 
        return CommaTokens[index - 1];
    }
}
 
/// <summary>
/// The type argument list for a method declaration. 
/// 
/// <code>
/// Ex: MyType.MyMethod[T, U, V](T t, U u, V v) 
///                    ^-----------------------  "[" = Open Token 
///                     ^------^   ------------  "T, U, V" = SeparatedStackFrameNodeList&lt;StackFrameTypeArgumentNode&gt;
///                             ^--------------  "]" = Close Token
/// </code>
/// 
/// </summary>
internal sealed class StackFrameTypeArgumentList : StackFrameNode
{
    public readonly StackFrameToken OpenToken;
    public readonly EmbeddedSeparatedSyntaxNodeList<StackFrameKind, StackFrameNode, StackFrameIdentifierNameNode> TypeArguments;
    public readonly StackFrameToken CloseToken;
 
    public StackFrameTypeArgumentList(
        StackFrameToken openToken,
        EmbeddedSeparatedSyntaxNodeList<StackFrameKind, StackFrameNode, StackFrameIdentifierNameNode> typeArguments,
        StackFrameToken closeToken)
        : base(StackFrameKind.TypeArgument)
    {
        Debug.Assert(openToken.Kind is StackFrameKind.OpenBracketToken or StackFrameKind.LessThanToken);
        Debug.Assert(typeArguments.Length > 0);
        Debug.Assert(openToken.Kind == StackFrameKind.OpenBracketToken ? closeToken.Kind == StackFrameKind.CloseBracketToken : closeToken.Kind == StackFrameKind.GreaterThanToken);
 
        OpenToken = openToken;
        TypeArguments = typeArguments;
        CloseToken = closeToken;
    }
 
    internal override int ChildCount => TypeArguments.NodesAndTokens.Length + 2;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
    {
        if (index >= ChildCount)
        {
            throw new InvalidOperationException();
        }
 
        if (index == 0)
        {
            return OpenToken;
        }
 
        if (index == ChildCount - 1)
        {
            return CloseToken;
        }
 
        // Includes both the nodes and separator tokens as children
        return TypeArguments.NodesAndTokens[index - 1];
    }
}
 
internal sealed class StackFrameParameterDeclarationNode : StackFrameDeclarationNode
{
    public readonly StackFrameTypeNode Type;
    public readonly StackFrameToken Identifier;
 
    internal override int ChildCount => 2;
 
    public StackFrameParameterDeclarationNode(StackFrameTypeNode type, StackFrameToken identifier)
        : base(StackFrameKind.Parameter)
    {
        Debug.Assert(identifier.Kind == StackFrameKind.IdentifierToken);
        Type = type;
        Identifier = identifier;
    }
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
        => index switch
        {
            0 => Type,
            1 => Identifier,
            _ => throw new InvalidOperationException()
        };
}
 
internal sealed class StackFrameParameterList : StackFrameNode
{
    public readonly StackFrameToken OpenParen;
    public readonly EmbeddedSeparatedSyntaxNodeList<StackFrameKind, StackFrameNode, StackFrameParameterDeclarationNode> Parameters;
    public readonly StackFrameToken CloseParen;
 
    public StackFrameParameterList(
        StackFrameToken openToken,
        EmbeddedSeparatedSyntaxNodeList<StackFrameKind, StackFrameNode, StackFrameParameterDeclarationNode> parameters,
        StackFrameToken closeToken)
        : base(StackFrameKind.ParameterList)
    {
        Debug.Assert(openToken.Kind == StackFrameKind.OpenParenToken);
        Debug.Assert(closeToken.Kind == StackFrameKind.CloseParenToken);
 
        OpenParen = openToken;
        Parameters = parameters;
        CloseParen = closeToken;
    }
 
    internal override int ChildCount => 2 + Parameters.NodesAndTokens.Length;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
    {
        visitor.Visit(this);
    }
 
    internal override StackFrameNodeOrToken ChildAt(int index)
    {
        if (index == 0)
        {
            return OpenParen;
        }
 
        if (index == ChildCount - 1)
        {
            return CloseParen;
        }
 
        // Include both nodes and tokens here as children of the StackFrameParameterList
        return Parameters.NodesAndTokens[index - 1];
    }
}
 
internal sealed class StackFrameFileInformationNode : StackFrameNode
{
    public readonly StackFrameToken Path;
    public readonly StackFrameToken? Colon;
    public readonly StackFrameToken? Line;
 
    public StackFrameFileInformationNode(StackFrameToken path, StackFrameToken? colon, StackFrameToken? line) : base(StackFrameKind.FileInformation)
    {
        Debug.Assert(path.Kind == StackFrameKind.PathToken);
        Debug.Assert(colon.HasValue == line.HasValue);
        Debug.Assert(!line.HasValue || line.Value.Kind == StackFrameKind.NumberToken);
 
        Path = path;
        Colon = colon;
        Line = line;
    }
 
    internal override int ChildCount => 3;
 
    public override void Accept(IStackFrameNodeVisitor visitor)
        => visitor.Visit(this);
 
    internal override StackFrameNodeOrToken ChildAt(int index)
        => index switch
        {
            0 => Path,
            1 => Colon.HasValue ? Colon.Value : null,
            2 => Line.HasValue ? Line.Value : null,
            _ => throw new InvalidOperationException()
        };
}