|
// 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
}
|