File: Editing\SyntaxGenerator.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editing;
 
/// <summary>
/// A language agnostic factory for creating syntax nodes.
/// 
/// This API can be used to create language specific syntax nodes that are semantically 
/// similar between languages.
/// 
/// The trees generated by this API will try to respect user preferences when
/// possible.  For example, generating <see cref="MemberAccessExpression(SyntaxNode, string)"/> 
/// will be done in a way such that "this." or "Me." will be simplified according to user
/// preference if <see cref="Simplifier" /> is used.
/// </summary>
public abstract class SyntaxGenerator : ILanguageService
{
    public static readonly SyntaxRemoveOptions DefaultRemoveOptions = SyntaxRemoveOptions.KeepUnbalancedDirectives | SyntaxRemoveOptions.AddElasticMarker;
 
    internal abstract SyntaxGeneratorInternal SyntaxGeneratorInternal { get; }
 
    internal SyntaxTrivia CarriageReturnLineFeed => this.SyntaxGeneratorInternal.CarriageReturnLineFeed;
    internal SyntaxTrivia ElasticCarriageReturnLineFeed => this.SyntaxGeneratorInternal.ElasticCarriageReturnLineFeed;
    internal abstract SyntaxTrivia ElasticMarker { get; }
 
    internal bool RequiresExplicitImplementationForInterfaceMembers
        => this.SyntaxGeneratorInternal.RequiresExplicitImplementationForInterfaceMembers;
 
    internal ISyntaxFacts SyntaxFacts
        => SyntaxGeneratorInternal.SyntaxFacts;
 
    internal abstract SyntaxTrivia Whitespace(string text);
 
    internal SyntaxTrivia SingleLineComment(string text)
        => this.SyntaxGeneratorInternal.SingleLineComment(text);
 
    internal abstract SyntaxToken CreateInterpolatedStringStartToken(bool isVerbatim);
    internal abstract SyntaxToken CreateInterpolatedStringEndToken();
 
    /// <summary>
    /// Gets the <see cref="SyntaxGenerator"/> for the specified language.
    /// </summary>
    public static SyntaxGenerator GetGenerator(Workspace workspace, string language)
        => GetGenerator(workspace.Services.SolutionServices, language);
 
    /// <summary>
    /// Gets the <see cref="SyntaxGenerator"/> for the specified language.
    /// </summary>
    internal static SyntaxGenerator GetGenerator(SolutionServices services, string language)
        => services.GetLanguageServices(language).GetRequiredService<SyntaxGenerator>();
 
    /// <summary>
    /// Gets the <see cref="SyntaxGenerator"/> for the language corresponding to the document.
    /// </summary>
    public static SyntaxGenerator GetGenerator(Document document)
        => GetGenerator(document.Project);
 
    /// <summary>
    /// Gets the <see cref="SyntaxGenerator"/> for the language corresponding to the project.
    /// </summary>
    public static SyntaxGenerator GetGenerator(Project project)
        => project.Services.GetRequiredService<SyntaxGenerator>();
 
    #region Declarations
 
    /// <summary>
    /// Returns the node if it is a declaration, the immediate enclosing declaration if one exists, or null.
    /// </summary>
    public SyntaxNode? GetDeclaration(SyntaxNode? node)
    {
        while (node != null)
        {
            if (GetDeclarationKind(node) != DeclarationKind.None)
            {
                return node;
            }
 
            node = node.Parent;
        }
 
        return null;
    }
 
    /// <summary>
    /// Returns the enclosing declaration of the specified kind or null.
    /// </summary>
    public SyntaxNode? GetDeclaration(SyntaxNode? node, DeclarationKind kind)
    {
        while (node != null)
        {
            if (GetDeclarationKind(node) == kind)
            {
                return node;
            }
 
            node = node.Parent;
        }
 
        return null;
    }
 
    /// <summary>
    /// Creates a field declaration.
    /// </summary>
    public abstract SyntaxNode FieldDeclaration(
        string name,
        SyntaxNode type,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        SyntaxNode? initializer = null);
 
    /// <summary>
    /// Creates a field declaration matching an existing field symbol.
    /// </summary>
    public SyntaxNode FieldDeclaration(IFieldSymbol field)
    {
        // don't use field references in initializers for fields in certain special types -  since those might reference the field being declared.
        var canUseFieldReference = !LiteralSpecialValues.HasSpecialValues(field.ContainingType.SpecialType);
        var initializer = field.HasConstantValue ? this.LiteralExpression(field.ConstantValue, canUseFieldReference) : null;
        return FieldDeclaration(field, initializer);
    }
 
    /// <summary>
    /// Creates a field declaration matching an existing field symbol.
    /// </summary>
    public SyntaxNode FieldDeclaration(IFieldSymbol field, SyntaxNode? initializer)
    {
        return FieldDeclaration(
            field.Name,
            TypeExpression(field.Type),
            field.DeclaredAccessibility,
            DeclarationModifiers.From(field),
            initializer);
    }
 
    //internal abstract SyntaxNode ObjectMemberInitializer(IEnumerable<SyntaxNode> fieldInitializers);
    //internal abstract SyntaxNode NamedFieldInitializer(SyntaxNode name, SyntaxNode value);
    //internal abstract SyntaxNode WithObjectCreationInitializer(SyntaxNode objectCreationExpression, SyntaxNode initializer);
 
    /// <summary>
    /// Creates a method declaration.
    /// </summary>
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
    public SyntaxNode MethodDeclaration(
        string name,
        IEnumerable<SyntaxNode>? parameters = null,
        IEnumerable<string>? typeParameters = null,
        SyntaxNode? returnType = null,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? statements = null)
    {
        return MethodDeclaration(
            name, parameters, typeParameters?.Select(n => TypeParameter(n)), returnType, accessibility, modifiers, statements);
    }
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
 
    private protected abstract SyntaxNode MethodDeclaration(
        string name,
        IEnumerable<SyntaxNode>? parameters,
        IEnumerable<SyntaxNode>? typeParameters,
        SyntaxNode? returnType,
        Accessibility accessibility,
        DeclarationModifiers modifiers,
        IEnumerable<SyntaxNode>? statements);
 
    /// <summary>
    /// Creates a method declaration matching an existing method symbol.
    /// </summary>
    public SyntaxNode MethodDeclaration(IMethodSymbol method, IEnumerable<SyntaxNode>? statements = null)
        => MethodDeclaration(method, method.Name, statements);
 
    internal SyntaxNode MethodDeclaration(IMethodSymbol method, string name, IEnumerable<SyntaxNode>? statements = null)
    {
        var decl = MethodDeclaration(
            name,
            typeParameters: method.TypeParameters.Select(p => TypeParameter(p)),
            parameters: method.Parameters.Select(p => ParameterDeclaration(p)),
            returnType: method.ReturnType.IsSystemVoid() ? null : TypeExpression(method.ReturnType, method.RefKind),
            accessibility: method.DeclaredAccessibility,
            modifiers: DeclarationModifiers.From(method),
            statements: statements);
 
        if (method.TypeParameters.Length > 0)
        {
            // Overrides are special.  Specifically, in an override, if a type parameter has no constraints, then we
            // want to still add `where T : default` if that type parameter is used with NRT (e.g. `T?`) that way
            // the language can distinguish if this is a Nullable Value Type or not.
            if (method.IsOverride)
            {
                foreach (var typeParameter in method.TypeParameters)
                {
                    if (HasNullableAnnotation(typeParameter, method))
                    {
                        if (!HasSomeConstraint(typeParameter))
                        {
                            // if there are no constraints, add `where T : default` so it's known this not an NVT
                            // and is just an unconstrained type parameter.
                            decl = WithDefaultConstraint(decl, typeParameter.Name);
                        }
                        else if (!typeParameter.HasValueTypeConstraint)
                        {
                            // if there are some constraints, add `where T : class` so it's known this is not an NVT
                            // and must specifically be some reference type.
                            decl = WithTypeConstraint(decl, typeParameter.Name, SpecialTypeConstraintKind.ReferenceType);
                        }
                    }
                }
            }
            else
            {
                decl = this.WithTypeParametersAndConstraints(decl, method.TypeParameters);
            }
        }
 
        if (method.ExplicitInterfaceImplementations.Length > 0)
        {
            decl = this.WithExplicitInterfaceImplementations(decl,
                ImmutableArray<ISymbol>.CastUp(method.ExplicitInterfaceImplementations));
        }
 
        return decl;
 
        bool HasNullableAnnotation(ITypeParameterSymbol typeParameter, IMethodSymbol method)
        {
            return method.ReturnType.GetReferencedTypeParameters().Any(t => IsNullableAnnotatedTypeParameter(typeParameter, t)) ||
                method.Parameters.Any(p => p.Type.GetReferencedTypeParameters().Any(t => IsNullableAnnotatedTypeParameter(typeParameter, t)));
        }
 
        static bool IsNullableAnnotatedTypeParameter(ITypeParameterSymbol typeParameter, ITypeParameterSymbol current)
        {
            return Equals(current, typeParameter) && current.NullableAnnotation == NullableAnnotation.Annotated;
        }
    }
 
    /// <summary>
    /// Creates a method declaration.
    /// </summary>
    public virtual SyntaxNode OperatorDeclaration(
        OperatorKind kind,
        IEnumerable<SyntaxNode>? parameters = null,
        SyntaxNode? returnType = null,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? statements = null)
    {
        throw new NotImplementedException();
    }
 
    private protected abstract SyntaxNode OperatorDeclaration(
        string operatorName,
        bool isImplicitConversion,
        IEnumerable<SyntaxNode>? parameters = null,
        SyntaxNode? returnType = null,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? statements = null);
 
    /// <summary>
    /// Creates a operator or conversion declaration matching an existing method symbol.
    /// </summary>
    public SyntaxNode OperatorDeclaration(IMethodSymbol method, IEnumerable<SyntaxNode>? statements = null)
    {
        if (method.MethodKind is not (MethodKind.UserDefinedOperator or MethodKind.Conversion))
            throw new ArgumentException($"Method kind '{method.MethodKind}' is not an operator.");
 
        var decl = OperatorDeclaration(
            method.Name,
            isImplicitConversion: method.Name is WellKnownMemberNames.ImplicitConversionName,
            parameters: method.Parameters.Select(p => ParameterDeclaration(p)),
            returnType: method.ReturnType.IsSystemVoid() ? null : TypeExpression(method.ReturnType, method.RefKind),
            accessibility: method.DeclaredAccessibility,
            modifiers: DeclarationModifiers.From(method),
            statements: statements);
 
        return decl;
    }
 
    /// <summary>
    /// Creates a parameter declaration.
    /// </summary>
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
    public SyntaxNode ParameterDeclaration(
        string name,
        SyntaxNode? type = null,
        SyntaxNode? initializer = null,
        RefKind refKind = RefKind.None)
    {
        return ParameterDeclaration(name, type, initializer, refKind, isExtension: false, isParams: false, isScoped: false);
    }
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
 
    private protected abstract SyntaxNode ParameterDeclaration(
        string name,
        SyntaxNode? type,
        SyntaxNode? initializer,
        RefKind refKind,
        bool isExtension,
        bool isParams,
        bool isScoped);
 
    /// <summary>
    /// Creates a parameter declaration matching an existing parameter symbol.
    /// </summary>
    public SyntaxNode ParameterDeclaration(IParameterSymbol symbol, SyntaxNode? initializer = null)
    {
        return ParameterDeclaration(
            symbol.Name,
            TypeExpression(symbol.Type),
            initializer is not null ? initializer :
            symbol.HasExplicitDefaultValue ? GenerateExpression(symbol.Type, symbol.ExplicitDefaultValue, canUseFieldReference: true) : null,
            symbol.RefKind,
            isExtension: symbol is { Ordinal: 0, ContainingSymbol: IMethodSymbol { IsExtensionMethod: true } },
            symbol.IsParams,
            isScoped: symbol is { RefKind: RefKind.Ref or RefKind.In or RefKind.RefReadOnlyParameter, ScopedKind: ScopedKind.ScopedRef }
                or { RefKind: RefKind.None, Type.IsRefLikeType: true, ScopedKind: ScopedKind.ScopedValue });
    }
 
