|
// 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.>MethodName$<(), 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.>ContainingMember<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<StackFrameTypeArgumentNode>
/// ^-------------- "]" = 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()
};
}
|