// Copyright (c) 2010-2020 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using Attribute = ICSharpCode.Decompiler.CSharp.Syntax.Attribute; namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { /// /// Outputs the AST. /// public class CSharpOutputVisitor : IAstVisitor { readonly protected TokenWriter writer; readonly protected CSharpFormattingOptions policy; readonly protected Stack containerStack = new Stack(); public CSharpOutputVisitor(TextWriter textWriter, CSharpFormattingOptions formattingPolicy) { if (textWriter == null) { throw new ArgumentNullException(nameof(textWriter)); } if (formattingPolicy == null) { throw new ArgumentNullException(nameof(formattingPolicy)); } this.writer = TokenWriter.Create(textWriter, formattingPolicy.IndentationString); this.policy = formattingPolicy; } public CSharpOutputVisitor(TokenWriter writer, CSharpFormattingOptions formattingPolicy) { if (writer == null) { throw new ArgumentNullException(nameof(writer)); } if (formattingPolicy == null) { throw new ArgumentNullException(nameof(formattingPolicy)); } this.writer = new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(writer)); this.policy = formattingPolicy; } #region StartNode/EndNode protected virtual void StartNode(AstNode node) { // Ensure that nodes are visited in the proper nested order. // Jumps to different subtrees are allowed only for the child of a placeholder node. Debug.Assert(containerStack.Count == 0 || node.Parent == containerStack.Peek() || containerStack.Peek().NodeType == NodeType.Pattern); containerStack.Push(node); writer.StartNode(node); } protected virtual void EndNode(AstNode node) { Debug.Assert(node == containerStack.Peek()); containerStack.Pop(); writer.EndNode(node); } #endregion #region Comma /// /// Writes a comma. /// /// The next node after the comma. /// When set prevents printing a space after comma. protected virtual void Comma(AstNode nextNode, bool noSpaceAfterComma = false) { Space(policy.SpaceBeforeBracketComma); // TODO: Comma policy has changed. writer.WriteToken(Roles.Comma, ","); isAfterSpace = false; Space(!noSpaceAfterComma && policy.SpaceAfterBracketComma); // TODO: Comma policy has changed. } /// /// Writes an optional comma, e.g. at the end of an enum declaration or in an array initializer /// protected virtual void OptionalComma(AstNode pos) { // Look if there's a comma after the current node, and insert it if it exists. while (pos != null && pos.NodeType == NodeType.Whitespace) { pos = pos.NextSibling; } if (pos != null && pos.Role == Roles.Comma) { Comma(null, noSpaceAfterComma: true); } } /// /// Writes an optional semicolon, e.g. at the end of a type or namespace declaration. /// protected virtual void OptionalSemicolon(AstNode pos) { // Look if there's a semicolon after the current node, and insert it if it exists. while (pos != null && pos.NodeType == NodeType.Whitespace) { pos = pos.PrevSibling; } if (pos != null && pos.Role == Roles.Semicolon) { Semicolon(); } } protected virtual void WriteCommaSeparatedList(IEnumerable list) { bool isFirst = true; foreach (AstNode node in list) { if (isFirst) { isFirst = false; } else { Comma(node); } node.AcceptVisitor(this); } } protected virtual void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) { LPar(); if (list.Any()) { Space(spaceWithin); WriteCommaSeparatedList(list); Space(spaceWithin); } RPar(); } protected virtual void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) { WriteToken(Roles.LBracket); if (list.Any()) { Space(spaceWithin); WriteCommaSeparatedList(list); Space(spaceWithin); } WriteToken(Roles.RBracket); } protected virtual void WriteCommaSeparatedListInBrackets(IEnumerable list) { WriteToken(Roles.LBracket); if (list.Any()) { Space(policy.SpacesWithinBrackets); WriteCommaSeparatedList(list); Space(policy.SpacesWithinBrackets); } WriteToken(Roles.RBracket); } #endregion #region Write tokens protected bool isAtStartOfLine = true; protected bool isAfterSpace; /// /// Writes a keyword, and all specials up to /// protected virtual void WriteKeyword(TokenRole tokenRole) { WriteKeyword(tokenRole.Token, tokenRole); } protected virtual void WriteKeyword(string token, Role tokenRole = null) { writer.WriteKeyword(tokenRole, token); isAtStartOfLine = false; isAfterSpace = false; } protected virtual void WriteIdentifier(Identifier identifier) { writer.WriteIdentifier(identifier); isAtStartOfLine = false; isAfterSpace = false; } protected virtual void WriteIdentifier(string identifier) { AstType.Create(identifier).AcceptVisitor(this); isAtStartOfLine = false; isAfterSpace = false; } protected virtual void WriteToken(TokenRole tokenRole) { WriteToken(tokenRole.Token, tokenRole); } protected virtual void WriteToken(string token, Role tokenRole) { writer.WriteToken(tokenRole, token); isAtStartOfLine = false; isAfterSpace = false; } protected virtual void LPar() { WriteToken(Roles.LPar); } protected virtual void RPar() { WriteToken(Roles.RPar); } /// /// Marks the end of a statement /// protected virtual void Semicolon() { // get the role of the current node Role role = containerStack.Peek().Role; if (!SkipToken()) { WriteToken(Roles.Semicolon); if (!SkipNewLine()) NewLine(); else Space(); } bool SkipToken() { return role == ForStatement.InitializerRole || role == ForStatement.IteratorRole || role == UsingStatement.ResourceAcquisitionRole; } bool SkipNewLine() { if (containerStack.Peek() is not Accessor accessor) return false; if (!(role == PropertyDeclaration.GetterRole || role == PropertyDeclaration.SetterRole)) return false; bool isAutoProperty = accessor.Body.IsNull && policy.AutoPropertyFormatting == PropertyFormatting.SingleLine; return isAutoProperty; } } /// /// Writes a space depending on policy. /// protected virtual void Space(bool addSpace = true) { if (addSpace && !isAfterSpace) { writer.Space(); isAfterSpace = true; } } protected virtual void NewLine() { writer.NewLine(); isAtStartOfLine = true; isAfterSpace = false; } int GetCallChainLengthLimited(MemberReferenceExpression expr) { int callChainLength = 0; var node = expr; while (node.Target is InvocationExpression invocation && invocation.Target is MemberReferenceExpression mre && callChainLength < 4) { node = mre; callChainLength++; } return callChainLength; } protected virtual bool InsertNewLineWhenInMethodCallChain(MemberReferenceExpression expr) { int callChainLength = GetCallChainLengthLimited(expr); if (callChainLength < 3) return false; if (expr.GetParent(n => n is Statement || n is LambdaExpression || n is InterpolatedStringContent) is InterpolatedStringContent) return false; if (callChainLength == 3) writer.Indent(); writer.NewLine(); isAtStartOfLine = true; isAfterSpace = false; return true; } protected virtual void OpenBrace(BraceStyle style, bool newLine = true) { switch (style) { case BraceStyle.EndOfLine: case BraceStyle.BannerStyle: if (!isAtStartOfLine) Space(); WriteToken("{", Roles.LBrace); break; case BraceStyle.EndOfLineWithoutSpace: WriteToken("{", Roles.LBrace); break; case BraceStyle.NextLine: if (!isAtStartOfLine) NewLine(); WriteToken("{", Roles.LBrace); break; case BraceStyle.NextLineShifted: NewLine(); writer.Indent(); WriteToken("{", Roles.LBrace); NewLine(); return; case BraceStyle.NextLineShifted2: NewLine(); writer.Indent(); WriteToken("{", Roles.LBrace); break; default: throw new ArgumentOutOfRangeException(); } if (newLine) { writer.Indent(); NewLine(); } } protected virtual void CloseBrace(BraceStyle style, bool unindent = true) { switch (style) { case BraceStyle.EndOfLine: case BraceStyle.EndOfLineWithoutSpace: case BraceStyle.NextLine: if (unindent) writer.Unindent(); WriteToken("}", Roles.RBrace); break; case BraceStyle.BannerStyle: case BraceStyle.NextLineShifted: WriteToken("}", Roles.RBrace); if (unindent) writer.Unindent(); break; case BraceStyle.NextLineShifted2: if (unindent) writer.Unindent(); WriteToken("}", Roles.RBrace); if (unindent) writer.Unindent(); break; default: throw new ArgumentOutOfRangeException(); } } #endregion #region IsKeyword Test static readonly HashSet unconditionalKeywords = new HashSet { "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while" }; static readonly HashSet queryKeywords = new HashSet { "from", "where", "join", "on", "equals", "into", "let", "orderby", "ascending", "descending", "select", "group", "by" }; static readonly int maxKeywordLength = unconditionalKeywords.Concat(queryKeywords).Max(s => s.Length); /// /// Determines whether the specified identifier is a keyword in the given context. /// public static bool IsKeyword(string identifier, AstNode context) { // only 2-10 char lower-case identifiers can be keywords if (identifier.Length > maxKeywordLength || identifier.Length < 2 || identifier[0] < 'a') { return false; } if (unconditionalKeywords.Contains(identifier)) { return true; } if (queryKeywords.Contains(identifier)) { return context.Ancestors.Any(ancestor => ancestor is QueryExpression); } if (identifier == "await") { foreach (AstNode ancestor in context.Ancestors) { // with lambdas/anonymous methods, if (ancestor is LambdaExpression) { return ((LambdaExpression)ancestor).IsAsync; } if (ancestor is AnonymousMethodExpression) { return ((AnonymousMethodExpression)ancestor).IsAsync; } if (ancestor is EntityDeclaration) { return (((EntityDeclaration)ancestor).Modifiers & Modifiers.Async) == Modifiers.Async; } } } return false; } #endregion #region Write constructs protected virtual void WriteTypeArguments(IEnumerable typeArguments) { if (typeArguments.Any()) { WriteToken(Roles.LChevron); WriteCommaSeparatedList(typeArguments); WriteToken(Roles.RChevron); } } public virtual void WriteTypeParameters(IEnumerable typeParameters) { if (typeParameters.Any()) { WriteToken(Roles.LChevron); WriteCommaSeparatedList(typeParameters); WriteToken(Roles.RChevron); } } protected virtual void WriteModifiers(IEnumerable modifierTokens) { foreach (CSharpModifierToken modifier in modifierTokens) { modifier.AcceptVisitor(this); Space(); } } protected virtual void WriteQualifiedIdentifier(IEnumerable identifiers) { bool first = true; foreach (Identifier ident in identifiers) { if (first) { first = false; } else { writer.WriteToken(Roles.Dot, "."); } writer.WriteIdentifier(ident); } } /// /// Writes an embedded statement. /// /// The statement to write. /// Determines whether a trailing newline should be written following a block. /// Non-blocks always write a trailing newline. /// /// Blocks may or may not write a leading newline depending on StatementBraceStyle. /// Non-blocks always write a leading newline. /// protected virtual void WriteEmbeddedStatement(Statement embeddedStatement, NewLinePlacement nlp = NewLinePlacement.NewLine) { if (embeddedStatement.IsNull) { NewLine(); return; } BlockStatement block = embeddedStatement as BlockStatement; if (block != null) { WriteBlock(block, policy.StatementBraceStyle); if (nlp == NewLinePlacement.SameLine) { Space(); // if not a trailing newline, then at least a trailing space } else { NewLine(); } } else { NewLine(); writer.Indent(); embeddedStatement.AcceptVisitor(this); writer.Unindent(); } } protected virtual void WriteMethodBody(BlockStatement body, BraceStyle style, bool newLine = true) { if (body.IsNull) { Semicolon(); } else { WriteBlock(body, style); NewLine(); } } protected virtual void WriteAttributes(IEnumerable attributes) { foreach (AttributeSection attr in attributes) { attr.AcceptVisitor(this); } } protected virtual void WritePrivateImplementationType(AstType privateImplementationType) { if (!privateImplementationType.IsNull) { privateImplementationType.AcceptVisitor(this); WriteToken(Roles.Dot); } } #endregion #region Expressions public virtual void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) { StartNode(anonymousMethodExpression); if (anonymousMethodExpression.IsAsync) { WriteKeyword(AnonymousMethodExpression.AsyncModifierRole); Space(); } WriteKeyword(AnonymousMethodExpression.DelegateKeywordRole); if (anonymousMethodExpression.HasParameterList) { Space(policy.SpaceBeforeAnonymousMethodParentheses); WriteCommaSeparatedListInParenthesis(anonymousMethodExpression.Parameters, policy.SpaceWithinAnonymousMethodParentheses); } WriteBlock(anonymousMethodExpression.Body, policy.AnonymousMethodBraceStyle); EndNode(anonymousMethodExpression); } public virtual void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) { StartNode(undocumentedExpression); switch (undocumentedExpression.UndocumentedExpressionType) { case UndocumentedExpressionType.ArgList: case UndocumentedExpressionType.ArgListAccess: WriteKeyword(UndocumentedExpression.ArglistKeywordRole); break; case UndocumentedExpressionType.MakeRef: WriteKeyword(UndocumentedExpression.MakerefKeywordRole); break; case UndocumentedExpressionType.RefType: WriteKeyword(UndocumentedExpression.ReftypeKeywordRole); break; case UndocumentedExpressionType.RefValue: WriteKeyword(UndocumentedExpression.RefvalueKeywordRole); break; } if (undocumentedExpression.UndocumentedExpressionType != UndocumentedExpressionType.ArgListAccess) { Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(undocumentedExpression.Arguments, policy.SpaceWithinMethodCallParentheses); } EndNode(undocumentedExpression); } public virtual void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) { StartNode(arrayCreateExpression); WriteKeyword(ArrayCreateExpression.NewKeywordRole); arrayCreateExpression.Type.AcceptVisitor(this); if (arrayCreateExpression.Arguments.Count > 0) { WriteCommaSeparatedListInBrackets(arrayCreateExpression.Arguments); } foreach (var specifier in arrayCreateExpression.AdditionalArraySpecifiers) { specifier.AcceptVisitor(this); } arrayCreateExpression.Initializer.AcceptVisitor(this); EndNode(arrayCreateExpression); } public virtual void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) { StartNode(arrayInitializerExpression); // "new List { { 1 } }" and "new List { 1 }" are the same semantically. // We also use the same AST for both: we always use two nested ArrayInitializerExpressions // for collection initializers, even if the user did not write nested brackets. // The output visitor will output nested braces only if they are necessary, // or if the braces tokens exist in the AST. bool bracesAreOptional = arrayInitializerExpression.Elements.Count == 1 && IsObjectOrCollectionInitializer(arrayInitializerExpression.Parent) && !CanBeConfusedWithObjectInitializer(arrayInitializerExpression.Elements.Single()); if (bracesAreOptional && arrayInitializerExpression.LBraceToken.IsNull) { arrayInitializerExpression.Elements.Single().AcceptVisitor(this); } else { PrintInitializerElements(arrayInitializerExpression.Elements); } EndNode(arrayInitializerExpression); } protected bool CanBeConfusedWithObjectInitializer(Expression expr) { // "int a; new List { a = 1 };" is an object initalizers and invalid, but // "int a; new List { { a = 1 } };" is a valid collection initializer. AssignmentExpression ae = expr as AssignmentExpression; return ae != null && ae.Operator == AssignmentOperatorType.Assign; } protected bool IsObjectOrCollectionInitializer(AstNode node) { if (!(node is ArrayInitializerExpression)) { return false; } if (node.Parent is ObjectCreateExpression) { return node.Role == ObjectCreateExpression.InitializerRole; } if (node.Parent is NamedExpression) { return node.Role == Roles.Expression; } return false; } protected virtual void PrintInitializerElements(AstNodeCollection elements) { bool wrapAlways = policy.ArrayInitializerWrapping == Wrapping.WrapAlways || (elements.Count > 1 && elements.Any(e => !IsSimpleExpression(e))) || elements.Any(IsComplexExpression); bool wrap = wrapAlways || elements.Count > 10; OpenBrace(wrap ? policy.ArrayInitializerBraceStyle : BraceStyle.EndOfLine, newLine: wrap); if (!wrap) Space(); AstNode last = null; foreach (var (idx, node) in elements.WithIndex()) { if (idx > 0) { Comma(node, noSpaceAfterComma: true); if (wrapAlways || idx % 10 == 0) NewLine(); else Space(); } last = node; node.AcceptVisitor(this); } if (last != null) OptionalComma(last.NextSibling); if (wrap) NewLine(); else Space(); CloseBrace(wrap ? policy.ArrayInitializerBraceStyle : BraceStyle.EndOfLine, unindent: wrap); bool IsSimpleExpression(Expression ex) { switch (ex) { case NullReferenceExpression _: case ThisReferenceExpression _: case PrimitiveExpression _: case IdentifierExpression _: case MemberReferenceExpression { Target: ThisReferenceExpression or IdentifierExpression or BaseReferenceExpression } _: return true; default: return false; } } bool IsComplexExpression(Expression ex) { switch (ex) { case AnonymousMethodExpression _: case LambdaExpression _: case AnonymousTypeCreateExpression _: case ObjectCreateExpression _: case NamedExpression _: return true; default: return false; } } } public virtual void VisitAsExpression(AsExpression asExpression) { StartNode(asExpression); asExpression.Expression.AcceptVisitor(this); Space(); WriteKeyword(AsExpression.AsKeywordRole); Space(); asExpression.Type.AcceptVisitor(this); EndNode(asExpression); } public virtual void VisitAssignmentExpression(AssignmentExpression assignmentExpression) { StartNode(assignmentExpression); assignmentExpression.Left.AcceptVisitor(this); Space(policy.SpaceAroundAssignment); WriteToken(AssignmentExpression.GetOperatorRole(assignmentExpression.Operator)); Space(policy.SpaceAroundAssignment); assignmentExpression.Right.AcceptVisitor(this); EndNode(assignmentExpression); } public virtual void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) { StartNode(baseReferenceExpression); WriteKeyword("base", baseReferenceExpression.Role); EndNode(baseReferenceExpression); } public virtual void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { StartNode(binaryOperatorExpression); binaryOperatorExpression.Left.AcceptVisitor(this); bool spacePolicy; switch (binaryOperatorExpression.Operator) { case BinaryOperatorType.BitwiseAnd: case BinaryOperatorType.BitwiseOr: case BinaryOperatorType.ExclusiveOr: spacePolicy = policy.SpaceAroundBitwiseOperator; break; case BinaryOperatorType.ConditionalAnd: case BinaryOperatorType.ConditionalOr: spacePolicy = policy.SpaceAroundLogicalOperator; break; case BinaryOperatorType.GreaterThan: case BinaryOperatorType.GreaterThanOrEqual: case BinaryOperatorType.LessThanOrEqual: case BinaryOperatorType.LessThan: spacePolicy = policy.SpaceAroundRelationalOperator; break; case BinaryOperatorType.Equality: case BinaryOperatorType.InEquality: spacePolicy = policy.SpaceAroundEqualityOperator; break; case BinaryOperatorType.Add: case BinaryOperatorType.Subtract: spacePolicy = policy.SpaceAroundAdditiveOperator; break; case BinaryOperatorType.Multiply: case BinaryOperatorType.Divide: case BinaryOperatorType.Modulus: spacePolicy = policy.SpaceAroundMultiplicativeOperator; break; case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: spacePolicy = policy.SpaceAroundShiftOperator; break; case BinaryOperatorType.NullCoalescing: spacePolicy = true; break; case BinaryOperatorType.Range: spacePolicy = false; break; default: throw new NotSupportedException("Invalid value for BinaryOperatorType"); } Space(spacePolicy); WriteToken(BinaryOperatorExpression.GetOperatorRole(binaryOperatorExpression.Operator)); Space(spacePolicy); binaryOperatorExpression.Right.AcceptVisitor(this); EndNode(binaryOperatorExpression); } public virtual void VisitCastExpression(CastExpression castExpression) { StartNode(castExpression); LPar(); Space(policy.SpacesWithinCastParentheses); castExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinCastParentheses); RPar(); Space(policy.SpaceAfterTypecast); castExpression.Expression.AcceptVisitor(this); EndNode(castExpression); } public virtual void VisitCheckedExpression(CheckedExpression checkedExpression) { StartNode(checkedExpression); WriteKeyword(CheckedExpression.CheckedKeywordRole); LPar(); Space(policy.SpacesWithinCheckedExpressionParantheses); checkedExpression.Expression.AcceptVisitor(this); Space(policy.SpacesWithinCheckedExpressionParantheses); RPar(); EndNode(checkedExpression); } public virtual void VisitConditionalExpression(ConditionalExpression conditionalExpression) { StartNode(conditionalExpression); conditionalExpression.Condition.AcceptVisitor(this); Space(policy.SpaceBeforeConditionalOperatorCondition); WriteToken(ConditionalExpression.QuestionMarkRole); Space(policy.SpaceAfterConditionalOperatorCondition); conditionalExpression.TrueExpression.AcceptVisitor(this); Space(policy.SpaceBeforeConditionalOperatorSeparator); WriteToken(ConditionalExpression.ColonRole); Space(policy.SpaceAfterConditionalOperatorSeparator); conditionalExpression.FalseExpression.AcceptVisitor(this); EndNode(conditionalExpression); } public virtual void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) { StartNode(defaultValueExpression); WriteKeyword(DefaultValueExpression.DefaultKeywordRole); LPar(); Space(policy.SpacesWithinTypeOfParentheses); defaultValueExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinTypeOfParentheses); RPar(); EndNode(defaultValueExpression); } public virtual void VisitDirectionExpression(DirectionExpression directionExpression) { StartNode(directionExpression); switch (directionExpression.FieldDirection) { case FieldDirection.Out: WriteKeyword(DirectionExpression.OutKeywordRole); break; case FieldDirection.Ref: WriteKeyword(DirectionExpression.RefKeywordRole); break; case FieldDirection.In: WriteKeyword(DirectionExpression.InKeywordRole); break; default: throw new NotSupportedException("Invalid value for FieldDirection"); } Space(); directionExpression.Expression.AcceptVisitor(this); EndNode(directionExpression); } public virtual void VisitDeclarationExpression(DeclarationExpression declarationExpression) { StartNode(declarationExpression); declarationExpression.Type.AcceptVisitor(this); Space(); declarationExpression.Designation.AcceptVisitor(this); EndNode(declarationExpression); } public virtual void VisitOutVarDeclarationExpression(OutVarDeclarationExpression outVarDeclarationExpression) { StartNode(outVarDeclarationExpression); WriteKeyword(OutVarDeclarationExpression.OutKeywordRole); Space(); outVarDeclarationExpression.Type.AcceptVisitor(this); Space(); outVarDeclarationExpression.Variable.AcceptVisitor(this); EndNode(outVarDeclarationExpression); } public virtual void VisitIdentifierExpression(IdentifierExpression identifierExpression) { StartNode(identifierExpression); WriteIdentifier(identifierExpression.IdentifierToken); WriteTypeArguments(identifierExpression.TypeArguments); EndNode(identifierExpression); } public virtual void VisitIndexerExpression(IndexerExpression indexerExpression) { StartNode(indexerExpression); indexerExpression.Target.AcceptVisitor(this); Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInBrackets(indexerExpression.Arguments); EndNode(indexerExpression); } public virtual void VisitInvocationExpression(InvocationExpression invocationExpression) { StartNode(invocationExpression); invocationExpression.Target.AcceptVisitor(this); Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(invocationExpression.Arguments, policy.SpaceWithinMethodCallParentheses); if (!(invocationExpression.Parent is MemberReferenceExpression)) { if (invocationExpression.Target is MemberReferenceExpression mre) { if (GetCallChainLengthLimited(mre) >= 3) writer.Unindent(); } } EndNode(invocationExpression); } public virtual void VisitIsExpression(IsExpression isExpression) { StartNode(isExpression); isExpression.Expression.AcceptVisitor(this); Space(); WriteKeyword(IsExpression.IsKeywordRole); isExpression.Type.AcceptVisitor(this); EndNode(isExpression); } public virtual void VisitLambdaExpression(LambdaExpression lambdaExpression) { StartNode(lambdaExpression); if (lambdaExpression.IsAsync) { WriteKeyword(LambdaExpression.AsyncModifierRole); Space(); } if (LambdaNeedsParenthesis(lambdaExpression)) { WriteCommaSeparatedListInParenthesis(lambdaExpression.Parameters, policy.SpaceWithinMethodDeclarationParentheses); } else { lambdaExpression.Parameters.Single().AcceptVisitor(this); } Space(); WriteToken(Roles.Arrow); if (lambdaExpression.Body is BlockStatement) { WriteBlock((BlockStatement)lambdaExpression.Body, policy.AnonymousMethodBraceStyle); } else { Space(); lambdaExpression.Body.AcceptVisitor(this); } EndNode(lambdaExpression); } protected bool LambdaNeedsParenthesis(LambdaExpression lambdaExpression) { if (lambdaExpression.Parameters.Count != 1) { return true; } var p = lambdaExpression.Parameters.Single(); return !(p.Type.IsNull && p.ParameterModifier == ParameterModifier.None); } public virtual void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { StartNode(memberReferenceExpression); memberReferenceExpression.Target.AcceptVisitor(this); bool insertedNewLine = InsertNewLineWhenInMethodCallChain(memberReferenceExpression); WriteToken(Roles.Dot); WriteIdentifier(memberReferenceExpression.MemberNameToken); WriteTypeArguments(memberReferenceExpression.TypeArguments); if (insertedNewLine && !(memberReferenceExpression.Parent is InvocationExpression)) { writer.Unindent(); } EndNode(memberReferenceExpression); } public virtual void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) { StartNode(namedArgumentExpression); WriteIdentifier(namedArgumentExpression.NameToken); WriteToken(Roles.Colon); Space(); namedArgumentExpression.Expression.AcceptVisitor(this); EndNode(namedArgumentExpression); } public virtual void VisitNamedExpression(NamedExpression namedExpression) { StartNode(namedExpression); WriteIdentifier(namedExpression.NameToken); Space(); WriteToken(Roles.Assign); Space(); namedExpression.Expression.AcceptVisitor(this); EndNode(namedExpression); } public virtual void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) { StartNode(nullReferenceExpression); writer.WritePrimitiveValue(null); isAfterSpace = false; EndNode(nullReferenceExpression); } public virtual void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) { StartNode(objectCreateExpression); WriteKeyword(ObjectCreateExpression.NewKeywordRole); objectCreateExpression.Type.AcceptVisitor(this); bool useParenthesis = objectCreateExpression.Arguments.Any() || objectCreateExpression.Initializer.IsNull; // also use parenthesis if there is an '(' token if (!objectCreateExpression.LParToken.IsNull) { useParenthesis = true; } if (useParenthesis) { Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(objectCreateExpression.Arguments, policy.SpaceWithinMethodCallParentheses); } objectCreateExpression.Initializer.AcceptVisitor(this); EndNode(objectCreateExpression); } public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) { StartNode(anonymousTypeCreateExpression); WriteKeyword(AnonymousTypeCreateExpression.NewKeywordRole); PrintInitializerElements(anonymousTypeCreateExpression.Initializers); EndNode(anonymousTypeCreateExpression); } public virtual void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) { StartNode(parenthesizedExpression); LPar(); Space(policy.SpacesWithinParentheses); parenthesizedExpression.Expression.AcceptVisitor(this); Space(policy.SpacesWithinParentheses); RPar(); EndNode(parenthesizedExpression); } public virtual void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) { StartNode(pointerReferenceExpression); pointerReferenceExpression.Target.AcceptVisitor(this); WriteToken(PointerReferenceExpression.ArrowRole); WriteIdentifier(pointerReferenceExpression.MemberNameToken); WriteTypeArguments(pointerReferenceExpression.TypeArguments); EndNode(pointerReferenceExpression); } #region VisitPrimitiveExpression public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) { StartNode(primitiveExpression); writer.WritePrimitiveValue(primitiveExpression.Value, primitiveExpression.Format); isAfterSpace = false; EndNode(primitiveExpression); } public virtual void VisitInterpolatedStringExpression(InterpolatedStringExpression interpolatedStringExpression) { StartNode(interpolatedStringExpression); writer.WriteToken(InterpolatedStringExpression.OpenQuote, "$\""); foreach (var element in interpolatedStringExpression.Content) { element.AcceptVisitor(this); } writer.WriteToken(InterpolatedStringExpression.CloseQuote, "\""); isAfterSpace = false; EndNode(interpolatedStringExpression); } public virtual void VisitInterpolation(Interpolation interpolation) { StartNode(interpolation); writer.WriteToken(Interpolation.LBrace, "{"); interpolation.Expression.AcceptVisitor(this); if (interpolation.Suffix != null) { writer.WriteToken(Roles.Colon, ":"); writer.WriteInterpolatedText(interpolation.Suffix); } writer.WriteToken(Interpolation.RBrace, "}"); EndNode(interpolation); } public virtual void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText) { StartNode(interpolatedStringText); writer.WriteInterpolatedText(interpolatedStringText.Text); EndNode(interpolatedStringText); } #endregion public virtual void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) { StartNode(sizeOfExpression); WriteKeyword(SizeOfExpression.SizeofKeywordRole); LPar(); Space(policy.SpacesWithinSizeOfParentheses); sizeOfExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinSizeOfParentheses); RPar(); EndNode(sizeOfExpression); } public virtual void VisitStackAllocExpression(StackAllocExpression stackAllocExpression) { StartNode(stackAllocExpression); WriteKeyword(StackAllocExpression.StackallocKeywordRole); stackAllocExpression.Type.AcceptVisitor(this); WriteCommaSeparatedListInBrackets(new[] { stackAllocExpression.CountExpression }); stackAllocExpression.Initializer.AcceptVisitor(this); EndNode(stackAllocExpression); } public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) { StartNode(thisReferenceExpression); WriteKeyword("this", thisReferenceExpression.Role); EndNode(thisReferenceExpression); } public virtual void VisitThrowExpression(ThrowExpression throwExpression) { StartNode(throwExpression); WriteKeyword(ThrowExpression.ThrowKeywordRole); Space(); throwExpression.Expression.AcceptVisitor(this); EndNode(throwExpression); } public virtual void VisitTupleExpression(TupleExpression tupleExpression) { Debug.Assert(tupleExpression.Elements.Count >= 2); StartNode(tupleExpression); LPar(); WriteCommaSeparatedList(tupleExpression.Elements); RPar(); EndNode(tupleExpression); } public virtual void VisitTypeOfExpression(TypeOfExpression typeOfExpression) { StartNode(typeOfExpression); WriteKeyword(TypeOfExpression.TypeofKeywordRole); LPar(); Space(policy.SpacesWithinTypeOfParentheses); typeOfExpression.Type.AcceptVisitor(this); Space(policy.SpacesWithinTypeOfParentheses); RPar(); EndNode(typeOfExpression); } public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) { StartNode(typeReferenceExpression); typeReferenceExpression.Type.AcceptVisitor(this); EndNode(typeReferenceExpression); } public virtual void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) { StartNode(unaryOperatorExpression); UnaryOperatorType opType = unaryOperatorExpression.Operator; var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); if (opType == UnaryOperatorType.Await) { WriteKeyword(opSymbol); Space(); } else if (!IsPostfixOperator(opType) && opSymbol != null) { WriteToken(opSymbol); } unaryOperatorExpression.Expression.AcceptVisitor(this); if (IsPostfixOperator(opType)) { WriteToken(opSymbol); } EndNode(unaryOperatorExpression); } static bool IsPostfixOperator(UnaryOperatorType op) { return op == UnaryOperatorType.PostIncrement || op == UnaryOperatorType.PostDecrement || op == UnaryOperatorType.NullConditional || op == UnaryOperatorType.SuppressNullableWarning; } public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) { StartNode(uncheckedExpression); WriteKeyword(UncheckedExpression.UncheckedKeywordRole); LPar(); Space(policy.SpacesWithinCheckedExpressionParantheses); uncheckedExpression.Expression.AcceptVisitor(this); Space(policy.SpacesWithinCheckedExpressionParantheses); RPar(); EndNode(uncheckedExpression); } public virtual void VisitWithInitializerExpression(WithInitializerExpression withInitializerExpression) { StartNode(withInitializerExpression); withInitializerExpression.Expression.AcceptVisitor(this); WriteKeyword("with", WithInitializerExpression.WithKeywordRole); withInitializerExpression.Initializer.AcceptVisitor(this); EndNode(withInitializerExpression); } #endregion #region Query Expressions public virtual void VisitQueryExpression(QueryExpression queryExpression) { StartNode(queryExpression); if (queryExpression.Role != QueryContinuationClause.PrecedingQueryRole) writer.Indent(); bool first = true; foreach (var clause in queryExpression.Clauses) { if (first) { first = false; } else { if (!(clause is QueryContinuationClause)) { NewLine(); } } clause.AcceptVisitor(this); } if (queryExpression.Role != QueryContinuationClause.PrecedingQueryRole) writer.Unindent(); EndNode(queryExpression); } public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) { StartNode(queryContinuationClause); queryContinuationClause.PrecedingQuery.AcceptVisitor(this); Space(); WriteKeyword(QueryContinuationClause.IntoKeywordRole); Space(); WriteIdentifier(queryContinuationClause.IdentifierToken); EndNode(queryContinuationClause); } public virtual void VisitQueryFromClause(QueryFromClause queryFromClause) { StartNode(queryFromClause); WriteKeyword(QueryFromClause.FromKeywordRole); queryFromClause.Type.AcceptVisitor(this); Space(); WriteIdentifier(queryFromClause.IdentifierToken); Space(); WriteKeyword(QueryFromClause.InKeywordRole); Space(); queryFromClause.Expression.AcceptVisitor(this); EndNode(queryFromClause); } public virtual void VisitQueryLetClause(QueryLetClause queryLetClause) { StartNode(queryLetClause); WriteKeyword(QueryLetClause.LetKeywordRole); Space(); WriteIdentifier(queryLetClause.IdentifierToken); Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); queryLetClause.Expression.AcceptVisitor(this); EndNode(queryLetClause); } public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause) { StartNode(queryWhereClause); WriteKeyword(QueryWhereClause.WhereKeywordRole); Space(); queryWhereClause.Condition.AcceptVisitor(this); EndNode(queryWhereClause); } public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause) { StartNode(queryJoinClause); WriteKeyword(QueryJoinClause.JoinKeywordRole); queryJoinClause.Type.AcceptVisitor(this); Space(); WriteIdentifier(queryJoinClause.JoinIdentifierToken); Space(); WriteKeyword(QueryJoinClause.InKeywordRole); Space(); queryJoinClause.InExpression.AcceptVisitor(this); Space(); WriteKeyword(QueryJoinClause.OnKeywordRole); Space(); queryJoinClause.OnExpression.AcceptVisitor(this); Space(); WriteKeyword(QueryJoinClause.EqualsKeywordRole); Space(); queryJoinClause.EqualsExpression.AcceptVisitor(this); if (queryJoinClause.IsGroupJoin) { Space(); WriteKeyword(QueryJoinClause.IntoKeywordRole); WriteIdentifier(queryJoinClause.IntoIdentifierToken); } EndNode(queryJoinClause); } public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause) { StartNode(queryOrderClause); WriteKeyword(QueryOrderClause.OrderbyKeywordRole); Space(); WriteCommaSeparatedList(queryOrderClause.Orderings); EndNode(queryOrderClause); } public virtual void VisitQueryOrdering(QueryOrdering queryOrdering) { StartNode(queryOrdering); queryOrdering.Expression.AcceptVisitor(this); switch (queryOrdering.Direction) { case QueryOrderingDirection.Ascending: Space(); WriteKeyword(QueryOrdering.AscendingKeywordRole); break; case QueryOrderingDirection.Descending: Space(); WriteKeyword(QueryOrdering.DescendingKeywordRole); break; } EndNode(queryOrdering); } public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause) { StartNode(querySelectClause); WriteKeyword(QuerySelectClause.SelectKeywordRole); Space(); querySelectClause.Expression.AcceptVisitor(this); EndNode(querySelectClause); } public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause) { StartNode(queryGroupClause); WriteKeyword(QueryGroupClause.GroupKeywordRole); Space(); queryGroupClause.Projection.AcceptVisitor(this); Space(); WriteKeyword(QueryGroupClause.ByKeywordRole); Space(); queryGroupClause.Key.AcceptVisitor(this); EndNode(queryGroupClause); } #endregion #region GeneralScope public virtual void VisitAttribute(Attribute attribute) { StartNode(attribute); attribute.Type.AcceptVisitor(this); if (attribute.Arguments.Count != 0 || attribute.HasArgumentList) { Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(attribute.Arguments, policy.SpaceWithinMethodCallParentheses); } EndNode(attribute); } public virtual void VisitAttributeSection(AttributeSection attributeSection) { StartNode(attributeSection); WriteToken(Roles.LBracket); if (!string.IsNullOrEmpty(attributeSection.AttributeTarget)) { WriteKeyword(attributeSection.AttributeTarget, Roles.Identifier); WriteToken(Roles.Colon); Space(); } WriteCommaSeparatedList(attributeSection.Attributes); WriteToken(Roles.RBracket); switch (attributeSection.Parent) { case ParameterDeclaration _: if (attributeSection.NextSibling is AttributeSection) Space(policy.SpaceBetweenParameterAttributeSections); else Space(); break; case TypeParameterDeclaration _: case ComposedType _: Space(); break; default: NewLine(); break; } EndNode(attributeSection); } public virtual void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { StartNode(delegateDeclaration); WriteAttributes(delegateDeclaration.Attributes); WriteModifiers(delegateDeclaration.ModifierTokens); WriteKeyword(Roles.DelegateKeyword); delegateDeclaration.ReturnType.AcceptVisitor(this); Space(); WriteIdentifier(delegateDeclaration.NameToken); WriteTypeParameters(delegateDeclaration.TypeParameters); Space(policy.SpaceBeforeDelegateDeclarationParentheses); WriteCommaSeparatedListInParenthesis(delegateDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); foreach (Constraint constraint in delegateDeclaration.Constraints) { constraint.AcceptVisitor(this); } Semicolon(); EndNode(delegateDeclaration); } public virtual void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { StartNode(namespaceDeclaration); WriteKeyword(Roles.NamespaceKeyword); namespaceDeclaration.NamespaceName.AcceptVisitor(this); OpenBrace(policy.NamespaceBraceStyle); foreach (var member in namespaceDeclaration.Members) { member.AcceptVisitor(this); MaybeNewLinesAfterUsings(member); } CloseBrace(policy.NamespaceBraceStyle); OptionalSemicolon(namespaceDeclaration.LastChild); NewLine(); EndNode(namespaceDeclaration); } public virtual void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { StartNode(typeDeclaration); WriteAttributes(typeDeclaration.Attributes); WriteModifiers(typeDeclaration.ModifierTokens); BraceStyle braceStyle; switch (typeDeclaration.ClassType) { case ClassType.Enum: WriteKeyword(Roles.EnumKeyword); braceStyle = policy.EnumBraceStyle; break; case ClassType.Interface: WriteKeyword(Roles.InterfaceKeyword); braceStyle = policy.InterfaceBraceStyle; break; case ClassType.Struct: WriteKeyword(Roles.StructKeyword); braceStyle = policy.StructBraceStyle; break; case ClassType.RecordClass: WriteKeyword(Roles.RecordKeyword); braceStyle = policy.ClassBraceStyle; break; default: WriteKeyword(Roles.ClassKeyword); braceStyle = policy.ClassBraceStyle; break; } WriteIdentifier(typeDeclaration.NameToken); WriteTypeParameters(typeDeclaration.TypeParameters); if (typeDeclaration.BaseTypes.Any()) { Space(); WriteToken(Roles.Colon); Space(); WriteCommaSeparatedList(typeDeclaration.BaseTypes); } foreach (Constraint constraint in typeDeclaration.Constraints) { constraint.AcceptVisitor(this); } OpenBrace(braceStyle); if (typeDeclaration.ClassType == ClassType.Enum) { bool first = true; AstNode last = null; foreach (var member in typeDeclaration.Members) { if (first) { first = false; } else { Comma(member, noSpaceAfterComma: true); NewLine(); } last = member; member.AcceptVisitor(this); } if (last != null) OptionalComma(last.NextSibling); NewLine(); } else { bool first = true; foreach (var member in typeDeclaration.Members) { if (!first) { for (int i = 0; i < policy.MinimumBlankLinesBetweenMembers; i++) NewLine(); } first = false; member.AcceptVisitor(this); } } CloseBrace(braceStyle); OptionalSemicolon(typeDeclaration.LastChild); NewLine(); EndNode(typeDeclaration); } public virtual void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration) { StartNode(usingAliasDeclaration); WriteKeyword(UsingAliasDeclaration.UsingKeywordRole); WriteIdentifier(usingAliasDeclaration.GetChildByRole(UsingAliasDeclaration.AliasRole)); Space(policy.SpaceAroundEqualityOperator); WriteToken(Roles.Assign); Space(policy.SpaceAroundEqualityOperator); usingAliasDeclaration.Import.AcceptVisitor(this); Semicolon(); EndNode(usingAliasDeclaration); } public virtual void VisitUsingDeclaration(UsingDeclaration usingDeclaration) { StartNode(usingDeclaration); WriteKeyword(UsingDeclaration.UsingKeywordRole); usingDeclaration.Import.AcceptVisitor(this); Semicolon(); EndNode(usingDeclaration); } public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) { StartNode(externAliasDeclaration); WriteKeyword(Roles.ExternKeyword); Space(); WriteKeyword(Roles.AliasKeyword); Space(); WriteIdentifier(externAliasDeclaration.NameToken); Semicolon(); EndNode(externAliasDeclaration); } #endregion #region Statements public virtual void VisitBlockStatement(BlockStatement blockStatement) { WriteBlock(blockStatement, policy.StatementBraceStyle); NewLine(); } /// /// Writes a block statement. /// Similar to VisitBlockStatement() except that: /// 1) it allows customizing the BraceStyle /// 2) it does not write a trailing newline after the '}' (this job is left to the caller) /// protected virtual void WriteBlock(BlockStatement blockStatement, BraceStyle style) { StartNode(blockStatement); OpenBrace(style); foreach (var node in blockStatement.Statements) { node.AcceptVisitor(this); } CloseBrace(style); EndNode(blockStatement); } public virtual void VisitBreakStatement(BreakStatement breakStatement) { StartNode(breakStatement); WriteKeyword("break", BreakStatement.BreakKeywordRole); Semicolon(); EndNode(breakStatement); } public virtual void VisitCheckedStatement(CheckedStatement checkedStatement) { StartNode(checkedStatement); WriteKeyword(CheckedStatement.CheckedKeywordRole); checkedStatement.Body.AcceptVisitor(this); EndNode(checkedStatement); } public virtual void VisitContinueStatement(ContinueStatement continueStatement) { StartNode(continueStatement); WriteKeyword("continue", ContinueStatement.ContinueKeywordRole); Semicolon(); EndNode(continueStatement); } public virtual void VisitDoWhileStatement(DoWhileStatement doWhileStatement) { StartNode(doWhileStatement); WriteKeyword(DoWhileStatement.DoKeywordRole); WriteEmbeddedStatement(doWhileStatement.EmbeddedStatement, policy.WhileNewLinePlacement); WriteKeyword(DoWhileStatement.WhileKeywordRole); Space(policy.SpaceBeforeWhileParentheses); LPar(); Space(policy.SpacesWithinWhileParentheses); doWhileStatement.Condition.AcceptVisitor(this); Space(policy.SpacesWithinWhileParentheses); RPar(); Semicolon(); EndNode(doWhileStatement); } public virtual void VisitEmptyStatement(EmptyStatement emptyStatement) { StartNode(emptyStatement); Semicolon(); EndNode(emptyStatement); } public virtual void VisitExpressionStatement(ExpressionStatement expressionStatement) { StartNode(expressionStatement); expressionStatement.Expression.AcceptVisitor(this); Semicolon(); EndNode(expressionStatement); } public virtual void VisitFixedStatement(FixedStatement fixedStatement) { StartNode(fixedStatement); WriteKeyword(FixedStatement.FixedKeywordRole); Space(policy.SpaceBeforeUsingParentheses); LPar(); Space(policy.SpacesWithinUsingParentheses); fixedStatement.Type.AcceptVisitor(this); Space(); WriteCommaSeparatedList(fixedStatement.Variables); Space(policy.SpacesWithinUsingParentheses); RPar(); WriteEmbeddedStatement(fixedStatement.EmbeddedStatement); EndNode(fixedStatement); } public virtual void VisitForeachStatement(ForeachStatement foreachStatement) { StartNode(foreachStatement); if (foreachStatement.IsAsync) WriteKeyword(ForeachStatement.AwaitRole); WriteKeyword(ForeachStatement.ForeachKeywordRole); Space(policy.SpaceBeforeForeachParentheses); LPar(); Space(policy.SpacesWithinForeachParentheses); foreachStatement.VariableType.AcceptVisitor(this); Space(); foreachStatement.VariableDesignation.AcceptVisitor(this); Space(); WriteKeyword(ForeachStatement.InKeywordRole); Space(); foreachStatement.InExpression.AcceptVisitor(this); Space(policy.SpacesWithinForeachParentheses); RPar(); WriteEmbeddedStatement(foreachStatement.EmbeddedStatement); EndNode(foreachStatement); } public virtual void VisitForStatement(ForStatement forStatement) { StartNode(forStatement); WriteKeyword(ForStatement.ForKeywordRole); Space(policy.SpaceBeforeForParentheses); LPar(); Space(policy.SpacesWithinForParentheses); WriteCommaSeparatedList(forStatement.Initializers); Space(policy.SpaceBeforeForSemicolon); WriteToken(Roles.Semicolon); Space(policy.SpaceAfterForSemicolon); forStatement.Condition.AcceptVisitor(this); Space(policy.SpaceBeforeForSemicolon); WriteToken(Roles.Semicolon); if (forStatement.Iterators.Any()) { Space(policy.SpaceAfterForSemicolon); WriteCommaSeparatedList(forStatement.Iterators); } Space(policy.SpacesWithinForParentheses); RPar(); WriteEmbeddedStatement(forStatement.EmbeddedStatement); EndNode(forStatement); } public virtual void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) { StartNode(gotoCaseStatement); WriteKeyword(GotoCaseStatement.GotoKeywordRole); WriteKeyword(GotoCaseStatement.CaseKeywordRole); Space(); gotoCaseStatement.LabelExpression.AcceptVisitor(this); Semicolon(); EndNode(gotoCaseStatement); } public virtual void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) { StartNode(gotoDefaultStatement); WriteKeyword(GotoDefaultStatement.GotoKeywordRole); WriteKeyword(GotoDefaultStatement.DefaultKeywordRole); Semicolon(); EndNode(gotoDefaultStatement); } public virtual void VisitGotoStatement(GotoStatement gotoStatement) { StartNode(gotoStatement); WriteKeyword(GotoStatement.GotoKeywordRole); WriteIdentifier(gotoStatement.GetChildByRole(Roles.Identifier)); Semicolon(); EndNode(gotoStatement); } public virtual void VisitIfElseStatement(IfElseStatement ifElseStatement) { StartNode(ifElseStatement); WriteKeyword(IfElseStatement.IfKeywordRole); Space(policy.SpaceBeforeIfParentheses); LPar(); Space(policy.SpacesWithinIfParentheses); ifElseStatement.Condition.AcceptVisitor(this); Space(policy.SpacesWithinIfParentheses); RPar(); if (ifElseStatement.FalseStatement.IsNull) { WriteEmbeddedStatement(ifElseStatement.TrueStatement); } else { WriteEmbeddedStatement(ifElseStatement.TrueStatement, policy.ElseNewLinePlacement); WriteKeyword(IfElseStatement.ElseKeywordRole); if (ifElseStatement.FalseStatement is IfElseStatement) { // don't put newline between 'else' and 'if' ifElseStatement.FalseStatement.AcceptVisitor(this); } else { WriteEmbeddedStatement(ifElseStatement.FalseStatement); } } EndNode(ifElseStatement); } public virtual void VisitLabelStatement(LabelStatement labelStatement) { StartNode(labelStatement); WriteIdentifier(labelStatement.GetChildByRole(Roles.Identifier)); WriteToken(Roles.Colon); bool foundLabelledStatement = false; for (AstNode tmp = labelStatement.NextSibling; tmp != null; tmp = tmp.NextSibling) { if (tmp.Role == labelStatement.Role) { foundLabelledStatement = true; } } if (!foundLabelledStatement) { // introduce an EmptyStatement so that the output becomes syntactically valid WriteToken(Roles.Semicolon); } NewLine(); EndNode(labelStatement); } public virtual void VisitLockStatement(LockStatement lockStatement) { StartNode(lockStatement); WriteKeyword(LockStatement.LockKeywordRole); Space(policy.SpaceBeforeLockParentheses); LPar(); Space(policy.SpacesWithinLockParentheses); lockStatement.Expression.AcceptVisitor(this); Space(policy.SpacesWithinLockParentheses); RPar(); WriteEmbeddedStatement(lockStatement.EmbeddedStatement); EndNode(lockStatement); } public virtual void VisitReturnStatement(ReturnStatement returnStatement) { StartNode(returnStatement); WriteKeyword(ReturnStatement.ReturnKeywordRole); if (!returnStatement.Expression.IsNull) { Space(); returnStatement.Expression.AcceptVisitor(this); } Semicolon(); EndNode(returnStatement); } public virtual void VisitSwitchStatement(SwitchStatement switchStatement) { StartNode(switchStatement); WriteKeyword(SwitchStatement.SwitchKeywordRole); Space(policy.SpaceBeforeSwitchParentheses); LPar(); Space(policy.SpacesWithinSwitchParentheses); switchStatement.Expression.AcceptVisitor(this); Space(policy.SpacesWithinSwitchParentheses); RPar(); OpenBrace(policy.StatementBraceStyle); if (!policy.IndentSwitchBody) { writer.Unindent(); } foreach (var section in switchStatement.SwitchSections) { section.AcceptVisitor(this); } if (!policy.IndentSwitchBody) { writer.Indent(); } CloseBrace(policy.StatementBraceStyle); NewLine(); EndNode(switchStatement); } public virtual void VisitSwitchSection(SwitchSection switchSection) { StartNode(switchSection); bool first = true; foreach (var label in switchSection.CaseLabels) { if (!first) { NewLine(); } label.AcceptVisitor(this); first = false; } bool isBlock = switchSection.Statements.Count == 1 && switchSection.Statements.Single() is BlockStatement; if (policy.IndentCaseBody && !isBlock) { writer.Indent(); } if (!isBlock) NewLine(); foreach (var statement in switchSection.Statements) { statement.AcceptVisitor(this); } if (policy.IndentCaseBody && !isBlock) { writer.Unindent(); } EndNode(switchSection); } public virtual void VisitCaseLabel(CaseLabel caseLabel) { StartNode(caseLabel); if (caseLabel.Expression.IsNull) { WriteKeyword(CaseLabel.DefaultKeywordRole); } else { WriteKeyword(CaseLabel.CaseKeywordRole); Space(); caseLabel.Expression.AcceptVisitor(this); } WriteToken(Roles.Colon); EndNode(caseLabel); } public virtual void VisitSwitchExpression(SwitchExpression switchExpression) { StartNode(switchExpression); switchExpression.Expression.AcceptVisitor(this); Space(); WriteKeyword(SwitchExpression.SwitchKeywordRole); OpenBrace(policy.StatementBraceStyle); foreach (AstNode node in switchExpression.SwitchSections) { node.AcceptVisitor(this); Comma(node); NewLine(); } CloseBrace(policy.StatementBraceStyle); EndNode(switchExpression); } public virtual void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection) { StartNode(switchExpressionSection); switchExpressionSection.Pattern.AcceptVisitor(this); Space(); WriteToken(Roles.Arrow); Space(); switchExpressionSection.Body.AcceptVisitor(this); EndNode(switchExpressionSection); } public virtual void VisitThrowStatement(ThrowStatement throwStatement) { StartNode(throwStatement); WriteKeyword(ThrowStatement.ThrowKeywordRole); if (!throwStatement.Expression.IsNull) { Space(); throwStatement.Expression.AcceptVisitor(this); } Semicolon(); EndNode(throwStatement); } public virtual void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { StartNode(tryCatchStatement); WriteKeyword(TryCatchStatement.TryKeywordRole); WriteBlock(tryCatchStatement.TryBlock, policy.StatementBraceStyle); foreach (var catchClause in tryCatchStatement.CatchClauses) { if (policy.CatchNewLinePlacement == NewLinePlacement.SameLine) Space(); else NewLine(); catchClause.AcceptVisitor(this); } if (!tryCatchStatement.FinallyBlock.IsNull) { if (policy.FinallyNewLinePlacement == NewLinePlacement.SameLine) Space(); else NewLine(); WriteKeyword(TryCatchStatement.FinallyKeywordRole); WriteBlock(tryCatchStatement.FinallyBlock, policy.StatementBraceStyle); } NewLine(); EndNode(tryCatchStatement); } public virtual void VisitCatchClause(CatchClause catchClause) { StartNode(catchClause); WriteKeyword(CatchClause.CatchKeywordRole); if (!catchClause.Type.IsNull) { Space(policy.SpaceBeforeCatchParentheses); LPar(); Space(policy.SpacesWithinCatchParentheses); catchClause.Type.AcceptVisitor(this); if (!string.IsNullOrEmpty(catchClause.VariableName)) { Space(); WriteIdentifier(catchClause.VariableNameToken); } Space(policy.SpacesWithinCatchParentheses); RPar(); } if (!catchClause.Condition.IsNull) { Space(); WriteKeyword(CatchClause.WhenKeywordRole); Space(policy.SpaceBeforeIfParentheses); WriteToken(CatchClause.CondLPar); Space(policy.SpacesWithinIfParentheses); catchClause.Condition.AcceptVisitor(this); Space(policy.SpacesWithinIfParentheses); WriteToken(CatchClause.CondRPar); } WriteBlock(catchClause.Body, policy.StatementBraceStyle); EndNode(catchClause); } public virtual void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) { StartNode(uncheckedStatement); WriteKeyword(UncheckedStatement.UncheckedKeywordRole); uncheckedStatement.Body.AcceptVisitor(this); EndNode(uncheckedStatement); } public virtual void VisitUnsafeStatement(UnsafeStatement unsafeStatement) { StartNode(unsafeStatement); WriteKeyword(UnsafeStatement.UnsafeKeywordRole); unsafeStatement.Body.AcceptVisitor(this); EndNode(unsafeStatement); } public virtual void VisitUsingStatement(UsingStatement usingStatement) { StartNode(usingStatement); if (usingStatement.IsAsync) { WriteKeyword(UsingStatement.AwaitRole); } WriteKeyword(UsingStatement.UsingKeywordRole); if (usingStatement.IsEnhanced) { Space(); } else { Space(policy.SpaceBeforeUsingParentheses); LPar(); Space(policy.SpacesWithinUsingParentheses); } usingStatement.ResourceAcquisition.AcceptVisitor(this); if (usingStatement.IsEnhanced) { Semicolon(); } else { Space(policy.SpacesWithinUsingParentheses); RPar(); } if (usingStatement.IsEnhanced) { if (usingStatement.EmbeddedStatement is BlockStatement blockStatement) { StartNode(blockStatement); foreach (var node in blockStatement.Statements) { node.AcceptVisitor(this); } EndNode(blockStatement); } else { usingStatement.EmbeddedStatement.AcceptVisitor(this); } } else { WriteEmbeddedStatement(usingStatement.EmbeddedStatement); } EndNode(usingStatement); } public virtual void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) { StartNode(variableDeclarationStatement); WriteModifiers(variableDeclarationStatement.GetChildrenByRole(VariableDeclarationStatement.ModifierRole)); variableDeclarationStatement.Type.AcceptVisitor(this); Space(); WriteCommaSeparatedList(variableDeclarationStatement.Variables); Semicolon(); EndNode(variableDeclarationStatement); } public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) { StartNode(localFunctionDeclarationStatement); localFunctionDeclarationStatement.Declaration.AcceptVisitor(this); EndNode(localFunctionDeclarationStatement); } public virtual void VisitWhileStatement(WhileStatement whileStatement) { StartNode(whileStatement); WriteKeyword(WhileStatement.WhileKeywordRole); Space(policy.SpaceBeforeWhileParentheses); LPar(); Space(policy.SpacesWithinWhileParentheses); whileStatement.Condition.AcceptVisitor(this); Space(policy.SpacesWithinWhileParentheses); RPar(); WriteEmbeddedStatement(whileStatement.EmbeddedStatement); EndNode(whileStatement); } public virtual void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) { StartNode(yieldBreakStatement); WriteKeyword(YieldBreakStatement.YieldKeywordRole); WriteKeyword(YieldBreakStatement.BreakKeywordRole); Semicolon(); EndNode(yieldBreakStatement); } public virtual void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement) { StartNode(yieldReturnStatement); WriteKeyword(YieldReturnStatement.YieldKeywordRole); WriteKeyword(YieldReturnStatement.ReturnKeywordRole); Space(); yieldReturnStatement.Expression.AcceptVisitor(this); Semicolon(); EndNode(yieldReturnStatement); } #endregion #region TypeMembers public virtual void VisitAccessor(Accessor accessor) { StartNode(accessor); WriteAttributes(accessor.Attributes); WriteModifiers(accessor.ModifierTokens); BraceStyle style = policy.StatementBraceStyle; if (accessor.Role == PropertyDeclaration.GetterRole) { WriteKeyword("get", PropertyDeclaration.GetKeywordRole); style = policy.PropertyGetBraceStyle; } else if (accessor.Role == PropertyDeclaration.SetterRole) { if (accessor.Keyword.Role == PropertyDeclaration.InitKeywordRole) { WriteKeyword("init", PropertyDeclaration.InitKeywordRole); } else { WriteKeyword("set", PropertyDeclaration.SetKeywordRole); } style = policy.PropertySetBraceStyle; } else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) { WriteKeyword("add", CustomEventDeclaration.AddKeywordRole); style = policy.EventAddBraceStyle; } else if (accessor.Role == CustomEventDeclaration.RemoveAccessorRole) { WriteKeyword("remove", CustomEventDeclaration.RemoveKeywordRole); style = policy.EventRemoveBraceStyle; } WriteMethodBody(accessor.Body, style); EndNode(accessor); } public virtual void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { StartNode(constructorDeclaration); WriteAttributes(constructorDeclaration.Attributes); WriteModifiers(constructorDeclaration.ModifierTokens); TypeDeclaration type = constructorDeclaration.Parent as TypeDeclaration; if (type != null && type.Name != constructorDeclaration.Name) WriteIdentifier((Identifier)type.NameToken.Clone()); else WriteIdentifier(constructorDeclaration.NameToken); Space(policy.SpaceBeforeConstructorDeclarationParentheses); WriteCommaSeparatedListInParenthesis(constructorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); if (!constructorDeclaration.Initializer.IsNull) { NewLine(); writer.Indent(); constructorDeclaration.Initializer.AcceptVisitor(this); writer.Unindent(); } WriteMethodBody(constructorDeclaration.Body, policy.ConstructorBraceStyle); EndNode(constructorDeclaration); } public virtual void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) { StartNode(constructorInitializer); WriteToken(Roles.Colon); Space(); if (constructorInitializer.ConstructorInitializerType == ConstructorInitializerType.This) { WriteKeyword(ConstructorInitializer.ThisKeywordRole); } else { WriteKeyword(ConstructorInitializer.BaseKeywordRole); } Space(policy.SpaceBeforeMethodCallParentheses); WriteCommaSeparatedListInParenthesis(constructorInitializer.Arguments, policy.SpaceWithinMethodCallParentheses); EndNode(constructorInitializer); } public virtual void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { StartNode(destructorDeclaration); WriteAttributes(destructorDeclaration.Attributes); WriteModifiers(destructorDeclaration.ModifierTokens); if (destructorDeclaration.ModifierTokens.Any()) { Space(); } WriteToken(DestructorDeclaration.TildeRole); TypeDeclaration type = destructorDeclaration.Parent as TypeDeclaration; if (type != null && type.Name != destructorDeclaration.Name) WriteIdentifier((Identifier)type.NameToken.Clone()); else WriteIdentifier(destructorDeclaration.NameToken); Space(policy.SpaceBeforeConstructorDeclarationParentheses); LPar(); RPar(); WriteMethodBody(destructorDeclaration.Body, policy.DestructorBraceStyle); EndNode(destructorDeclaration); } public virtual void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) { StartNode(enumMemberDeclaration); WriteAttributes(enumMemberDeclaration.Attributes); WriteModifiers(enumMemberDeclaration.ModifierTokens); WriteIdentifier(enumMemberDeclaration.NameToken); if (!enumMemberDeclaration.Initializer.IsNull) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); enumMemberDeclaration.Initializer.AcceptVisitor(this); } EndNode(enumMemberDeclaration); } public virtual void VisitEventDeclaration(EventDeclaration eventDeclaration) { StartNode(eventDeclaration); WriteAttributes(eventDeclaration.Attributes); WriteModifiers(eventDeclaration.ModifierTokens); WriteKeyword(EventDeclaration.EventKeywordRole); eventDeclaration.ReturnType.AcceptVisitor(this); Space(); WriteCommaSeparatedList(eventDeclaration.Variables); Semicolon(); EndNode(eventDeclaration); } public virtual void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration) { StartNode(customEventDeclaration); WriteAttributes(customEventDeclaration.Attributes); WriteModifiers(customEventDeclaration.ModifierTokens); WriteKeyword(CustomEventDeclaration.EventKeywordRole); customEventDeclaration.ReturnType.AcceptVisitor(this); Space(); WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType); WriteIdentifier(customEventDeclaration.NameToken); OpenBrace(policy.EventBraceStyle); // output add/remove in their original order foreach (AstNode node in customEventDeclaration.Children) { if (node.Role == CustomEventDeclaration.AddAccessorRole || node.Role == CustomEventDeclaration.RemoveAccessorRole) { node.AcceptVisitor(this); } } CloseBrace(policy.EventBraceStyle); NewLine(); EndNode(customEventDeclaration); } public virtual void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { StartNode(fieldDeclaration); WriteAttributes(fieldDeclaration.Attributes); WriteModifiers(fieldDeclaration.ModifierTokens); fieldDeclaration.ReturnType.AcceptVisitor(this); Space(); WriteCommaSeparatedList(fieldDeclaration.Variables); Semicolon(); EndNode(fieldDeclaration); } public virtual void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) { StartNode(fixedFieldDeclaration); WriteAttributes(fixedFieldDeclaration.Attributes); WriteModifiers(fixedFieldDeclaration.ModifierTokens); WriteKeyword(FixedFieldDeclaration.FixedKeywordRole); Space(); fixedFieldDeclaration.ReturnType.AcceptVisitor(this); Space(); WriteCommaSeparatedList(fixedFieldDeclaration.Variables); Semicolon(); EndNode(fixedFieldDeclaration); } public virtual void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) { StartNode(fixedVariableInitializer); WriteIdentifier(fixedVariableInitializer.NameToken); if (!fixedVariableInitializer.CountExpression.IsNull) { WriteToken(Roles.LBracket); Space(policy.SpacesWithinBrackets); fixedVariableInitializer.CountExpression.AcceptVisitor(this); Space(policy.SpacesWithinBrackets); WriteToken(Roles.RBracket); } EndNode(fixedVariableInitializer); } public virtual void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { StartNode(indexerDeclaration); WriteAttributes(indexerDeclaration.Attributes); WriteModifiers(indexerDeclaration.ModifierTokens); indexerDeclaration.ReturnType.AcceptVisitor(this); Space(); WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); WriteKeyword(IndexerDeclaration.ThisKeywordRole); Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInBrackets(indexerDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); if (indexerDeclaration.ExpressionBody.IsNull) { bool isSingleLine = (indexerDeclaration.Getter.IsNull || indexerDeclaration.Getter.Body.IsNull) && (indexerDeclaration.Setter.IsNull || indexerDeclaration.Setter.Body.IsNull); OpenBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, newLine: !isSingleLine); if (isSingleLine) Space(); // output get/set in their original order foreach (AstNode node in indexerDeclaration.Children) { if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { node.AcceptVisitor(this); } } CloseBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, unindent: !isSingleLine); NewLine(); } else { Space(); WriteToken(Roles.Arrow); Space(); indexerDeclaration.ExpressionBody.AcceptVisitor(this); Semicolon(); } EndNode(indexerDeclaration); } public virtual void VisitMethodDeclaration(MethodDeclaration methodDeclaration) { StartNode(methodDeclaration); WriteAttributes(methodDeclaration.Attributes); WriteModifiers(methodDeclaration.ModifierTokens); methodDeclaration.ReturnType.AcceptVisitor(this); Space(); WritePrivateImplementationType(methodDeclaration.PrivateImplementationType); WriteIdentifier(methodDeclaration.NameToken); WriteTypeParameters(methodDeclaration.TypeParameters); Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInParenthesis(methodDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); foreach (Constraint constraint in methodDeclaration.Constraints) { constraint.AcceptVisitor(this); } WriteMethodBody(methodDeclaration.Body, policy.MethodBraceStyle); EndNode(methodDeclaration); } public virtual void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { StartNode(operatorDeclaration); WriteAttributes(operatorDeclaration.Attributes); WriteModifiers(operatorDeclaration.ModifierTokens); if (operatorDeclaration.OperatorType == OperatorType.Explicit) { WriteKeyword(OperatorDeclaration.ExplicitRole); } else if (operatorDeclaration.OperatorType == OperatorType.Implicit) { WriteKeyword(OperatorDeclaration.ImplicitRole); } else { operatorDeclaration.ReturnType.AcceptVisitor(this); } WriteKeyword(OperatorDeclaration.OperatorKeywordRole); Space(); if (operatorDeclaration.OperatorType == OperatorType.Explicit || operatorDeclaration.OperatorType == OperatorType.Implicit) { operatorDeclaration.ReturnType.AcceptVisitor(this); } else { WriteToken(OperatorDeclaration.GetToken(operatorDeclaration.OperatorType), OperatorDeclaration.GetRole(operatorDeclaration.OperatorType)); } Space(policy.SpaceBeforeMethodDeclarationParentheses); WriteCommaSeparatedListInParenthesis(operatorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); WriteMethodBody(operatorDeclaration.Body, policy.MethodBraceStyle); EndNode(operatorDeclaration); } public virtual void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) { StartNode(parameterDeclaration); WriteAttributes(parameterDeclaration.Attributes); if (parameterDeclaration.HasThisModifier) { WriteKeyword(ParameterDeclaration.ThisModifierRole); Space(); } switch (parameterDeclaration.ParameterModifier) { case ParameterModifier.Ref: WriteKeyword(ParameterDeclaration.RefModifierRole); Space(); break; case ParameterModifier.Out: WriteKeyword(ParameterDeclaration.OutModifierRole); Space(); break; case ParameterModifier.Params: WriteKeyword(ParameterDeclaration.ParamsModifierRole); Space(); break; case ParameterModifier.In: WriteKeyword(ParameterDeclaration.InModifierRole); Space(); break; } parameterDeclaration.Type.AcceptVisitor(this); if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) { Space(); } if (!string.IsNullOrEmpty(parameterDeclaration.Name)) { WriteIdentifier(parameterDeclaration.NameToken); } if (!parameterDeclaration.DefaultExpression.IsNull) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); parameterDeclaration.DefaultExpression.AcceptVisitor(this); } EndNode(parameterDeclaration); } public virtual void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { StartNode(propertyDeclaration); WriteAttributes(propertyDeclaration.Attributes); WriteModifiers(propertyDeclaration.ModifierTokens); propertyDeclaration.ReturnType.AcceptVisitor(this); Space(); WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); WriteIdentifier(propertyDeclaration.NameToken); if (propertyDeclaration.ExpressionBody.IsNull) { bool isSingleLine = (propertyDeclaration.Getter.IsNull || propertyDeclaration.Getter.Body.IsNull) && (propertyDeclaration.Setter.IsNull || propertyDeclaration.Setter.Body.IsNull); OpenBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, newLine: !isSingleLine); if (isSingleLine) Space(); // output get/set in their original order foreach (AstNode node in propertyDeclaration.Children) { if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { node.AcceptVisitor(this); } } CloseBrace(isSingleLine ? BraceStyle.EndOfLine : policy.PropertyBraceStyle, unindent: !isSingleLine); if (!propertyDeclaration.Initializer.IsNull) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); propertyDeclaration.Initializer.AcceptVisitor(this); Semicolon(); } NewLine(); } else { Space(); WriteToken(Roles.Arrow); Space(); propertyDeclaration.ExpressionBody.AcceptVisitor(this); Semicolon(); } EndNode(propertyDeclaration); } #endregion #region Other nodes public virtual void VisitVariableInitializer(VariableInitializer variableInitializer) { StartNode(variableInitializer); WriteIdentifier(variableInitializer.NameToken); if (!variableInitializer.Initializer.IsNull) { Space(policy.SpaceAroundAssignment); WriteToken(Roles.Assign); Space(policy.SpaceAroundAssignment); variableInitializer.Initializer.AcceptVisitor(this); } EndNode(variableInitializer); } void MaybeNewLinesAfterUsings(AstNode node) { var nextSibling = node.NextSibling; if ((node is UsingDeclaration || node is UsingAliasDeclaration) && !(nextSibling is UsingDeclaration || nextSibling is UsingAliasDeclaration)) { for (int i = 0; i < policy.MinimumBlankLinesAfterUsings; i++) NewLine(); } } public virtual void VisitSyntaxTree(SyntaxTree syntaxTree) { // don't do node tracking as we visit all children directly foreach (AstNode node in syntaxTree.Children) { node.AcceptVisitor(this); MaybeNewLinesAfterUsings(node); } } public virtual void VisitSimpleType(SimpleType simpleType) { StartNode(simpleType); WriteIdentifier(simpleType.IdentifierToken); WriteTypeArguments(simpleType.TypeArguments); EndNode(simpleType); } public virtual void VisitMemberType(MemberType memberType) { StartNode(memberType); memberType.Target.AcceptVisitor(this); if (memberType.IsDoubleColon) { WriteToken(Roles.DoubleColon); } else { WriteToken(Roles.Dot); } WriteIdentifier(memberType.MemberNameToken); WriteTypeArguments(memberType.TypeArguments); EndNode(memberType); } public virtual void VisitTupleType(TupleAstType tupleType) { Debug.Assert(tupleType.Elements.Count >= 2); StartNode(tupleType); LPar(); WriteCommaSeparatedList(tupleType.Elements); RPar(); EndNode(tupleType); } public virtual void VisitTupleTypeElement(TupleTypeElement tupleTypeElement) { StartNode(tupleTypeElement); tupleTypeElement.Type.AcceptVisitor(this); if (!tupleTypeElement.NameToken.IsNull) { Space(); tupleTypeElement.NameToken.AcceptVisitor(this); } EndNode(tupleTypeElement); } public virtual void VisitFunctionPointerType(FunctionPointerAstType functionPointerType) { StartNode(functionPointerType); WriteKeyword(Roles.DelegateKeyword); WriteToken(FunctionPointerAstType.PointerRole); if (functionPointerType.HasUnmanagedCallingConvention) { Space(); WriteKeyword("unmanaged"); } if (functionPointerType.CallingConventions.Any()) { WriteToken(Roles.LBracket); WriteCommaSeparatedList(functionPointerType.CallingConventions); WriteToken(Roles.RBracket); } WriteToken(Roles.LChevron); WriteCommaSeparatedList( functionPointerType.Parameters.Concat(new[] { functionPointerType.ReturnType })); WriteToken(Roles.RChevron); EndNode(functionPointerType); } public virtual void VisitComposedType(ComposedType composedType) { StartNode(composedType); if (composedType.Attributes.Any()) { foreach (var attr in composedType.Attributes) { attr.AcceptVisitor(this); } } if (composedType.HasRefSpecifier) { WriteKeyword(ComposedType.RefRole); } if (composedType.HasReadOnlySpecifier) { WriteKeyword(ComposedType.ReadonlyRole); } composedType.BaseType.AcceptVisitor(this); if (composedType.HasNullableSpecifier) { WriteToken(ComposedType.NullableRole); } for (int i = 0; i < composedType.PointerRank; i++) { WriteToken(ComposedType.PointerRole); } foreach (var node in composedType.ArraySpecifiers) { node.AcceptVisitor(this); } EndNode(composedType); } public virtual void VisitArraySpecifier(ArraySpecifier arraySpecifier) { StartNode(arraySpecifier); WriteToken(Roles.LBracket); foreach (var comma in arraySpecifier.GetChildrenByRole(Roles.Comma)) { writer.WriteToken(Roles.Comma, ","); } WriteToken(Roles.RBracket); EndNode(arraySpecifier); } public virtual void VisitPrimitiveType(PrimitiveType primitiveType) { StartNode(primitiveType); writer.WritePrimitiveType(primitiveType.Keyword); EndNode(primitiveType); } public virtual void VisitSingleVariableDesignation(SingleVariableDesignation singleVariableDesignation) { StartNode(singleVariableDesignation); writer.WriteIdentifier(singleVariableDesignation.IdentifierToken); EndNode(singleVariableDesignation); } public virtual void VisitParenthesizedVariableDesignation(ParenthesizedVariableDesignation parenthesizedVariableDesignation) { StartNode(parenthesizedVariableDesignation); LPar(); WriteCommaSeparatedList(parenthesizedVariableDesignation.VariableDesignations); RPar(); EndNode(parenthesizedVariableDesignation); } public virtual void VisitComment(Comment comment) { writer.StartNode(comment); writer.WriteComment(comment.CommentType, comment.Content); writer.EndNode(comment); } public virtual void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) { writer.StartNode(preProcessorDirective); writer.WritePreProcessorDirective(preProcessorDirective.Type, preProcessorDirective.Argument); writer.EndNode(preProcessorDirective); } public virtual void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) { StartNode(typeParameterDeclaration); WriteAttributes(typeParameterDeclaration.Attributes); switch (typeParameterDeclaration.Variance) { case VarianceModifier.Invariant: break; case VarianceModifier.Covariant: WriteKeyword(TypeParameterDeclaration.OutVarianceKeywordRole); break; case VarianceModifier.Contravariant: WriteKeyword(TypeParameterDeclaration.InVarianceKeywordRole); break; default: throw new NotSupportedException("Invalid value for VarianceModifier"); } WriteIdentifier(typeParameterDeclaration.NameToken); EndNode(typeParameterDeclaration); } public virtual void VisitConstraint(Constraint constraint) { StartNode(constraint); Space(); WriteKeyword(Roles.WhereKeyword); constraint.TypeParameter.AcceptVisitor(this); Space(); WriteToken(Roles.Colon); Space(); WriteCommaSeparatedList(constraint.BaseTypes); EndNode(constraint); } public virtual void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) { CSharpModifierToken mod = cSharpTokenNode as CSharpModifierToken; if (mod != null) { // ITokenWriter assumes that each node processed between a // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. WriteKeyword(CSharpModifierToken.GetModifierName(mod.Modifier), cSharpTokenNode.Role); } else { throw new NotSupportedException("Should never visit individual tokens"); } } public virtual void VisitIdentifier(Identifier identifier) { // Do not call StartNode and EndNode for Identifier, because they are handled by the ITokenWriter. // ITokenWriter assumes that each node processed between a // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. WriteIdentifier(identifier); } void IAstVisitor.VisitNullNode(AstNode nullNode) { } void IAstVisitor.VisitErrorNode(AstNode errorNode) { StartNode(errorNode); EndNode(errorNode); } #endregion #region Pattern Nodes public virtual void VisitPatternPlaceholder(AstNode placeholder, Pattern pattern) { StartNode(placeholder); VisitNodeInPattern(pattern); EndNode(placeholder); } void VisitAnyNode(AnyNode anyNode) { if (!string.IsNullOrEmpty(anyNode.GroupName)) { WriteIdentifier(anyNode.GroupName); WriteToken(Roles.Colon); } } void VisitBackreference(Backreference backreference) { WriteKeyword("backreference"); LPar(); WriteIdentifier(backreference.ReferencedGroupName); RPar(); } void VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference) { WriteKeyword("identifierBackreference"); LPar(); WriteIdentifier(identifierExpressionBackreference.ReferencedGroupName); RPar(); } void VisitChoice(Choice choice) { WriteKeyword("choice"); Space(); LPar(); NewLine(); writer.Indent(); foreach (INode alternative in choice) { VisitNodeInPattern(alternative); if (alternative != choice.Last()) { WriteToken(Roles.Comma); } NewLine(); } writer.Unindent(); RPar(); } void VisitNamedNode(NamedNode namedNode) { if (!string.IsNullOrEmpty(namedNode.GroupName)) { WriteIdentifier(namedNode.GroupName); WriteToken(Roles.Colon); } VisitNodeInPattern(namedNode.ChildNode); } void VisitRepeat(Repeat repeat) { WriteKeyword("repeat"); LPar(); if (repeat.MinCount != 0 || repeat.MaxCount != int.MaxValue) { WriteIdentifier(repeat.MinCount.ToString()); WriteToken(Roles.Comma); WriteIdentifier(repeat.MaxCount.ToString()); WriteToken(Roles.Comma); } VisitNodeInPattern(repeat.ChildNode); RPar(); } void VisitOptionalNode(OptionalNode optionalNode) { WriteKeyword("optional"); LPar(); VisitNodeInPattern(optionalNode.ChildNode); RPar(); } void VisitNodeInPattern(INode childNode) { if (childNode is AstNode) { ((AstNode)childNode).AcceptVisitor(this); } else if (childNode is IdentifierExpressionBackreference) { VisitIdentifierExpressionBackreference((IdentifierExpressionBackreference)childNode); } else if (childNode is Choice) { VisitChoice((Choice)childNode); } else if (childNode is AnyNode) { VisitAnyNode((AnyNode)childNode); } else if (childNode is Backreference) { VisitBackreference((Backreference)childNode); } else if (childNode is NamedNode) { VisitNamedNode((NamedNode)childNode); } else if (childNode is OptionalNode) { VisitOptionalNode((OptionalNode)childNode); } else if (childNode is Repeat) { VisitRepeat((Repeat)childNode); } else { writer.WritePrimitiveValue(childNode); } } #endregion #region Documentation Reference public virtual void VisitDocumentationReference(DocumentationReference documentationReference) { StartNode(documentationReference); if (!documentationReference.DeclaringType.IsNull) { documentationReference.DeclaringType.AcceptVisitor(this); if (documentationReference.SymbolKind != SymbolKind.TypeDefinition) { WriteToken(Roles.Dot); } } switch (documentationReference.SymbolKind) { case SymbolKind.TypeDefinition: // we already printed the DeclaringType break; case SymbolKind.Indexer: WriteKeyword(IndexerDeclaration.ThisKeywordRole); break; case SymbolKind.Operator: var opType = documentationReference.OperatorType; if (opType == OperatorType.Explicit) { WriteKeyword(OperatorDeclaration.ExplicitRole); } else if (opType == OperatorType.Implicit) { WriteKeyword(OperatorDeclaration.ImplicitRole); } WriteKeyword(OperatorDeclaration.OperatorKeywordRole); Space(); if (opType == OperatorType.Explicit || opType == OperatorType.Implicit) { documentationReference.ConversionOperatorReturnType.AcceptVisitor(this); } else { WriteToken(OperatorDeclaration.GetToken(opType), OperatorDeclaration.GetRole(opType)); } break; default: WriteIdentifier(documentationReference.GetChildByRole(Roles.Identifier)); break; } WriteTypeArguments(documentationReference.TypeArguments); if (documentationReference.HasParameterList) { Space(policy.SpaceBeforeMethodDeclarationParentheses); if (documentationReference.SymbolKind == SymbolKind.Indexer) { WriteCommaSeparatedListInBrackets(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); } else { WriteCommaSeparatedListInParenthesis(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); } } EndNode(documentationReference); } #endregion /// /// Converts special characters to escape sequences within the given string. /// public static string ConvertString(string text) { return TextWriterTokenWriter.ConvertString(text); } } }