    private protected abstract SyntaxNode TypeParameter(ITypeParameterSymbol typeParameter);
    private protected abstract SyntaxNode TypeParameter(string name);
 
    private protected abstract SyntaxNode GenerateExpression(ITypeSymbol? type, object? value, bool canUseFieldReference);
 
    /// <summary>
    /// Creates a property declaration. The property will have a <c>get</c> accessor if
    /// <see cref="DeclarationModifiers.IsWriteOnly"/> is <see langword="false"/> and will have
    /// a <c>set</c> accessor if <see cref="DeclarationModifiers.IsReadOnly"/> is <see
    /// langword="false"/>.
    /// </summary>
    /// <remarks>
    /// In C# there is a distinction between passing in <see langword="null"/> for <paramref
    /// name="getAccessorStatements"/> or <paramref name="setAccessorStatements"/> versus
    /// passing in an empty list. <see langword="null"/> will produce an auto-property-accessor
    /// (i.e. <c>get;</c>) whereas an empty list will produce an accessor with an empty block
    /// (i.e. <c>get { }</c>).
    /// </remarks>
    public abstract SyntaxNode PropertyDeclaration(
        string name,
        SyntaxNode type,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? getAccessorStatements = null,
        IEnumerable<SyntaxNode>? setAccessorStatements = null);
 
    /// <summary>
    /// Creates a property declaration using an existing property symbol as a signature.
    /// </summary>
    public SyntaxNode PropertyDeclaration(
        IPropertySymbol property,
        IEnumerable<SyntaxNode>? getAccessorStatements = null,
        IEnumerable<SyntaxNode>? setAccessorStatements = null)
    {
        var propertyAccessibility = property.DeclaredAccessibility;
        var getMethodSymbol = property.GetMethod;
        var setMethodSymbol = property.SetMethod;
 
        SyntaxNode? getAccessor = null;
        SyntaxNode? setAccessor = null;
 
        if (getMethodSymbol is not null)
        {
            var getMethodAccessibility = getMethodSymbol.DeclaredAccessibility;
            getAccessor = GetAccessorDeclaration(getMethodAccessibility < propertyAccessibility ? getMethodAccessibility : Accessibility.NotApplicable, getAccessorStatements);
        }
 
        if (setMethodSymbol is not null)
        {
            var setMethodAccessibility = setMethodSymbol.DeclaredAccessibility;
            setAccessor = SetAccessorDeclaration(
                setMethodAccessibility < propertyAccessibility ? setMethodAccessibility : Accessibility.NotApplicable,
                isInitOnly: setMethodSymbol.IsInitOnly,
                setAccessorStatements);
        }
 
        var propDecl = PropertyDeclaration(
            property.Name,
            TypeExpression(property.Type, property.RefKind),
            getAccessor,
            setAccessor,
            propertyAccessibility,
            DeclarationModifiers.From(property));
 
        if (property.ExplicitInterfaceImplementations.Length > 0)
        {
            propDecl = this.WithExplicitInterfaceImplementations(propDecl,
                ImmutableArray<ISymbol>.CastUp(property.ExplicitInterfaceImplementations));
        }
 
        return propDecl;
    }
 
    private protected abstract SyntaxNode PropertyDeclaration(
        string name,
        SyntaxNode type,
        SyntaxNode? getAccessor,
        SyntaxNode? setAccessor,
        Accessibility accessibility,
        DeclarationModifiers modifiers);
 
    public SyntaxNode WithAccessorDeclarations(SyntaxNode declaration, params SyntaxNode[] accessorDeclarations)
        => WithAccessorDeclarations(declaration, (IEnumerable<SyntaxNode>)accessorDeclarations);
 
    public abstract SyntaxNode WithAccessorDeclarations(SyntaxNode declaration, IEnumerable<SyntaxNode> accessorDeclarations);
 
    public abstract SyntaxNode GetAccessorDeclaration(
        Accessibility accessibility = Accessibility.NotApplicable,
        IEnumerable<SyntaxNode>? statements = null);
 
    public SyntaxNode SetAccessorDeclaration(
        Accessibility accessibility = Accessibility.NotApplicable,
        IEnumerable<SyntaxNode>? statements = null)
        => SetAccessorDeclaration(accessibility, isInitOnly: false, statements);
 
    private protected abstract SyntaxNode SetAccessorDeclaration(
        Accessibility accessibility, bool isInitOnly, IEnumerable<SyntaxNode>? statements);
 
    /// <summary>
    /// Creates an indexer declaration.
    /// </summary>
    public abstract SyntaxNode IndexerDeclaration(
        IEnumerable<SyntaxNode> parameters,
        SyntaxNode type,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? getAccessorStatements = null,
        IEnumerable<SyntaxNode>? setAccessorStatements = null);
 
    /// <summary>
    /// Creates an indexer declaration matching an existing indexer symbol.
    /// </summary>
    public SyntaxNode IndexerDeclaration(
        IPropertySymbol indexer,
        IEnumerable<SyntaxNode>? getAccessorStatements = null,
        IEnumerable<SyntaxNode>? setAccessorStatements = null)
    {
        var indexerDecl = IndexerDeclaration(
            indexer.Parameters.Select(p => this.ParameterDeclaration(p)),
            TypeExpression(indexer.Type, indexer.RefKind),
            indexer.DeclaredAccessibility,
            DeclarationModifiers.From(indexer),
            getAccessorStatements,
            setAccessorStatements);
 
        if (indexer.ExplicitInterfaceImplementations.Length > 0)
        {
            indexerDecl = this.WithExplicitInterfaceImplementations(indexerDecl,
                ImmutableArray<ISymbol>.CastUp(indexer.ExplicitInterfaceImplementations));
        }
 
        return indexerDecl;
    }
 
    /// <summary>
    /// Creates a statement that adds the given handler to the given event.
    /// </summary>
    public abstract SyntaxNode AddEventHandler(SyntaxNode @event, SyntaxNode handler);
 
    /// <summary>
    /// Creates a statement that removes the given handler from the given event.
    /// </summary>
    public abstract SyntaxNode RemoveEventHandler(SyntaxNode @event, SyntaxNode handler);
 
    /// <summary>
    /// Creates an event declaration.
    /// </summary>
    public abstract SyntaxNode EventDeclaration(
        string name,
        SyntaxNode type,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default);
 
    /// <summary>
    /// Creates an event declaration from an existing event symbol
    /// </summary>
    public SyntaxNode EventDeclaration(IEventSymbol symbol)
    {
        var ev = EventDeclaration(
            symbol.Name,
            TypeExpression(symbol.Type),
            symbol.DeclaredAccessibility,
            DeclarationModifiers.From(symbol));
 
        if (symbol.ExplicitInterfaceImplementations.Length > 0)
        {
            ev = this.WithExplicitInterfaceImplementations(ev,
                ImmutableArray<ISymbol>.CastUp(symbol.ExplicitInterfaceImplementations));
        }
 
        return ev;
    }
 
    /// <summary>
    /// Creates a custom event declaration.
    /// </summary>
    public abstract SyntaxNode CustomEventDeclaration(
        string name,
        SyntaxNode type,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? parameters = null,
        IEnumerable<SyntaxNode>? addAccessorStatements = null,
        IEnumerable<SyntaxNode>? removeAccessorStatements = null);
 
    /// <summary>
    /// Creates a custom event declaration from an existing event symbol.
    /// </summary>
    public SyntaxNode CustomEventDeclaration(
        IEventSymbol symbol,
        IEnumerable<SyntaxNode>? addAccessorStatements = null,
        IEnumerable<SyntaxNode>? removeAccessorStatements = null)
    {
        var invoke = symbol.Type.GetMembers("Invoke").FirstOrDefault(m => m.Kind == SymbolKind.Method) as IMethodSymbol;
        var parameters = invoke?.Parameters.Select(p => this.ParameterDeclaration(p));
 
        return CustomEventDeclaration(
            symbol.Name,
            TypeExpression(symbol.Type),
            symbol.DeclaredAccessibility,
            DeclarationModifiers.From(symbol),
            parameters: parameters,
            addAccessorStatements: addAccessorStatements,
            removeAccessorStatements: removeAccessorStatements);
    }
 
    /// <summary>
    /// Creates a constructor declaration.
    /// </summary>
    public abstract SyntaxNode ConstructorDeclaration(
        string? containingTypeName = null,
        IEnumerable<SyntaxNode>? parameters = null,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? baseConstructorArguments = null,
        IEnumerable<SyntaxNode>? statements = null);
 
    /// <summary>
    /// Create a constructor declaration using 
    /// </summary>
    public SyntaxNode ConstructorDeclaration(
        IMethodSymbol constructorMethod,
        IEnumerable<SyntaxNode>? baseConstructorArguments = null,
        IEnumerable<SyntaxNode>? statements = null)
    {
        return ConstructorDeclaration(
            constructorMethod.ContainingType != null ? constructorMethod.ContainingType.Name : "New",
            constructorMethod.Parameters.Select(p => ParameterDeclaration(p)),
            constructorMethod.DeclaredAccessibility,
            DeclarationModifiers.From(constructorMethod),
            baseConstructorArguments,
            statements);
    }
 
    private protected abstract SyntaxNode DestructorDeclaration(IMethodSymbol destructorMethod);
 
    /// <summary>
    /// Converts method, property and indexer declarations into public interface implementations.
    /// This is equivalent to an implicit C# interface implementation (you can access it via the interface or directly via the named member.)
    /// </summary>
    public SyntaxNode? AsPublicInterfaceImplementation(SyntaxNode declaration, SyntaxNode interfaceType)
        => AsPublicInterfaceImplementation(declaration, interfaceType, interfaceMemberName: null);
 
    /// <summary>
    /// Converts method, property and indexer declarations into public interface implementations.
    /// This is equivalent to an implicit C# interface implementation (you can access it via the interface or directly via the named member.)
    /// </summary>
    public abstract SyntaxNode? AsPublicInterfaceImplementation(SyntaxNode declaration, SyntaxNode interfaceType, string? interfaceMemberName);
 
    /// <summary>
    /// Converts method, property and indexer declarations into private interface implementations.
    /// This is equivalent to a C# explicit interface implementation (you can declare it for access via the interface, but cannot call it directly).
    /// </summary>
    public SyntaxNode? AsPrivateInterfaceImplementation(SyntaxNode declaration, SyntaxNode interfaceType)
        => AsPrivateInterfaceImplementation(declaration, interfaceType, interfaceMemberName: null);
 
    /// <summary>
    /// Converts method, property and indexer declarations into private interface implementations.
    /// This is equivalent to a C# explicit interface implementation (you can declare it for access via the interface, but cannot call it directly).
    /// </summary>
    public abstract SyntaxNode? AsPrivateInterfaceImplementation(SyntaxNode declaration, SyntaxNode interfaceType, string? interfaceMemberName);
 
    /// <summary>
    /// Creates a class declaration.
    /// </summary>
    public SyntaxNode ClassDeclaration(
        string name,
        IEnumerable<string>? typeParameters = null,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        SyntaxNode? baseType = null,
        IEnumerable<SyntaxNode>? interfaceTypes = null,
        IEnumerable<SyntaxNode>? members = null)
    {
        return ClassDeclaration(
            isRecord: false, name, typeParameters?.Select(TypeParameter), accessibility, modifiers, baseType, interfaceTypes, members);
    }
 
    private protected abstract SyntaxNode ClassDeclaration(
        bool isRecord,
        string name,
        IEnumerable<SyntaxNode>? typeParameters,
        Accessibility accessibility,
        DeclarationModifiers modifiers,
        SyntaxNode? baseType,
        IEnumerable<SyntaxNode>? interfaceTypes,
        IEnumerable<SyntaxNode>? members);
 
