/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs
3 755 строк
162 KB
Rikki Gibson
Implement approved public API for closed class feature (#84045)
17 июн 2026, 20:04
Не верифицирован
17 июн 2026, 20:04
ba78906
Код
Авторство
О чём код?
// 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.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; using static CSharpSyntaxTokens; [ExportLanguageService(typeof(SyntaxGenerator), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Incorrectly used in production code: https://github.com/dotnet/roslyn/issues/42839")] internal sealed class CSharpSyntaxGenerator() : SyntaxGenerator { // A bit hacky, but we need to actually run ParseToken on the "nameof" text as there's no // other way to get a token back that has the appropriate internal bit set that indicates // this has the .ContextualKind of SyntaxKind.NameOfKeyword. private static readonly IdentifierNameSyntax s_nameOfIdentifier = SyntaxFactory.IdentifierName(SyntaxFactory.ParseToken("nameof")); internal override SyntaxTrivia ElasticMarker => SyntaxFactory.ElasticMarker; internal override SyntaxGeneratorInternal SyntaxGeneratorInternal => CSharpSyntaxGeneratorInternal.Instance; internal override SyntaxTrivia Whitespace(string text) => SyntaxFactory.Whitespace(text); internal override SeparatedSyntaxList<TElement> SeparatedList<TElement>(SyntaxNodeOrTokenList list) => SyntaxFactory.SeparatedList<TElement>(list); internal override SyntaxToken CreateInterpolatedStringStartToken(bool isVerbatim) { const string InterpolatedVerbatimText = "$@\""; return isVerbatim ? SyntaxFactory.Token(default, SyntaxKind.InterpolatedVerbatimStringStartToken, InterpolatedVerbatimText, InterpolatedVerbatimText, default) : InterpolatedStringStartToken; } internal override SyntaxToken CreateInterpolatedStringEndToken() => InterpolatedStringEndToken; internal override SeparatedSyntaxList<TElement> SeparatedList<TElement>(IEnumerable<TElement> nodes, IEnumerable<SyntaxToken> separators) => SyntaxFactory.SeparatedList(nodes, separators); internal override SyntaxTrivia Trivia(SyntaxNode node) { if (node is StructuredTriviaSyntax structuredTriviaSyntax) { return SyntaxFactory.Trivia(structuredTriviaSyntax); } throw ExceptionUtilities.UnexpectedValue(node.Kind()); } internal override SyntaxNode DocumentationCommentTrivia(IEnumerable<SyntaxNode> nodes, SyntaxTriviaList trailingTrivia, string endOfLineString) { var docTrivia = SyntaxFactory.DocumentationCommentTrivia( SyntaxKind.MultiLineDocumentationCommentTrivia, (SyntaxList<XmlNodeSyntax>)SyntaxFactory.List(nodes), EndOfDocumentationCommentToken); docTrivia = docTrivia.WithLeadingTrivia(SyntaxFactory.DocumentationCommentExterior("/// ")) .WithTrailingTrivia(trailingTrivia); return docTrivia.WithTrailingTrivia(SyntaxFactory.EndOfLine(endOfLineString)); } internal override SyntaxNode? DocumentationCommentTriviaWithUpdatedContent(SyntaxTrivia trivia, IEnumerable<SyntaxNode> content) { if (trivia.GetStructure() is DocumentationCommentTriviaSyntax documentationCommentTrivia) { return SyntaxFactory.DocumentationCommentTrivia(documentationCommentTrivia.Kind(), (SyntaxList<XmlNodeSyntax>)SyntaxFactory.List(content), documentationCommentTrivia.EndOfComment); } return null; } public static readonly SyntaxGenerator Instance = new CSharpSyntaxGenerator(); #region Declarations public override SyntaxNode CompilationUnit(IEnumerable<SyntaxNode> declarations) { return SyntaxFactory.CompilationUnit() .WithUsings(this.AsUsingDirectives(declarations)) .WithMembers(AsNamespaceMembers(declarations)); } private SyntaxList<UsingDirectiveSyntax> AsUsingDirectives(IEnumerable<SyntaxNode> declarations) { return declarations != null ? [.. declarations.Select(this.AsUsingDirective).OfType<UsingDirectiveSyntax>()] : default; } private SyntaxNode? AsUsingDirective(SyntaxNode node) { if (node is NameSyntax name) { return this.NamespaceImportDeclaration(name); } return node as UsingDirectiveSyntax; } private static SyntaxList<MemberDeclarationSyntax> AsNamespaceMembers(IEnumerable<SyntaxNode> declarations) { return declarations != null ? [.. declarations.Select(AsNamespaceMember).OfType<MemberDeclarationSyntax>()] : default; } private static SyntaxNode? AsNamespaceMember(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.NamespaceDeclaration: case SyntaxKind.FileScopedNamespaceDeclaration: case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.DelegateDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: return declaration; default: return null; } } public override SyntaxNode NamespaceImportDeclaration(SyntaxNode name) => SyntaxFactory.UsingDirective((NameSyntax)name); public override SyntaxNode AliasImportDeclaration(string aliasIdentifierName, SyntaxNode name) => SyntaxFactory.UsingDirective(SyntaxFactory.NameEquals(aliasIdentifierName), (NameSyntax)name); public override SyntaxNode NamespaceDeclaration(SyntaxNode name, IEnumerable<SyntaxNode> declarations) { return SyntaxFactory.NamespaceDeclaration( (NameSyntax)name, default, this.AsUsingDirectives(declarations), AsNamespaceMembers(declarations)); } public override SyntaxNode FieldDeclaration( string name, SyntaxNode type, Accessibility accessibility, DeclarationModifiers modifiers, SyntaxNode? initializer) { // some constant types will also appear as readonly when read from metadata modifiers = modifiers.IsConst ? modifiers.WithIsReadOnly(false) : modifiers; return SyntaxFactory.FieldDeclaration( default, AsModifierList(accessibility, modifiers, SyntaxKind.FieldDeclaration), SyntaxFactory.VariableDeclaration( (TypeSyntax)type, [SyntaxFactory.VariableDeclarator( name.ToIdentifierToken(), null, initializer != null ? SyntaxFactory.EqualsValueClause((ExpressionSyntax)initializer) : null)])); } private protected override SyntaxNode ParameterDeclaration( string name, SyntaxNode? type, SyntaxNode? initializer, RefKind refKind, bool isExtension, bool isParams, bool isScoped) { var modifiers = CSharpSyntaxGeneratorInternal.GetParameterModifiers(isScoped, refKind, isParams); if (isExtension) modifiers = modifiers.Insert(0, ThisKeyword); return SyntaxFactory.Parameter( default, modifiers, (TypeSyntax?)type, name.ToIdentifierToken(), initializer != null ? SyntaxFactory.EqualsValueClause((ExpressionSyntax)initializer) : null); } internal static SyntaxToken GetArgumentModifiers(RefKind refKind) { switch (refKind) { case RefKind.None: case RefKind.In: return default; case RefKind.Out: return OutKeyword; case RefKind.Ref: return RefKeyword; case RefKind.RefReadOnlyParameter: return InKeyword; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } private protected override SyntaxNode MethodDeclaration( string name, IEnumerable<SyntaxNode>? parameters, IEnumerable<SyntaxNode>? typeParameters, SyntaxNode? returnType, Accessibility accessibility, DeclarationModifiers modifiers, IEnumerable<SyntaxNode>? statements) { var hasBody = !modifiers.IsAbstract && (!modifiers.IsPartial || statements != null); if (!hasBody) modifiers -= DeclarationModifiers.Async; name = StripExplicitInterfaceName(name); return SyntaxFactory.MethodDeclaration( attributeLists: default, modifiers: AsModifierList(accessibility, modifiers, SyntaxKind.MethodDeclaration), returnType: returnType != null ? (TypeSyntax)returnType : SyntaxFactory.PredefinedType(VoidKeyword), explicitInterfaceSpecifier: null, identifier: name.ToIdentifierToken(), typeParameterList: AsTypeParameterList(typeParameters), parameterList: AsParameterList(parameters), constraintClauses: default, body: hasBody ? CreateBlock(statements) : null, expressionBody: null, semicolonToken: !hasBody ? SemicolonToken : default); } private static string StripExplicitInterfaceName(string name) { // Only keep what's after the dot (if this is an explicit impl). The explicit impl part will be added by // the caller. return name.LastIndexOf('.') is var index && index >= 0 ? name[(index + 1)..] : name; } public override SyntaxNode OperatorDeclaration(OperatorKind kind, IEnumerable<SyntaxNode>? parameters = null, SyntaxNode? returnType = null, Accessibility accessibility = Accessibility.NotApplicable, DeclarationModifiers modifiers = default, IEnumerable<SyntaxNode>? statements = null) { return OperatorDeclaration(GetOperatorName(kind), isImplicitConversion: kind == OperatorKind.ImplicitConversion, parameters, returnType, accessibility, modifiers, statements); } private protected override SyntaxNode OperatorDeclaration(string operatorName, bool isImplicitConversion, IEnumerable<SyntaxNode>? parameters = null, SyntaxNode? returnType = null, Accessibility accessibility = Accessibility.NotApplicable, DeclarationModifiers modifiers = default, IEnumerable<SyntaxNode>? statements = null) { var hasBody = !modifiers.IsAbstract && (!modifiers.IsPartial || statements != null); var returnTypeNode = returnType != null ? (TypeSyntax)returnType : SyntaxFactory.PredefinedType(VoidKeyword); var parameterList = AsParameterList(parameters); var body = hasBody ? CreateBlock(statements) : null; var semicolon = !hasBody ? SemicolonToken : default; var modifierList = AsModifierList(accessibility, modifiers, SyntaxKind.OperatorDeclaration); var attributes = default(SyntaxList<AttributeListSyntax>); if (operatorName is WellKnownMemberNames.ImplicitConversionName or WellKnownMemberNames.ExplicitConversionName or WellKnownMemberNames.CheckedExplicitConversionName) { var isImplicit = operatorName is WellKnownMemberNames.ImplicitConversionName; return SyntaxFactory.ConversionOperatorDeclaration( attributes, modifierList, isImplicit ? ImplicitKeyword : ExplicitKeyword, explicitInterfaceSpecifier: null, OperatorKeyword, checkedKeyword: CSharp.SyntaxFacts.IsCheckedOperator(operatorName) ? CheckedKeyword : default, returnTypeNode, parameterList, body, expressionBody: null, semicolon); } else { return SyntaxFactory.OperatorDeclaration( attributes, modifierList, returnTypeNode, explicitInterfaceSpecifier: null, OperatorKeyword, checkedKeyword: CSharp.SyntaxFacts.IsCheckedOperator(operatorName) ? CheckedKeyword : default, operatorToken: SyntaxFactory.Token(GetOperatorSyntaxKind(operatorName)), parameterList, body, expressionBody: null, semicolon); } } private static SyntaxKind GetOperatorSyntaxKind(string operatorName) { var operatorKind = CSharp.SyntaxFacts.GetOperatorKind(operatorName); if (operatorKind == SyntaxKind.None) { throw new ArgumentException("Unknown operator kind."); } return operatorKind; } private static string GetOperatorName(OperatorKind kind) => kind switch { OperatorKind.ImplicitConversion => WellKnownMemberNames.ImplicitConversionName, OperatorKind.ExplicitConversion => WellKnownMemberNames.ExplicitConversionName, OperatorKind.Addition => WellKnownMemberNames.AdditionOperatorName, OperatorKind.BitwiseAnd => WellKnownMemberNames.BitwiseAndOperatorName, OperatorKind.BitwiseOr => WellKnownMemberNames.BitwiseOrOperatorName, OperatorKind.Decrement => WellKnownMemberNames.DecrementOperatorName, OperatorKind.Division => WellKnownMemberNames.DivisionOperatorName, OperatorKind.Equality => WellKnownMemberNames.EqualityOperatorName, OperatorKind.ExclusiveOr => WellKnownMemberNames.ExclusiveOrOperatorName, OperatorKind.False => WellKnownMemberNames.FalseOperatorName, OperatorKind.GreaterThan => WellKnownMemberNames.GreaterThanOperatorName, OperatorKind.GreaterThanOrEqual => WellKnownMemberNames.GreaterThanOrEqualOperatorName, OperatorKind.Increment => WellKnownMemberNames.IncrementOperatorName, OperatorKind.Inequality => WellKnownMemberNames.InequalityOperatorName, OperatorKind.LeftShift => WellKnownMemberNames.LeftShiftOperatorName, OperatorKind.LessThan => WellKnownMemberNames.LessThanOperatorName, OperatorKind.LessThanOrEqual => WellKnownMemberNames.LessThanOrEqualOperatorName, OperatorKind.LogicalNot => WellKnownMemberNames.LogicalNotOperatorName, OperatorKind.Modulus => WellKnownMemberNames.ModulusOperatorName, OperatorKind.Multiply => WellKnownMemberNames.MultiplyOperatorName, OperatorKind.OnesComplement => WellKnownMemberNames.OnesComplementOperatorName, OperatorKind.RightShift => WellKnownMemberNames.RightShiftOperatorName, OperatorKind.UnsignedRightShift => WellKnownMemberNames.UnsignedRightShiftOperatorName, OperatorKind.Subtraction => WellKnownMemberNames.SubtractionOperatorName, OperatorKind.True => WellKnownMemberNames.TrueOperatorName, OperatorKind.UnaryNegation => WellKnownMemberNames.UnaryNegationOperatorName, OperatorKind.UnaryPlus => WellKnownMemberNames.UnaryPlusOperatorName, OperatorKind.AdditionAssignment => WellKnownMemberNames.AdditionAssignmentOperatorName, OperatorKind.SubtractionAssignment => WellKnownMemberNames.SubtractionAssignmentOperatorName, OperatorKind.MultiplicationAssignment => WellKnownMemberNames.MultiplicationAssignmentOperatorName, OperatorKind.DivisionAssignment => WellKnownMemberNames.DivisionAssignmentOperatorName, OperatorKind.ModulusAssignment => WellKnownMemberNames.ModulusAssignmentOperatorName, OperatorKind.ExclusiveOrAssignment => WellKnownMemberNames.ExclusiveOrAssignmentOperatorName, OperatorKind.BitwiseAndAssignment => WellKnownMemberNames.BitwiseAndAssignmentOperatorName, OperatorKind.BitwiseOrAssignment => WellKnownMemberNames.BitwiseOrAssignmentOperatorName, OperatorKind.LeftShiftAssignment => WellKnownMemberNames.LeftShiftAssignmentOperatorName, OperatorKind.RightShiftAssignment => WellKnownMemberNames.RightShiftAssignmentOperatorName, OperatorKind.UnsignedRightShiftAssignment => WellKnownMemberNames.UnsignedRightShiftAssignmentOperatorName, _ => throw new ArgumentException("Unknown operator kind."), }; private static ParameterListSyntax AsParameterList(IEnumerable<SyntaxNode>? parameters) { return parameters != null ? SyntaxFactory.ParameterList([.. parameters.Cast<ParameterSyntax>()]) : SyntaxFactory.ParameterList(); } public override SyntaxNode ConstructorDeclaration( string? name, IEnumerable<SyntaxNode>? parameters, Accessibility accessibility, DeclarationModifiers modifiers, IEnumerable<SyntaxNode>? baseConstructorArguments, IEnumerable<SyntaxNode>? statements) { return SyntaxFactory.ConstructorDeclaration( default, AsModifierList(accessibility, modifiers, SyntaxKind.ConstructorDeclaration), (name ?? "ctor").ToIdentifierToken(), AsParameterList(parameters), baseConstructorArguments != null ? SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList([.. baseConstructorArguments.Select(AsArgument)])) : null, CreateBlock(statements)); } private protected override SyntaxNode DestructorDeclaration(IMethodSymbol destructorMethod) => SyntaxFactory.DestructorDeclaration(destructorMethod.ContainingType.Name).WithBody(SyntaxFactory.Block()); public override SyntaxNode PropertyDeclaration( string name, SyntaxNode type, Accessibility accessibility, DeclarationModifiers modifiers, IEnumerable<SyntaxNode>? getAccessorStatements, IEnumerable<SyntaxNode>? setAccessorStatements) { SyntaxNode? getAccessor = null; SyntaxNode? setAccessor = null; if (!modifiers.IsWriteOnly) getAccessor = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, modifiers.IsAbstract ? null : getAccessorStatements); if (!modifiers.IsReadOnly) setAccessor = AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, modifiers.IsAbstract ? null : setAccessorStatements); return PropertyDeclaration(name, type, getAccessor, setAccessor, accessibility, modifiers); } private protected override SyntaxNode PropertyDeclaration( string name, SyntaxNode type, SyntaxNode? getAccessor, SyntaxNode? setAccessor, Accessibility accessibility, DeclarationModifiers modifiers) { var accessors = new List<AccessorDeclarationSyntax>(); accessors.AddIfNotNull((AccessorDeclarationSyntax?)getAccessor); accessors.AddIfNotNull((AccessorDeclarationSyntax?)setAccessor); var actualModifiers = modifiers - (DeclarationModifiers.ReadOnly | DeclarationModifiers.WriteOnly); name = StripExplicitInterfaceName(name); return SyntaxFactory.PropertyDeclaration( attributeLists: default, AsModifierList(accessibility, actualModifiers, SyntaxKind.PropertyDeclaration), (TypeSyntax)type, explicitInterfaceSpecifier: null, name.ToIdentifierToken(), SyntaxFactory.AccessorList([.. accessors])); } public override SyntaxNode GetAccessorDeclaration(Accessibility accessibility, IEnumerable<SyntaxNode>? statements) => AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, accessibility, statements); private protected override SyntaxNode SetAccessorDeclaration(Accessibility accessibility, bool isInitOnly, IEnumerable<SyntaxNode>? statements) => AccessorDeclaration(isInitOnly ? SyntaxKind.InitAccessorDeclaration : SyntaxKind.SetAccessorDeclaration, accessibility, statements); private static AccessorDeclarationSyntax AccessorDeclaration( SyntaxKind kind, Accessibility accessibility, IEnumerable<SyntaxNode>? statements) { var accessor = SyntaxFactory .AccessorDeclaration(kind) .WithModifiers( AsModifierList(accessibility, DeclarationModifiers.None, SyntaxKind.PropertyDeclaration)); accessor = statements == null ? accessor.WithSemicolonToken(SemicolonToken) : accessor.WithBody(CreateBlock(statements)); return accessor; } public override SyntaxNode WithAccessorDeclarations(SyntaxNode declaration, IEnumerable<SyntaxNode> accessorDeclarations) => declaration switch { PropertyDeclarationSyntax property => property.WithAccessorList(CreateAccessorList(property.AccessorList, accessorDeclarations)) .WithExpressionBody(null) .WithSemicolonToken(property.Initializer is null ? default : property.SemicolonToken), IndexerDeclarationSyntax indexer => indexer.WithAccessorList(CreateAccessorList(indexer.AccessorList, accessorDeclarations)) .WithExpressionBody(null) .WithSemicolonToken(default), _ => declaration, }; private static AccessorListSyntax CreateAccessorList(AccessorListSyntax? accessorList, IEnumerable<SyntaxNode> accessorDeclarations) { var list = SyntaxFactory.List(accessorDeclarations.Cast<AccessorDeclarationSyntax>()); return accessorList == null ? SyntaxFactory.AccessorList(list) : accessorList.WithAccessors(list); } public override SyntaxNode IndexerDeclaration( IEnumerable<SyntaxNode> parameters, SyntaxNode type, Accessibility accessibility, DeclarationModifiers modifiers, IEnumerable<SyntaxNode>? getAccessorStatements, IEnumerable<SyntaxNode>? setAccessorStatements) { var accessors = new List<AccessorDeclarationSyntax>(); var hasGetter = !modifiers.IsWriteOnly; var hasSetter = !modifiers.IsReadOnly; if (modifiers.IsAbstract) { getAccessorStatements = null; setAccessorStatements = null; } else { if (getAccessorStatements == null && hasGetter) getAccessorStatements = []; if (setAccessorStatements == null && hasSetter) setAccessorStatements = []; } if (hasGetter) { accessors.Add(AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, getAccessorStatements)); } if (hasSetter) { accessors.Add(AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, setAccessorStatements)); } var actualModifiers = modifiers - (DeclarationModifiers.ReadOnly | DeclarationModifiers.WriteOnly); return SyntaxFactory.IndexerDeclaration( default, AsModifierList(accessibility, actualModifiers, SyntaxKind.IndexerDeclaration), (TypeSyntax)type, explicitInterfaceSpecifier: null, AsBracketedParameterList(parameters), SyntaxFactory.AccessorList([.. accessors])); } private static BracketedParameterListSyntax AsBracketedParameterList(IEnumerable<SyntaxNode> parameters) { return parameters != null ? SyntaxFactory.BracketedParameterList([.. parameters.Cast<ParameterSyntax>()]) : SyntaxFactory.BracketedParameterList(); } private static AccessorDeclarationSyntax AccessorDeclaration(SyntaxKind kind, IEnumerable<SyntaxNode>? statements) { var ad = SyntaxFactory.AccessorDeclaration( kind, statements != null ? CreateBlock(statements) : null); if (statements == null) { ad = ad.WithSemicolonToken(SemicolonToken); } return ad; } public override SyntaxNode EventDeclaration( string name, SyntaxNode type, Accessibility accessibility, DeclarationModifiers modifiers) { return SyntaxFactory.EventFieldDeclaration( default, AsModifierList(accessibility, modifiers, SyntaxKind.EventFieldDeclaration), SyntaxFactory.VariableDeclaration( (TypeSyntax)type, [SyntaxFactory.VariableDeclarator(name)])); } public override SyntaxNode CustomEventDeclaration( string name, SyntaxNode type, Accessibility accessibility, DeclarationModifiers modifiers, IEnumerable<SyntaxNode>? parameters, IEnumerable<SyntaxNode>? addAccessorStatements, IEnumerable<SyntaxNode>? removeAccessorStatements) { var accessors = new List<AccessorDeclarationSyntax>(); if (modifiers.IsAbstract) { addAccessorStatements = null; removeAccessorStatements = null; } else { addAccessorStatements ??= []; removeAccessorStatements ??= []; } accessors.Add(AccessorDeclaration(SyntaxKind.AddAccessorDeclaration, addAccessorStatements)); accessors.Add(AccessorDeclaration(SyntaxKind.RemoveAccessorDeclaration, removeAccessorStatements)); return SyntaxFactory.EventDeclaration( default, AsModifierList(accessibility, modifiers, SyntaxKind.EventDeclaration), (TypeSyntax)type, null, name.ToIdentifierToken(), SyntaxFactory.AccessorList([.. accessors])); } public override SyntaxNode? AsPublicInterfaceImplementation(SyntaxNode declaration, SyntaxNode interfaceTypeName, string? interfaceMemberName) { // C# interface implementations are implicit/not-specified -- so they are just named the name as the interface member return PreserveTrivia(declaration, d => { d = WithInterfaceSpecifier(d, specifier: null); d = this.AsImplementation(d, Accessibility.Public); if (interfaceMemberName != null) { d = this.WithName(d, interfaceMemberName); } return d; }); } public override SyntaxNode? AsPrivateInterfaceImplementation(SyntaxNode declaration, SyntaxNode interfaceTypeName, string? interfaceMemberName) { return PreserveTrivia(declaration, d => { d = this.AsImplementation(d, Accessibility.NotApplicable); d = this.WithoutConstraints(d); if (interfaceMemberName != null) { d = this.WithName(d, interfaceMemberName); } return WithInterfaceSpecifier(d, SyntaxFactory.ExplicitInterfaceSpecifier((NameSyntax)interfaceTypeName)); }); } private SyntaxNode WithoutConstraints(SyntaxNode declaration) { if (declaration is MethodDeclarationSyntax method) { if (method.ConstraintClauses.Count > 0) { return RemoveNodes(method, method.ConstraintClauses); } } return declaration; } private static SyntaxNode WithInterfaceSpecifier(SyntaxNode declaration, ExplicitInterfaceSpecifierSyntax? specifier) => declaration.Kind() switch { SyntaxKind.MethodDeclaration => ((MethodDeclarationSyntax)declaration).WithExplicitInterfaceSpecifier(specifier), SyntaxKind.PropertyDeclaration => ((PropertyDeclarationSyntax)declaration).WithExplicitInterfaceSpecifier(specifier), SyntaxKind.OperatorDeclaration => ((OperatorDeclarationSyntax)declaration).WithExplicitInterfaceSpecifier(specifier), SyntaxKind.ConversionOperatorDeclaration => ((ConversionOperatorDeclarationSyntax)declaration).WithExplicitInterfaceSpecifier(specifier), SyntaxKind.IndexerDeclaration => ((IndexerDeclarationSyntax)declaration).WithExplicitInterfaceSpecifier(specifier), SyntaxKind.EventDeclaration => ((EventDeclarationSyntax)declaration).WithExplicitInterfaceSpecifier(specifier), _ => declaration, }; private SyntaxNode AsImplementation(SyntaxNode declaration, Accessibility requiredAccess) { declaration = this.WithAccessibility(declaration, requiredAccess); declaration = this.WithModifiers(declaration, this.GetModifiers(declaration) - DeclarationModifiers.Abstract); declaration = WithBodies(declaration); return declaration; } private static SyntaxNode WithBodies(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.MethodDeclaration: case SyntaxKind.ConversionOperatorDeclaration: case SyntaxKind.OperatorDeclaration: var method = (BaseMethodDeclarationSyntax)declaration; return method is { Body: null, ExpressionBody: null } ? method.WithSemicolonToken(default).WithBody(CreateBlock()) : method; case SyntaxKind.PropertyDeclaration: var prop = (PropertyDeclarationSyntax)declaration; return (prop.AccessorList != null) ? prop.WithAccessorList(WithBodies(prop.AccessorList)) : prop; case SyntaxKind.IndexerDeclaration: var ind = (IndexerDeclarationSyntax)declaration; return (ind.AccessorList != null) ? ind.WithAccessorList(WithBodies(ind.AccessorList)) : ind; case SyntaxKind.EventDeclaration: var ev = (EventDeclarationSyntax)declaration; return (ev.AccessorList != null) ? ev.WithAccessorList(WithBodies(ev.AccessorList)) : ev; } return declaration; } private static AccessorListSyntax WithBodies(AccessorListSyntax accessorList) => accessorList.WithAccessors([.. accessorList.Accessors.Select(WithBody)]); private static AccessorDeclarationSyntax WithBody(AccessorDeclarationSyntax accessor) => accessor is { Body: null, ExpressionBody: null } ? accessor.WithSemicolonToken(default).WithBody(CreateBlock(null)) : accessor; private static AccessorListSyntax? WithoutBodies(AccessorListSyntax? accessorList) => accessorList?.WithAccessors([.. accessorList.Accessors.Select(WithoutBody)]); private static AccessorDeclarationSyntax WithoutBody(AccessorDeclarationSyntax accessor) => accessor.Body != null ? accessor.WithSemicolonToken(SemicolonToken).WithBody(null) : accessor.ExpressionBody != null ? accessor.WithExpressionBody(null) : accessor; private protected override SyntaxNode ClassDeclaration( bool isRecord, string name, IEnumerable<SyntaxNode>? typeParameters, Accessibility accessibility, DeclarationModifiers modifiers, SyntaxNode? baseType, IEnumerable<SyntaxNode>? interfaceTypes, IEnumerable<SyntaxNode>? members) { using var _ = ArrayBuilder<BaseTypeSyntax>.GetInstance(out var baseTypes); if (baseType != null || interfaceTypes != null) { if (baseType != null) baseTypes.Add(SyntaxFactory.SimpleBaseType((TypeSyntax)baseType)); if (interfaceTypes != null) baseTypes.AddRange(interfaceTypes.Select(i => SyntaxFactory.SimpleBaseType((TypeSyntax)i))); } var kind = isRecord ? SyntaxKind.RecordDeclaration : SyntaxKind.ClassDeclaration; var modifierList = AsModifierList(accessibility, modifiers, kind); var nameToken = name.ToIdentifierToken(); var typeParameterList = AsTypeParameterList(typeParameters); var baseTypeList = baseTypes.Count > 0 ? SyntaxFactory.BaseList([.. baseTypes]) : null; var typeMembers = this.AsClassMembers(name, members); return isRecord ? SyntaxFactory.RecordDeclaration(default, modifierList, RecordKeyword, nameToken, typeParameterList, null, baseTypeList, default, typeMembers) : SyntaxFactory.ClassDeclaration(default, modifierList, nameToken, typeParameterList, baseTypeList, default, typeMembers); } private SyntaxList<MemberDeclarationSyntax> AsClassMembers(string className, IEnumerable<SyntaxNode>? members) { return members != null ? [.. members.Select(m => AsClassMember(m, className)).WhereNotNull()] : default; } private MemberDeclarationSyntax? AsClassMember(SyntaxNode node, string className) { switch (node.Kind()) { case SyntaxKind.ConstructorDeclaration: node = ((ConstructorDeclarationSyntax)node).WithIdentifier(className.ToIdentifierToken()); break; case SyntaxKind.VariableDeclaration: case SyntaxKind.VariableDeclarator: node = AsIsolatedDeclaration(node); break; } return node as MemberDeclarationSyntax; } private protected override SyntaxNode StructDeclaration( bool isRecord, string name, IEnumerable<SyntaxNode>? typeParameters, Accessibility accessibility, DeclarationModifiers modifiers, IEnumerable<SyntaxNode>? interfaceTypes, IEnumerable<SyntaxNode>? members) { var itypes = interfaceTypes?.Select(i => (BaseTypeSyntax)SyntaxFactory.SimpleBaseType((TypeSyntax)i)).ToList(); var kind = isRecord ? SyntaxKind.RecordStructDeclaration : SyntaxKind.StructDeclaration; var modifierList = AsModifierList(accessibility, modifiers, kind); var nameToken = name.ToIdentifierToken(); var typeParameterList = AsTypeParameterList(typeParameters); var baseTypeList = itypes?.Count > 0 ? SyntaxFactory.BaseList([.. itypes]) : null; var structMembers = this.AsClassMembers(name, members); return isRecord ? SyntaxFactory.RecordDeclaration(default, modifierList, RecordKeyword, nameToken, typeParameterList, null, baseTypeList, default, structMembers).WithClassOrStructKeyword(StructKeyword) : SyntaxFactory.StructDeclaration(default, modifierList, nameToken, typeParameterList, baseTypeList, default, structMembers); } private protected override SyntaxNode InterfaceDeclaration( string name, IEnumerable<SyntaxNode>? typeParameters, Accessibility accessibility, IEnumerable<SyntaxNode>? interfaceTypes = null, IEnumerable<SyntaxNode>? members = null) { var itypes = interfaceTypes?.Select(i => (BaseTypeSyntax)SyntaxFactory.SimpleBaseType((TypeSyntax)i)).ToList(); if (itypes?.Count == 0) { itypes = null; } return SyntaxFactory.InterfaceDeclaration( default, AsModifierList(accessibility, DeclarationModifiers.None), name.ToIdentifierToken(), AsTypeParameterList(typeParameters), itypes != null ? SyntaxFactory.BaseList([.. itypes]) : null, default, this.AsInterfaceMembers(members)); } private SyntaxList<MemberDeclarationSyntax> AsInterfaceMembers(IEnumerable<SyntaxNode>? members) { return members != null ? [.. members.Select(AsInterfaceMember).OfType<MemberDeclarationSyntax>()] : default; } internal override SyntaxNode AsInterfaceMember(SyntaxNode m) { return Isolate(m, member => { // return any nested member "as is" without any additional changes if (member is BaseTypeDeclarationSyntax or DelegateDeclarationSyntax) return member; switch (member.Kind()) { case SyntaxKind.MethodDeclaration: return ((MethodDeclarationSyntax)member) .WithModifiers(default) .WithSemicolonToken(SemicolonToken) .WithBody(null) .WithExpressionBody(null); case SyntaxKind.OperatorDeclaration: var operatorDeclaration = (OperatorDeclarationSyntax)member; var abstractVirtualModifiers = operatorDeclaration.Modifiers.Where(x => x.Kind() == SyntaxKind.AbstractKeyword || x.Kind() == SyntaxKind.VirtualKeyword || x.Kind() == SyntaxKind.PublicKeyword); var modifiersToken = SyntaxFactory.TokenList(abstractVirtualModifiers); modifiersToken = modifiersToken.Insert(0, StaticKeyword); return operatorDeclaration .WithModifiers(modifiersToken) .WithSemicolonToken(SemicolonToken) .WithBody(null); case SyntaxKind.PropertyDeclaration: var property = (PropertyDeclarationSyntax)member; return property .WithModifiers(default) .WithAccessorList(WithoutBodies(property.AccessorList)); case SyntaxKind.IndexerDeclaration: var indexer = (IndexerDeclarationSyntax)member; return indexer .WithModifiers(default) .WithAccessorList(WithoutBodies(indexer.AccessorList)); // convert event into field event case SyntaxKind.EventDeclaration: var ev = (EventDeclarationSyntax)member; return this.EventDeclaration( ev.Identifier.ValueText, ev.Type, Accessibility.NotApplicable, DeclarationModifiers.None); case SyntaxKind.EventFieldDeclaration: var ef = (EventFieldDeclarationSyntax)member; return ef.WithModifiers(default); // convert field into property case SyntaxKind.FieldDeclaration: var f = (FieldDeclarationSyntax)member; GetAccessibilityAndModifiers(f.Modifiers, out var acc, out var modifiers, out _); return AsInterfaceMember( PropertyDeclaration(GetName(f), ClearTrivia(f.Declaration.Type), acc, modifiers, getAccessorStatements: null, setAccessorStatements: null)); default: throw ExceptionUtilities.UnexpectedValue(member.Kind()); } }); } public override SyntaxNode EnumDeclaration( string name, Accessibility accessibility, DeclarationModifiers modifiers, IEnumerable<SyntaxNode>? members) { return EnumDeclaration(name, underlyingType: null, accessibility, modifiers, members); } internal override SyntaxNode EnumDeclaration(string name, SyntaxNode? underlyingType, Accessibility accessibility = Accessibility.NotApplicable, DeclarationModifiers modifiers = default, IEnumerable<SyntaxNode>? members = null) { return SyntaxFactory.EnumDeclaration( default, AsModifierList(accessibility, modifiers, SyntaxKind.EnumDeclaration), name.ToIdentifierToken(), underlyingType != null ? SyntaxFactory.BaseList([SyntaxFactory.SimpleBaseType((TypeSyntax)underlyingType)]) : null, this.AsEnumMembers(members)); } public override SyntaxNode EnumMember(string name, SyntaxNode? expression) { return SyntaxFactory.EnumMemberDeclaration( default, name.ToIdentifierToken(), expression != null ? SyntaxFactory.EqualsValueClause((ExpressionSyntax)expression) : null); } private EnumMemberDeclarationSyntax AsEnumMember(SyntaxNode node) { switch (node.Kind()) { case SyntaxKind.IdentifierName: var id = (IdentifierNameSyntax)node; return (EnumMemberDeclarationSyntax)this.EnumMember(id.Identifier.ToString(), null); case SyntaxKind.FieldDeclaration: var fd = (FieldDeclarationSyntax)node; if (fd.Declaration.Variables.Count == 1) { var vd = fd.Declaration.Variables[0]; return (EnumMemberDeclarationSyntax)this.EnumMember(vd.Identifier.ToString(), vd.Initializer?.Value); } break; } return (EnumMemberDeclarationSyntax)node; } private SeparatedSyntaxList<EnumMemberDeclarationSyntax> AsEnumMembers(IEnumerable<SyntaxNode>? members) => members != null ? [.. members.Select(this.AsEnumMember)] : default; private protected override SyntaxNode DelegateDeclaration( string name, IEnumerable<SyntaxNode>? parameters, IEnumerable<SyntaxNode>? typeParameters, SyntaxNode? returnType, Accessibility accessibility = Accessibility.NotApplicable, DeclarationModifiers modifiers = default) { return SyntaxFactory.DelegateDeclaration( default, AsModifierList(accessibility, modifiers, SyntaxKind.DelegateDeclaration), returnType != null ? (TypeSyntax)returnType : SyntaxFactory.PredefinedType(VoidKeyword), name.ToIdentifierToken(), AsTypeParameterList(typeParameters), AsParameterList(parameters), default); } public override SyntaxNode Attribute(SyntaxNode name, IEnumerable<SyntaxNode>? attributeArguments) => AsAttributeList(SyntaxFactory.Attribute((NameSyntax)name, AsAttributeArgumentList(attributeArguments))); public override SyntaxNode AttributeArgument(string? name, SyntaxNode expression) { return name != null ? SyntaxFactory.AttributeArgument(SyntaxFactory.NameEquals(name.ToIdentifierName()), nameColon: null, (ExpressionSyntax)expression) : SyntaxFactory.AttributeArgument((ExpressionSyntax)expression); } private static AttributeArgumentListSyntax? AsAttributeArgumentList(IEnumerable<SyntaxNode>? arguments) => arguments != null ? SyntaxFactory.AttributeArgumentList([.. arguments.Select(AsAttributeArgument)]) : null; private static AttributeArgumentSyntax AsAttributeArgument(SyntaxNode node) { if (node is ExpressionSyntax expr) { return SyntaxFactory.AttributeArgument(expr); } if (node is ArgumentSyntax arg && arg.Expression != null) { return SyntaxFactory.AttributeArgument(null, arg.NameColon, arg.Expression); } return (AttributeArgumentSyntax)node; } [return: MaybeNull, NotNullIfNotNull(nameof(node))] public override TNode ClearTrivia<TNode>([MaybeNull] TNode node) { if (node != null) { return node.WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker); } else { return null; } } private static SyntaxList<AttributeListSyntax> AsAttributeLists(IEnumerable<SyntaxNode> attributes) { return attributes == null ? default : [.. attributes.Select(AsAttributeList)]; } private static AttributeListSyntax AsAttributeList(SyntaxNode node) { return node is AttributeSyntax attr ? SyntaxFactory.AttributeList([attr]) : (AttributeListSyntax)node; } private static readonly ConditionalWeakTable<SyntaxNode, IReadOnlyList<SyntaxNode>> s_declAttributes = new(); public override IReadOnlyList<SyntaxNode> GetAttributes(SyntaxNode declaration) { if (!s_declAttributes.TryGetValue(declaration, out var attrs)) { attrs = s_declAttributes.GetValue(declaration, declaration => Flatten(declaration.GetAttributeLists().Where(al => !IsReturnAttribute(al)))); } return attrs; } private static readonly ConditionalWeakTable<SyntaxNode, IReadOnlyList<SyntaxNode>> s_declReturnAttributes = new(); public override IReadOnlyList<SyntaxNode> GetReturnAttributes(SyntaxNode declaration) { if (!s_declReturnAttributes.TryGetValue(declaration, out var attrs)) { attrs = s_declReturnAttributes.GetValue(declaration, declaration => Flatten(declaration.GetAttributeLists().Where(al => IsReturnAttribute(al)))); } return attrs; } private static bool IsReturnAttribute(AttributeListSyntax list) => list.Target?.Identifier.IsKind(SyntaxKind.ReturnKeyword) ?? false; public override SyntaxNode InsertAttributes(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> attributes) => this.Isolate(declaration, d => this.InsertAttributesInternal(d, index, attributes)); private SyntaxNode InsertAttributesInternal(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> attributes) { var newAttributes = AsAttributeLists(attributes); var existingAttributes = this.GetAttributes(declaration); if (index >= 0 && index < existingAttributes.Count) { return this.InsertNodesBefore(declaration, existingAttributes[index], WithRequiredTargetSpecifier(newAttributes, declaration)); } else if (existingAttributes.Count > 0) { return this.InsertNodesAfter(declaration, existingAttributes[existingAttributes.Count - 1], WithRequiredTargetSpecifier(newAttributes, declaration)); } else { var lists = declaration.GetAttributeLists(); var newList = lists.AddRange(newAttributes); return WithAttributeLists(declaration, newList); } } public override SyntaxNode InsertReturnAttributes(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> attributes) { switch (declaration.Kind()) { case SyntaxKind.MethodDeclaration: case SyntaxKind.OperatorDeclaration: case SyntaxKind.ConversionOperatorDeclaration: case SyntaxKind.DelegateDeclaration: return this.Isolate(declaration, d => this.InsertReturnAttributesInternal(d, index, attributes)); default: return declaration; } } private SyntaxNode InsertReturnAttributesInternal(SyntaxNode d, int index, IEnumerable<SyntaxNode> attributes) { var newAttributes = AsReturnAttributes(attributes); var existingAttributes = this.GetReturnAttributes(d); if (index >= 0 && index < existingAttributes.Count) { return this.InsertNodesBefore(d, existingAttributes[index], newAttributes); } else if (existingAttributes.Count > 0) { return this.InsertNodesAfter(d, existingAttributes[existingAttributes.Count - 1], newAttributes); } else { var lists = d.GetAttributeLists(); var newList = lists.AddRange(newAttributes); return WithAttributeLists(d, newList); } } private static IEnumerable<AttributeListSyntax> AsReturnAttributes(IEnumerable<SyntaxNode> attributes) { return AsAttributeLists(attributes) .Select(list => list.WithTarget(SyntaxFactory.AttributeTargetSpecifier(ReturnKeyword))); } private static SyntaxList<AttributeListSyntax> AsAssemblyAttributes(IEnumerable<AttributeListSyntax> attributes) { return [.. attributes.Select(list => list.WithTarget(SyntaxFactory.AttributeTargetSpecifier(AssemblyKeyword)))]; } private static SyntaxList<AttributeListSyntax> WithRequiredTargetSpecifier(SyntaxList<AttributeListSyntax> attributes, SyntaxNode declaration) { if (!declaration.IsKind(SyntaxKind.CompilationUnit)) { return attributes; } return AsAssemblyAttributes(attributes); } public override IReadOnlyList<SyntaxNode> GetAttributeArguments(SyntaxNode attributeDeclaration) { switch (attributeDeclaration.Kind()) { case SyntaxKind.AttributeList: var list = (AttributeListSyntax)attributeDeclaration; if (list.Attributes.Count == 1) { return this.GetAttributeArguments(list.Attributes[0]); } break; case SyntaxKind.Attribute: var attr = (AttributeSyntax)attributeDeclaration; if (attr.ArgumentList != null) { return attr.ArgumentList.Arguments; } break; } return []; } public override SyntaxNode InsertAttributeArguments(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> attributeArguments) => this.Isolate(declaration, d => InsertAttributeArgumentsInternal(d, index, attributeArguments)); private static SyntaxNode InsertAttributeArgumentsInternal(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> attributeArguments) { var newArgumentList = AsAttributeArgumentList(attributeArguments); var existingArgumentList = GetAttributeArgumentList(declaration); if (existingArgumentList == null) { return WithAttributeArgumentList(declaration, newArgumentList); } else if (newArgumentList != null) { return WithAttributeArgumentList(declaration, existingArgumentList.WithArguments(existingArgumentList.Arguments.InsertRange(index, newArgumentList.Arguments))); } else { return declaration; } } private static AttributeArgumentListSyntax? GetAttributeArgumentList(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.AttributeList: var list = (AttributeListSyntax)declaration; if (list.Attributes.Count == 1) { return list.Attributes[0].ArgumentList; } break; case SyntaxKind.Attribute: var attr = (AttributeSyntax)declaration; return attr.ArgumentList; } return null; } private static SyntaxNode WithAttributeArgumentList(SyntaxNode declaration, AttributeArgumentListSyntax? argList) { switch (declaration.Kind()) { case SyntaxKind.AttributeList: var list = (AttributeListSyntax)declaration; if (list.Attributes.Count == 1) { return ReplaceWithTrivia(declaration, list.Attributes[0], list.Attributes[0].WithArgumentList(argList)); } break; case SyntaxKind.Attribute: var attr = (AttributeSyntax)declaration; return attr.WithArgumentList(argList); } return declaration; } internal static SyntaxList<AttributeListSyntax> GetAttributeLists(SyntaxNode declaration) => declaration switch { MemberDeclarationSyntax memberDecl => memberDecl.AttributeLists, AccessorDeclarationSyntax accessor => accessor.AttributeLists, ParameterSyntax parameter => parameter.AttributeLists, CompilationUnitSyntax compilationUnit => compilationUnit.AttributeLists, StatementSyntax statement => statement.AttributeLists, _ => default, }; private static SyntaxNode WithAttributeLists(SyntaxNode declaration, SyntaxList<AttributeListSyntax> attributeLists) => declaration switch { MemberDeclarationSyntax memberDecl => memberDecl.WithAttributeLists(attributeLists), AccessorDeclarationSyntax accessor => accessor.WithAttributeLists(attributeLists), ParameterSyntax parameter => parameter.WithAttributeLists(attributeLists), CompilationUnitSyntax compilationUnit => compilationUnit.WithAttributeLists(AsAssemblyAttributes(attributeLists)), StatementSyntax statement => statement.WithAttributeLists(attributeLists), TypeParameterSyntax typeParameter => typeParameter.WithAttributeLists(attributeLists), LambdaExpressionSyntax lambdaExpression => lambdaExpression.WithAttributeLists(attributeLists), _ => declaration, }; internal override SyntaxNode? GetPrimaryConstructorParameterList(SyntaxNode declaration) => declaration is TypeDeclarationSyntax { ParameterList: { } parameterList } ? parameterList : null; internal override ImmutableArray<SyntaxNode> GetTypeInheritance(SyntaxNode declaration) => declaration is BaseTypeDeclarationSyntax baseType && baseType.BaseList != null ? [baseType.BaseList] : ImmutableArray<SyntaxNode>.Empty; public override IReadOnlyList<SyntaxNode> GetNamespaceImports(SyntaxNode declaration) => declaration switch { CompilationUnitSyntax compilationUnit => compilationUnit.Usings, BaseNamespaceDeclarationSyntax namespaceDeclaration => namespaceDeclaration.Usings, _ => [], }; public override SyntaxNode InsertNamespaceImports(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> imports) { var result = PreserveTrivia(declaration, d => InsertNamespaceImportsInternal(d, index, imports)); Contract.ThrowIfNull(result); return result; } private SyntaxNode InsertNamespaceImportsInternal(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> imports) { var usingsToInsert = this.AsUsingDirectives(imports); return declaration switch { CompilationUnitSyntax cu => cu.WithUsings(cu.Usings.InsertRange(index, usingsToInsert)), BaseNamespaceDeclarationSyntax nd => nd.WithUsings(nd.Usings.InsertRange(index, usingsToInsert)), _ => declaration, }; } public override IReadOnlyList<SyntaxNode> GetMembers(SyntaxNode declaration) => Flatten(declaration switch { TypeDeclarationSyntax type => type.Members, EnumDeclarationSyntax @enum => @enum.Members, BaseNamespaceDeclarationSyntax @namespace => @namespace.Members, CompilationUnitSyntax compilationUnit => compilationUnit.Members, _ => [], }); private static ImmutableArray<SyntaxNode> Flatten(IEnumerable<SyntaxNode> declarations) { var builder = ArrayBuilder<SyntaxNode>.GetInstance(); foreach (var declaration in declarations) { switch (declaration.Kind()) { case SyntaxKind.FieldDeclaration: FlattenDeclaration(builder, declaration, ((FieldDeclarationSyntax)declaration).Declaration); break; case SyntaxKind.EventFieldDeclaration: FlattenDeclaration(builder, declaration, ((EventFieldDeclarationSyntax)declaration).Declaration); break; case SyntaxKind.LocalDeclarationStatement: FlattenDeclaration(builder, declaration, ((LocalDeclarationStatementSyntax)declaration).Declaration); break; case SyntaxKind.VariableDeclaration: FlattenDeclaration(builder, declaration, (VariableDeclarationSyntax)declaration); break; case SyntaxKind.AttributeList: var attrList = (AttributeListSyntax)declaration; if (attrList.Attributes.Count > 1) { builder.AddRange(attrList.Attributes); } else { builder.Add(attrList); } break; default: builder.Add(declaration); break; } } return builder.ToImmutableAndFree(); static void FlattenDeclaration(ArrayBuilder<SyntaxNode> builder, SyntaxNode declaration, VariableDeclarationSyntax variableDeclaration) { if (variableDeclaration.Variables.Count > 1) { builder.AddRange(variableDeclaration.Variables); } else { builder.Add(declaration); } } } private static int GetDeclarationCount(SyntaxNode declaration) => declaration.Kind() switch { SyntaxKind.FieldDeclaration => ((FieldDeclarationSyntax)declaration).Declaration.Variables.Count, SyntaxKind.EventFieldDeclaration => ((EventFieldDeclarationSyntax)declaration).Declaration.Variables.Count, SyntaxKind.LocalDeclarationStatement => ((LocalDeclarationStatementSyntax)declaration).Declaration.Variables.Count, SyntaxKind.VariableDeclaration => ((VariableDeclarationSyntax)declaration).Variables.Count, SyntaxKind.AttributeList => ((AttributeListSyntax)declaration).Attributes.Count, _ => 1, }; private static SyntaxNode EnsureTypeDeclarationHasBody(SyntaxNode declaration) { if (declaration is BaseTypeDeclarationSyntax typeDeclaration) { return typeDeclaration .WithSemicolonToken(default) .WithOpenBraceToken(typeDeclaration.OpenBraceToken == default ? OpenBraceToken : typeDeclaration.OpenBraceToken) .WithCloseBraceToken(typeDeclaration.CloseBraceToken == default ? CloseBraceToken : typeDeclaration.CloseBraceToken); } return declaration; } public override SyntaxNode InsertMembers(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> members) { declaration = EnsureTypeDeclarationHasBody(declaration); var newMembers = this.AsMembersOf(declaration, members); var existingMembers = this.GetMembers(declaration); if (index >= 0 && index < existingMembers.Count) { return this.InsertNodesBefore(declaration, existingMembers[index], newMembers); } else if (existingMembers.Count > 0) { return this.InsertNodesAfter(declaration, existingMembers[existingMembers.Count - 1], newMembers); } else { return declaration switch { TypeDeclarationSyntax type => type.WithMembers(type.Members.AddRange(newMembers)), EnumDeclarationSyntax @enum => @enum.WithMembers(@enum.Members.AddRange(newMembers.OfType<EnumMemberDeclarationSyntax>())), BaseNamespaceDeclarationSyntax @namespace => @namespace.WithMembers(@namespace.Members.AddRange(newMembers)), CompilationUnitSyntax compilationUnit => compilationUnit.WithMembers(compilationUnit.Members.AddRange(newMembers)), _ => declaration, }; } } [return: NotNullIfNotNull(nameof(members))] private IEnumerable<MemberDeclarationSyntax>? AsMembersOf(SyntaxNode declaration, IEnumerable<SyntaxNode>? members) => members?.Select(m => AsMemberOf(declaration, m)).OfType<MemberDeclarationSyntax>(); private SyntaxNode? AsMemberOf(SyntaxNode declaration, SyntaxNode member) => declaration switch { InterfaceDeclarationSyntax => AsInterfaceMember(member), TypeDeclarationSyntax typeDeclaration => AsClassMember(member, typeDeclaration.Identifier.Text), EnumDeclarationSyntax => AsEnumMember(member), BaseNamespaceDeclarationSyntax => AsNamespaceMember(member), CompilationUnitSyntax => AsNamespaceMember(member), _ => null, }; private static bool CanHaveAccessibility(SyntaxNode declaration) // For certain declarations, the answer of CanHaveAccessibility depends on the modifiers. // For example, static constructors cannot have accessibility, but constructors in general can. // The same applies to file-local declarations (e.g, "file class C { }"). // For such declarations, we want to return true. This is because we can be explicitly asked to put accessibility. // In such cases, we'll drop the modifier that prevents us from having accessibility. => CSharpAccessibilityFacts.Instance.CanHaveAccessibility(declaration, ignoreDeclarationModifiers: true); public override Accessibility GetAccessibility(SyntaxNode declaration) => CSharpAccessibilityFacts.Instance.GetAccessibility(declaration); private static void GetAccessibilityAndModifiers(SyntaxTokenList modifierList, out Accessibility accessibility, out DeclarationModifiers declarationModifiers, out bool isDefault) { CSharpAccessibilityFacts.GetAccessibilityAndModifiers(modifierList, out accessibility, out var modifiers, out isDefault); declarationModifiers = modifiers.ToDeclarationModifiers(); } public override SyntaxNode WithAccessibility(SyntaxNode declaration, Accessibility accessibility) { if (!CanHaveAccessibility(declaration) && accessibility != Accessibility.NotApplicable) { return declaration; } return this.Isolate(declaration, d => { var tokens = GetModifierTokens(d); GetAccessibilityAndModifiers(tokens, out _, out var modifiers, out _); if (modifiers.IsFile && accessibility != Accessibility.NotApplicable) { // If user wants to set accessibility for a file-local declaration, we remove file. // Otherwise, code will be in error: // error CS9052: File-local type '{0}' cannot use accessibility modifiers. modifiers = modifiers.WithIsFile(false); } if (modifiers.IsStatic && declaration.IsKind(SyntaxKind.ConstructorDeclaration) && accessibility != Accessibility.NotApplicable) { // If user wants to add accessibility for a static constructor, we remove static modifier modifiers = modifiers.WithIsStatic(false); } // We're updating the modifiers for something. We don't want to add elastic trivia in that case as // we don't want the act of adding/removing/modifying modifiers to change the formatting of the parent // construct. var newTokens = Merge(tokens, AsModifierList(accessibility, modifiers, withLeadingElasticMarker: false)); return SetModifierTokens(d, newTokens); }); } private static readonly DeclarationModifiers s_fieldModifiers = DeclarationModifiers.Const | DeclarationModifiers.New | DeclarationModifiers.ReadOnly | DeclarationModifiers.Ref | DeclarationModifiers.Required | DeclarationModifiers.Static | DeclarationModifiers.Unsafe | DeclarationModifiers.Volatile; private static readonly DeclarationModifiers s_methodModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.Async | DeclarationModifiers.Extern | DeclarationModifiers.New | DeclarationModifiers.Override | DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Sealed | DeclarationModifiers.Static | DeclarationModifiers.Virtual | DeclarationModifiers.Unsafe; private static readonly DeclarationModifiers s_constructorModifiers = DeclarationModifiers.Extern | DeclarationModifiers.Partial | DeclarationModifiers.Static | DeclarationModifiers.Unsafe; private static readonly DeclarationModifiers s_destructorModifiers = DeclarationModifiers.Unsafe; private static readonly DeclarationModifiers s_propertyModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.Extern | DeclarationModifiers.New | DeclarationModifiers.Override | DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Required | DeclarationModifiers.Sealed | DeclarationModifiers.Static | DeclarationModifiers.Virtual | DeclarationModifiers.Unsafe; private static readonly DeclarationModifiers s_eventModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.Extern | DeclarationModifiers.New | DeclarationModifiers.Override | DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Sealed | DeclarationModifiers.Static | DeclarationModifiers.Virtual | DeclarationModifiers.Unsafe; private static readonly DeclarationModifiers s_indexerModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.Extern | DeclarationModifiers.New | DeclarationModifiers.Override | DeclarationModifiers.ReadOnly | DeclarationModifiers.Sealed | DeclarationModifiers.Static | DeclarationModifiers.Virtual | DeclarationModifiers.Unsafe; private static readonly DeclarationModifiers s_classModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.Sealed | DeclarationModifiers.Static | DeclarationModifiers.Unsafe | DeclarationModifiers.File | DeclarationModifiers.Closed; private static readonly DeclarationModifiers s_recordModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.Sealed | DeclarationModifiers.Unsafe | DeclarationModifiers.File; private static readonly DeclarationModifiers s_structModifiers = DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Ref | DeclarationModifiers.Unsafe | DeclarationModifiers.File; private static readonly DeclarationModifiers s_interfaceModifiers = DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.Unsafe | DeclarationModifiers.File; private static readonly DeclarationModifiers s_eventAccessorModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.New | DeclarationModifiers.Override | DeclarationModifiers.Virtual; private static readonly DeclarationModifiers s_propertyAccessorModifiers = s_eventAccessorModifiers | DeclarationModifiers.ReadOnly; private static readonly DeclarationModifiers s_localFunctionModifiers = DeclarationModifiers.Async | DeclarationModifiers.Static | DeclarationModifiers.Unsafe | DeclarationModifiers.Extern; private static readonly DeclarationModifiers s_lambdaModifiers = DeclarationModifiers.Async | DeclarationModifiers.Static; private static DeclarationModifiers GetAllowedModifiers(SyntaxKind kind) { switch (kind) { case SyntaxKind.RecordDeclaration: return s_recordModifiers; case SyntaxKind.ClassDeclaration: return s_classModifiers; case SyntaxKind.EnumDeclaration: return DeclarationModifiers.New | DeclarationModifiers.File; case SyntaxKind.DelegateDeclaration: return DeclarationModifiers.New | DeclarationModifiers.Unsafe | DeclarationModifiers.File; case SyntaxKind.InterfaceDeclaration: return s_interfaceModifiers; case SyntaxKind.StructDeclaration: case SyntaxKind.RecordStructDeclaration: return s_structModifiers; case SyntaxKind.MethodDeclaration: case SyntaxKind.OperatorDeclaration: case SyntaxKind.ConversionOperatorDeclaration: return s_methodModifiers; case SyntaxKind.ConstructorDeclaration: return s_constructorModifiers; case SyntaxKind.DestructorDeclaration: return s_destructorModifiers; case SyntaxKind.FieldDeclaration: return s_fieldModifiers; case SyntaxKind.PropertyDeclaration: return s_propertyModifiers; case SyntaxKind.IndexerDeclaration: return s_indexerModifiers; case SyntaxKind.EventFieldDeclaration: case SyntaxKind.EventDeclaration: return s_eventModifiers; case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: return s_propertyAccessorModifiers; case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: return s_eventAccessorModifiers; case SyntaxKind.LocalFunctionStatement: return s_localFunctionModifiers; case SyntaxKind.ParenthesizedLambdaExpression: case SyntaxKind.SimpleLambdaExpression: case SyntaxKind.AnonymousMethodExpression: return s_lambdaModifiers; case SyntaxKind.EnumMemberDeclaration: case SyntaxKind.Parameter: case SyntaxKind.LocalDeclarationStatement: default: return DeclarationModifiers.None; } } public override DeclarationModifiers GetModifiers(SyntaxNode declaration) { var modifierTokens = GetModifierTokens(declaration); GetAccessibilityAndModifiers(modifierTokens, out _, out var modifiers, out _); return modifiers; } private static SyntaxTokenList GetModifierTokens(SyntaxNode declaration) => CSharpAccessibilityFacts.GetModifierTokens(declaration); internal override TSyntaxNode WithModifiers<TSyntaxNode>(TSyntaxNode declaration, DeclarationModifiers modifiers) => (TSyntaxNode)this.Isolate(declaration, d => this.WithModifiersInternal(d, modifiers)); private SyntaxNode WithModifiersInternal(SyntaxNode declaration, DeclarationModifiers modifiers) { modifiers &= GetAllowedModifiers(declaration.Kind()); var existingModifiers = this.GetModifiers(declaration); if (modifiers != existingModifiers) { return this.Isolate(declaration, d => { var tokens = GetModifierTokens(d); GetAccessibilityAndModifiers(tokens, out var accessibility, out var tmp, out _); if (accessibility != Accessibility.NotApplicable) { if (modifiers.IsFile || (modifiers.IsStatic && declaration.IsKind(SyntaxKind.ConstructorDeclaration))) { // We remove the accessibility if the modifiers don't allow it. accessibility = Accessibility.NotApplicable; } } // 'closed' implies abstract on classes and 'closed abstract' can't be explicitly combined. if (modifiers.IsClosed && modifiers.IsAbstract) { modifiers = modifiers.WithIsAbstract(false); } // We're updating the modifiers for something. We don't want to add elastic trivia in that case as // we don't want the act of adding/removing/modifying modifiers to change the formatting of the parent // construct. var newTokens = Merge(tokens, AsModifierList(accessibility, modifiers, withLeadingElasticMarker: false)); return SetModifierTokens(d, newTokens); }); } else { // no change return declaration; } } private static SyntaxNode SetModifierTokens(SyntaxNode declaration, SyntaxTokenList modifiers) => declaration switch { MemberDeclarationSyntax memberDecl => memberDecl.WithModifiers(modifiers), ParameterSyntax parameter => parameter.WithModifiers(modifiers), LocalDeclarationStatementSyntax localDecl => localDecl.WithModifiers(modifiers), LocalFunctionStatementSyntax localFunc => localFunc.WithModifiers(modifiers), AccessorDeclarationSyntax accessor => accessor.WithModifiers(modifiers), AnonymousFunctionExpressionSyntax anonymous => anonymous.WithModifiers(modifiers), _ => declaration, }; private static SyntaxTokenList AsModifierList( Accessibility accessibility, DeclarationModifiers modifiers, SyntaxKind kind, bool withLeadingElasticMarker = true) => AsModifierList(accessibility, GetAllowedModifiers(kind) & modifiers, withLeadingElasticMarker); private static SyntaxTokenList AsModifierList( Accessibility accessibility, DeclarationModifiers modifiers, bool withLeadingElasticMarker = true) { using var _ = ArrayBuilder<SyntaxToken>.GetInstance(out var list); list.AddRange((IEnumerable<SyntaxToken>)(accessibility switch { Accessibility.Internal => [InternalKeyword], Accessibility.Public => [PublicKeyword], Accessibility.Private => [PrivateKeyword], Accessibility.Protected => [ProtectedKeyword], Accessibility.ProtectedOrInternal => [ProtectedKeyword, InternalKeyword], Accessibility.ProtectedAndInternal => [PrivateKeyword, ProtectedKeyword], _ => [], })); AddIf(modifiers.IsFile, FileKeyword); AddIf(modifiers.IsAbstract, AbstractKeyword); AddIf(modifiers.IsNew, NewKeyword); AddIf(modifiers.IsSealed, SealedKeyword); AddIf(modifiers.IsOverride, OverrideKeyword); AddIf(modifiers.IsVirtual, VirtualKeyword); AddIf(modifiers.IsStatic, StaticKeyword); AddIf(modifiers.IsAsync, AsyncKeyword); AddIf(modifiers.IsConst, ConstKeyword); AddIf(modifiers.IsReadOnly, ReadOnlyKeyword); AddIf(modifiers.IsUnsafe, UnsafeKeyword); AddIf(modifiers.IsVolatile, VolatileKeyword); AddIf(modifiers.IsExtern, ExternKeyword); AddIf(modifiers.IsRequired, RequiredKeyword); AddIf(modifiers.IsFixed, FixedKeyword); AddIf(modifiers.IsClosed, ClosedKeyword); // partial and ref must be last AddIf(modifiers.IsRef, RefKeyword); AddIf(modifiers.IsPartial, PartialKeyword); for (int i = 0, n = list.Count; i < n; i++) { // By default, do not place leading elastic trivia on modifiers we make. Just because something is // adding/removing/modifying modifiers does not mean we want the parent construct to change its formatting // respective to what's around it. if (!withLeadingElasticMarker) list[i] = list[i].WithoutLeadingTrivia(); list[i] = list[i].WithTrailingTrivia(SyntaxFactory.ElasticSpace); } return [.. list]; void AddIf(bool test, SyntaxToken token) { if (test) list.Add(token); } } private protected override SyntaxNode TypeParameter(string name) => SyntaxFactory.TypeParameter(name); private protected override SyntaxNode TypeParameter(ITypeParameterSymbol typeParameter) { return SyntaxFactory.TypeParameter( attributeLists: default, varianceKeyword: typeParameter.Variance switch { VarianceKind.In => InKeyword, VarianceKind.Out => OutKeyword, _ => default, }, SyntaxFactory.Identifier(typeParameter.Name)); } private static TypeParameterListSyntax? AsTypeParameterList(IEnumerable<SyntaxNode>? typeParameterNodes) { var typeParameters = typeParameterNodes != null ? SyntaxFactory.TypeParameterList([.. typeParameterNodes.Cast<TypeParameterSyntax>()]) : null; return typeParameters?.Parameters.Count > 0 ? typeParameters : null; } private protected override SyntaxNode WithTypeParameters(SyntaxNode declaration, IEnumerable<SyntaxNode> typeParameters) { var typeParameterList = AsTypeParameterList(typeParameters); return declaration switch { MethodDeclarationSyntax method => method.WithTypeParameterList(typeParameterList), TypeDeclarationSyntax type => type.WithTypeParameterList(typeParameterList), DelegateDeclarationSyntax @delegate => @delegate.WithTypeParameterList(typeParameterList), _ => declaration, }; } internal override SyntaxNode WithExplicitInterfaceImplementations( SyntaxNode declaration, ImmutableArray<ISymbol> explicitInterfaceImplementations, bool removeDefaults) => WithAccessibility(declaration switch { MethodDeclarationSyntax method => WithoutConstraints( method.ReplaceNodes(method.ParameterList.Parameters, (_, p) => RemoveDefaultValue(p, removeDefaults)) .WithExplicitInterfaceSpecifier(CreateExplicitInterfaceSpecifier(explicitInterfaceImplementations))), BasePropertyDeclarationSyntax member => member.WithExplicitInterfaceSpecifier(CreateExplicitInterfaceSpecifier(explicitInterfaceImplementations)), _ => declaration, }, Accessibility.NotApplicable); private static MethodDeclarationSyntax WithoutConstraints(MethodDeclarationSyntax method) { if (method.ConstraintClauses.Count == 0) return method; return method.WithConstraintClauses(default) .WithParameterList(method.ParameterList.WithTrailingTrivia( method.ParameterList.GetTrailingTrivia().Add(SyntaxFactory.ElasticMarker).AddRange(method.ConstraintClauses.Last().GetTrailingTrivia()))); } private static SyntaxNode RemoveDefaultValue(ParameterSyntax parameter, bool removeDefaults) { if (!removeDefaults) return parameter; if (parameter.Default == null) return parameter; parameter = parameter.WithDefault(null); if (!parameter.Identifier.TrailingTrivia.Any(t => t.IsSingleOrMultiLineComment())) parameter = parameter.WithIdentifier(parameter.Identifier.WithoutTrailingTrivia()); return parameter; } private static ExplicitInterfaceSpecifierSyntax CreateExplicitInterfaceSpecifier(ImmutableArray<ISymbol> explicitInterfaceImplementations) => SyntaxFactory.ExplicitInterfaceSpecifier(explicitInterfaceImplementations[0].ContainingType.GenerateNameSyntax()); private protected override SyntaxNode WithTypeConstraint( SyntaxNode declaration, string typeParameterName, SpecialTypeConstraintKind kinds, bool isUnmanagedType, IEnumerable<SyntaxNode>? types) => declaration switch { MethodDeclarationSyntax method => method.WithConstraintClauses(WithTypeConstraints(method.ConstraintClauses, typeParameterName, kinds, isUnmanagedType, types)), TypeDeclarationSyntax type => type.WithConstraintClauses(WithTypeConstraints(type.ConstraintClauses, typeParameterName, kinds, isUnmanagedType, types)), DelegateDeclarationSyntax @delegate => @delegate.WithConstraintClauses(WithTypeConstraints(@delegate.ConstraintClauses, typeParameterName, kinds, isUnmanagedType, types)), _ => declaration, }; private protected override SyntaxNode WithDefaultConstraint(SyntaxNode declaration, string typeParameterName) { var method = (MethodDeclarationSyntax)declaration; return method.AddConstraintClauses(SyntaxFactory.TypeParameterConstraintClause( typeParameterName).AddConstraints(SyntaxFactory.DefaultConstraint())); } private static SyntaxList<TypeParameterConstraintClauseSyntax> WithTypeConstraints( SyntaxList<TypeParameterConstraintClauseSyntax> clauses, string typeParameterName, SpecialTypeConstraintKind kinds, bool isUnmanagedType, IEnumerable<SyntaxNode>? types) { var constraints = types != null ? SyntaxFactory.SeparatedList<TypeParameterConstraintSyntax>(types.Select(t => SyntaxFactory.TypeConstraint((TypeSyntax)t))) : []; if ((kinds & SpecialTypeConstraintKind.Constructor) != 0) { constraints = constraints.Add(SyntaxFactory.ConstructorConstraint()); } var isReferenceType = (kinds & SpecialTypeConstraintKind.ReferenceType) != 0; var isValueType = (kinds & SpecialTypeConstraintKind.ValueType) != 0 && !isUnmanagedType; if (isReferenceType || isValueType) { constraints = constraints.Insert(0, SyntaxFactory.ClassOrStructConstraint(isReferenceType ? SyntaxKind.ClassConstraint : SyntaxKind.StructConstraint)); } else if (isUnmanagedType) { constraints = constraints.Insert(0, SyntaxFactory.TypeConstraint(SyntaxFactory.IdentifierName("unmanaged"))); } var clause = clauses.FirstOrDefault(c => c.Name.Identifier.ToString() == typeParameterName); if (clause == null) { if (constraints.Count > 0) { return clauses.Add(SyntaxFactory.TypeParameterConstraintClause(typeParameterName.ToIdentifierName(), constraints)); } else { return clauses; } } else if (constraints.Count == 0) { return clauses.Remove(clause); } else { return clauses.Replace(clause, clause.WithConstraints(constraints)); } } public override DeclarationKind GetDeclarationKind(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.ClassDeclaration: case SyntaxKind.RecordDeclaration: return DeclarationKind.Class; case SyntaxKind.StructDeclaration: case SyntaxKind.RecordStructDeclaration: return DeclarationKind.Struct; case SyntaxKind.InterfaceDeclaration: return DeclarationKind.Interface; case SyntaxKind.EnumDeclaration: return DeclarationKind.Enum; case SyntaxKind.DelegateDeclaration: return DeclarationKind.Delegate; case SyntaxKind.MethodDeclaration: return DeclarationKind.Method; case SyntaxKind.OperatorDeclaration: return DeclarationKind.Operator; case SyntaxKind.ConversionOperatorDeclaration: return DeclarationKind.ConversionOperator; case SyntaxKind.ConstructorDeclaration: return DeclarationKind.Constructor; case SyntaxKind.DestructorDeclaration: return DeclarationKind.Destructor; case SyntaxKind.PropertyDeclaration: return DeclarationKind.Property; case SyntaxKind.IndexerDeclaration: return DeclarationKind.Indexer; case SyntaxKind.EventDeclaration: return DeclarationKind.CustomEvent; case SyntaxKind.EnumMemberDeclaration: return DeclarationKind.EnumMember; case SyntaxKind.CompilationUnit: return DeclarationKind.CompilationUnit; case SyntaxKind.NamespaceDeclaration: case SyntaxKind.FileScopedNamespaceDeclaration: return DeclarationKind.Namespace; case SyntaxKind.UsingDirective: return DeclarationKind.NamespaceImport; case SyntaxKind.Parameter: return DeclarationKind.Parameter; case SyntaxKind.ParenthesizedLambdaExpression: case SyntaxKind.SimpleLambdaExpression: return DeclarationKind.LambdaExpression; case SyntaxKind.FieldDeclaration: var fd = (FieldDeclarationSyntax)declaration; if (fd.Declaration != null && fd.Declaration.Variables.Count == 1) { // this node is considered the declaration if it contains only one variable. return DeclarationKind.Field; } else { return DeclarationKind.None; } case SyntaxKind.EventFieldDeclaration: var ef = (EventFieldDeclarationSyntax)declaration; if (ef.Declaration != null && ef.Declaration.Variables.Count == 1) { // this node is considered the declaration if it contains only one variable. return DeclarationKind.Event; } else { return DeclarationKind.None; } case SyntaxKind.LocalDeclarationStatement: var ld = (LocalDeclarationStatementSyntax)declaration; if (ld.Declaration != null && ld.Declaration.Variables.Count == 1) { // this node is considered the declaration if it contains only one variable. return DeclarationKind.Variable; } else { return DeclarationKind.None; } case SyntaxKind.VariableDeclaration: { var vd = (VariableDeclarationSyntax)declaration; if (vd.Variables.Count == 1 && vd.Parent == null) { // this node is the declaration if it contains only one variable and has no parent. return DeclarationKind.Variable; } else { return DeclarationKind.None; } } case SyntaxKind.VariableDeclarator: { var vd = declaration.Parent as VariableDeclarationSyntax; // this node is considered the declaration if it is one among many, or it has no parent if (vd == null || vd.Variables.Count > 1) { if (ParentIsFieldDeclaration(vd)) { return DeclarationKind.Field; } else if (ParentIsEventFieldDeclaration(vd)) { return DeclarationKind.Event; } else { return DeclarationKind.Variable; } } break; } case SyntaxKind.AttributeList: var list = (AttributeListSyntax)declaration; if (list.Attributes.Count == 1) { return DeclarationKind.Attribute; } break; case SyntaxKind.Attribute: if (declaration.Parent is not AttributeListSyntax parentList || parentList.Attributes.Count > 1) { return DeclarationKind.Attribute; } break; case SyntaxKind.GetAccessorDeclaration: return DeclarationKind.GetAccessor; case SyntaxKind.SetAccessorDeclaration: case SyntaxKind.InitAccessorDeclaration: return DeclarationKind.SetAccessor; case SyntaxKind.AddAccessorDeclaration: return DeclarationKind.AddAccessor; case SyntaxKind.RemoveAccessorDeclaration: return DeclarationKind.RemoveAccessor; } return DeclarationKind.None; } private static bool ParentIsFieldDeclaration([NotNullWhen(true)] SyntaxNode? node) => node?.Parent.IsKind(SyntaxKind.FieldDeclaration) ?? false; private static bool ParentIsEventFieldDeclaration([NotNullWhen(true)] SyntaxNode? node) => node?.Parent.IsKind(SyntaxKind.EventFieldDeclaration) ?? false; private static bool ParentIsLocalDeclarationStatement([NotNullWhen(true)] SyntaxNode? node) => node?.Parent.IsKind(SyntaxKind.LocalDeclarationStatement) ?? false; public override string GetName(SyntaxNode declaration) => declaration switch { BaseTypeDeclarationSyntax baseTypeDeclaration => baseTypeDeclaration.Identifier.ValueText, DelegateDeclarationSyntax delegateDeclaration => delegateDeclaration.Identifier.ValueText, MethodDeclarationSyntax methodDeclaration => methodDeclaration.Identifier.ValueText, BaseFieldDeclarationSyntax baseFieldDeclaration => this.GetName(baseFieldDeclaration.Declaration), PropertyDeclarationSyntax propertyDeclaration => propertyDeclaration.Identifier.ValueText, EnumMemberDeclarationSyntax enumMemberDeclaration => enumMemberDeclaration.Identifier.ValueText, EventDeclarationSyntax eventDeclaration => eventDeclaration.Identifier.ValueText, BaseNamespaceDeclarationSyntax namespaceDeclaration => namespaceDeclaration.Name.ToString(), UsingDirectiveSyntax usingDirective => usingDirective.Name?.ToString() ?? string.Empty, ParameterSyntax parameter => parameter.Identifier.ValueText, LocalDeclarationStatementSyntax localDeclaration => this.GetName(localDeclaration.Declaration), VariableDeclarationSyntax variableDeclaration when variableDeclaration.Variables.Count == 1 => variableDeclaration.Variables[0].Identifier.ValueText, VariableDeclaratorSyntax variableDeclarator => variableDeclarator.Identifier.ValueText, TypeParameterSyntax typeParameter => typeParameter.Identifier.ValueText, AttributeListSyntax attributeList when attributeList.Attributes.Count == 1 => attributeList.Attributes[0].Name.ToString(), AttributeSyntax attribute => attribute.Name.ToString(), _ => string.Empty }; public override SyntaxNode WithName(SyntaxNode declaration, string name) => this.Isolate(declaration, d => this.WithNameInternal(d, name)); private SyntaxNode WithNameInternal(SyntaxNode declaration, string name) { var id = name.ToIdentifierToken(); return declaration switch { BaseTypeDeclarationSyntax typeDeclaration => ReplaceWithTrivia(declaration, typeDeclaration.Identifier, id), DelegateDeclarationSyntax delegateDeclaration => ReplaceWithTrivia(declaration, delegateDeclaration.Identifier, id), MethodDeclarationSyntax methodDeclaration => ReplaceWithTrivia(declaration, methodDeclaration.Identifier, id), BaseFieldDeclarationSyntax fieldDeclaration when fieldDeclaration.Declaration.Variables.Count == 1 => ReplaceWithTrivia(declaration, fieldDeclaration.Declaration.Variables[0].Identifier, id), PropertyDeclarationSyntax propertyDeclaration => ReplaceWithTrivia(declaration, propertyDeclaration.Identifier, id), EnumMemberDeclarationSyntax enumMemberDeclaration => ReplaceWithTrivia(declaration, enumMemberDeclaration.Identifier, id), EventDeclarationSyntax eventDeclaration => ReplaceWithTrivia(declaration, eventDeclaration.Identifier, id), BaseNamespaceDeclarationSyntax namespaceDeclaration => ReplaceWithTrivia(declaration, namespaceDeclaration.Name, this.DottedName(name)), UsingDirectiveSyntax usingDeclaration => ReplaceWithTrivia(declaration, usingDeclaration.NamespaceOrType, this.DottedName(name)), ParameterSyntax parameter => ReplaceWithTrivia(declaration, parameter.Identifier, id), LocalDeclarationStatementSyntax localDeclaration when localDeclaration.Declaration.Variables.Count == 1 => ReplaceWithTrivia(declaration, localDeclaration.Declaration.Variables[0].Identifier, id), TypeParameterSyntax typeParameter => ReplaceWithTrivia(declaration, typeParameter.Identifier, id), AttributeListSyntax attributeList when attributeList.Attributes.Count == 1 => ReplaceWithTrivia(declaration, attributeList.Attributes[0].Name, this.DottedName(name)), AttributeSyntax attribute => ReplaceWithTrivia(declaration, attribute.Name, this.DottedName(name)), VariableDeclarationSyntax variableDeclaration when variableDeclaration.Variables.Count == 1 => ReplaceWithTrivia(declaration, variableDeclaration.Variables[0].Identifier, id), VariableDeclaratorSyntax variableDeclarator => ReplaceWithTrivia(declaration, variableDeclarator.Identifier, id), _ => declaration }; } public override SyntaxNode? GetType(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.DelegateDeclaration: return NotVoid(((DelegateDeclarationSyntax)declaration).ReturnType); case SyntaxKind.MethodDeclaration: return NotVoid(((MethodDeclarationSyntax)declaration).ReturnType); case SyntaxKind.FieldDeclaration: return ((FieldDeclarationSyntax)declaration).Declaration.Type; case SyntaxKind.PropertyDeclaration: return ((PropertyDeclarationSyntax)declaration).Type; case SyntaxKind.IndexerDeclaration: return ((IndexerDeclarationSyntax)declaration).Type; case SyntaxKind.EventFieldDeclaration: return ((EventFieldDeclarationSyntax)declaration).Declaration.Type; case SyntaxKind.EventDeclaration: return ((EventDeclarationSyntax)declaration).Type; case SyntaxKind.Parameter: return ((ParameterSyntax)declaration).Type; case SyntaxKind.LocalDeclarationStatement: return ((LocalDeclarationStatementSyntax)declaration).Declaration.Type; case SyntaxKind.VariableDeclaration: return ((VariableDeclarationSyntax)declaration).Type; case SyntaxKind.VariableDeclarator: if (declaration.Parent != null) { return this.GetType(declaration.Parent); } break; } return null; } private static TypeSyntax? NotVoid(TypeSyntax type) => type is PredefinedTypeSyntax pd && pd.Keyword.IsKind(SyntaxKind.VoidKeyword) ? null : type; public override SyntaxNode WithType(SyntaxNode declaration, SyntaxNode type) => Isolate(declaration, d => WithTypeInternal(d, type)); private static SyntaxNode WithTypeInternal(SyntaxNode declaration, SyntaxNode type) => declaration.Kind() switch { SyntaxKind.DelegateDeclaration => ((DelegateDeclarationSyntax)declaration).WithReturnType((TypeSyntax)type), SyntaxKind.MethodDeclaration => ((MethodDeclarationSyntax)declaration).WithReturnType((TypeSyntax)type), SyntaxKind.FieldDeclaration => ((FieldDeclarationSyntax)declaration).WithDeclaration(((FieldDeclarationSyntax)declaration).Declaration.WithType((TypeSyntax)type)), SyntaxKind.PropertyDeclaration => ((PropertyDeclarationSyntax)declaration).WithType((TypeSyntax)type), SyntaxKind.IndexerDeclaration => ((IndexerDeclarationSyntax)declaration).WithType((TypeSyntax)type), SyntaxKind.EventFieldDeclaration => ((EventFieldDeclarationSyntax)declaration).WithDeclaration(((EventFieldDeclarationSyntax)declaration).Declaration.WithType((TypeSyntax)type)), SyntaxKind.EventDeclaration => ((EventDeclarationSyntax)declaration).WithType((TypeSyntax)type), SyntaxKind.Parameter => ((ParameterSyntax)declaration).WithType((TypeSyntax)type), SyntaxKind.LocalDeclarationStatement => ((LocalDeclarationStatementSyntax)declaration).WithDeclaration(((LocalDeclarationStatementSyntax)declaration).Declaration.WithType((TypeSyntax)type)), SyntaxKind.VariableDeclaration => ((VariableDeclarationSyntax)declaration).WithType((TypeSyntax)type), _ => declaration, }; private SyntaxNode Isolate(SyntaxNode declaration, Func<SyntaxNode, SyntaxNode> editor) => PreserveTrivia(AsIsolatedDeclaration(declaration), editor); private SyntaxNode AsIsolatedDeclaration(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.VariableDeclaration: var vd = (VariableDeclarationSyntax)declaration; if (vd.Parent != null && vd.Variables.Count == 1) { return AsIsolatedDeclaration(vd.Parent); } break; case SyntaxKind.VariableDeclarator: var v = (VariableDeclaratorSyntax)declaration; if (v.Parent != null && v.Parent.Parent != null) { return ClearTrivia(WithVariable(v.Parent.Parent, v)); } break; case SyntaxKind.Attribute: var attr = (AttributeSyntax)declaration; if (attr.Parent != null) { var attrList = (AttributeListSyntax)attr.Parent; return attrList.WithAttributes([attr]).WithTarget(null); } break; } return declaration; } private static SyntaxNode WithVariable(SyntaxNode declaration, VariableDeclaratorSyntax variable) { var vd = GetVariableDeclaration(declaration); if (vd != null) { return WithVariableDeclaration(declaration, vd.WithVariables([variable])); } return declaration; } private static VariableDeclarationSyntax? GetVariableDeclaration(SyntaxNode declaration) => declaration.Kind() switch { SyntaxKind.FieldDeclaration => ((FieldDeclarationSyntax)declaration).Declaration, SyntaxKind.EventFieldDeclaration => ((EventFieldDeclarationSyntax)declaration).Declaration, SyntaxKind.LocalDeclarationStatement => ((LocalDeclarationStatementSyntax)declaration).Declaration, _ => null, }; private static SyntaxNode WithVariableDeclaration(SyntaxNode declaration, VariableDeclarationSyntax variables) => declaration.Kind() switch { SyntaxKind.FieldDeclaration => ((FieldDeclarationSyntax)declaration).WithDeclaration(variables), SyntaxKind.EventFieldDeclaration => ((EventFieldDeclarationSyntax)declaration).WithDeclaration(variables), SyntaxKind.LocalDeclarationStatement => ((LocalDeclarationStatementSyntax)declaration).WithDeclaration(variables), _ => declaration, }; private static SyntaxNode GetFullDeclaration(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.VariableDeclaration: var vd = (VariableDeclarationSyntax)declaration; if (ParentIsFieldDeclaration(vd) || ParentIsEventFieldDeclaration(vd) || ParentIsLocalDeclarationStatement(vd)) { Contract.ThrowIfNull(vd.Parent); return vd.Parent; } else { return vd; } case SyntaxKind.VariableDeclarator: case SyntaxKind.Attribute: if (declaration.Parent != null) { return GetFullDeclaration(declaration.Parent); } break; } return declaration; } private SyntaxNode? AsNodeLike(SyntaxNode existingNode, SyntaxNode newNode) { switch (GetDeclarationKind(existingNode)) { case DeclarationKind.Class: case DeclarationKind.Interface: case DeclarationKind.Struct: case DeclarationKind.Enum: case DeclarationKind.Namespace: case DeclarationKind.CompilationUnit: var container = GetDeclaration(existingNode.Parent); if (container != null) { return AsMemberOf(container, newNode); } break; case DeclarationKind.Attribute: return AsAttributeList(newNode); } return newNode; } public override IReadOnlyList<SyntaxNode> GetParameters(SyntaxNode declaration) { var list = declaration.GetParameterList(); return list != null ? list.Parameters : declaration is SimpleLambdaExpressionSyntax simpleLambda ? [simpleLambda.Parameter] : []; } public override SyntaxNode InsertParameters(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> parameters) { var newParameters = AsParameterList(parameters); var currentList = declaration.GetParameterList(); currentList ??= declaration.IsKind(SyntaxKind.IndexerDeclaration) ? SyntaxFactory.BracketedParameterList() : SyntaxFactory.ParameterList(); var newList = currentList.WithParameters(currentList.Parameters.InsertRange(index, newParameters.Parameters)); return WithParameterList(declaration, newList); } public override IReadOnlyList<SyntaxNode> GetSwitchSections(SyntaxNode switchStatement) { var statement = switchStatement as SwitchStatementSyntax; return statement?.Sections ?? []; } public override SyntaxNode InsertSwitchSections(SyntaxNode switchStatement, int index, IEnumerable<SyntaxNode> switchSections) { if (switchStatement is not SwitchStatementSyntax statement) { return switchStatement; } var newSections = statement.Sections.InsertRange(index, switchSections.Cast<SwitchSectionSyntax>()); return AddMissingTokens(statement, recurse: false).WithSections(newSections); } private static TNode AddMissingTokens<TNode>(TNode node, bool recurse) where TNode : CSharpSyntaxNode { var rewriter = new AddMissingTokensRewriter(recurse); return (TNode)rewriter.Visit(node); } private sealed class AddMissingTokensRewriter(bool recurse) : CSharpSyntaxRewriter { private readonly bool _recurse = recurse; private bool _firstVisit = true; [return: NotNullIfNotNull(nameof(node))] public override SyntaxNode? Visit(SyntaxNode? node) { if (!_recurse && !_firstVisit) { return node; } _firstVisit = false; return base.Visit(node); } public override SyntaxToken VisitToken(SyntaxToken token) { var rewrittenToken = base.VisitToken(token); if (!rewrittenToken.IsMissing || !CSharp.SyntaxFacts.IsPunctuationOrKeyword(token.Kind())) { return rewrittenToken; } return SyntaxFactory.Token(token.Kind()).WithTriviaFrom(rewrittenToken); } } internal override SyntaxNode? GetParameterListNode(SyntaxNode declaration) => declaration.GetParameterList(); private static SyntaxNode WithParameterList(SyntaxNode declaration, BaseParameterListSyntax list) { switch (declaration.Kind()) { case SyntaxKind.DelegateDeclaration: return ((DelegateDeclarationSyntax)declaration).WithParameterList(list); case SyntaxKind.MethodDeclaration: return ((MethodDeclarationSyntax)declaration).WithParameterList(list); case SyntaxKind.OperatorDeclaration: return ((OperatorDeclarationSyntax)declaration).WithParameterList(list); case SyntaxKind.ConversionOperatorDeclaration: return ((ConversionOperatorDeclarationSyntax)declaration).WithParameterList(list); case SyntaxKind.ConstructorDeclaration: return ((ConstructorDeclarationSyntax)declaration).WithParameterList(list); case SyntaxKind.DestructorDeclaration: return ((DestructorDeclarationSyntax)declaration).WithParameterList(list); case SyntaxKind.IndexerDeclaration: return ((IndexerDeclarationSyntax)declaration).WithParameterList(list); case SyntaxKind.LocalFunctionStatement: return ((LocalFunctionStatementSyntax)declaration).WithParameterList((ParameterListSyntax)list); case SyntaxKind.ParenthesizedLambdaExpression: return ((ParenthesizedLambdaExpressionSyntax)declaration).WithParameterList((ParameterListSyntax)list); case SyntaxKind.SimpleLambdaExpression: var lambda = (SimpleLambdaExpressionSyntax)declaration; var parameters = list.Parameters; if (parameters.Count == 1 && IsSimpleLambdaParameter(parameters[0])) { return lambda.WithParameter(parameters[0]); } else { return SyntaxFactory.ParenthesizedLambdaExpression(AsParameterList(parameters), lambda.Body) .WithLeadingTrivia(lambda.GetLeadingTrivia()) .WithTrailingTrivia(lambda.GetTrailingTrivia()); } case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.InterfaceDeclaration: return ((TypeDeclarationSyntax)declaration).WithParameterList((ParameterListSyntax)list); default: return declaration; } } public override SyntaxNode? GetExpression(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.ParenthesizedLambdaExpression: return ((ParenthesizedLambdaExpressionSyntax)declaration).Body as ExpressionSyntax; case SyntaxKind.SimpleLambdaExpression: return ((SimpleLambdaExpressionSyntax)declaration).Body as ExpressionSyntax; case SyntaxKind.PropertyDeclaration: var pd = (PropertyDeclarationSyntax)declaration; if (pd.ExpressionBody != null) { return pd.ExpressionBody.Expression; } goto default; case SyntaxKind.IndexerDeclaration: var id = (IndexerDeclarationSyntax)declaration; if (id.ExpressionBody != null) { return id.ExpressionBody.Expression; } goto default; case SyntaxKind.MethodDeclaration: var method = (MethodDeclarationSyntax)declaration; if (method.ExpressionBody != null) { return method.ExpressionBody.Expression; } goto default; case SyntaxKind.LocalFunctionStatement: var local = (LocalFunctionStatementSyntax)declaration; if (local.ExpressionBody != null) { return local.ExpressionBody.Expression; } goto default; default: return GetEqualsValue(declaration)?.Value; } } public override SyntaxNode WithExpression(SyntaxNode declaration, SyntaxNode expression) => this.Isolate(declaration, d => WithExpressionInternal(d, expression)); private static SyntaxNode WithExpressionInternal(SyntaxNode declaration, SyntaxNode expression) { var expr = (ExpressionSyntax)expression; switch (declaration.Kind()) { case SyntaxKind.ParenthesizedLambdaExpression: return ((ParenthesizedLambdaExpressionSyntax)declaration).WithBody((CSharpSyntaxNode)expr ?? CreateBlock()); case SyntaxKind.SimpleLambdaExpression: return ((SimpleLambdaExpressionSyntax)declaration).WithBody((CSharpSyntaxNode)expr ?? CreateBlock()); case SyntaxKind.PropertyDeclaration: var pd = (PropertyDeclarationSyntax)declaration; if (pd.ExpressionBody != null) { return ReplaceWithTrivia(pd, pd.ExpressionBody.Expression, expr); } goto default; case SyntaxKind.IndexerDeclaration: var id = (IndexerDeclarationSyntax)declaration; if (id.ExpressionBody != null) { return ReplaceWithTrivia(id, id.ExpressionBody.Expression, expr); } goto default; case SyntaxKind.MethodDeclaration: var method = (MethodDeclarationSyntax)declaration; if (method.ExpressionBody != null) { return ReplaceWithTrivia(method, method.ExpressionBody.Expression, expr); } goto default; case SyntaxKind.LocalFunctionStatement: var local = (LocalFunctionStatementSyntax)declaration; if (local.ExpressionBody != null) { return ReplaceWithTrivia(local, local.ExpressionBody.Expression, expr); } goto default; default: var eq = GetEqualsValue(declaration); if (eq != null) { if (expression == null) { return WithEqualsValue(declaration, initializer: null); } else { // use replace so we only change the value part. return ReplaceWithTrivia(declaration, eq.Value, expr); } } else if (expression != null) { return WithEqualsValue(declaration, SyntaxFactory.EqualsValueClause(expr)); } else { return declaration; } } } private static EqualsValueClauseSyntax? GetEqualsValue(SyntaxNode declaration) { switch (declaration.Kind()) { case SyntaxKind.FieldDeclaration: var fd = (FieldDeclarationSyntax)declaration; if (fd.Declaration.Variables.Count == 1) { return fd.Declaration.Variables[0].Initializer; } break; case SyntaxKind.PropertyDeclaration: var pd = (PropertyDeclarationSyntax)declaration; return pd.Initializer; case SyntaxKind.LocalDeclarationStatement: var ld = (LocalDeclarationStatementSyntax)declaration; if (ld.Declaration.Variables.Count == 1) { return ld.Declaration.Variables[0].Initializer; } break; case SyntaxKind.VariableDeclaration: var vd = (VariableDeclarationSyntax)declaration; if (vd.Variables.Count == 1) { return vd.Variables[0].Initializer; } break; case SyntaxKind.VariableDeclarator: return ((VariableDeclaratorSyntax)declaration).Initializer; case SyntaxKind.Parameter: return ((ParameterSyntax)declaration).Default; } return null; } private static SyntaxNode WithEqualsValue(SyntaxNode declaration, EqualsValueClauseSyntax? initializer) { switch (declaration.Kind()) { case SyntaxKind.FieldDeclaration: var fd = (FieldDeclarationSyntax)declaration; if (fd.Declaration.Variables.Count == 1) { return ReplaceWithTrivia(declaration, fd.Declaration.Variables[0], fd.Declaration.Variables[0].WithInitializer(initializer)); } break; case SyntaxKind.PropertyDeclaration: var pd = (PropertyDeclarationSyntax)declaration; return pd.WithInitializer(initializer); case SyntaxKind.LocalDeclarationStatement: var ld = (LocalDeclarationStatementSyntax)declaration; if (ld.Declaration.Variables.Count == 1) { return ReplaceWithTrivia(declaration, ld.Declaration.Variables[0], ld.Declaration.Variables[0].WithInitializer(initializer)); } break; case SyntaxKind.VariableDeclaration: var vd = (VariableDeclarationSyntax)declaration; if (vd.Variables.Count == 1) { return ReplaceWithTrivia(declaration, vd.Variables[0], vd.Variables[0].WithInitializer(initializer)); } break; case SyntaxKind.VariableDeclarator: return ((VariableDeclaratorSyntax)declaration).WithInitializer(initializer); case SyntaxKind.Parameter: return ((ParameterSyntax)declaration).WithDefault(initializer); } return declaration; } public override IReadOnlyList<SyntaxNode> GetStatements(SyntaxNode declaration) { var result = declaration.Kind() switch { SyntaxKind.MethodDeclaration => ((MethodDeclarationSyntax)declaration).Body?.Statements, SyntaxKind.OperatorDeclaration => ((OperatorDeclarationSyntax)declaration).Body?.Statements, SyntaxKind.ConversionOperatorDeclaration => ((ConversionOperatorDeclarationSyntax)declaration).Body?.Statements, SyntaxKind.ConstructorDeclaration => ((ConstructorDeclarationSyntax)declaration).Body?.Statements, SyntaxKind.DestructorDeclaration => ((DestructorDeclarationSyntax)declaration).Body?.Statements, SyntaxKind.LocalFunctionStatement => ((LocalFunctionStatementSyntax)declaration).Body?.Statements, SyntaxKind.AnonymousMethodExpression => (((AnonymousMethodExpressionSyntax)declaration).Body as BlockSyntax)?.Statements, SyntaxKind.ParenthesizedLambdaExpression => (((ParenthesizedLambdaExpressionSyntax)declaration).Body as BlockSyntax)?.Statements, SyntaxKind.SimpleLambdaExpression => (((SimpleLambdaExpressionSyntax)declaration).Body as BlockSyntax)?.Statements, SyntaxKind.GetAccessorDeclaration or SyntaxKind.SetAccessorDeclaration or SyntaxKind.AddAccessorDeclaration or SyntaxKind.RemoveAccessorDeclaration => ((AccessorDeclarationSyntax)declaration).Body?.Statements, _ => [], }; return result ?? []; } public override SyntaxNode WithStatements(SyntaxNode declaration, IEnumerable<SyntaxNode> statements) { var existingBlock = declaration switch { BaseMethodDeclarationSyntax baseMethod => baseMethod.Body, AccessorDeclarationSyntax accessor => accessor.Body, LocalFunctionStatementSyntax localFunction => localFunction.Body, AnonymousFunctionExpressionSyntax anonymousFunction => anonymousFunction.Block, _ => null, }; var body = CreateBlock(statements, existingBlock, addSimplifierAnnotation: false); var somebody = statements != null ? body : null; var semicolon = statements == null ? SemicolonToken : default; return declaration switch { BaseMethodDeclarationSyntax baseMethod => baseMethod.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), AccessorDeclarationSyntax accessor => accessor.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), LocalFunctionStatementSyntax localFunction => localFunction.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), AnonymousFunctionExpressionSyntax anonymousFunction => anonymousFunction.WithBody(body), _ => declaration, }; } public override IReadOnlyList<SyntaxNode> GetAccessors(SyntaxNode declaration) { var list = GetAccessorList(declaration); return list?.Accessors ?? []; } public override SyntaxNode InsertAccessors(SyntaxNode declaration, int index, IEnumerable<SyntaxNode> accessors) { var newAccessors = AsAccessorList(accessors, declaration.Kind()); var currentList = GetAccessorList(declaration); if (currentList == null) { if (CanHaveAccessors(declaration)) { currentList = SyntaxFactory.AccessorList(); } else { return declaration; } } var newList = currentList.WithAccessors(currentList.Accessors.InsertRange(index, newAccessors.Accessors)); return WithAccessorList(declaration, newList); } internal static AccessorListSyntax? GetAccessorList(SyntaxNode declaration) => (declaration as BasePropertyDeclarationSyntax)?.AccessorList; private static bool CanHaveAccessors(SyntaxNode declaration) => declaration.Kind() switch { SyntaxKind.PropertyDeclaration => ((PropertyDeclarationSyntax)declaration).ExpressionBody == null, SyntaxKind.IndexerDeclaration => ((IndexerDeclarationSyntax)declaration).ExpressionBody == null, SyntaxKind.EventDeclaration => true, _ => false, }; private static SyntaxNode WithAccessorList(SyntaxNode declaration, AccessorListSyntax accessorList) => declaration switch { BasePropertyDeclarationSyntax baseProperty => baseProperty.WithAccessorList(accessorList), _ => declaration, }; private static AccessorListSyntax AsAccessorList(IEnumerable<SyntaxNode> nodes, SyntaxKind parentKind) => SyntaxFactory.AccessorList([.. nodes.Select(n => AsAccessor(n, parentKind)).WhereNotNull()]); private static AccessorDeclarationSyntax? AsAccessor(SyntaxNode node, SyntaxKind parentKind) { switch (parentKind) { case SyntaxKind.PropertyDeclaration: case SyntaxKind.IndexerDeclaration: switch (node.Kind()) { case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: return (AccessorDeclarationSyntax)node; } break; case SyntaxKind.EventDeclaration: switch (node.Kind()) { case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: return (AccessorDeclarationSyntax)node; } break; } return null; } private static AccessorDeclarationSyntax? GetAccessor(SyntaxNode declaration, SyntaxKind kind) { var accessorList = GetAccessorList(declaration); return accessorList?.Accessors.FirstOrDefault(a => a.IsKind(kind)); } private SyntaxNode WithAccessor(SyntaxNode declaration, SyntaxKind kind, AccessorDeclarationSyntax accessor) => WithAccessor(declaration, GetAccessorList(declaration), kind, accessor); private SyntaxNode WithAccessor(SyntaxNode declaration, AccessorListSyntax? accessorList, SyntaxKind kind, AccessorDeclarationSyntax? accessor) { if (accessorList != null) { var acc = accessorList.Accessors.FirstOrDefault(a => a.IsKind(kind)); if (acc != null) { return this.ReplaceNode(declaration, acc, accessor); } else if (accessor != null) { return this.ReplaceNode(declaration, accessorList, accessorList.AddAccessors(accessor)); } } return declaration; } public override IReadOnlyList<SyntaxNode> GetGetAccessorStatements(SyntaxNode declaration) { var accessor = GetAccessor(declaration, SyntaxKind.GetAccessorDeclaration); return accessor?.Body?.Statements ?? []; } public override IReadOnlyList<SyntaxNode> GetSetAccessorStatements(SyntaxNode declaration) { var accessor = GetAccessor(declaration, SyntaxKind.SetAccessorDeclaration); return accessor?.Body?.Statements ?? []; } public override SyntaxNode WithGetAccessorStatements(SyntaxNode declaration, IEnumerable<SyntaxNode> statements) => this.WithAccessorStatements(declaration, SyntaxKind.GetAccessorDeclaration, statements); public override SyntaxNode WithSetAccessorStatements(SyntaxNode declaration, IEnumerable<SyntaxNode> statements) => this.WithAccessorStatements(declaration, SyntaxKind.SetAccessorDeclaration, statements); private SyntaxNode WithAccessorStatements(SyntaxNode declaration, SyntaxKind kind, IEnumerable<SyntaxNode> statements) { var accessor = GetAccessor(declaration, kind); if (accessor == null) { accessor = AccessorDeclaration(kind, statements); return this.WithAccessor(declaration, kind, accessor); } else { return this.WithAccessor(declaration, kind, (AccessorDeclarationSyntax)this.WithStatements(accessor, statements)); } } public override IReadOnlyList<SyntaxNode> GetBaseAndInterfaceTypes(SyntaxNode declaration) { var baseList = GetBaseList(declaration); if (baseList != null) { return baseList.Types.OfType<SimpleBaseTypeSyntax>().Select(bt => bt.Type).ToReadOnlyCollection(); } else { return []; } } public override SyntaxNode AddBaseType(SyntaxNode declaration, SyntaxNode baseType) { var baseList = GetBaseList(declaration); if (baseList != null) { return WithBaseList(declaration, baseList.WithTypes(baseList.Types.Insert(0, SyntaxFactory.SimpleBaseType((TypeSyntax)baseType)))); } else { return AddBaseList(declaration, SyntaxFactory.BaseList([SyntaxFactory.SimpleBaseType((TypeSyntax)baseType)])); } } public override SyntaxNode AddInterfaceType(SyntaxNode declaration, SyntaxNode interfaceType) { var baseList = GetBaseList(declaration); if (baseList != null) { return WithBaseList(declaration, baseList.WithTypes(baseList.Types.Insert(baseList.Types.Count, SyntaxFactory.SimpleBaseType((TypeSyntax)interfaceType)))); } else { return AddBaseList(declaration, SyntaxFactory.BaseList([SyntaxFactory.SimpleBaseType((TypeSyntax)interfaceType)])); } } private static SyntaxNode AddBaseList(SyntaxNode declaration, BaseListSyntax baseList) { var newDecl = WithBaseList(declaration, baseList); // move trivia from type identifier to after base list return ShiftTrivia(newDecl, GetBaseList(newDecl)!); } private static BaseListSyntax? GetBaseList(SyntaxNode declaration) => declaration is TypeDeclarationSyntax typeDeclaration ? typeDeclaration.BaseList : null; private static SyntaxNode WithBaseList(SyntaxNode declaration, BaseListSyntax baseList) => declaration is TypeDeclarationSyntax typeDeclaration ? typeDeclaration.WithBaseList(baseList) : declaration; #endregion #region Remove, Replace, Insert public override SyntaxNode ReplaceNode(SyntaxNode root, SyntaxNode declaration, SyntaxNode? newDeclaration) { if (newDeclaration == null) { return RemoveNode(root, declaration); } newDeclaration = AsNodeLike(declaration, newDeclaration); if (newDeclaration == null) { return RemoveNode(root, declaration); } if (root.Span.Contains(declaration.Span)) { var newFullDecl = AsIsolatedDeclaration(newDeclaration); var fullDecl = GetFullDeclaration(declaration); // special handling for replacing at location of sub-declaration if (fullDecl != declaration && fullDecl.IsKind(newFullDecl.Kind())) { // try to replace inline if possible if (GetDeclarationCount(newFullDecl) == 1) { var newSubDecl = GetSubDeclarations(newFullDecl)[0]; if (AreInlineReplaceableSubDeclarations(declaration, newSubDecl)) { return base.ReplaceNode(root, declaration, newSubDecl); } } // replace sub declaration by splitting full declaration and inserting between var index = IndexOf(GetSubDeclarations(fullDecl), declaration); // replace declaration with multiple declarations return ReplaceRange(root, fullDecl, SplitAndReplace(fullDecl, index, [newDeclaration])); } // attempt normal replace return base.ReplaceNode(root, declaration, newFullDecl); } else { return base.ReplaceNode(root, declaration, newDeclaration); } } // returns true if one sub-declaration can be replaced inline with another sub-declaration private static bool AreInlineReplaceableSubDeclarations(SyntaxNode decl1, SyntaxNode decl2) { var kind = decl1.Kind(); if (decl2.IsKind(kind)) { switch (kind) { case SyntaxKind.Attribute: case SyntaxKind.VariableDeclarator: return AreSimilarExceptForSubDeclarations(decl1.Parent, decl2.Parent); } } return false; } private static bool AreSimilarExceptForSubDeclarations(SyntaxNode? decl1, SyntaxNode? decl2) { if (decl1 == decl2) { return true; } if (decl1 == null || decl2 == null) { return false; } var kind = decl1.Kind(); if (decl2.IsKind(kind)) { switch (kind) { case SyntaxKind.FieldDeclaration: var fd1 = (FieldDeclarationSyntax)decl1; var fd2 = (FieldDeclarationSyntax)decl2; return SyntaxFactory.AreEquivalent(fd1.Modifiers, fd2.Modifiers) && SyntaxFactory.AreEquivalent(fd1.AttributeLists, fd2.AttributeLists); case SyntaxKind.EventFieldDeclaration: var efd1 = (EventFieldDeclarationSyntax)decl1; var efd2 = (EventFieldDeclarationSyntax)decl2; return SyntaxFactory.AreEquivalent(efd1.Modifiers, efd2.Modifiers) && SyntaxFactory.AreEquivalent(efd1.AttributeLists, efd2.AttributeLists); case SyntaxKind.LocalDeclarationStatement: var ld1 = (LocalDeclarationStatementSyntax)decl1; var ld2 = (LocalDeclarationStatementSyntax)decl2; return SyntaxFactory.AreEquivalent(ld1.Modifiers, ld2.Modifiers); case SyntaxKind.AttributeList: // don't compare targets, since aren't part of the abstraction return true; case SyntaxKind.VariableDeclaration: var vd1 = (VariableDeclarationSyntax)decl1; var vd2 = (VariableDeclarationSyntax)decl2; return SyntaxFactory.AreEquivalent(vd1.Type, vd2.Type) && AreSimilarExceptForSubDeclarations(vd1.Parent, vd2.Parent); } } return false; } // replaces sub-declaration by splitting multi-part declaration first private IEnumerable<SyntaxNode> SplitAndReplace(SyntaxNode multiPartDeclaration, int index, IEnumerable<SyntaxNode> newDeclarations) { var count = GetDeclarationCount(multiPartDeclaration); if (index >= 0 && index < count) { var newNodes = new List<SyntaxNode>(); if (index > 0) { // make a single declaration with only sub-declarations before the sub-declaration being replaced newNodes.Add(this.WithSubDeclarationsRemoved(multiPartDeclaration, index, count - index).WithTrailingTrivia(SyntaxFactory.ElasticSpace)); } newNodes.AddRange(newDeclarations); if (index < count - 1) { // make a single declaration with only the sub-declarations after the sub-declaration being replaced newNodes.Add(this.WithSubDeclarationsRemoved(multiPartDeclaration, 0, index + 1).WithLeadingTrivia(SyntaxFactory.ElasticSpace)); } return newNodes; } else { return newDeclarations; } } public override SyntaxNode InsertNodesBefore(SyntaxNode root, SyntaxNode declaration, IEnumerable<SyntaxNode> newDeclarations) { if (declaration.Parent.IsKind(SyntaxKind.GlobalStatement)) { // Insert global statements before this global statement declaration = declaration.Parent; newDeclarations = newDeclarations.Select(declaration => declaration is StatementSyntax statement ? SyntaxFactory.GlobalStatement(statement) : declaration); } if (root.Span.Contains(declaration.Span)) { return this.Isolate(root.TrackNodes(declaration), r => this.InsertNodesBeforeInternal(r, r.GetCurrentNode(declaration)!, newDeclarations)); } else { return base.InsertNodesBefore(root, declaration, newDeclarations); } } private SyntaxNode InsertNodesBeforeInternal(SyntaxNode root, SyntaxNode declaration, IEnumerable<SyntaxNode> newDeclarations) { var fullDecl = GetFullDeclaration(declaration); if (fullDecl == declaration || GetDeclarationCount(fullDecl) == 1) { return base.InsertNodesBefore(root, fullDecl, newDeclarations); } var subDecls = GetSubDeclarations(fullDecl); var index = this.IndexOf(subDecls, declaration); // insert new declaration between full declaration split into two if (index > 0) { return ReplaceRange(root, fullDecl, this.SplitAndInsert(fullDecl, index, newDeclarations)); } return base.InsertNodesBefore(root, fullDecl, newDeclarations); } public override SyntaxNode InsertNodesAfter(SyntaxNode root, SyntaxNode declaration, IEnumerable<SyntaxNode> newDeclarations) { if (declaration.Parent.IsKind(SyntaxKind.GlobalStatement)) { // Insert global statements before this global statement declaration = declaration.Parent; newDeclarations = newDeclarations.Select(declaration => declaration is StatementSyntax statement ? SyntaxFactory.GlobalStatement(statement) : declaration); } if (root.Span.Contains(declaration.Span)) { return this.Isolate(root.TrackNodes(declaration), r => this.InsertNodesAfterInternal(r, r.GetCurrentNode(declaration)!, newDeclarations)); } else { return base.InsertNodesAfter(root, declaration, newDeclarations); } } private SyntaxNode InsertNodesAfterInternal(SyntaxNode root, SyntaxNode declaration, IEnumerable<SyntaxNode> newDeclarations) { var fullDecl = GetFullDeclaration(declaration); if (fullDecl == declaration || GetDeclarationCount(fullDecl) == 1) { return base.InsertNodesAfter(root, fullDecl, newDeclarations); } var subDecls = GetSubDeclarations(fullDecl); var count = subDecls.Count; var index = this.IndexOf(subDecls, declaration); // insert new declaration between full declaration split into two if (index >= 0 && index < count - 1) { return ReplaceRange(root, fullDecl, this.SplitAndInsert(fullDecl, index + 1, newDeclarations)); } return base.InsertNodesAfter(root, fullDecl, newDeclarations); } private IEnumerable<SyntaxNode> SplitAndInsert(SyntaxNode multiPartDeclaration, int index, IEnumerable<SyntaxNode> newDeclarations) { var count = GetDeclarationCount(multiPartDeclaration); var newNodes = new List<SyntaxNode> { this.WithSubDeclarationsRemoved(multiPartDeclaration, index, count - index).WithTrailingTrivia(SyntaxFactory.ElasticSpace) }; newNodes.AddRange(newDeclarations); newNodes.Add(this.WithSubDeclarationsRemoved(multiPartDeclaration, 0, index).WithLeadingTrivia(SyntaxFactory.ElasticSpace)); return newNodes; } private SyntaxNode WithSubDeclarationsRemoved(SyntaxNode declaration, int index, int count) => RemoveNodes(declaration, GetSubDeclarations(declaration).Skip(index).Take(count)); private static IReadOnlyList<SyntaxNode> GetSubDeclarations(SyntaxNode declaration) => declaration.Kind() switch { SyntaxKind.FieldDeclaration => ((FieldDeclarationSyntax)declaration).Declaration.Variables, SyntaxKind.EventFieldDeclaration => ((EventFieldDeclarationSyntax)declaration).Declaration.Variables, SyntaxKind.LocalDeclarationStatement => ((LocalDeclarationStatementSyntax)declaration).Declaration.Variables, SyntaxKind.VariableDeclaration => ((VariableDeclarationSyntax)declaration).Variables, SyntaxKind.AttributeList => ((AttributeListSyntax)declaration).Attributes, _ => [], }; public override SyntaxNode RemoveNode(SyntaxNode root, SyntaxNode node) => RemoveNode(root, node, DefaultRemoveOptions); public override SyntaxNode RemoveNode(SyntaxNode root, SyntaxNode node, SyntaxRemoveOptions options) { Contract.ThrowIfTrue(ReferenceEquals(root, node)); if (node.Parent.IsKind(SyntaxKind.GlobalStatement)) { // Remove the entire global statement as part of the edit node = node.Parent; } if (root.Span.Contains(node.Span)) { // node exists within normal span of the root (not in trivia) return Isolate(root.TrackNodes(node), r => RemoveNodeInternal(r, r.GetCurrentNode(node)!, options)); } else { return RemoveNodeInternal(root, node, options); } } private SyntaxNode RemoveNodeInternal(SyntaxNode root, SyntaxNode declaration, SyntaxRemoveOptions options) { Contract.ThrowIfTrue(ReferenceEquals(root, declaration)); switch (declaration.Kind()) { case SyntaxKind.Attribute: var attr = (AttributeSyntax)declaration; if (attr.Parent is AttributeListSyntax attrList && attrList.Attributes.Count == 1) { // remove entire list if only one attribute return RemoveNodeInternal(root, attrList, options); } break; case SyntaxKind.AttributeArgument: if (declaration.Parent != null && ((AttributeArgumentListSyntax)declaration.Parent).Arguments.Count == 1) { // remove entire argument list if only one argument return RemoveNodeInternal(root, declaration.Parent, options); } break; case SyntaxKind.VariableDeclarator: var full = GetFullDeclaration(declaration); if (full != declaration && GetDeclarationCount(full) == 1) { // remove full declaration if only one declarator return RemoveNodeInternal(root, full, options); } break; case SyntaxKind.SimpleBaseType: if (declaration.Parent is BaseListSyntax baseList && baseList.Types.Count == 1) { // remove entire base list if this is the only base type. return RemoveNodeInternal(root, baseList, options); } break; default: var parent = declaration.Parent; if (parent != null) { switch (parent.Kind()) { case SyntaxKind.SimpleBaseType: return RemoveNodeInternal(root, parent, options); } } break; } return base.RemoveNode(root, declaration, options); } /// <summary> /// Moves the trailing trivia from the node's previous token to the end of the node /// </summary> private static SyntaxNode ShiftTrivia(SyntaxNode root, SyntaxNode node) { var firstToken = node.GetFirstToken(); var previousToken = firstToken.GetPreviousToken(); if (previousToken != default && root.Contains(previousToken.Parent)) { var newNode = node.WithTrailingTrivia(node.GetTrailingTrivia().AddRange(previousToken.TrailingTrivia)); var newPreviousToken = previousToken.WithTrailingTrivia(default(SyntaxTriviaList)); return root.ReplaceSyntax( nodes: [node], computeReplacementNode: (o, r) => newNode, tokens: [previousToken], computeReplacementToken: (o, r) => newPreviousToken, trivia: null, computeReplacementTrivia: null); } return root; } internal override bool IsRegularOrDocComment(SyntaxTrivia trivia) => trivia.IsRegularOrDocComment(); #endregion #region Statements and Expressions public override SyntaxNode AddEventHandler(SyntaxNode @event, SyntaxNode handler) => SyntaxFactory.AssignmentExpression(SyntaxKind.AddAssignmentExpression, (ExpressionSyntax)@event, (ExpressionSyntax)ParenthesizeNonSimple(handler)); public override SyntaxNode RemoveEventHandler(SyntaxNode @event, SyntaxNode handler) => SyntaxFactory.AssignmentExpression(SyntaxKind.SubtractAssignmentExpression, (ExpressionSyntax)@event, (ExpressionSyntax)ParenthesizeNonSimple(handler)); public override SyntaxNode AwaitExpression(SyntaxNode expression) => SyntaxFactory.AwaitExpression((ExpressionSyntax)expression); public override SyntaxNode NameOfExpression(SyntaxNode expression) => this.InvocationExpression(s_nameOfIdentifier, expression); public override SyntaxNode ReturnStatement(SyntaxNode? expression = null) => SyntaxFactory.ReturnStatement((ExpressionSyntax?)expression); public override SyntaxNode ThrowStatement(SyntaxNode? expression = null) => SyntaxFactory.ThrowStatement((ExpressionSyntax?)expression); public override SyntaxNode ThrowExpression(SyntaxNode expression) => SyntaxFactory.ThrowExpression((ExpressionSyntax)expression); public override SyntaxNode IfStatement(SyntaxNode condition, IEnumerable<SyntaxNode> trueStatements, IEnumerable<SyntaxNode>? falseStatements = null) { if (falseStatements == null) { return SyntaxFactory.IfStatement( (ExpressionSyntax)condition, CreateBlock(trueStatements)); } else { var falseArray = falseStatements.ToList(); // make else-if chain if false-statements contain only an if-statement return SyntaxFactory.IfStatement( (ExpressionSyntax)condition, CreateBlock(trueStatements), SyntaxFactory.ElseClause( falseArray is [IfStatementSyntax ifStatement] ? ifStatement : CreateBlock(falseArray))); } } private static BlockSyntax CreateBlock( IEnumerable<SyntaxNode>? statements = null, BlockSyntax? existingBlock = null, bool addSimplifierAnnotation = true) { var block = existingBlock ?? SyntaxFactory.Block(); var statementList = AsStatementList(statements); // If we're adding any statements, make sure the open brace can move around. This allows `{ }` on an existing // one-line construct to have the braces move to their own lines in accordance to the user's formatting rules. if (statementList.Count > 0) block = block.WithOpenBraceToken(block.OpenBraceToken.WithAdditionalAnnotations(Formatter.Annotation)); block = block.WithStatements(statementList); return addSimplifierAnnotation ? block.WithAdditionalAnnotations(Simplifier.Annotation) : block; } private static SyntaxList<StatementSyntax> AsStatementList(IEnumerable<SyntaxNode>? nodes) => nodes == null ? default : [.. nodes.Select(AsStatement)]; private static StatementSyntax AsStatement(SyntaxNode node) { if (node is ExpressionSyntax expression) { return SyntaxFactory.ExpressionStatement(expression); } return (StatementSyntax)node; } public override SyntaxNode ExpressionStatement(SyntaxNode expression) => SyntaxFactory.ExpressionStatement((ExpressionSyntax)expression); public override SyntaxNode ConditionalAccessExpression(SyntaxNode expression, SyntaxNode whenNotNull) => SyntaxGeneratorInternal.ConditionalAccessExpression(expression, whenNotNull); public override SyntaxNode MemberBindingExpression(SyntaxNode name) => SyntaxGeneratorInternal.MemberBindingExpression(name); public override SyntaxNode ElementBindingExpression(IEnumerable<SyntaxNode> arguments) => SyntaxFactory.ElementBindingExpression( SyntaxFactory.BracketedArgumentList([.. arguments.Cast<ArgumentSyntax>()])); private static SeparatedSyntaxList<ExpressionSyntax> AsExpressionList(IEnumerable<SyntaxNode> expressions) => [.. expressions.OfType<ExpressionSyntax>()]; public override SyntaxNode ArrayCreationExpression(SyntaxNode elementType, SyntaxNode size) { var arrayType = SyntaxFactory.ArrayType((TypeSyntax)elementType, [SyntaxFactory.ArrayRankSpecifier([(ExpressionSyntax)size])]); return SyntaxFactory.ArrayCreationExpression(arrayType); } public override SyntaxNode ArrayCreationExpression(SyntaxNode elementType, IEnumerable<SyntaxNode> elements) { var arrayType = SyntaxFactory.ArrayType((TypeSyntax)elementType, [SyntaxFactory.ArrayRankSpecifier([SyntaxFactory.OmittedArraySizeExpression()])]); var initializer = SyntaxFactory.InitializerExpression(SyntaxKind.ArrayInitializerExpression, AsExpressionList(elements)); return SyntaxFactory.ArrayCreationExpression(arrayType, initializer); } public override SyntaxNode ObjectCreationExpression(SyntaxNode type, IEnumerable<SyntaxNode> arguments) => SyntaxFactory.ObjectCreationExpression((TypeSyntax)type, CreateArgumentList(arguments), null); internal override SyntaxNode ObjectCreationExpression(SyntaxNode type, SyntaxToken openParen, SeparatedSyntaxList<SyntaxNode> arguments, SyntaxToken closeParen) => SyntaxFactory.ObjectCreationExpression( (TypeSyntax)type, SyntaxFactory.ArgumentList(openParen, (SeparatedSyntaxList<ArgumentSyntax>)arguments, closeParen), initializer: null); private static ArgumentListSyntax CreateArgumentList(IEnumerable<SyntaxNode> arguments) => SyntaxFactory.ArgumentList(CreateArguments(arguments)); private static SeparatedSyntaxList<ArgumentSyntax> CreateArguments(IEnumerable<SyntaxNode> arguments) => [.. arguments.Select(AsArgument)]; private static ArgumentSyntax AsArgument(SyntaxNode argOrExpression) => argOrExpression as ArgumentSyntax ?? SyntaxFactory.Argument((ExpressionSyntax)argOrExpression); public override SyntaxNode InvocationExpression(SyntaxNode expression, IEnumerable<SyntaxNode> arguments) => SyntaxFactory.InvocationExpression(CSharpSyntaxGeneratorInternal.ParenthesizeLeft((ExpressionSyntax)expression), CreateArgumentList(arguments)); public override SyntaxNode ElementAccessExpression(SyntaxNode expression, IEnumerable<SyntaxNode> arguments) => SyntaxFactory.ElementAccessExpression(CSharpSyntaxGeneratorInternal.ParenthesizeLeft((ExpressionSyntax)expression), SyntaxFactory.BracketedArgumentList(CreateArguments(arguments))); internal override SyntaxToken NumericLiteralToken(string text, ulong value) => SyntaxFactory.Literal(text, value); private static SyntaxNode ParenthesizeNonSimple(SyntaxNode expression) => CSharpSyntaxGeneratorInternal.ParenthesizeNonSimple(expression); public override SyntaxNode IsTypeExpression(SyntaxNode expression, SyntaxNode type) => SyntaxFactory.BinaryExpression(SyntaxKind.IsExpression, (ExpressionSyntax)ParenthesizeNonSimple(expression), (TypeSyntax)type); public override SyntaxNode TypeOfExpression(SyntaxNode type) => SyntaxFactory.TypeOfExpression((TypeSyntax)type); public override SyntaxNode TryCastExpression(SyntaxNode expression, SyntaxNode type) => SyntaxFactory.BinaryExpression(SyntaxKind.AsExpression, (ExpressionSyntax)ParenthesizeNonSimple(expression), (TypeSyntax)type); public override SyntaxNode AssignmentStatement(SyntaxNode left, SyntaxNode right) => SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, (ExpressionSyntax)left, (ExpressionSyntax)ParenthesizeNonSimple(right)); private static SyntaxNode CreateBinaryExpression(SyntaxKind syntaxKind, SyntaxNode left, SyntaxNode right) => CSharpSyntaxGeneratorInternal.CreateBinaryExpression(syntaxKind, left, right); public override SyntaxNode ValueEqualsExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.EqualsExpression, left, right); public override SyntaxNode ReferenceEqualsExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.EqualsExpression, left, right); public override SyntaxNode ValueNotEqualsExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.NotEqualsExpression, left, right); public override SyntaxNode ReferenceNotEqualsExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.NotEqualsExpression, left, right); public override SyntaxNode LessThanExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.LessThanExpression, left, right); public override SyntaxNode LessThanOrEqualExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.LessThanOrEqualExpression, left, right); public override SyntaxNode GreaterThanExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.GreaterThanExpression, left, right); public override SyntaxNode GreaterThanOrEqualExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.GreaterThanOrEqualExpression, left, right); public override SyntaxNode NegateExpression(SyntaxNode expression) => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.UnaryMinusExpression, (ExpressionSyntax)ParenthesizeNonSimple(expression)); public override SyntaxNode AddExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.AddExpression, left, right); public override SyntaxNode SubtractExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.SubtractExpression, left, right); public override SyntaxNode MultiplyExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.MultiplyExpression, left, right); public override SyntaxNode DivideExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.DivideExpression, left, right); public override SyntaxNode ModuloExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.ModuloExpression, left, right); public override SyntaxNode BitwiseAndExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.BitwiseAndExpression, left, right); public override SyntaxNode BitwiseNotExpression(SyntaxNode operand) => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.BitwiseNotExpression, (ExpressionSyntax)ParenthesizeNonSimple(operand)); public override SyntaxNode LogicalAndExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.LogicalAndExpression, left, right); public override SyntaxNode LogicalOrExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.LogicalOrExpression, left, right); public override SyntaxNode LogicalNotExpression(SyntaxNode expression) => SyntaxFactory.PrefixUnaryExpression( SyntaxKind.LogicalNotExpression, (ExpressionSyntax)ParenthesizeNonSimple(expression.WithoutLeadingTrivia())).WithLeadingTrivia(expression.GetLeadingTrivia()); public override SyntaxNode ConditionalExpression(SyntaxNode condition, SyntaxNode whenTrue, SyntaxNode whenFalse) => SyntaxFactory.ConditionalExpression((ExpressionSyntax)ParenthesizeNonSimple(condition), (ExpressionSyntax)ParenthesizeNonSimple(whenTrue), (ExpressionSyntax)ParenthesizeNonSimple(whenFalse)); public override SyntaxNode CoalesceExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.CoalesceExpression, left, right); public override SyntaxNode ThisExpression() => SyntaxFactory.ThisExpression(); public override SyntaxNode BaseExpression() => SyntaxFactory.BaseExpression(); public override SyntaxNode TypedConstantExpression(TypedConstant value) => ExpressionGenerator.GenerateExpression(value); private protected override SyntaxNode GenerateExpression(ITypeSymbol? type, object? value, bool canUseFieldReference) => ExpressionGenerator.GenerateExpression(type, value, canUseFieldReference); public override SyntaxNode GenericName(string identifier, IEnumerable<SyntaxNode> typeArguments) => GenericName(identifier.ToIdentifierToken(), typeArguments); internal override SyntaxNode GenericName(SyntaxToken identifier, IEnumerable<SyntaxNode> typeArguments) => SyntaxFactory.GenericName(identifier, SyntaxFactory.TypeArgumentList([.. typeArguments.Cast<TypeSyntax>()])); public override SyntaxNode WithTypeArguments(SyntaxNode expression, IEnumerable<SyntaxNode> typeArguments) { switch (expression.Kind()) { case SyntaxKind.IdentifierName: var sname = (SimpleNameSyntax)expression; return SyntaxFactory.GenericName(sname.Identifier, SyntaxFactory.TypeArgumentList([.. typeArguments.Cast<TypeSyntax>()])); case SyntaxKind.GenericName: var gname = (GenericNameSyntax)expression; return gname.WithTypeArgumentList(SyntaxFactory.TypeArgumentList([.. typeArguments.Cast<TypeSyntax>()])); case SyntaxKind.QualifiedName: var qname = (QualifiedNameSyntax)expression; return qname.WithRight((SimpleNameSyntax)this.WithTypeArguments(qname.Right, typeArguments)); case SyntaxKind.AliasQualifiedName: var aname = (AliasQualifiedNameSyntax)expression; return aname.WithName((SimpleNameSyntax)this.WithTypeArguments(aname.Name, typeArguments)); case SyntaxKind.SimpleMemberAccessExpression: case SyntaxKind.PointerMemberAccessExpression: var sma = (MemberAccessExpressionSyntax)expression; return sma.WithName((SimpleNameSyntax)this.WithTypeArguments(sma.Name, typeArguments)); default: return expression; } } public override SyntaxNode QualifiedName(SyntaxNode left, SyntaxNode right) => SyntaxFactory.QualifiedName((NameSyntax)left, (SimpleNameSyntax)right).WithAdditionalAnnotations(Simplifier.Annotation); internal override SyntaxNode GlobalAliasedName(SyntaxNode name) => SyntaxFactory.AliasQualifiedName( SyntaxFactory.IdentifierName(GlobalKeyword), (SimpleNameSyntax)name); public override SyntaxNode NameExpression(INamespaceOrTypeSymbol namespaceOrTypeSymbol) => namespaceOrTypeSymbol.GenerateNameSyntax(); public override SyntaxNode TypeExpression(SpecialType specialType) => SyntaxFactory.PredefinedType(specialType switch { SpecialType.System_Boolean => BoolKeyword, SpecialType.System_Byte => ByteKeyword, SpecialType.System_Char => CharKeyword, SpecialType.System_Decimal => DecimalKeyword, SpecialType.System_Double => DoubleKeyword, SpecialType.System_Int16 => ShortKeyword, SpecialType.System_Int32 => IntKeyword, SpecialType.System_Int64 => LongKeyword, SpecialType.System_Object => ObjectKeyword, SpecialType.System_SByte => SByteKeyword, SpecialType.System_Single => FloatKeyword, SpecialType.System_String => StringKeyword, SpecialType.System_UInt16 => UShortKeyword, SpecialType.System_UInt32 => UIntKeyword, SpecialType.System_UInt64 => ULongKeyword, SpecialType.System_Void => VoidKeyword, _ => throw new NotSupportedException("Unsupported SpecialType"), }); public override SyntaxNode ArrayTypeExpression(SyntaxNode type) => SyntaxFactory.ArrayType((TypeSyntax)type, [SyntaxFactory.ArrayRankSpecifier()]); public override SyntaxNode NullableTypeExpression(SyntaxNode type) { if (type is NullableTypeSyntax) { return type; } else { return SyntaxFactory.NullableType((TypeSyntax)type); } } internal override SyntaxNode CreateTupleType(IEnumerable<SyntaxNode> elements) => SyntaxFactory.TupleType([.. elements.Cast<TupleElementSyntax>()]); public override SyntaxNode TupleElementExpression(SyntaxNode type, string? name = null) => SyntaxFactory.TupleElement((TypeSyntax)type, name?.ToIdentifierToken() ?? default); public override SyntaxNode Argument(string? name, RefKind refKind, SyntaxNode expression) { return SyntaxFactory.Argument( name == null ? null : SyntaxFactory.NameColon(name), GetArgumentModifiers(refKind), (ExpressionSyntax)expression); } public override SyntaxNode LocalDeclarationStatement(SyntaxNode? type, string name, SyntaxNode? initializer, bool isConst) => CSharpSyntaxGeneratorInternal.Instance.LocalDeclarationStatement(type, name.ToIdentifierToken(), initializer, isConst); public override SyntaxNode UsingStatement(SyntaxNode? type, string name, SyntaxNode expression, IEnumerable<SyntaxNode> statements) { return SyntaxFactory.UsingStatement( CSharpSyntaxGeneratorInternal.VariableDeclaration(type, name.ToIdentifierToken(), expression), expression: null, statement: CreateBlock(statements)); } public override SyntaxNode UsingStatement(SyntaxNode expression, IEnumerable<SyntaxNode> statements) { return SyntaxFactory.UsingStatement( declaration: null, expression: (ExpressionSyntax)expression, statement: CreateBlock(statements)); } public override SyntaxNode LockStatement(SyntaxNode expression, IEnumerable<SyntaxNode> statements) { return SyntaxFactory.LockStatement( expression: (ExpressionSyntax)expression, statement: CreateBlock(statements)); } public override SyntaxNode TryCatchStatement(IEnumerable<SyntaxNode>? tryStatements, IEnumerable<SyntaxNode>? catchClauses, IEnumerable<SyntaxNode>? finallyStatements = null) { return SyntaxFactory.TryStatement( CreateBlock(tryStatements), catchClauses != null ? [.. catchClauses.Cast<CatchClauseSyntax>()] : default, finallyStatements != null ? SyntaxFactory.FinallyClause(CreateBlock(finallyStatements)) : null); } public override SyntaxNode CatchClause(SyntaxNode type, string name, IEnumerable<SyntaxNode> statements) { return SyntaxFactory.CatchClause( SyntaxFactory.CatchDeclaration((TypeSyntax)type, name.ToIdentifierToken()), filter: null, block: CreateBlock(statements)); } public override SyntaxNode WhileStatement(SyntaxNode condition, IEnumerable<SyntaxNode> statements) => SyntaxFactory.WhileStatement((ExpressionSyntax)condition, CreateBlock(statements)); public override SyntaxNode SwitchStatement(SyntaxNode expression, IEnumerable<SyntaxNode> caseClauses) { if (expression is TupleExpressionSyntax) { return SyntaxFactory.SwitchStatement( (ExpressionSyntax)expression, [.. caseClauses.Cast<SwitchSectionSyntax>()]); } else { return SyntaxFactory.SwitchStatement( SwitchKeyword, OpenParenToken, (ExpressionSyntax)expression, CloseParenToken, OpenBraceToken, [.. caseClauses.Cast<SwitchSectionSyntax>()], CloseBraceToken); } } public override SyntaxNode SwitchSection(IEnumerable<SyntaxNode> expressions, IEnumerable<SyntaxNode> statements) => SyntaxFactory.SwitchSection(AsSwitchLabels(expressions), AsStatementList(statements)); internal override SyntaxNode SwitchSectionFromLabels(IEnumerable<SyntaxNode> labels, IEnumerable<SyntaxNode> statements) { return SyntaxFactory.SwitchSection( [.. labels.Cast<SwitchLabelSyntax>()], AsStatementList(statements)); } public override SyntaxNode DefaultSwitchSection(IEnumerable<SyntaxNode> statements) => SyntaxFactory.SwitchSection([SyntaxFactory.DefaultSwitchLabel()], AsStatementList(statements)); private static SyntaxList<SwitchLabelSyntax> AsSwitchLabels(IEnumerable<SyntaxNode> expressions) { var labels = default(SyntaxList<SwitchLabelSyntax>); if (expressions != null) { labels = labels.AddRange(expressions.Select(e => SyntaxFactory.CaseSwitchLabel((ExpressionSyntax)e))); } return labels; } public override SyntaxNode ExitSwitchStatement() => SyntaxFactory.BreakStatement(); internal override SyntaxNode ScopeBlock(IEnumerable<SyntaxNode> statements) => SyntaxFactory.Block(statements.Cast<StatementSyntax>()); internal override SyntaxNode GlobalStatement(SyntaxNode statement) => SyntaxFactory.GlobalStatement((StatementSyntax)statement); public override SyntaxNode ValueReturningLambdaExpression(IEnumerable<SyntaxNode>? parameterDeclarations, SyntaxNode expression) { var parameters = parameterDeclarations?.Cast<ParameterSyntax>().ToList(); return parameters is [var parameter] && IsSimpleLambdaParameter(parameter) ? SyntaxFactory.SimpleLambdaExpression(parameter, (CSharpSyntaxNode)expression) : SyntaxFactory.ParenthesizedLambdaExpression(AsParameterList(parameters), (CSharpSyntaxNode)expression); } private static bool IsSimpleLambdaParameter(SyntaxNode node) => node is ParameterSyntax { Type: null, Default: null, Modifiers.Count: 0 }; public override SyntaxNode VoidReturningLambdaExpression(IEnumerable<SyntaxNode>? lambdaParameters, SyntaxNode expression) => this.ValueReturningLambdaExpression(lambdaParameters, expression); public override SyntaxNode ValueReturningLambdaExpression(IEnumerable<SyntaxNode>? parameterDeclarations, IEnumerable<SyntaxNode> statements) => this.ValueReturningLambdaExpression(parameterDeclarations, CreateBlock(statements)); public override SyntaxNode VoidReturningLambdaExpression(IEnumerable<SyntaxNode>? lambdaParameters, IEnumerable<SyntaxNode> statements) => this.ValueReturningLambdaExpression(lambdaParameters, statements); public override SyntaxNode LambdaParameter(string identifier, SyntaxNode? type = null) => this.ParameterDeclaration(identifier, type, initializer: null, RefKind.None); internal override SyntaxNode IdentifierName(SyntaxToken identifier) => SyntaxFactory.IdentifierName(identifier); internal override SyntaxNode NamedAnonymousObjectMemberDeclarator(SyntaxNode identifier, SyntaxNode expression) { return SyntaxFactory.AnonymousObjectMemberDeclarator( SyntaxFactory.NameEquals((IdentifierNameSyntax)identifier), (ExpressionSyntax)expression); } public override SyntaxNode TupleExpression(IEnumerable<SyntaxNode> arguments) => SyntaxFactory.TupleExpression([.. arguments.Select(AsArgument)]); internal override SyntaxNode RemoveAllComments(SyntaxNode node) { var modifiedNode = RemoveLeadingAndTrailingComments(node); if (modifiedNode is TypeDeclarationSyntax declarationSyntax) { return declarationSyntax.WithOpenBraceToken(RemoveLeadingAndTrailingComments(declarationSyntax.OpenBraceToken)) .WithCloseBraceToken(RemoveLeadingAndTrailingComments(declarationSyntax.CloseBraceToken)); } return modifiedNode; } internal override SyntaxTriviaList RemoveCommentLines(SyntaxTriviaList syntaxTriviaList) { static IEnumerable<IEnumerable<SyntaxTrivia>> splitIntoLines(SyntaxTriviaList triviaList) { var index = 0; for (var i = 0; i < triviaList.Count; i++) { if (triviaList[i].IsEndOfLine()) { yield return triviaList.TakeRange(index, i); index = i + 1; } } if (index < triviaList.Count) { yield return triviaList.TakeRange(index, triviaList.Count - 1); } } var syntaxWithoutComments = splitIntoLines(syntaxTriviaList) .Where(trivia => !trivia.Any(t => t.IsRegularOrDocComment())) .SelectMany(t => t); return [.. syntaxWithoutComments]; } internal override SyntaxNode ParseExpression(string stringToParse) => SyntaxFactory.ParseExpression(stringToParse); internal override SyntaxNode ParseTypeName(string stringToParse) => SyntaxFactory.ParseTypeName(stringToParse); internal override SyntaxNode ExtensionBlockDeclaration( SyntaxNode extensionParameter, IEnumerable<SyntaxNode>? typeParameters, IEnumerable<SyntaxNode> members) { SyntaxList<MemberDeclarationSyntax> extensionMembers = [.. members.OfType<MemberDeclarationSyntax>().WhereNotNull()]; var typeParameterList = AsTypeParameterList(typeParameters); return SyntaxFactory.ExtensionBlockDeclaration(attributeLists: default, modifiers: default, ExtensionKeyword, typeParameterList, parameterList: AsParameterList([extensionParameter]), constraintClauses: default, OpenBraceToken, extensionMembers, CloseBraceToken, default); } #endregion }