    /// <summary>
    /// Creates a struct declaration.
    /// </summary>
    public SyntaxNode StructDeclaration(
        string name,
        IEnumerable<string>? typeParameters = null,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? interfaceTypes = null,
        IEnumerable<SyntaxNode>? members = null)
    {
        return StructDeclaration(
            isRecord: false, name, typeParameters?.Select(TypeParameter), accessibility, modifiers, interfaceTypes, members);
    }
 
    private protected abstract SyntaxNode StructDeclaration(
        bool isRecord,
        string name,
        IEnumerable<SyntaxNode>? typeParameters,
        Accessibility accessibility,
        DeclarationModifiers modifiers,
        IEnumerable<SyntaxNode>? interfaceTypes,
        IEnumerable<SyntaxNode>? members);
 
    /// <summary>
    /// Creates a interface declaration.
    /// </summary>
    public SyntaxNode InterfaceDeclaration(
        string name,
        IEnumerable<string>? typeParameters = null,
        Accessibility accessibility = Accessibility.NotApplicable,
        IEnumerable<SyntaxNode>? interfaceTypes = null,
        IEnumerable<SyntaxNode>? members = null)
    {
        return InterfaceDeclaration(
            name, typeParameters?.Select(n => TypeParameter(n)), accessibility, interfaceTypes, members);
    }
 
    private protected abstract SyntaxNode InterfaceDeclaration(
        string name,
        IEnumerable<SyntaxNode>? typeParameters,
        Accessibility accessibility,
        IEnumerable<SyntaxNode>? interfaceTypes,
        IEnumerable<SyntaxNode>? members);
 
    /// <summary>
    /// Creates an enum declaration.
    /// </summary>
    public abstract SyntaxNode EnumDeclaration(
        string name,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? members = null);
 
    /// <summary>
    /// Creates an enum declaration
    /// </summary>
    internal abstract SyntaxNode EnumDeclaration(
        string name,
        SyntaxNode? underlyingType,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default,
        IEnumerable<SyntaxNode>? members = null);
 
    /// <summary>
    /// Creates an enum member
    /// </summary>
    public abstract SyntaxNode EnumMember(string name, SyntaxNode? expression = null);
 
    /// <summary>
    /// Creates a delegate declaration.
    /// </summary>
    public SyntaxNode DelegateDeclaration(
        string name,
        IEnumerable<SyntaxNode>? parameters = null,
        IEnumerable<string>? typeParameters = null,
        SyntaxNode? returnType = null,
        Accessibility accessibility = Accessibility.NotApplicable,
        DeclarationModifiers modifiers = default)
    {
        return DelegateDeclaration(
            name, parameters, typeParameters?.Select(n => TypeParameter(n)), returnType, accessibility, modifiers);
    }
 
    private protected abstract SyntaxNode DelegateDeclaration(
        string name,
        IEnumerable<SyntaxNode>? parameters,
        IEnumerable<SyntaxNode>? typeParameters,
        SyntaxNode? returnType,
        Accessibility accessibility,
        DeclarationModifiers modifiers);
 
    /// <summary>
    /// Creates a declaration matching an existing symbol.
    /// </summary>
    public SyntaxNode Declaration(ISymbol symbol)
    {
        switch (symbol.Kind)
        {
            case SymbolKind.Field:
                return FieldDeclaration((IFieldSymbol)symbol);
 
            case SymbolKind.Property:
                var property = (IPropertySymbol)symbol;
                return property.IsIndexer ? IndexerDeclaration(property) : PropertyDeclaration(property);
 
            case SymbolKind.Event:
                var ev = (IEventSymbol)symbol;
                return EventDeclaration(ev);
 
            case SymbolKind.Method:
                var method = (IMethodSymbol)symbol;
                switch (method.MethodKind)
                {
                    case MethodKind.Constructor:
                    case MethodKind.SharedConstructor:
                        return ConstructorDeclaration(method);
 
                    case MethodKind.Destructor:
                        return DestructorDeclaration(method);
 
                    case MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation:
                        return MethodDeclaration(method);
 
                    case MethodKind.UserDefinedOperator or MethodKind.Conversion:
                        return OperatorDeclaration(method);
                }
 
                break;
 
            case SymbolKind.Parameter:
                return ParameterDeclaration((IParameterSymbol)symbol);
 
            case SymbolKind.NamedType:
                var type = (INamedTypeSymbol)symbol;
 
                var declaration = type.TypeKind switch
                {
                    TypeKind.Class => ClassDeclaration(
                        type.IsRecord,
                        type.Name,
                        type.TypeParameters.Select(TypeParameter),
                        accessibility: type.DeclaredAccessibility,
                        modifiers: DeclarationModifiers.From(type),
                        baseType: type.BaseType != null ? TypeExpression(type.BaseType) : null,
                        interfaceTypes: type.Interfaces.Select(TypeExpression),
                        members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)),
                    TypeKind.Struct => StructDeclaration(
                        type.IsRecord,
                        type.Name,
                        type.TypeParameters.Select(TypeParameter),
                        accessibility: type.DeclaredAccessibility,
                        modifiers: DeclarationModifiers.From(type),
                        interfaceTypes: type.Interfaces.Select(TypeExpression),
                        members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)),
                    TypeKind.Interface => InterfaceDeclaration(
                        type.Name,
                        type.TypeParameters.Select(TypeParameter),
                        accessibility: type.DeclaredAccessibility,
                        interfaceTypes: type.Interfaces.Select(TypeExpression),
                        members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)),
                    TypeKind.Enum => EnumDeclaration(
                        type.Name,
                        underlyingType: type.EnumUnderlyingType is null or { SpecialType: SpecialType.System_Int32 }
                            ? null
                            : TypeExpression(type.EnumUnderlyingType.SpecialType),
                        accessibility: type.DeclaredAccessibility,
                        members: type.GetMembers().Where(s => s.Kind == SymbolKind.Field).Select(Declaration)),
                    TypeKind.Delegate => type.GetMembers(WellKnownMemberNames.DelegateInvokeName) is [IMethodSymbol invoke, ..]
                        ? DelegateDeclaration(
                            type.Name,
                            typeParameters: type.TypeParameters.Select(TypeParameter),
                            parameters: invoke.Parameters.Select(p => ParameterDeclaration(p)),
                            returnType: invoke.ReturnsVoid ? null : TypeExpression(invoke.ReturnType),
                            accessibility: type.DeclaredAccessibility,
                            modifiers: DeclarationModifiers.From(type))
                        : null,
                    _ => null,
                };
 
                if (declaration != null)
                    return WithTypeParametersAndConstraints(declaration, type.TypeParameters);
 
                break;
        }
 
        throw new ArgumentException("Symbol cannot be converted to a declaration");
    }
 
    private static bool CanBeDeclared(ISymbol symbol)
    {
        // Skip implicitly declared members from a record.  No need to synthesize those as the compiler will do it
        // anyways.
        if (symbol.ContainingType?.IsRecord is true)
        {
            if (symbol.IsImplicitlyDeclared)
                return false;
        }
 
        switch (symbol.Kind)
        {
            case SymbolKind.Field:
            case SymbolKind.Property:
            case SymbolKind.Event:
            case SymbolKind.Parameter:
                return symbol.CanBeReferencedByName;
 
            case SymbolKind.Method:
                var method = (IMethodSymbol)symbol;
                switch (method.MethodKind)
                {
                    case MethodKind.Constructor:
                    case MethodKind.SharedConstructor:
                        return true;
                    case MethodKind.Ordinary:
                        return method.CanBeReferencedByName;
                }
 
                break;
 
            case SymbolKind.NamedType:
                var type = (INamedTypeSymbol)symbol;
                switch (type.TypeKind)
                {
                    case TypeKind.Class:
                    case TypeKind.Struct:
                    case TypeKind.Interface:
                    case TypeKind.Enum:
                    case TypeKind.Delegate:
                        return type.CanBeReferencedByName;
                }
 
                break;
        }
 
        return false;
    }
 
    private SyntaxNode WithTypeParametersAndConstraints(SyntaxNode declaration, ImmutableArray<ITypeParameterSymbol> typeParameters)
    {
        if (typeParameters.Length > 0)
        {
            declaration = WithTypeParameters(declaration, typeParameters.Select(tp => TypeParameter(tp)));
 
            foreach (var tp in typeParameters)
            {
                if (HasSomeConstraint(tp))
                {
                    declaration = this.WithTypeConstraint(declaration, tp.Name,
                        kinds: (tp.HasConstructorConstraint ? SpecialTypeConstraintKind.Constructor : SpecialTypeConstraintKind.None)
                               | (tp.HasReferenceTypeConstraint ? SpecialTypeConstraintKind.ReferenceType : SpecialTypeConstraintKind.None)
                               | (tp.HasValueTypeConstraint ? SpecialTypeConstraintKind.ValueType : SpecialTypeConstraintKind.None),
                        isUnamangedType: tp.HasUnmanagedTypeConstraint,
                        types: tp.ConstraintTypes.Select(TypeExpression));
                }
            }
        }
 
        return declaration;
    }
 
    private static bool HasSomeConstraint(ITypeParameterSymbol typeParameter)
        => typeParameter.HasConstructorConstraint || typeParameter.HasReferenceTypeConstraint || typeParameter.HasValueTypeConstraint || typeParameter.ConstraintTypes.Length > 0;
 
    internal abstract SyntaxNode WithExplicitInterfaceImplementations(
        SyntaxNode declaration, ImmutableArray<ISymbol> explicitInterfaceImplementations, bool removeDefaults = true);
 
    /// <summary>
    /// Converts a declaration (method, class, etc) into a declaration with type parameters.
    /// </summary>
    public SyntaxNode WithTypeParameters(SyntaxNode declaration, IEnumerable<string> typeParameters)
        => WithTypeParameters(declaration, typeParameters.Select(n => TypeParameter(n)));
 
    private protected abstract SyntaxNode WithTypeParameters(SyntaxNode declaration, IEnumerable<SyntaxNode> typeParameters);
 
    /// <summary>
    /// Converts a declaration (method, class, etc) into a declaration with type parameters.
    /// </summary>
    public SyntaxNode WithTypeParameters(SyntaxNode declaration, params string[] typeParameters)
        => WithTypeParameters(declaration, (IEnumerable<string>)typeParameters);
 
    /// <summary>
    /// Adds a type constraint to a type parameter of a declaration.
    /// </summary>
#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
    public SyntaxNode WithTypeConstraint(SyntaxNode declaration, string typeParameterName, SpecialTypeConstraintKind kinds, IEnumerable<SyntaxNode>? types = null)
        => WithTypeConstraint(declaration, typeParameterName, kinds, isUnamangedType: false, types);
#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads
 
    private protected abstract SyntaxNode WithTypeConstraint(
        SyntaxNode declaration, string typeParameterName, SpecialTypeConstraintKind kinds, bool isUnamangedType, IEnumerable<SyntaxNode>? types);
 
    private protected abstract SyntaxNode WithDefaultConstraint(SyntaxNode declaration, string typeParameterName);
 
    /// <summary>
    /// Adds a type constraint to a type parameter of a declaration.
    /// </summary>
    public SyntaxNode WithTypeConstraint(SyntaxNode declaration, string typeParameterName, SpecialTypeConstraintKind kinds, params SyntaxNode[] types)
        => WithTypeConstraint(declaration, typeParameterName, kinds, (IEnumerable<SyntaxNode>)types);
 
    /// <summary>
    /// Adds a type constraint to a type parameter of a declaration.
    /// </summary>
    public SyntaxNode WithTypeConstraint(SyntaxNode declaration, string typeParameterName, params SyntaxNode[] types)
        => WithTypeConstraint(declaration, typeParameterName, SpecialTypeConstraintKind.None, (IEnumerable<SyntaxNode>)types);
 
    /// <summary>
    /// Creates a namespace declaration.
    /// </summary>
    /// <param name="name">The name of the namespace.</param>
    /// <param name="declarations">Zero or more namespace or type declarations.</param>
    public abstract SyntaxNode NamespaceDeclaration(SyntaxNode name, IEnumerable<SyntaxNode> declarations);
 
    /// <summary>
    /// Creates a namespace declaration.
    /// </summary>
    /// <param name="name">The name of the namespace.</param>
    /// <param name="declarations">Zero or more namespace or type declarations.</param>
    public SyntaxNode NamespaceDeclaration(SyntaxNode name, params SyntaxNode[] declarations)
        => NamespaceDeclaration(name, (IEnumerable<SyntaxNode>)declarations);
 
    /// <summary>
    /// Creates a namespace declaration.
    /// </summary>
    /// <param name="name">The name of the namespace.</param>
    /// <param name="declarations">Zero or more namespace or type declarations.</param>
    public SyntaxNode NamespaceDeclaration(string name, IEnumerable<SyntaxNode> declarations)
        => NamespaceDeclaration(DottedName(name), declarations);
 
    /// <summary>
    /// Creates a namespace declaration.
    /// </summary>
    /// <param name="name">The name of the namespace.</param>
    /// <param name="declarations">Zero or more namespace or type declarations.</param>
    public SyntaxNode NamespaceDeclaration(string name, params SyntaxNode[] declarations)
        => NamespaceDeclaration(DottedName(name), (IEnumerable<SyntaxNode>)declarations);
 
    /// <summary>
    /// Creates a compilation unit declaration
    /// </summary>
    /// <param name="declarations">Zero or more namespace import, namespace or type declarations.</param>
    public abstract SyntaxNode CompilationUnit(IEnumerable<SyntaxNode> declarations);
 
    /// <summary>
    /// Creates a compilation unit declaration
    /// </summary>
    /// <param name="declarations">Zero or more namespace import, namespace or type declarations.</param>
    public SyntaxNode CompilationUnit(params SyntaxNode[] declarations)
        => CompilationUnit((IEnumerable<SyntaxNode>)declarations);
 
    /// <summary>
    /// Creates a namespace import declaration.
    /// </summary>
    /// <param name="name">The name of the namespace being imported.</param>
    public abstract SyntaxNode NamespaceImportDeclaration(SyntaxNode name);
 
    /// <summary>
    /// Creates a namespace import declaration.
    /// </summary>
    /// <param name="name">The name of the namespace being imported.</param>
    public SyntaxNode NamespaceImportDeclaration(string name)
        => NamespaceImportDeclaration(DottedName(name));
 
    /// <summary>
    /// Creates an alias import declaration.
    /// </summary>
    /// <param name="aliasIdentifierName">The name of the alias.</param>
    /// <param name="symbol">The namespace or type to be aliased.</param>
    public SyntaxNode AliasImportDeclaration(string aliasIdentifierName, INamespaceOrTypeSymbol symbol)
        => AliasImportDeclaration(aliasIdentifierName, NameExpression(symbol));
 
    /// <summary>
    /// Creates an alias import declaration.
    /// </summary>
    /// <param name="aliasIdentifierName">The name of the alias.</param>
    /// <param name="name">The namespace or type to be aliased.</param>
    public abstract SyntaxNode AliasImportDeclaration(string aliasIdentifierName, SyntaxNode name);
 
    /// <summary>
    /// Creates an attribute.
    /// </summary>
    public abstract SyntaxNode Attribute(SyntaxNode name, IEnumerable<SyntaxNode>? attributeArguments = null);
 
    /// <summary>
    /// Creates an attribute.
    /// </summary>
    public SyntaxNode Attribute(string name, IEnumerable<SyntaxNode>? attributeArguments = null)
        => Attribute(DottedName(name), attributeArguments);
 
    /// <summary>
    /// Creates an attribute.
    /// </summary>
    public SyntaxNode Attribute(string name, params SyntaxNode[] attributeArguments)
        => Attribute(name, (IEnumerable<SyntaxNode>)attributeArguments);
 
    /// <summary>
    /// Creates an attribute matching existing attribute data.
    /// </summary>
    public SyntaxNode Attribute(AttributeData attribute)
    {
        Contract.ThrowIfNull(attribute.AttributeClass);
 
        var args = attribute.ConstructorArguments.Select(a => this.AttributeArgument(this.TypedConstantExpression(a)))
                .Concat(attribute.NamedArguments.Select(n => this.AttributeArgument(n.Key, this.TypedConstantExpression(n.Value))))
                .ToBoxedImmutableArray();
 
        return Attribute(
            name: this.TypeExpression(attribute.AttributeClass),
            attributeArguments: args.Count > 0 ? args : null);
    }
 
    /// <summary>
    /// Creates an attribute argument.
    /// </summary>
    public abstract SyntaxNode AttributeArgument(string? name, SyntaxNode expression);
 
    /// <summary>
    /// Creates an attribute argument.
    /// </summary>
    public SyntaxNode AttributeArgument(SyntaxNode expression)
        => AttributeArgument(name: null, expression);
 
    /// <summary>
    /// Removes all attributes from the declaration, including return attributes.
    /// </summary>
    public SyntaxNode RemoveAllAttributes(SyntaxNode declaration)
        => RemoveNodes(declaration, GetAttributes(declaration).Concat(GetReturnAttributes(declaration)));
 
    /// <summary>
    /// Removes comments from leading and trailing trivia, as well
    /// as potentially removing comments from opening and closing tokens.
    /// </summary>
    internal abstract SyntaxNode RemoveAllComments(SyntaxNode node);
 
    internal SyntaxNode RemovePrimaryConstructor(SyntaxNode declaration)
    {
        var node = GetPrimaryConstructorParameterList(declaration);
        return RemoveNodes(declaration, node is not null ? [node] : []);
    }
 
    internal abstract SyntaxNode? GetPrimaryConstructorParameterList(SyntaxNode declaration);
 
    internal SyntaxNode RemoveLeadingAndTrailingComments(SyntaxNode node)
    {
        return node.WithLeadingTrivia(RemoveCommentLines(node.GetLeadingTrivia()))
            .WithTrailingTrivia(RemoveCommentLines(node.GetTrailingTrivia()));
    }
 
    internal SyntaxToken RemoveLeadingAndTrailingComments(SyntaxToken token)
    {
        return token.WithLeadingTrivia(RemoveCommentLines(token.LeadingTrivia))
            .WithTrailingTrivia(RemoveCommentLines(token.TrailingTrivia));
    }
 
    internal abstract SyntaxTriviaList RemoveCommentLines(SyntaxTriviaList syntaxTriviaList);
 
    internal abstract bool IsRegularOrDocComment(SyntaxTrivia trivia);
 
    internal SyntaxNode RemoveAllTypeInheritance(SyntaxNode declaration)
        => RemoveNodes(declaration, GetTypeInheritance(declaration));
 
    internal abstract ImmutableArray<SyntaxNode> GetTypeInheritance(SyntaxNode declaration);
 
    /// <summary>
    /// Gets the attributes of a declaration, not including the return attributes.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetAttributes(SyntaxNode declaration);
 
    /// <summary>
    /// Creates a new instance of the declaration with the attributes inserted.
    /// </summary>
    public abstract SyntaxNode InsertAttributes(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> attributes);
 
    /// <summary>
    /// Creates a new instance of the declaration with the attributes inserted.
    /// </summary>
    public SyntaxNode InsertAttributes(SyntaxNode declaration, int index, params SyntaxNode[] attributes)
        => this.InsertAttributes(declaration, index, (IEnumerable<SyntaxNode>)attributes);
 
    /// <summary>
    /// Creates a new instance of a declaration with the specified attributes added.
    /// </summary>
    public SyntaxNode AddAttributes(SyntaxNode declaration, IEnumerable<SyntaxNode> attributes)
        => this.InsertAttributes(declaration, this.GetAttributes(declaration).Count, attributes);
 
    /// <summary>
    /// Creates a new instance of a declaration with the specified attributes added.
    /// </summary>
    public SyntaxNode AddAttributes(SyntaxNode declaration, params SyntaxNode[] attributes)
        => AddAttributes(declaration, (IEnumerable<SyntaxNode>)attributes);
 
    /// <summary>
    /// Gets the return attributes from the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetReturnAttributes(SyntaxNode declaration);
 
    /// <summary>
    /// Creates a new instance of a method declaration with return attributes inserted.
    /// </summary>
    public abstract SyntaxNode InsertReturnAttributes(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> attributes);
 
    /// <summary>
    /// Creates a new instance of a method declaration with return attributes inserted.
    /// </summary>
    public SyntaxNode InsertReturnAttributes(SyntaxNode declaration, int index, params SyntaxNode[] attributes)
        => this.InsertReturnAttributes(declaration, index, (IEnumerable<SyntaxNode>)attributes);
 
    /// <summary>
    /// Creates a new instance of a method declaration with return attributes added.
    /// </summary>
    public SyntaxNode AddReturnAttributes(SyntaxNode declaration, IEnumerable<SyntaxNode> attributes)
        => this.InsertReturnAttributes(declaration, this.GetReturnAttributes(declaration).Count, attributes);
 
    /// <summary>
    /// Creates a new instance of a method declaration node with return attributes added.
    /// </summary>
    public SyntaxNode AddReturnAttributes(SyntaxNode declaration, params SyntaxNode[] attributes)
        => AddReturnAttributes(declaration, (IEnumerable<SyntaxNode>)attributes);
 
    /// <summary>
    /// Gets the attribute arguments for the attribute declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetAttributeArguments(SyntaxNode attributeDeclaration);
 
    /// <summary>
    /// Creates a new instance of the attribute with the arguments inserted.
    /// </summary>
    public abstract SyntaxNode InsertAttributeArguments(SyntaxNode attributeDeclaration, int index, IEnumerable<SyntaxNode> attributeArguments);
 
    /// <summary>
    /// Creates a new instance of the attribute with the arguments added.
    /// </summary>
    public SyntaxNode AddAttributeArguments(SyntaxNode attributeDeclaration, IEnumerable<SyntaxNode> attributeArguments)
        => this.InsertAttributeArguments(attributeDeclaration, this.GetAttributeArguments(attributeDeclaration).Count, attributeArguments);
 
    /// <summary>
    /// Gets the namespace imports that are part of the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetNamespaceImports(SyntaxNode declaration);
 
    /// <summary>
    /// Creates a new instance of the declaration with the namespace imports inserted.
    /// </summary>
    public abstract SyntaxNode InsertNamespaceImports(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> imports);
 
    /// <summary>
    /// Creates a new instance of the declaration with the namespace imports inserted.
    /// </summary>
    public SyntaxNode InsertNamespaceImports(SyntaxNode declaration, int index, params SyntaxNode[] imports)
        => this.InsertNamespaceImports(declaration, index, (IEnumerable<SyntaxNode>)imports);
 
    /// <summary>
    /// Creates a new instance of the declaration with the namespace imports added.
    /// </summary>
    public SyntaxNode AddNamespaceImports(SyntaxNode declaration, IEnumerable<SyntaxNode> imports)
        => this.InsertNamespaceImports(declaration, this.GetNamespaceImports(declaration).Count, imports);
 
    /// <summary>
    /// Creates a new instance of the declaration with the namespace imports added.
    /// </summary>
    public SyntaxNode AddNamespaceImports(SyntaxNode declaration, params SyntaxNode[] imports)
        => this.AddNamespaceImports(declaration, (IEnumerable<SyntaxNode>)imports);
 
    /// <summary>
    /// Gets the current members of the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetMembers(SyntaxNode declaration);
 
    /// <summary>
    /// Creates a new instance of the declaration with the members inserted.
    /// </summary>
    public abstract SyntaxNode InsertMembers(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> members);
 
    /// <summary>
    /// Creates a new instance of the declaration with the members inserted.
    /// </summary>
    public SyntaxNode InsertMembers(SyntaxNode declaration, int index, params SyntaxNode[] members)
        => this.InsertMembers(declaration, index, (IEnumerable<SyntaxNode>)members);
 
    /// <summary>
    /// Creates a new instance of the declaration with the members added to the end.
    /// </summary>
    public SyntaxNode AddMembers(SyntaxNode declaration, IEnumerable<SyntaxNode> members)
        => this.InsertMembers(declaration, this.GetMembers(declaration).Count, members);
 
    /// <summary>
    /// Creates a new instance of the declaration with the members added to the end.
    /// </summary>
    public SyntaxNode AddMembers(SyntaxNode declaration, params SyntaxNode[] members)
        => this.AddMembers(declaration, (IEnumerable<SyntaxNode>)members);
 
    /// <summary>
    /// Gets the accessibility of the declaration.
    /// </summary>
    public abstract Accessibility GetAccessibility(SyntaxNode declaration);
 
    /// <summary>
    /// Changes the accessibility of the declaration.
    /// </summary>
    public abstract SyntaxNode WithAccessibility(SyntaxNode declaration, Accessibility accessibility);
 
    /// <summary>
    /// Gets the <see cref="DeclarationModifiers"/> for the declaration.
    /// </summary>
    public abstract DeclarationModifiers GetModifiers(SyntaxNode declaration);
 
    /// <summary>
    /// Changes the <see cref="DeclarationModifiers"/> for the declaration.
    /// </summary>
    public abstract SyntaxNode WithModifiers(SyntaxNode declaration, DeclarationModifiers modifiers);
 
    /// <summary>
    /// Gets the <see cref="DeclarationKind"/> for the declaration.
    /// </summary>
    public abstract DeclarationKind GetDeclarationKind(SyntaxNode declaration);
 
    /// <summary>
    /// Gets the name of the declaration.
    /// </summary>
    public abstract string GetName(SyntaxNode declaration);
 
    /// <summary>
    /// Changes the name of the declaration.
    /// </summary>
    public abstract SyntaxNode WithName(SyntaxNode declaration, string name);
 
    /// <summary>
    /// Gets the type of the declaration.
    /// </summary>
    public abstract SyntaxNode? GetType(SyntaxNode declaration);
 
    /// <summary>
    /// Changes the type of the declaration.
    /// </summary>
    public abstract SyntaxNode WithType(SyntaxNode declaration, SyntaxNode type);
 
    /// <summary>
    /// Gets the list of parameters for the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetParameters(SyntaxNode declaration);
 
    internal abstract SyntaxNode? GetParameterListNode(SyntaxNode declaration);
 
    /// <summary>
    /// Inserts the parameters at the specified index into the declaration.
    /// </summary>
    public abstract SyntaxNode InsertParameters(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> parameters);
 
    /// <summary>
    /// Adds the parameters to the declaration.
    /// </summary>
    public SyntaxNode AddParameters(SyntaxNode declaration, IEnumerable<SyntaxNode> parameters)
        => this.InsertParameters(declaration, this.GetParameters(declaration).Count, parameters);
 
    /// <summary>
    /// Gets the list of switch sections for the statement.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetSwitchSections(SyntaxNode switchStatement);
 
    /// <summary>
    /// Inserts the switch sections at the specified index into the statement.
    /// </summary>
    public abstract SyntaxNode InsertSwitchSections(SyntaxNode switchStatement, int index, IEnumerable<SyntaxNode> switchSections);
 
    /// <summary>
    /// Adds the switch sections to the statement.
    /// </summary>
    public SyntaxNode AddSwitchSections(SyntaxNode switchStatement, IEnumerable<SyntaxNode> switchSections)
        => this.InsertSwitchSections(switchStatement, this.GetSwitchSections(switchStatement).Count, switchSections);
 
    /// <summary>
    /// Gets the expression associated with the declaration.
    /// </summary>
    public abstract SyntaxNode? GetExpression(SyntaxNode declaration);
 
    /// <summary>
    /// Changes the expression associated with the declaration.
    /// </summary>
    public abstract SyntaxNode WithExpression(SyntaxNode declaration, SyntaxNode expression);
 
    /// <summary>
    /// Gets the statements for the body of the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetStatements(SyntaxNode declaration);
 
    /// <summary>
    /// Changes the statements for the body of the declaration.
    /// </summary>
    public abstract SyntaxNode WithStatements(SyntaxNode declaration, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Gets the accessors for the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetAccessors(SyntaxNode declaration);
 
    /// <summary>
    /// Gets the accessor of the specified kind for the declaration.
    /// </summary>
    public SyntaxNode? GetAccessor(SyntaxNode declaration, DeclarationKind kind)
        => this.GetAccessors(declaration).FirstOrDefault(a => GetDeclarationKind(a) == kind);
 
    /// <summary>
    /// Creates a new instance of the declaration with the accessors inserted.
    /// </summary>
    public abstract SyntaxNode InsertAccessors(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> accessors);
 
    /// <summary>
    /// Creates a new instance of the declaration with the accessors added.
    /// </summary>
    public SyntaxNode AddAccessors(SyntaxNode declaration, IEnumerable<SyntaxNode> accessors)
        => this.InsertAccessors(declaration, this.GetAccessors(declaration).Count, accessors);
 
    /// <summary>
    /// Gets the statements for the body of the get-accessor of the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetGetAccessorStatements(SyntaxNode declaration);
 
    /// <summary>
    /// Changes the statements for the body of the get-accessor of the declaration.
    /// </summary>
    public abstract SyntaxNode WithGetAccessorStatements(SyntaxNode declaration, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Gets the statements for the body of the set-accessor of the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetSetAccessorStatements(SyntaxNode declaration);
 
    /// <summary>
    /// Changes the statements for the body of the set-accessor of the declaration.
    /// </summary>
    public abstract SyntaxNode WithSetAccessorStatements(SyntaxNode declaration, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Gets a list of the base and interface types for the declaration.
    /// </summary>
    public abstract IReadOnlyList<SyntaxNode> GetBaseAndInterfaceTypes(SyntaxNode declaration);
 
    /// <summary>
    /// Adds a base type to the declaration
    /// </summary>
    public abstract SyntaxNode AddBaseType(SyntaxNode declaration, SyntaxNode baseType);
 
    /// <summary>
    /// Adds an interface type to the declaration
    /// </summary>
    public abstract SyntaxNode AddInterfaceType(SyntaxNode declaration, SyntaxNode interfaceType);
 
    internal abstract SyntaxNode AsInterfaceMember(SyntaxNode member);
 
    #endregion
 
    #region Remove, Replace, Insert
 
    /// <summary>
    /// Replaces the node in the root's tree with the new node.
    /// </summary>
    public virtual SyntaxNode ReplaceNode(SyntaxNode root, SyntaxNode node, SyntaxNode? newDeclaration)
        => (newDeclaration != null) ? root.ReplaceNode(node, newDeclaration) : RemoveNode(root, node);
 
    internal static SyntaxNode ReplaceNode(SyntaxNode root, SyntaxNode node, IEnumerable<SyntaxNode> newDeclarations)
    {
        Contract.ThrowIfTrue(ReferenceEquals(root, node));
        return root.ReplaceNode(node, newDeclarations);
    }
 
    /// <summary>
    /// Inserts the new node before the specified declaration.
    /// </summary>
    public virtual SyntaxNode InsertNodesBefore(SyntaxNode root, SyntaxNode node, IEnumerable<SyntaxNode> newDeclarations)
        => root.InsertNodesBefore(node, newDeclarations);
 
    /// <summary>
    /// Inserts the new node before the specified declaration.
    /// </summary>
    public virtual SyntaxNode InsertNodesAfter(SyntaxNode root, SyntaxNode node, IEnumerable<SyntaxNode> newDeclarations)
        => root.InsertNodesAfter(node, newDeclarations);
 
    /// <summary>
    /// Removes the node from the sub tree starting at the root.
    /// </summary>
    public virtual SyntaxNode RemoveNode(SyntaxNode root, SyntaxNode node)
        => RemoveNode(root, node, DefaultRemoveOptions);
 
    /// <summary>
    /// Removes the node from the sub tree starting at the root.
    /// </summary>
    public virtual SyntaxNode RemoveNode(SyntaxNode root, SyntaxNode node, SyntaxRemoveOptions options)
        => root.RemoveNode(node, options) ?? throw new InvalidOperationException($"Can't remove root node.");
 
    /// <summary>
    /// Removes all the declarations from the sub tree starting at the root.
    /// </summary>
    public SyntaxNode RemoveNodes(SyntaxNode root, IEnumerable<SyntaxNode> declarations)
    {
        var newRoot = root.TrackNodes(declarations);
 
        foreach (var declaration in declarations)
        {
            var newDeclaration = newRoot.GetCurrentNode(declaration);
            if (newDeclaration == null)
            {
                continue;
            }
 
            newRoot = RemoveNode(newRoot, newDeclaration);
        }
 
        return newRoot;
    }
 
    #endregion
 
    #region Utility
 
    internal abstract SeparatedSyntaxList<TElement> SeparatedList<TElement>(SyntaxNodeOrTokenList list) where TElement : SyntaxNode;
 
    internal abstract SeparatedSyntaxList<TElement> SeparatedList<TElement>(IEnumerable<TElement> nodes, IEnumerable<SyntaxToken> separators) where TElement : SyntaxNode;
 
    internal static SyntaxTokenList Merge(SyntaxTokenList original, SyntaxTokenList newList)
    {
        // return tokens from newList, but use original tokens of kind matches
        return new SyntaxTokenList(newList.Select(
            token => Any(original, token.RawKind)
                ? original.First(tk => tk.RawKind == token.RawKind)
                : token));
    }
 
    private static bool Any(SyntaxTokenList original, int rawKind)
    {
        foreach (var token in original)
        {
            if (token.RawKind == rawKind)
            {
                return true;
            }
        }
 
        return false;
    }
 
    [return: NotNullIfNotNull(nameof(node))]
    protected static SyntaxNode? PreserveTrivia<TNode>(TNode? node, Func<TNode, SyntaxNode> nodeChanger) where TNode : SyntaxNode
    {
        if (node == null)
        {
            return node;
        }
 
        var nodeWithoutTrivia = node.WithoutLeadingTrivia().WithoutTrailingTrivia();
 
        var changedNode = nodeChanger(nodeWithoutTrivia);
        if (changedNode == nodeWithoutTrivia)
        {
            return node;
        }
 
        return changedNode
            .WithLeadingTrivia(node.GetLeadingTrivia().Concat(changedNode.GetLeadingTrivia()))
            .WithTrailingTrivia(changedNode.GetTrailingTrivia().Concat(node.GetTrailingTrivia()));
    }
 
    protected static SyntaxNode ReplaceWithTrivia(SyntaxNode root, SyntaxNode original, SyntaxNode replacement)
    {
        var combinedTriviaReplacement =
            replacement.WithLeadingTrivia(original.GetLeadingTrivia().AddRange(replacement.GetLeadingTrivia()))
                       .WithTrailingTrivia(replacement.GetTrailingTrivia().AddRange(original.GetTrailingTrivia()));
 
        return root.ReplaceNode(original, combinedTriviaReplacement);
    }
 
    protected static SyntaxNode ReplaceWithTrivia<TNode>(SyntaxNode root, TNode original, Func<TNode, SyntaxNode> replacer)
        where TNode : SyntaxNode
    {
        return ReplaceWithTrivia(root, original, replacer(original));
    }
 
    protected static SyntaxNode ReplaceWithTrivia(SyntaxNode root, SyntaxToken original, SyntaxToken replacement)
    {
        var combinedTriviaReplacement =
            replacement.WithLeadingTrivia(original.LeadingTrivia.AddRange(replacement.LeadingTrivia))
                       .WithTrailingTrivia(replacement.TrailingTrivia.AddRange(original.TrailingTrivia));
 
        return root.ReplaceToken(original, combinedTriviaReplacement);
    }
 
    /// <summary>
    /// Creates a new instance of the node with the leading and trailing trivia removed and replaced with elastic markers.
    /// </summary>
    [return: MaybeNull, NotNullIfNotNull(nameof(node))]
    public abstract TNode ClearTrivia<TNode>([MaybeNull] TNode node) where TNode : SyntaxNode;
 
#pragma warning disable CA1822 // Mark members as static - shipped public API
    protected int IndexOf<T>(IReadOnlyList<T> list, T element)
#pragma warning restore CA1822 // Mark members as static
    {
        for (int i = 0, count = list.Count; i < count; i++)
        {
            if (EqualityComparer<T>.Default.Equals(list[i], element))
            {
                return i;
            }
        }
 
        return -1;
    }
 
    protected static SyntaxNode ReplaceRange(SyntaxNode root, SyntaxNode node, IEnumerable<SyntaxNode> replacements)
    {
        var first = replacements.First();
        var trackedFirst = first.TrackNodes(first);
        var newRoot = root.ReplaceNode(node, trackedFirst);
        var currentFirst = newRoot.GetCurrentNode(first);
        Contract.ThrowIfNull(currentFirst);
 
        return newRoot.InsertNodesAfter(currentFirst, replacements.Skip(1));
    }
 
    protected static SeparatedSyntaxList<TNode> RemoveRange<TNode>(SeparatedSyntaxList<TNode> list, int offset, int count)
        where TNode : SyntaxNode
    {
        for (; count > 0 && offset < list.Count; count--)
        {
            list = list.RemoveAt(offset);
        }
 
        return list;
    }
 
    protected static SyntaxList<TNode> RemoveRange<TNode>(SyntaxList<TNode> list, int offset, int count)
        where TNode : SyntaxNode
    {
        for (; count > 0 && offset < list.Count; count--)
        {
            list = list.RemoveAt(offset);
        }
 
        return list;
    }
 
    #endregion
 
    #region Statements
    /// <summary>
    /// Creates statement that allows an expression to execute in a statement context.
    /// This is typically an invocation or assignment expression.
    /// </summary>
    /// <param name="expression">The expression that is to be executed. This is usually a method invocation expression.</param>
    public abstract SyntaxNode ExpressionStatement(SyntaxNode expression);
 
    /// <summary>
    /// Creates a statement that can be used to return a value from a method body.
    /// </summary>
    /// <param name="expression">An optional expression that can be returned.</param>
    public abstract SyntaxNode ReturnStatement(SyntaxNode? expression = null);
 
    /// <summary>
    /// Creates a statement that can be used to yield a value from an iterator method.
    /// </summary>
    /// <param name="expression">An expression that can be yielded.</param>
    internal SyntaxNode YieldReturnStatement(SyntaxNode expression)
        => SyntaxGeneratorInternal.YieldReturnStatement(expression);
 
    /// <summary>
    /// Creates a statement that can be used to throw an exception.
    /// </summary>
    /// <param name="expression">An optional expression that can be thrown.</param>
    public abstract SyntaxNode ThrowStatement(SyntaxNode? expression = null);
 
    /// <summary>
    /// Creates an expression that can be used to throw an exception.
    /// </summary>
    public abstract SyntaxNode ThrowExpression(SyntaxNode expression);
 
    /// <summary>
    /// True if <see cref="ThrowExpression"/> can be used
    /// </summary>
    internal bool SupportsThrowExpression()
        => this.SyntaxGeneratorInternal.SupportsThrowExpression();
 
    /// <summary>
    /// <see langword="true"/> if the language requires a <see cref="TypeExpression(ITypeSymbol)"/>
    /// (including <see langword="var"/>) to be stated when making a 
    /// <see cref="LocalDeclarationStatement(ITypeSymbol, string, SyntaxNode, bool)"/>.
    /// <see langword="false"/> if the language allows the type node to be entirely elided.
    /// </summary>
    internal bool RequiresLocalDeclarationType() => SyntaxGeneratorInternal.RequiresLocalDeclarationType();
 
    /// <summary>
    /// Creates a statement that declares a single local variable.
    /// </summary>
    public abstract SyntaxNode LocalDeclarationStatement(
        SyntaxNode? type, string identifier, SyntaxNode? initializer = null, bool isConst = false);
 
    internal SyntaxNode LocalDeclarationStatement(SyntaxNode? type, SyntaxToken identifier, SyntaxNode? initializer = null, bool isConst = false)
        => SyntaxGeneratorInternal.LocalDeclarationStatement(type, identifier, initializer, isConst);
 
    internal SyntaxNode WithInitializer(SyntaxNode variableDeclarator, SyntaxNode initializer)
        => SyntaxGeneratorInternal.WithInitializer(variableDeclarator, initializer);
 
    internal SyntaxNode EqualsValueClause(SyntaxToken operatorToken, SyntaxNode value)
        => SyntaxGeneratorInternal.EqualsValueClause(operatorToken, value);
 
    /// <summary>
    /// Creates a statement that declares a single local variable.
    /// </summary>
    public SyntaxNode LocalDeclarationStatement(ITypeSymbol type, string name, SyntaxNode? initializer = null, bool isConst = false)
        => LocalDeclarationStatement(TypeExpression(type), name, initializer, isConst);
 
    /// <summary>
    /// Creates a statement that declares a single local variable.
    /// </summary>
    public SyntaxNode LocalDeclarationStatement(string name, SyntaxNode initializer)
        => LocalDeclarationStatement(type: (SyntaxNode?)null, name, initializer);
 
    /// <summary>
    /// Creates a statement that declares a single local variable.
    /// </summary>
    internal SyntaxNode LocalDeclarationStatement(SyntaxToken name, SyntaxNode initializer)
        => LocalDeclarationStatement(type: null, name, initializer);
 
    /// <summary>
    /// Creates an if-statement
    /// </summary>
    /// <param name="condition">A condition expression.</param>
    /// <param name="trueStatements">The statements that are executed if the condition is true.</param>
    /// <param name="falseStatements">The statements that are executed if the condition is false.</param>
    public abstract SyntaxNode IfStatement(
        SyntaxNode condition, IEnumerable<SyntaxNode> trueStatements, IEnumerable<SyntaxNode>? falseStatements = null);
 
    /// <summary>
    /// Creates an if statement
    /// </summary>
    /// <param name="condition">A condition expression.</param>
    /// <param name="trueStatements">The statements that are executed if the condition is true.</param>
    /// <param name="falseStatement">A single statement that is executed if the condition is false.</param>
    public SyntaxNode IfStatement(SyntaxNode condition, IEnumerable<SyntaxNode> trueStatements, SyntaxNode falseStatement)
        => IfStatement(condition, trueStatements, [falseStatement]);
 
    /// <summary>
    /// Creates a switch statement that branches to individual sections based on the value of the specified expression.
    /// </summary>
    public abstract SyntaxNode SwitchStatement(SyntaxNode expression, IEnumerable<SyntaxNode> sections);
 
    /// <summary>
    /// Creates a switch statement that branches to individual sections based on the value of the specified expression.
    /// </summary>
    public SyntaxNode SwitchStatement(SyntaxNode expression, params SyntaxNode[] sections)
        => SwitchStatement(expression, (IEnumerable<SyntaxNode>)sections);
 
    /// <summary>
    /// Creates a section for a switch statement.
    /// </summary>
    public abstract SyntaxNode SwitchSection(IEnumerable<SyntaxNode> caseExpressions, IEnumerable<SyntaxNode> statements);
 
    internal abstract SyntaxNode SwitchSectionFromLabels(IEnumerable<SyntaxNode> labels, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Creates a single-case section a switch statement.
    /// </summary>
    public SyntaxNode SwitchSection(SyntaxNode caseExpression, IEnumerable<SyntaxNode> statements)
        => SwitchSection([caseExpression], statements);
 
    /// <summary>
    /// Creates a default section for a switch statement.
    /// </summary>
    public abstract SyntaxNode DefaultSwitchSection(IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Create a statement that exits a switch statement and continues after it.
    /// </summary>
    public abstract SyntaxNode ExitSwitchStatement();
 
    /// <summary>
    /// Creates a statement that represents a using-block pattern.
    /// </summary>
    public abstract SyntaxNode UsingStatement(SyntaxNode? type, string name, SyntaxNode expression, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Creates a statement that represents a using-block pattern.
    /// </summary>
    public SyntaxNode UsingStatement(string name, SyntaxNode expression, IEnumerable<SyntaxNode> statements)
        => UsingStatement(type: null, name, expression, statements);
 
    /// <summary>
    /// Creates a statement that represents a using-block pattern.
    /// </summary>
    public abstract SyntaxNode UsingStatement(SyntaxNode expression, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Creates a statement that represents a lock-block pattern.
    /// </summary>
    public abstract SyntaxNode LockStatement(SyntaxNode expression, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Creates a try-catch or try-catch-finally statement.
    /// </summary>
    public abstract SyntaxNode TryCatchStatement(IEnumerable<SyntaxNode> tryStatements, IEnumerable<SyntaxNode>? catchClauses, IEnumerable<SyntaxNode>? finallyStatements = null);
 
    /// <summary>
    /// Creates a try-catch or try-catch-finally statement.
    /// </summary>
    public SyntaxNode TryCatchStatement(IEnumerable<SyntaxNode> tryStatements, params SyntaxNode[] catchClauses)
        => TryCatchStatement(tryStatements, (IEnumerable<SyntaxNode>?)catchClauses);
 
    /// <summary>
    /// Creates a try-finally statement.
    /// </summary>
    public SyntaxNode TryFinallyStatement(IEnumerable<SyntaxNode> tryStatements, IEnumerable<SyntaxNode> finallyStatements)
        => TryCatchStatement(tryStatements, catchClauses: null, finallyStatements: finallyStatements);
 
    /// <summary>
    /// Creates a catch-clause.
    /// </summary>
    public abstract SyntaxNode CatchClause(SyntaxNode type, string identifier, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Creates a catch-clause.
    /// </summary>
    public SyntaxNode CatchClause(ITypeSymbol type, string identifier, IEnumerable<SyntaxNode> statements)
        => CatchClause(TypeExpression(type), identifier, statements);
 
    /// <summary>
    /// Creates a while-loop statement
    /// </summary>
    public abstract SyntaxNode WhileStatement(SyntaxNode condition, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Creates a block of statements. Not supported in VB.
    /// </summary>
    internal abstract SyntaxNode ScopeBlock(IEnumerable<SyntaxNode> statements);
 
    internal abstract SyntaxNode GlobalStatement(SyntaxNode statement);
 
    #endregion
 
    #region Expressions
 
    internal abstract SyntaxToken NumericLiteralToken(string text, ulong value);
 
    internal SyntaxToken InterpolatedStringTextToken(string content, string value)
        => SyntaxGeneratorInternal.InterpolatedStringTextToken(content, value);
    internal SyntaxNode InterpolatedStringText(SyntaxToken textToken)
        => SyntaxGeneratorInternal.InterpolatedStringText(textToken);
    internal SyntaxNode Interpolation(SyntaxNode syntaxNode)
        => SyntaxGeneratorInternal.Interpolation(syntaxNode);
    internal SyntaxNode InterpolatedStringExpression(SyntaxToken startToken, IEnumerable<SyntaxNode> content, SyntaxToken endToken)
        => SyntaxGeneratorInternal.InterpolatedStringExpression(startToken, content, endToken);
    internal SyntaxNode InterpolationAlignmentClause(SyntaxNode alignment)
        => SyntaxGeneratorInternal.InterpolationAlignmentClause(alignment);
    internal SyntaxNode InterpolationFormatClause(string format)
        => SyntaxGeneratorInternal.InterpolationFormatClause(format);
 
    /// <summary>
    /// An expression that represents the default value of a type.
    /// This is typically a null value for reference types or a zero-filled value for value types.
    /// </summary>
    public abstract SyntaxNode DefaultExpression(SyntaxNode type);
    public abstract SyntaxNode DefaultExpression(ITypeSymbol type);
 
    /// <summary>
    /// Creates an expression that denotes the containing method's this-parameter.
    /// </summary>
    public abstract SyntaxNode ThisExpression();
 
    /// <summary>
    /// Creates an expression that denotes the containing method's base-parameter.
    /// </summary>
    public abstract SyntaxNode BaseExpression();
 
    /// <summary>
    /// Creates a literal expression. This is typically numeric primitives, strings or chars.
    /// </summary>
    public SyntaxNode LiteralExpression(object? value)
        => LiteralExpression(value, canUseFieldReference: true);
 
    /// <summary>
    /// Creates a literal expression. This is typically numeric primitives, strings or chars.
    /// </summary>
    private SyntaxNode LiteralExpression(object? value, bool canUseFieldReference)
        => GenerateExpression(type: null, value, canUseFieldReference);
 
    /// <summary>
    /// Creates an expression for a typed constant.
    /// </summary>
    public abstract SyntaxNode TypedConstantExpression(TypedConstant value);
 
    /// <summary>
    /// Creates an expression that denotes the boolean false literal.
    /// </summary>
    public SyntaxNode FalseLiteralExpression()
        => LiteralExpression(false);
 
    /// <summary>
    /// Creates an expression that denotes the boolean true literal.
    /// </summary>
    public SyntaxNode TrueLiteralExpression()
        => LiteralExpression(true);
 
    /// <summary>
    /// Creates an expression that denotes the null literal.
    /// </summary>
    public SyntaxNode NullLiteralExpression()
        => LiteralExpression(value: null);
 
    /// <summary>
    /// Creates an expression that denotes a simple identifier name.
    /// </summary>
    /// <param name="identifier"></param>
    /// <returns></returns>
    public abstract SyntaxNode IdentifierName(string identifier);
 
    internal abstract SyntaxNode IdentifierName(SyntaxToken identifier);
    internal SyntaxToken Identifier(string identifier) => SyntaxGeneratorInternal.Identifier(identifier);
    internal abstract SyntaxNode NamedAnonymousObjectMemberDeclarator(SyntaxNode identifier, SyntaxNode expression);
 
    /// <summary>
    /// Creates an expression that denotes a generic identifier name.
    /// </summary>
    public abstract SyntaxNode GenericName(string identifier, IEnumerable<SyntaxNode> typeArguments);
 
    internal abstract SyntaxNode GenericName(SyntaxToken identifier, IEnumerable<SyntaxNode> typeArguments);
 
    /// <summary>
    /// Creates an expression that denotes a generic identifier name.
    /// </summary>
    public SyntaxNode GenericName(string identifier, IEnumerable<ITypeSymbol> typeArguments)
        => GenericName(identifier, typeArguments.Select(TypeExpression));
 
    /// <summary>
    /// Creates an expression that denotes a generic identifier name.
    /// </summary>
    public SyntaxNode GenericName(string identifier, params SyntaxNode[] typeArguments)
        => GenericName(identifier, (IEnumerable<SyntaxNode>)typeArguments);
 
    /// <summary>
    /// Creates an expression that denotes a generic identifier name.
    /// </summary>
    public SyntaxNode GenericName(string identifier, params ITypeSymbol[] typeArguments)
        => GenericName(identifier, (IEnumerable<ITypeSymbol>)typeArguments);
 
    /// <summary>
    /// Converts an expression that ends in a name into an expression that ends in a generic name.
    /// If the expression already ends in a generic name, the new type arguments are used instead.
    /// </summary>
    public abstract SyntaxNode WithTypeArguments(SyntaxNode expression, IEnumerable<SyntaxNode> typeArguments);
 
    /// <summary>
    /// Converts an expression that ends in a name into an expression that ends in a generic name.
    /// If the expression already ends in a generic name, the new type arguments are used instead.
    /// </summary>
    public SyntaxNode WithTypeArguments(SyntaxNode expression, params SyntaxNode[] typeArguments)
        => WithTypeArguments(expression, (IEnumerable<SyntaxNode>)typeArguments);
 
    /// <summary>
    /// Creates a name expression that denotes a qualified name. 
    /// The left operand can be any name expression.
    /// The right operand can be either and identifier or generic name.
    /// </summary>
    public abstract SyntaxNode QualifiedName(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Returns a new name node qualified with the 'global' alias ('Global' in VB).
    /// </summary>
    internal abstract SyntaxNode GlobalAliasedName(SyntaxNode name);
 
    /// <summary>
    /// Creates a name expression from a dotted name string.
    /// </summary>
    public SyntaxNode DottedName(string dottedName)
    {
        if (dottedName == null)
        {
            throw new ArgumentNullException(nameof(dottedName));
        }
 
        var parts = dottedName.Split(s_dotSeparator);
        Debug.Assert(!parts.IsEmpty());
 
        SyntaxNode? name = null;
        foreach (var part in parts)
        {
            if (name == null)
            {
                name = IdentifierName(part);
            }
            else
            {
                name = QualifiedName(name, IdentifierName(part)).WithAdditionalAnnotations(Simplifier.Annotation);
            }
        }
 
        Contract.ThrowIfNull(name);
        return name;
    }
 
    private static readonly char[] s_dotSeparator = ['.'];
 
    /// <summary>
    /// Creates a name that denotes a type or namespace.
    /// </summary>
    /// <param name="namespaceOrTypeSymbol">The symbol to create a name for.</param>
    /// <returns></returns>
    public abstract SyntaxNode NameExpression(INamespaceOrTypeSymbol namespaceOrTypeSymbol);
 
    /// <summary>
    /// Creates an expression that denotes a type.
    /// </summary>
    public SyntaxNode TypeExpression(ITypeSymbol typeSymbol)
        => TypeExpression(typeSymbol, RefKind.None);
 
    private protected abstract SyntaxNode TypeExpression(ITypeSymbol typeSymbol, RefKind refKind);
 
    /// <summary>
    /// Creates an expression that denotes a type. If addImport is false,
    /// adds a <see cref="DoNotAddImportsAnnotation"/> which will prevent any
    /// imports or usings from being added for the type.
    /// </summary>
    public SyntaxNode TypeExpression(ITypeSymbol typeSymbol, bool addImport)
    {
        var expression = TypeExpression(typeSymbol);
        return addImport
            ? expression
            : expression.WithAdditionalAnnotations(DoNotAddImportsAnnotation.Annotation);
    }
 
    /// <summary>
    /// Creates an expression that denotes a special type name.
    /// </summary>
    public abstract SyntaxNode TypeExpression(SpecialType specialType);
 
    /// <summary>
    /// Creates an expression that denotes an array type.
    /// </summary>
    public abstract SyntaxNode ArrayTypeExpression(SyntaxNode type);
 
    /// <summary>
    /// Creates an expression that denotes a nullable type.
    /// </summary>
    public abstract SyntaxNode NullableTypeExpression(SyntaxNode type);
 
    /// <summary>
    /// Creates an expression that denotes a tuple type.
    /// </summary>
    public SyntaxNode TupleTypeExpression(IEnumerable<SyntaxNode> elements)
    {
        if (elements == null)
        {
            throw new ArgumentNullException(nameof(elements));
        }
 
        if (elements.Count() <= 1)
        {
            throw new ArgumentException("Tuples must have at least two elements.", nameof(elements));
        }
 
        return CreateTupleType(elements);
    }
 
    internal abstract SyntaxNode CreateTupleType(IEnumerable<SyntaxNode> elements);
 
    /// <summary>
    /// Creates an expression that denotes a tuple type.
    /// </summary>
    public SyntaxNode TupleTypeExpression(params SyntaxNode[] elements)
        => TupleTypeExpression((IEnumerable<SyntaxNode>)elements);
 
    /// <summary>
    /// Creates an expression that denotes a tuple type.
    /// </summary>
    public SyntaxNode TupleTypeExpression(IEnumerable<ITypeSymbol> elementTypes, IEnumerable<string>? elementNames = null)
    {
        if (elementTypes == null)
        {
            throw new ArgumentNullException(nameof(elementTypes));
        }
 
        if (elementNames != null)
        {
            if (elementNames.Count() != elementTypes.Count())
            {
                throw new ArgumentException("The number of element names must match the cardinality of the tuple.", nameof(elementNames));
            }
 
            return TupleTypeExpression(elementTypes.Zip(elementNames, TupleElementExpression));
        }
 
        return TupleTypeExpression(elementTypes.Select(type => TupleElementExpression(type, name: null)));
    }
 
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
    /// <summary>
    /// Creates an expression that denotes a tuple element.
    /// </summary>
    public abstract SyntaxNode TupleElementExpression(SyntaxNode type, string? name = null);
 
    /// <summary>
    /// Creates an expression that denotes a tuple element.
    /// </summary>
    public SyntaxNode TupleElementExpression(ITypeSymbol type, string? name = null)
        => TupleElementExpression(TypeExpression(type), name);
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
 
    /// <summary>
    /// Creates an expression that denotes an assignment from the right argument to left argument.
    /// </summary>
    public abstract SyntaxNode AssignmentStatement(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a value-type equality test operation.
    /// </summary>
    public abstract SyntaxNode ValueEqualsExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a reference-type equality test operation.
    /// </summary>
    public abstract SyntaxNode ReferenceEqualsExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a value-type inequality test operation.
    /// </summary>
    public abstract SyntaxNode ValueNotEqualsExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a reference-type inequality test operation.
    /// </summary>
    public abstract SyntaxNode ReferenceNotEqualsExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a less-than test operation.
    /// </summary>
    public abstract SyntaxNode LessThanExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a less-than-or-equal test operation.
    /// </summary>
    public abstract SyntaxNode LessThanOrEqualExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a greater-than test operation.
    /// </summary>
    public abstract SyntaxNode GreaterThanExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a greater-than-or-equal test operation.
    /// </summary>
    public abstract SyntaxNode GreaterThanOrEqualExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a unary negation operation.
    /// </summary>
    public abstract SyntaxNode NegateExpression(SyntaxNode expression);
 
    /// <summary>
    /// Creates an expression that denotes an addition operation.
    /// </summary>
    public abstract SyntaxNode AddExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes an subtraction operation.
    /// </summary>
    public abstract SyntaxNode SubtractExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a multiplication operation.
    /// </summary>
    public abstract SyntaxNode MultiplyExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a division operation.
    /// </summary>
    public abstract SyntaxNode DivideExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a modulo operation.
    /// </summary>
    public abstract SyntaxNode ModuloExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a bitwise-and operation.
    /// </summary>
    public abstract SyntaxNode BitwiseAndExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a bitwise-or operation.
    /// </summary>
    public abstract SyntaxNode BitwiseOrExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a bitwise-not operation
    /// </summary>
    public abstract SyntaxNode BitwiseNotExpression(SyntaxNode operand);
 
    /// <summary>
    /// Creates an expression that denotes a logical-and operation.
    /// </summary>
    public abstract SyntaxNode LogicalAndExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a logical-or operation.
    /// </summary>
    public abstract SyntaxNode LogicalOrExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates an expression that denotes a logical not operation.
    /// </summary>
    public abstract SyntaxNode LogicalNotExpression(SyntaxNode expression);
 
    /// <summary>
    /// Creates an expression that denotes a conditional evaluation operation.
    /// </summary>
    public abstract SyntaxNode ConditionalExpression(SyntaxNode condition, SyntaxNode whenTrue, SyntaxNode whenFalse);
 
    /// <summary>
    /// Creates an expression that denotes a conditional access operation. Use <see
    /// cref="MemberBindingExpression"/> and <see
    /// cref="ElementBindingExpression(IEnumerable{SyntaxNode})"/> to generate the <paramref
    /// name="whenNotNull"/> argument.
    /// </summary>
    public abstract SyntaxNode ConditionalAccessExpression(SyntaxNode expression, SyntaxNode whenNotNull);
 
    /// <summary>
    /// Creates an expression that denotes a member binding operation.
    /// </summary>
    public abstract SyntaxNode MemberBindingExpression(SyntaxNode name);
 
    /// <summary>
    /// Creates an expression that denotes an element binding operation.
    /// </summary>
    public abstract SyntaxNode ElementBindingExpression(IEnumerable<SyntaxNode> arguments);
 
    /// <summary>
    /// Creates an expression that denotes an element binding operation.
    /// </summary>
    public SyntaxNode ElementBindingExpression(params SyntaxNode[] arguments)
        => ElementBindingExpression((IEnumerable<SyntaxNode>)arguments);
 
    /// <summary>
    /// Creates an expression that denotes a coalesce operation. 
    /// </summary>
    public abstract SyntaxNode CoalesceExpression(SyntaxNode left, SyntaxNode right);
 
    /// <summary>
    /// Creates a member access expression.
    /// </summary>
    public virtual SyntaxNode MemberAccessExpression(SyntaxNode? expression, SyntaxNode memberName)
    {
        return MemberAccessExpressionWorker(expression, memberName)
            .WithAdditionalAnnotations(Simplifier.Annotation);
    }
 
    internal abstract SyntaxNode MemberAccessExpressionWorker(SyntaxNode? expression, SyntaxNode memberName);
 
    internal SyntaxNode RefExpression(SyntaxNode expression)
        => SyntaxGeneratorInternal.RefExpression(expression);
 
    /// <summary>
    /// Creates a member access expression.
    /// </summary>
    public SyntaxNode MemberAccessExpression(SyntaxNode? expression, string memberName)
        => MemberAccessExpression(expression, IdentifierName(memberName));
 
    /// <summary>
    /// Creates an array creation expression for a single dimensional array of specified size.
    /// </summary>
    public abstract SyntaxNode ArrayCreationExpression(SyntaxNode elementType, SyntaxNode size);
 
    /// <summary>
    /// Creates an array creation expression for a single dimensional array with specified initial element values.
    /// </summary>
    public abstract SyntaxNode ArrayCreationExpression(SyntaxNode elementType, IEnumerable<SyntaxNode> elements);
 
    /// <summary>
    /// Creates an object creation expression.
    /// </summary>
    public abstract SyntaxNode ObjectCreationExpression(SyntaxNode namedType, IEnumerable<SyntaxNode> arguments);
 
    internal abstract SyntaxNode ObjectCreationExpression(
        SyntaxNode namedType, SyntaxToken openParen, SeparatedSyntaxList<SyntaxNode> arguments, SyntaxToken closeParen);
 
    /// <summary>
    /// Creates an object creation expression.
    /// </summary>
    public SyntaxNode ObjectCreationExpression(ITypeSymbol type, IEnumerable<SyntaxNode> arguments)
        => ObjectCreationExpression(TypeExpression(type), arguments);
 
    /// <summary>
    /// Creates an object creation expression.
    /// </summary>
    public SyntaxNode ObjectCreationExpression(SyntaxNode type, params SyntaxNode[] arguments)
        => ObjectCreationExpression(type, (IEnumerable<SyntaxNode>)arguments);
 
    /// <summary>
    /// Creates an object creation expression.
    /// </summary>
    public SyntaxNode ObjectCreationExpression(ITypeSymbol type, params SyntaxNode[] arguments)
        => ObjectCreationExpression(type, (IEnumerable<SyntaxNode>)arguments);
 
    /// <summary>
    /// Creates a invocation expression.
    /// </summary>
    public abstract SyntaxNode InvocationExpression(SyntaxNode expression, IEnumerable<SyntaxNode> arguments);
 
    /// <summary>
    /// Creates a invocation expression
    /// </summary>
    public SyntaxNode InvocationExpression(SyntaxNode expression, params SyntaxNode[] arguments)
        => InvocationExpression(expression, (IEnumerable<SyntaxNode>)arguments);
 
    /// <summary>
    /// Creates a node that is an argument to an invocation.
    /// </summary>
    public abstract SyntaxNode Argument(string? name, RefKind refKind, SyntaxNode expression);
 
    /// <summary>
    /// Creates a node that is an argument to an invocation.
    /// </summary>
    public SyntaxNode Argument(RefKind refKind, SyntaxNode expression)
        => Argument(name: null, refKind, expression);
 
    /// <summary>
    /// Creates a node that is an argument to an invocation.
    /// </summary>
    public SyntaxNode Argument(SyntaxNode expression)
        => Argument(name: null, RefKind.None, expression);
 
    /// <summary>
    /// Creates an expression that access an element of an array or indexer.
    /// </summary>
    public abstract SyntaxNode ElementAccessExpression(SyntaxNode expression, IEnumerable<SyntaxNode> arguments);
 
    /// <summary>
    /// Creates an expression that access an element of an array or indexer.
    /// </summary>
    public SyntaxNode ElementAccessExpression(SyntaxNode expression, params SyntaxNode[] arguments)
        => ElementAccessExpression(expression, (IEnumerable<SyntaxNode>)arguments);
 
    /// <summary>
    /// Creates an expression that evaluates to the type at runtime.
    /// </summary>
    public abstract SyntaxNode TypeOfExpression(SyntaxNode type);
 
    /// <summary>
    /// Creates an expression that denotes an is-type-check operation.
    /// </summary>
    public abstract SyntaxNode IsTypeExpression(SyntaxNode expression, SyntaxNode type);
 
    /// <summary>
    /// Creates an expression that denotes an is-type-check operation.
    /// </summary>
    public SyntaxNode IsTypeExpression(SyntaxNode expression, ITypeSymbol type)
        => IsTypeExpression(expression, TypeExpression(type));
 
    /// <summary>
    /// Creates an expression that denotes an try-cast operation.
    /// </summary>
    public abstract SyntaxNode TryCastExpression(SyntaxNode expression, SyntaxNode type);
 
    /// <summary>
    /// Creates an expression that denotes an try-cast operation.
    /// </summary>
    public SyntaxNode TryCastExpression(SyntaxNode expression, ITypeSymbol type)
        => TryCastExpression(expression, TypeExpression(type));
 
    /// <summary>
    /// Creates an expression that denotes a type cast operation.
    /// </summary>
    public abstract SyntaxNode CastExpression(SyntaxNode type, SyntaxNode expression);
 
    /// <summary>
    /// Creates an expression that denotes a type cast operation.
    /// </summary>
    public SyntaxNode CastExpression(ITypeSymbol type, SyntaxNode expression)
        => CastExpression(TypeExpression(type), expression);
 
    /// <summary>
    /// Creates an expression that denotes a type conversion operation.
    /// </summary>
    public abstract SyntaxNode ConvertExpression(SyntaxNode type, SyntaxNode expression);
 
    /// <summary>
    /// Creates an expression that denotes a type conversion operation.
    /// </summary>
    public SyntaxNode ConvertExpression(ITypeSymbol type, SyntaxNode expression)
        => ConvertExpression(TypeExpression(type), expression);
 
    /// <summary>
    /// Creates an expression that declares a value returning lambda expression.
    /// </summary>
    public abstract SyntaxNode ValueReturningLambdaExpression(IEnumerable<SyntaxNode>? lambdaParameters, SyntaxNode expression);
 
    /// <summary>
    /// Creates an expression that declares a void returning lambda expression
    /// </summary>
    public abstract SyntaxNode VoidReturningLambdaExpression(IEnumerable<SyntaxNode>? lambdaParameters, SyntaxNode expression);
 
    /// <summary>
    /// Creates an expression that declares a value returning lambda expression.
    /// </summary>
    public abstract SyntaxNode ValueReturningLambdaExpression(IEnumerable<SyntaxNode>? lambdaParameters, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Creates an expression that declares a void returning lambda expression.
    /// </summary>
    public abstract SyntaxNode VoidReturningLambdaExpression(IEnumerable<SyntaxNode>? lambdaParameters, IEnumerable<SyntaxNode> statements);
 
    /// <summary>
    /// Creates an expression that declares a single parameter value returning lambda expression.
    /// </summary>
    public SyntaxNode ValueReturningLambdaExpression(string parameterName, SyntaxNode expression)
        => ValueReturningLambdaExpression(new[] { LambdaParameter(parameterName) }, expression);
 
    /// <summary>
    /// Creates an expression that declares a single parameter void returning lambda expression.
    /// </summary>
    public SyntaxNode VoidReturningLambdaExpression(string parameterName, SyntaxNode expression)
        => VoidReturningLambdaExpression(new[] { LambdaParameter(parameterName) }, expression);
 
    /// <summary>
    /// Creates an expression that declares a single parameter value returning lambda expression.
    /// </summary>
    public SyntaxNode ValueReturningLambdaExpression(string parameterName, IEnumerable<SyntaxNode> statements)
        => ValueReturningLambdaExpression(new[] { LambdaParameter(parameterName) }, statements);
 
    /// <summary>
    /// Creates an expression that declares a single parameter void returning lambda expression.
    /// </summary>
    public SyntaxNode VoidReturningLambdaExpression(string parameterName, IEnumerable<SyntaxNode> statements)
        => VoidReturningLambdaExpression(new[] { LambdaParameter(parameterName) }, statements);
 
    /// <summary>
    /// Creates an expression that declares a zero parameter value returning lambda expression.
    /// </summary>
    public SyntaxNode ValueReturningLambdaExpression(SyntaxNode expression)
        => ValueReturningLambdaExpression((IEnumerable<SyntaxNode>?)null, expression);
 
    /// <summary>
    /// Creates an expression that declares a zero parameter void returning lambda expression.
    /// </summary>
    public SyntaxNode VoidReturningLambdaExpression(SyntaxNode expression)
        => VoidReturningLambdaExpression((IEnumerable<SyntaxNode>?)null, expression);
 
    /// <summary>
    /// Creates an expression that declares a zero parameter value returning lambda expression.
    /// </summary>
    public SyntaxNode ValueReturningLambdaExpression(IEnumerable<SyntaxNode> statements)
        => ValueReturningLambdaExpression((IEnumerable<SyntaxNode>?)null, statements);
 
    /// <summary>
    /// Creates an expression that declares a zero parameter void returning lambda expression.
    /// </summary>
    public SyntaxNode VoidReturningLambdaExpression(IEnumerable<SyntaxNode> statements)
        => VoidReturningLambdaExpression((IEnumerable<SyntaxNode>?)null, statements);
 
    /// <summary>
    /// Creates a lambda parameter.
    /// </summary>
    public abstract SyntaxNode LambdaParameter(string identifier, SyntaxNode? type = null);
 
    /// <summary>
    /// Creates a lambda parameter.
    /// </summary>
    public SyntaxNode LambdaParameter(string identifier, ITypeSymbol type)
        => LambdaParameter(identifier, TypeExpression(type));
 
    /// <summary>
    /// Creates an await expression.
    /// </summary>
    public abstract SyntaxNode AwaitExpression(SyntaxNode expression);
 
    /// <summary>
    /// Wraps with parens.
    /// </summary>
    internal SyntaxNode AddParentheses(SyntaxNode expression, bool includeElasticTrivia = true, bool addSimplifierAnnotation = true)
        => SyntaxGeneratorInternal.AddParentheses(expression, includeElasticTrivia, addSimplifierAnnotation);
 
    /// <summary>
    /// Creates an nameof expression.
    /// </summary>
    public abstract SyntaxNode NameOfExpression(SyntaxNode expression);
 
    /// <summary>
    /// Creates an tuple expression.
    /// </summary>
    public abstract SyntaxNode TupleExpression(IEnumerable<SyntaxNode> arguments);
 
    /// <summary>
    /// Parses an expression from string
    /// </summary>
    internal abstract SyntaxNode ParseExpression(string stringToParse);
 
    internal abstract SyntaxTrivia Trivia(SyntaxNode node);
 
    internal abstract SyntaxNode DocumentationCommentTrivia(IEnumerable<SyntaxNode> nodes, SyntaxTriviaList trailingTrivia, string endOfLineString);
 
    internal abstract SyntaxNode? DocumentationCommentTriviaWithUpdatedContent(SyntaxTrivia trivia, IEnumerable<SyntaxNode> content);
 
    #endregion
}