File: Syntax\SyntaxNodeTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Syntax\Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
using InternalSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class SyntaxNodeTests
    {
        [Fact]
        [WorkItem(565382, "https://developercommunity.visualstudio.com/content/problem/565382/compiling-causes-a-stack-overflow-error.html")]
        public void TestLargeFluentCallWithDirective()
        {
            var builder = new StringBuilder();
            builder.AppendLine(
    @"
class C {
    C M(string x) { return this; }
    void M2() {
        new C()
#region Region
");
            for (int i = 0; i < 20000; i++)
            {
                builder.AppendLine(@"            .M(""test"")");
            }
            builder.AppendLine(
               @"            .M(""test"");
#endregion
    }
}");
 
            var tree = SyntaxFactory.ParseSyntaxTree(builder.ToString());
            var directives = tree.GetRoot().GetDirectives();
            Assert.Equal(2, directives.Count);
        }
 
        [Fact]
        public void TestQualifiedNameSyntaxWith()
        {
            // this is just a test to prove that at least one generate With method exists and functions correctly. :-)
            var qname = (QualifiedNameSyntax)SyntaxFactory.ParseName("A.B");
            var qname2 = qname.WithRight(SyntaxFactory.IdentifierName("C"));
            var text = qname2.ToString();
            Assert.Equal("A.C", text);
        }
 
        [WorkItem(9229, "DevDiv_Projects/Roslyn")]
        [Fact]
        public void TestAddBaseListTypes()
        {
            var cls = SyntaxFactory.ParseCompilationUnit("class C { }").Members[0] as ClassDeclarationSyntax;
            var cls2 = cls.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("B")));
        }
 
        [Fact]
        public void TestChildNodes()
        {
            var text = "m(a,b,c)";
            var expression = SyntaxFactory.ParseExpression(text);
 
            var nodes = expression.ChildNodes().ToList();
            Assert.Equal(2, nodes.Count);
            Assert.Equal(SyntaxKind.IdentifierName, nodes[0].Kind());
            Assert.Equal(SyntaxKind.ArgumentList, nodes[1].Kind());
        }
 
        [Fact]
        public void TestAncestors()
        {
            var text = "a + (b - (c * (d / e)))";
            var expression = SyntaxFactory.ParseExpression(text);
            var e = expression.DescendantNodes().OfType<IdentifierNameSyntax>().First(n => n.Identifier.Text == "e");
 
            var nodes = e.Ancestors().ToList();
            Assert.Equal(7, nodes.Count);
            Assert.Equal(SyntaxKind.DivideExpression, nodes[0].Kind());
            Assert.Equal(SyntaxKind.ParenthesizedExpression, nodes[1].Kind());
            Assert.Equal(SyntaxKind.MultiplyExpression, nodes[2].Kind());
            Assert.Equal(SyntaxKind.ParenthesizedExpression, nodes[3].Kind());
            Assert.Equal(SyntaxKind.SubtractExpression, nodes[4].Kind());
            Assert.Equal(SyntaxKind.ParenthesizedExpression, nodes[5].Kind());
            Assert.Equal(SyntaxKind.AddExpression, nodes[6].Kind());
        }
 
        [Fact]
        public void TestAncestorsAndSelf()
        {
            var text = "a + (b - (c * (d / e)))";
            var expression = SyntaxFactory.ParseExpression(text);
            var e = expression.DescendantNodes().OfType<IdentifierNameSyntax>().First(n => n.Identifier.Text == "e");
 
            var nodes = e.AncestorsAndSelf().ToList();
            Assert.Equal(8, nodes.Count);
            Assert.Equal(SyntaxKind.IdentifierName, nodes[0].Kind());
            Assert.Equal(SyntaxKind.DivideExpression, nodes[1].Kind());
            Assert.Equal(SyntaxKind.ParenthesizedExpression, nodes[2].Kind());
            Assert.Equal(SyntaxKind.MultiplyExpression, nodes[3].Kind());
            Assert.Equal(SyntaxKind.ParenthesizedExpression, nodes[4].Kind());
            Assert.Equal(SyntaxKind.SubtractExpression, nodes[5].Kind());
            Assert.Equal(SyntaxKind.ParenthesizedExpression, nodes[6].Kind());
            Assert.Equal(SyntaxKind.AddExpression, nodes[7].Kind());
        }
 
        [Fact]
        public void TestFirstAncestorOrSelf()
        {
            var text = "a + (b - (c * (d / e)))";
            var expression = SyntaxFactory.ParseExpression(text);
            var e = expression.DescendantNodes().OfType<IdentifierNameSyntax>().First(n => n.Identifier.Text == "e");
 
            var firstParens = e.FirstAncestorOrSelf<ExpressionSyntax>(n => n.Kind() == SyntaxKind.ParenthesizedExpression);
            Assert.NotNull(firstParens);
            Assert.Equal("(d / e)", firstParens.ToString());
        }
 
        [Fact]
        public void TestDescendantNodes()
        {
            var text = "#if true\r\n  return true;";
            var statement = SyntaxFactory.ParseStatement(text);
 
            var nodes = statement.DescendantNodes().ToList();
            Assert.Equal(1, nodes.Count);
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[0].Kind());
 
            nodes = statement.DescendantNodes(descendIntoTrivia: true).ToList();
            Assert.Equal(3, nodes.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodes[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[1].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[2].Kind());
 
            nodes = statement.DescendantNodes(n => n is StatementSyntax).ToList();
            Assert.Equal(1, nodes.Count);
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[0].Kind());
 
            nodes = statement.DescendantNodes(n => n is StatementSyntax, descendIntoTrivia: true).ToList();
            Assert.Equal(2, nodes.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodes[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[1].Kind());
 
            // all over again with spans
            nodes = statement.DescendantNodes(statement.FullSpan).ToList();
            Assert.Equal(1, nodes.Count);
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[0].Kind());
 
            nodes = statement.DescendantNodes(statement.FullSpan, descendIntoTrivia: true).ToList();
            Assert.Equal(3, nodes.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodes[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[1].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[2].Kind());
 
            nodes = statement.DescendantNodes(statement.FullSpan, n => n is StatementSyntax).ToList();
            Assert.Equal(1, nodes.Count);
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[0].Kind());
 
            nodes = statement.DescendantNodes(statement.FullSpan, n => n is StatementSyntax, descendIntoTrivia: true).ToList();
            Assert.Equal(2, nodes.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodes[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[1].Kind());
        }
 
        [Fact]
        public void TestDescendantNodesAndSelf()
        {
            var text = "#if true\r\n  return true;";
            var statement = SyntaxFactory.ParseStatement(text);
 
            var nodes = statement.DescendantNodesAndSelf().ToList();
            Assert.Equal(2, nodes.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodes[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[1].Kind());
 
            nodes = statement.DescendantNodesAndSelf(descendIntoTrivia: true).ToList();
            Assert.Equal(4, nodes.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodes[0].Kind());
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodes[1].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[2].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[3].Kind());
 
            nodes = statement.DescendantNodesAndSelf(n => n is StatementSyntax).ToList();
            Assert.Equal(2, nodes.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodes[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[1].Kind());
 
            nodes = statement.DescendantNodesAndSelf(n => n is StatementSyntax, descendIntoTrivia: true).ToList();
            Assert.Equal(3, nodes.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodes[0].Kind());
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodes[1].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[2].Kind());
 
            // all over again with spans
            nodes = statement.DescendantNodesAndSelf(statement.FullSpan).ToList();
            Assert.Equal(2, nodes.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodes[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[1].Kind());
 
            nodes = statement.DescendantNodesAndSelf(statement.FullSpan, descendIntoTrivia: true).ToList();
            Assert.Equal(4, nodes.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodes[0].Kind());
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodes[1].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[2].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[3].Kind());
 
            nodes = statement.DescendantNodesAndSelf(statement.FullSpan, n => n is StatementSyntax).ToList();
            Assert.Equal(2, nodes.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodes[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[1].Kind());
 
            nodes = statement.DescendantNodesAndSelf(statement.FullSpan, n => n is StatementSyntax, descendIntoTrivia: true).ToList();
            Assert.Equal(3, nodes.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodes[0].Kind());
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodes[1].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodes[2].Kind());
        }
 
        [Fact]
        public void TestDescendantNodesAndTokens()
        {
            var text = "#if true\r\n  return true;";
            var statement = SyntaxFactory.ParseStatement(text);
 
            var nodesAndTokens = statement.DescendantNodesAndTokens().ToList();
            Assert.Equal(4, nodesAndTokens.Count);
            Assert.Equal(SyntaxKind.ReturnKeyword, nodesAndTokens[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodesAndTokens[1].Kind());
            Assert.Equal(SyntaxKind.TrueKeyword, nodesAndTokens[2].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, nodesAndTokens[3].Kind());
 
            nodesAndTokens = statement.DescendantNodesAndTokens(descendIntoTrivia: true).ToList();
            Assert.Equal(10, nodesAndTokens.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodesAndTokens[0].Kind());
            Assert.Equal(SyntaxKind.HashToken, nodesAndTokens[1].Kind());
            Assert.Equal(SyntaxKind.IfKeyword, nodesAndTokens[2].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodesAndTokens[3].Kind());
            Assert.Equal(SyntaxKind.TrueKeyword, nodesAndTokens[4].Kind());
            Assert.Equal(SyntaxKind.EndOfDirectiveToken, nodesAndTokens[5].Kind());
            Assert.Equal(SyntaxKind.ReturnKeyword, nodesAndTokens[6].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodesAndTokens[7].Kind());
            Assert.Equal(SyntaxKind.TrueKeyword, nodesAndTokens[8].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, nodesAndTokens[9].Kind());
 
            // with span
            nodesAndTokens = statement.DescendantNodesAndTokens(statement.FullSpan).ToList();
            Assert.Equal(4, nodesAndTokens.Count);
            Assert.Equal(SyntaxKind.ReturnKeyword, nodesAndTokens[0].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodesAndTokens[1].Kind());
            Assert.Equal(SyntaxKind.TrueKeyword, nodesAndTokens[2].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, nodesAndTokens[3].Kind());
        }
 
        [Fact]
        public void TestDescendantNodesAndTokensAndSelf()
        {
            var text = "#if true\r\n  return true;";
            var statement = SyntaxFactory.ParseStatement(text);
 
            var nodesAndTokens = statement.DescendantNodesAndTokensAndSelf().ToList();
            Assert.Equal(5, nodesAndTokens.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodesAndTokens[0].Kind());
            Assert.Equal(SyntaxKind.ReturnKeyword, nodesAndTokens[1].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodesAndTokens[2].Kind());
            Assert.Equal(SyntaxKind.TrueKeyword, nodesAndTokens[3].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, nodesAndTokens[4].Kind());
 
            nodesAndTokens = statement.DescendantNodesAndTokensAndSelf(descendIntoTrivia: true).ToList();
            Assert.Equal(11, nodesAndTokens.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodesAndTokens[0].Kind());
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, nodesAndTokens[1].Kind());
            Assert.Equal(SyntaxKind.HashToken, nodesAndTokens[2].Kind());
            Assert.Equal(SyntaxKind.IfKeyword, nodesAndTokens[3].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodesAndTokens[4].Kind());
            Assert.Equal(SyntaxKind.TrueKeyword, nodesAndTokens[5].Kind());
            Assert.Equal(SyntaxKind.EndOfDirectiveToken, nodesAndTokens[6].Kind());
            Assert.Equal(SyntaxKind.ReturnKeyword, nodesAndTokens[7].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodesAndTokens[8].Kind());
            Assert.Equal(SyntaxKind.TrueKeyword, nodesAndTokens[9].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, nodesAndTokens[10].Kind());
 
            // with span
            nodesAndTokens = statement.DescendantNodesAndTokensAndSelf(statement.FullSpan).ToList();
            Assert.Equal(5, nodesAndTokens.Count);
            Assert.Equal(SyntaxKind.ReturnStatement, nodesAndTokens[0].Kind());
            Assert.Equal(SyntaxKind.ReturnKeyword, nodesAndTokens[1].Kind());
            Assert.Equal(SyntaxKind.TrueLiteralExpression, nodesAndTokens[2].Kind());
            Assert.Equal(SyntaxKind.TrueKeyword, nodesAndTokens[3].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, nodesAndTokens[4].Kind());
        }
 
        [Fact]
        public void TestDescendantNodesAndTokensAndSelfForEmptyCompilationUnit()
        {
            var text = "";
            var cu = SyntaxFactory.ParseCompilationUnit(text);
            var nodesAndTokens = cu.DescendantNodesAndTokensAndSelf().ToList();
            Assert.Equal(2, nodesAndTokens.Count);
            Assert.Equal(SyntaxKind.CompilationUnit, nodesAndTokens[0].Kind());
            Assert.Equal(SyntaxKind.EndOfFileToken, nodesAndTokens[1].Kind());
        }
 
        [Fact]
        public void TestDescendantNodesAndTokensAndSelfForDocumentationComment()
        {
            var text = "/// Goo\r\n x";
            var expr = SyntaxFactory.ParseExpression(text);
 
            var nodesAndTokens = expr.DescendantNodesAndTokensAndSelf(descendIntoTrivia: true).ToList();
            Assert.Equal(7, nodesAndTokens.Count);
            Assert.Equal(SyntaxKind.IdentifierName, nodesAndTokens[0].Kind());
            Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, nodesAndTokens[1].Kind());
            Assert.Equal(SyntaxKind.XmlText, nodesAndTokens[2].Kind());
            Assert.Equal(SyntaxKind.XmlTextLiteralToken, nodesAndTokens[3].Kind());
            Assert.Equal(SyntaxKind.XmlTextLiteralNewLineToken, nodesAndTokens[4].Kind());
            Assert.Equal(SyntaxKind.EndOfDocumentationCommentToken, nodesAndTokens[5].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, nodesAndTokens[6].Kind());
        }
 
        [Fact]
        public void TestGetAllDirectivesUsingDescendantNodes()
        {
            var text = "#if false\r\n  eat a sandwich\r\n#endif\r\n x";
            var expr = SyntaxFactory.ParseExpression(text);
 
            var directives = expr.GetDirectives();
            var descendantDirectives = expr.DescendantNodesAndSelf(n => n.ContainsDirectives, descendIntoTrivia: true).OfType<DirectiveTriviaSyntax>().ToList();
 
            Assert.Equal(directives.Count, descendantDirectives.Count);
            for (int i = 0; i < directives.Count; i++)
            {
                Assert.Equal(directives[i], descendantDirectives[i]);
            }
        }
 
        [Fact]
        public void TestContainsDirective()
        {
            // Empty compilation unit shouldn't have any directives in it.
            for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++)
                Assert.False(SyntaxFactory.ParseCompilationUnit("").ContainsDirective(kind));
 
            // basic file shouldn't have any directives in it.
            for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++)
                Assert.False(SyntaxFactory.ParseCompilationUnit("namespace N { }").ContainsDirective(kind));
 
            // directive in trailing trivia is not a thing
            for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++)
            {
                var compilationUnit = SyntaxFactory.ParseCompilationUnit("namespace N { } #if false");
                compilationUnit.GetDiagnostics().Verify(
                    // (1,17): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line
                    // namespace N { } #if false
                    TestBase.Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 17));
                Assert.False(compilationUnit.ContainsDirective(kind));
            }
 
            testContainsHelper1("#define x", SyntaxKind.DefineDirectiveTrivia);
            testContainsHelper1("#if true\r\n#elif true", SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElifDirectiveTrivia);
            testContainsHelper1("#if false\r\n#elif true", SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElifDirectiveTrivia);
            testContainsHelper1("#if false\r\n#elif false", SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElifDirectiveTrivia);
            testContainsHelper1("#elif true", SyntaxKind.BadDirectiveTrivia);
            testContainsHelper1("#if true\r\n#else", SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElseDirectiveTrivia);
            testContainsHelper1("#else", SyntaxKind.BadDirectiveTrivia);
            testContainsHelper1("#if true\r\n#endif", SyntaxKind.IfDirectiveTrivia, SyntaxKind.EndIfDirectiveTrivia);
            testContainsHelper1("#endif", SyntaxKind.BadDirectiveTrivia);
            testContainsHelper1("#region\r\n#endregion", SyntaxKind.RegionDirectiveTrivia, SyntaxKind.EndRegionDirectiveTrivia);
            testContainsHelper1("#endregion", SyntaxKind.BadDirectiveTrivia);
            testContainsHelper1("#error", SyntaxKind.ErrorDirectiveTrivia);
            testContainsHelper1("#if true", SyntaxKind.IfDirectiveTrivia);
            testContainsHelper1("#nullable enable", SyntaxKind.NullableDirectiveTrivia);
            testContainsHelper1("#region enable", SyntaxKind.RegionDirectiveTrivia);
            testContainsHelper1("#undef x", SyntaxKind.UndefDirectiveTrivia);
            testContainsHelper1("#warning", SyntaxKind.WarningDirectiveTrivia);
 
            // #! is special and is only recognized at start of a script file and nowhere else.
            testContainsHelper2(new[] { SyntaxKind.ShebangDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Script));
            testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit(" #!command", options: TestOptions.Script));
            testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Regular));
 
            return;
 
            static void testContainsHelper1(string directive, params SyntaxKind[] directiveKinds)
            {
                Assert.True(directiveKinds.Length > 0);
 
                // directive on its own.
                testContainsHelper2(directiveKinds, SyntaxFactory.ParseCompilationUnit(directive));
 
                // Two of the same directive back to back.
                testContainsHelper2(directiveKinds, SyntaxFactory.ParseCompilationUnit($$"""
                    {{directive}}
                    {{directive}}
                    """));
 
                // Two of the same directive back to back with additional trivia
                testContainsHelper2(directiveKinds, SyntaxFactory.ParseCompilationUnit($$"""
                       {{directive}}
                       {{directive}}
                    """));
 
                // Directive inside a namespace
                testContainsHelper2(directiveKinds, SyntaxFactory.ParseCompilationUnit($$"""
                    namespace N
                    {
                    {{directive}}
                    }
                    """));
 
                // Multiple Directive inside a namespace
                testContainsHelper2(directiveKinds, SyntaxFactory.ParseCompilationUnit($$"""
                    namespace N
                    {
                    {{directive}}
                    {{directive}}
                    }
                    """));
 
                // Multiple Directive inside a namespace with additional trivia
                testContainsHelper2(directiveKinds, SyntaxFactory.ParseCompilationUnit($$"""
                    namespace N
                    {
                       {{directive}}
                       {{directive}}
                    }
                    """));
 
                // Directives on different elements in a namespace
                testContainsHelper2(directiveKinds, SyntaxFactory.ParseCompilationUnit($$"""
                    namespace N
                    {
                    {{directive}}
                        class C
                        {
                        }
                    {{directive}}
                        class D
                        {
                        }
                    }
                    """));
 
                // Directives on different elements in a namespace with additional trivia
                testContainsHelper2(directiveKinds, SyntaxFactory.ParseCompilationUnit($$"""
                    namespace N
                    {
                        {{directive}}
                        class C
                        {
                        }
                        {{directive}}
                        class D
                        {
                        }
                    }
                    """));
            }
 
            static void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSyntax compilationUnit)
            {
                Assert.True(compilationUnit.ContainsDirectives);
                foreach (var directiveKind in directiveKinds)
                    Assert.True(compilationUnit.ContainsDirective(directiveKind));
 
                for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++)
                {
                    if (!directiveKinds.Contains(kind))
                        Assert.False(compilationUnit.ContainsDirective(kind));
                }
            }
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")]
        public void TestContainsDirective_IfIf()
        {
            var compilationUnit = SyntaxFactory.ParseCompilationUnit("""
                if (#if)
                """);
            compilationUnit.GetDiagnostics().Verify(
                // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line
                // if (#if)
                TestBase.Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 5),
                // (1,9): error CS1733: Expected expression
                // if (#if)
                TestBase.Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9),
                // (1,9): error CS1026: ) expected
                // if (#if)
                TestBase.Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 9),
                // (1,9): error CS1733: Expected expression
                // if (#if)
                TestBase.Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9),
                // (1,9): error CS1002: ; expected
                // if (#if)
                TestBase.Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 9));
            Assert.False(compilationUnit.ContainsDirectives);
            Assert.False(compilationUnit.ContainsDirective(SyntaxKind.IfDirectiveTrivia));
        }
 
        [Fact]
        public void TestGetAllAnnotatedNodesUsingDescendantNodes()
        {
            var text = "a + (b - (c * (d / e)))";
            var expr = SyntaxFactory.ParseExpression(text);
            var myAnnotation = new SyntaxAnnotation();
 
            var identifierNodes = expr.DescendantNodes().OfType<IdentifierNameSyntax>().ToList();
            var exprWithAnnotations = expr.ReplaceNodes(identifierNodes, (e, e2) => e2.WithAdditionalAnnotations(myAnnotation));
 
            var nodesWithMyAnnotations = exprWithAnnotations.DescendantNodesAndSelf(n => n.ContainsAnnotations).Where(n => n.HasAnnotation(myAnnotation)).ToList();
 
            Assert.Equal(identifierNodes.Count, nodesWithMyAnnotations.Count);
 
            for (int i = 0; i < identifierNodes.Count; i++)
            {
                // compare text because node identity changed when adding the annotation
                Assert.Equal(identifierNodes[i].ToString(), nodesWithMyAnnotations[i].ToString());
            }
        }
 
        [Fact]
        public void TestDescendantTokens()
        {
            var s1 = "using Goo;";
            var t1 = SyntaxFactory.ParseSyntaxTree(s1);
            var tokens = t1.GetCompilationUnitRoot().DescendantTokens().ToList();
            Assert.Equal(4, tokens.Count);
            Assert.Equal(SyntaxKind.UsingKeyword, tokens[0].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, tokens[1].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, tokens[2].Kind());
            Assert.Equal(SyntaxKind.EndOfFileToken, tokens[3].Kind());
        }
 
        [Fact]
        public void TestDescendantTokensWithExtraWhitespace()
        {
            var s1 = "  using Goo  ;  ";
            var t1 = SyntaxFactory.ParseSyntaxTree(s1);
            var tokens = t1.GetCompilationUnitRoot().DescendantTokens().ToList();
            Assert.Equal(4, tokens.Count);
            Assert.Equal(SyntaxKind.UsingKeyword, tokens[0].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, tokens[1].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, tokens[2].Kind());
            Assert.Equal(SyntaxKind.EndOfFileToken, tokens[3].Kind());
        }
 
        [Fact]
        public void TestDescendantTokensEntireRange()
        {
            var s1 = "extern alias Bar;\r\n" + "using Goo;";
            var t1 = SyntaxFactory.ParseSyntaxTree(s1);
            var tokens = t1.GetCompilationUnitRoot().DescendantTokens().ToList();
            Assert.Equal(8, tokens.Count);
            Assert.Equal(SyntaxKind.ExternKeyword, tokens[0].Kind());
            Assert.Equal(SyntaxKind.AliasKeyword, tokens[1].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, tokens[2].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, tokens[3].Kind());
            Assert.Equal(SyntaxKind.UsingKeyword, tokens[4].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, tokens[5].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, tokens[6].Kind());
            Assert.Equal(SyntaxKind.EndOfFileToken, tokens[7].Kind());
        }
 
        [Fact]
        public void TestDescendantTokensOverFullSpan()
        {
            var s1 = "extern alias Bar;\r\n" + "using Goo;";
            var t1 = SyntaxFactory.ParseSyntaxTree(s1);
            var tokens = t1.GetCompilationUnitRoot().DescendantTokens(new TextSpan(0, 16)).ToList();
            Assert.Equal(3, tokens.Count);
            Assert.Equal(SyntaxKind.ExternKeyword, tokens[0].Kind());
            Assert.Equal(SyntaxKind.AliasKeyword, tokens[1].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, tokens[2].Kind());
        }
 
        [Fact]
        public void TestDescendantTokensOverInsideSpan()
        {
            var s1 = "extern alias Bar;\r\n" + "using Goo;";
            var t1 = SyntaxFactory.ParseSyntaxTree(s1);
            var tokens = t1.GetCompilationUnitRoot().DescendantTokens(new TextSpan(1, 14)).ToList();
            Assert.Equal(3, tokens.Count);
            Assert.Equal(SyntaxKind.ExternKeyword, tokens[0].Kind());
            Assert.Equal(SyntaxKind.AliasKeyword, tokens[1].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, tokens[2].Kind());
        }
 
        [Fact]
        public void TestDescendantTokensOverFullSpanOffset()
        {
            var s1 = "extern alias Bar;\r\n" + "using Goo;";
            var t1 = SyntaxFactory.ParseSyntaxTree(s1);
            var tokens = t1.GetCompilationUnitRoot().DescendantTokens(new TextSpan(7, 17)).ToList();
            Assert.Equal(4, tokens.Count);
            Assert.Equal(SyntaxKind.AliasKeyword, tokens[0].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, tokens[1].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, tokens[2].Kind());
            Assert.Equal(SyntaxKind.UsingKeyword, tokens[3].Kind());
        }
 
        [Fact]
        public void TestDescendantTokensOverInsideSpanOffset()
        {
            var s1 = "extern alias Bar;\r\n" + "using Goo;";
            var t1 = SyntaxFactory.ParseSyntaxTree(s1);
            var tokens = t1.GetCompilationUnitRoot().DescendantTokens(new TextSpan(8, 15)).ToList();
            Assert.Equal(4, tokens.Count);
            Assert.Equal(SyntaxKind.AliasKeyword, tokens[0].Kind());
            Assert.Equal(SyntaxKind.IdentifierToken, tokens[1].Kind());
            Assert.Equal(SyntaxKind.SemicolonToken, tokens[2].Kind());
            Assert.Equal(SyntaxKind.UsingKeyword, tokens[3].Kind());
        }
 
        [Fact]
        public void TestDescendantTrivia()
        {
            var text = "// goo\r\na + b";
            var expr = SyntaxFactory.ParseExpression(text);
 
            var list = expr.DescendantTrivia().ToList();
            Assert.Equal(4, list.Count);
            Assert.Equal(SyntaxKind.SingleLineCommentTrivia, list[0].Kind());
            Assert.Equal(SyntaxKind.EndOfLineTrivia, list[1].Kind());
            Assert.Equal(SyntaxKind.WhitespaceTrivia, list[2].Kind());
            Assert.Equal(SyntaxKind.WhitespaceTrivia, list[3].Kind());
        }
 
        [Fact]
        public void TestDescendantTriviaIntoStructuredTrivia()
        {
            var text = @"
/// <goo >
/// </goo>
a + b";
            var expr = SyntaxFactory.ParseExpression(text);
 
            var list = expr.DescendantTrivia(descendIntoTrivia: true).ToList();
            Assert.Equal(7, list.Count);
            Assert.Equal(SyntaxKind.EndOfLineTrivia, list[0].Kind());
            Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, list[1].Kind());
            Assert.Equal(SyntaxKind.DocumentationCommentExteriorTrivia, list[2].Kind());
            Assert.Equal(SyntaxKind.WhitespaceTrivia, list[3].Kind());
            Assert.Equal(SyntaxKind.DocumentationCommentExteriorTrivia, list[4].Kind());
            Assert.Equal(SyntaxKind.WhitespaceTrivia, list[5].Kind());
            Assert.Equal(SyntaxKind.WhitespaceTrivia, list[6].Kind());
        }
 
        [Fact]
        public void Bug877223()
        {
            var s1 = "using Goo;";
            var t1 = SyntaxFactory.ParseSyntaxTree(s1);
 
            // var node = t1.GetCompilationUnitRoot().Usings[0].GetTokens(new TextSpan(6, 3)).First();
            var node = t1.GetCompilationUnitRoot().DescendantTokens(new TextSpan(6, 3)).First();
            Assert.Equal("Goo", node.ToString());
        }
 
        [Fact]
        public void TestFindToken()
        {
            var text = "class\n #if XX\n#endif\n goo { }";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
 
            var token = tree.GetCompilationUnitRoot().FindToken("class\n #i".Length);
            Assert.Equal(SyntaxKind.IdentifierToken, token.Kind());
            Assert.Equal("goo", token.ToString());
            token = tree.GetCompilationUnitRoot().FindToken("class\n #i".Length, findInsideTrivia: true);
            Assert.Equal(SyntaxKind.IfKeyword, token.Kind());
        }
 
        [Theory, CombinatorialData]
        public void TestFindTokenInLargeList(bool collectionExpression)
        {
            var identifier = SyntaxFactory.Identifier("x");
            var missingIdentifier = SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken);
            var name = SyntaxFactory.IdentifierName(identifier);
            var missingName = SyntaxFactory.IdentifierName(missingIdentifier);
            var comma = SyntaxFactory.Token(SyntaxKind.CommaToken);
            var missingComma = SyntaxFactory.MissingToken(SyntaxKind.CommaToken);
            var argument = SyntaxFactory.Argument(name);
            var missingArgument = SyntaxFactory.Argument(missingName);
 
            // make a large list that has lots of zero-length nodes (that shouldn't be found)
            var nodesAndTokens = SyntaxFactory.NodeOrTokenList(
                missingArgument, missingComma,
                missingArgument, missingComma,
                missingArgument, missingComma,
                missingArgument, missingComma,
                missingArgument, missingComma,
                missingArgument, missingComma,
                missingArgument, missingComma,
                missingArgument, missingComma,
                argument);
 
            var argumentList = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList<ArgumentSyntax>(collectionExpression
                ? [.. nodesAndTokens]
                : SyntaxFactory.NodeOrTokenList(nodesAndTokens)));
            var invocation = SyntaxFactory.InvocationExpression(name, argumentList);
            CheckFindToken(invocation);
        }
 
        private void CheckFindToken(SyntaxNode node)
        {
            for (int i = 0; i < node.FullSpan.End; i++)
            {
                var token = node.FindToken(i);
                Assert.True(token.FullSpan.Contains(i));
            }
        }
 
        [WorkItem(755236, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755236")]
        [Fact]
        public void TestFindNode()
        {
            var text = "class\n #if XX\n#endif\n goo { }\n class bar { }";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
 
            var root = tree.GetRoot();
            Assert.Equal(root, root.FindNode(root.Span, findInsideTrivia: false));
            Assert.Equal(root, root.FindNode(root.Span, findInsideTrivia: true));
 
            var classDecl = (TypeDeclarationSyntax)root.ChildNodes().First();
 
            // IdentifierNameSyntax in trivia.
            var identifier = root.DescendantNodes(descendIntoTrivia: true).Single(n => n is IdentifierNameSyntax);
            var position = identifier.Span.Start + 1;
 
            Assert.Equal(classDecl, root.FindNode(identifier.Span, findInsideTrivia: false));
            Assert.Equal(identifier, root.FindNode(identifier.Span, findInsideTrivia: true));
 
            // Token span.
            Assert.Equal(classDecl, root.FindNode(classDecl.Identifier.Span, findInsideTrivia: false));
 
            // EOF Token span.
            var EOFSpan = new TextSpan(root.FullSpan.End, 0);
            Assert.Equal(root, root.FindNode(EOFSpan, findInsideTrivia: false));
            Assert.Equal(root, root.FindNode(EOFSpan, findInsideTrivia: true));
 
            // EOF Invalid span for childnode
            var classDecl2 = (TypeDeclarationSyntax)root.ChildNodes().Last();
            Assert.Throws<ArgumentOutOfRangeException>(() => classDecl2.FindNode(EOFSpan));
 
            // Check end position included in node span
            var nodeEndPositionSpan = new TextSpan(classDecl.FullSpan.End, 0);
 
            Assert.Equal(classDecl2, root.FindNode(nodeEndPositionSpan, findInsideTrivia: false));
            Assert.Equal(classDecl2, root.FindNode(nodeEndPositionSpan, findInsideTrivia: true));
            Assert.Equal(classDecl2, classDecl2.FindNode(nodeEndPositionSpan, findInsideTrivia: false));
            Assert.Equal(classDecl2, classDecl2.FindNode(nodeEndPositionSpan, findInsideTrivia: true));
 
            Assert.Throws<ArgumentOutOfRangeException>(() => classDecl.FindNode(nodeEndPositionSpan));
 
            // Invalid spans.
            var invalidSpan = new TextSpan(100, 100);
            Assert.Throws<ArgumentOutOfRangeException>(() => root.FindNode(invalidSpan));
            invalidSpan = new TextSpan(root.FullSpan.End - 1, 2);
            Assert.Throws<ArgumentOutOfRangeException>(() => root.FindNode(invalidSpan));
            invalidSpan = new TextSpan(classDecl2.FullSpan.Start - 1, root.FullSpan.End);
            Assert.Throws<ArgumentOutOfRangeException>(() => classDecl2.FindNode(invalidSpan));
            invalidSpan = new TextSpan(classDecl.FullSpan.End, root.FullSpan.End);
            Assert.Throws<ArgumentOutOfRangeException>(() => classDecl2.FindNode(invalidSpan));
            // Parent node's span.
            Assert.Throws<ArgumentOutOfRangeException>(() => classDecl.FindNode(root.FullSpan));
        }
 
        [WorkItem(539941, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539941")]
        [Fact]
        public void TestFindTriviaNoTriviaExistsAtPosition()
        {
            var code = @"class Goo
{
    void Bar()
    {
    }
}";
            var tree = SyntaxFactory.ParseSyntaxTree(code);
            var position = tree.GetText().Lines[2].End - 1;
            // position points to the closing parenthesis on the line that has "void Bar()"
            // There should be no trivia at this position
            var trivia = tree.GetCompilationUnitRoot().FindTrivia(position);
            Assert.Equal(SyntaxKind.None, trivia.Kind());
            Assert.Equal(0, trivia.SpanStart);
            Assert.Equal(0, trivia.Span.End);
            Assert.Equal(default(SyntaxTrivia), trivia);
        }
 
        [Fact]
        public void TestTreeEquivalentToSelf()
        {
            var text = "class goo { }";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            Assert.True(tree.GetCompilationUnitRoot().IsEquivalentTo(tree.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void TestTreeNotEquivalentToNull()
        {
            var text = "class goo { }";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            Assert.False(tree.GetCompilationUnitRoot().IsEquivalentTo(null));
        }
 
        [Fact]
        public void TestTreesFromSameSourceEquivalent()
        {
            var text = "class goo { }";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = SyntaxFactory.ParseSyntaxTree(text);
            Assert.NotEqual(tree1.GetCompilationUnitRoot(), tree2.GetCompilationUnitRoot());
            Assert.True(tree1.GetCompilationUnitRoot().IsEquivalentTo(tree2.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void TestDifferentTreesNotEquivalent()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class goo { }");
            var tree2 = SyntaxFactory.ParseSyntaxTree("class bar { }");
            Assert.NotEqual(tree1.GetCompilationUnitRoot(), tree2.GetCompilationUnitRoot());
            Assert.False(tree1.GetCompilationUnitRoot().IsEquivalentTo(tree2.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void TestVastlyDifferentTreesNotEquivalent()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class goo { }");
            var tree2 = SyntaxFactory.ParseSyntaxTree(string.Empty);
            Assert.NotEqual(tree1.GetCompilationUnitRoot(), tree2.GetCompilationUnitRoot());
            Assert.False(tree1.GetCompilationUnitRoot().IsEquivalentTo(tree2.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void TestSimilarSubtreesEquivalent()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class goo { void M() { } }");
            var tree2 = SyntaxFactory.ParseSyntaxTree("class bar { void M() { } }");
            var m1 = ((TypeDeclarationSyntax)tree1.GetCompilationUnitRoot().Members[0]).Members[0];
            var m2 = ((TypeDeclarationSyntax)tree2.GetCompilationUnitRoot().Members[0]).Members[0];
            Assert.Equal(SyntaxKind.MethodDeclaration, m1.Kind());
            Assert.Equal(SyntaxKind.MethodDeclaration, m2.Kind());
            Assert.NotEqual(m1, m2);
            Assert.True(m1.IsEquivalentTo(m2));
        }
 
        [Fact]
        public void TestTreesWithDifferentTriviaAreNotEquivalent()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class goo {void M() { }}");
            var tree2 = SyntaxFactory.ParseSyntaxTree("class goo { void M() { } }");
            Assert.False(tree1.GetCompilationUnitRoot().IsEquivalentTo(tree2.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void TestNodeIncrementallyEquivalentToSelf()
        {
            var text = "class goo { }";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            Assert.True(tree.GetCompilationUnitRoot().IsIncrementallyIdenticalTo(tree.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void TestTokenIncrementallyEquivalentToSelf()
        {
            var text = "class goo { }";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            Assert.True(tree.GetCompilationUnitRoot().EndOfFileToken.IsIncrementallyIdenticalTo(tree.GetCompilationUnitRoot().EndOfFileToken));
        }
 
        [Fact]
        public void TestDifferentTokensFromSameTreeNotIncrementallyEquivalentToSelf()
        {
            var text = "class goo { }";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            Assert.False(tree.GetCompilationUnitRoot().GetFirstToken().IsIncrementallyIdenticalTo(tree.GetCompilationUnitRoot().GetFirstToken().GetNextToken()));
        }
 
        [Fact]
        public void TestCachedTokensFromDifferentTreesIncrementallyEquivalentToSelf()
        {
            var text = "class goo { }";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = SyntaxFactory.ParseSyntaxTree(text);
            Assert.True(tree1.GetCompilationUnitRoot().GetFirstToken().IsIncrementallyIdenticalTo(tree2.GetCompilationUnitRoot().GetFirstToken()));
        }
 
        [Fact]
        public void TestNodesFromSameContentNotIncrementallyParsedNotIncrementallyEquivalent()
        {
            var text = "class goo { }";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = SyntaxFactory.ParseSyntaxTree(text);
            Assert.False(tree1.GetCompilationUnitRoot().IsIncrementallyIdenticalTo(tree2.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void TestNodesFromIncrementalParseIncrementallyEquivalent1()
        {
            var text = "class goo { void M() { } }";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = tree1.WithChangedText(tree1.GetText().WithChanges(new TextChange(default, " ")));
            Assert.True(
                tree1.GetCompilationUnitRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single().IsIncrementallyIdenticalTo(
                tree2.GetCompilationUnitRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single()));
        }
 
        [Fact]
        public void TestNodesFromIncrementalParseNotIncrementallyEquivalent1()
        {
            var text = "class goo { void M() { } }";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = tree1.WithChangedText(tree1.GetText().WithChanges(new TextChange(new TextSpan(22, 0), " return; ")));
            Assert.False(
                tree1.GetCompilationUnitRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single().IsIncrementallyIdenticalTo(
                tree2.GetCompilationUnitRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single()));
        }
 
        [Fact, WorkItem(536664, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536664")]
        public void TestTriviaNodeCached()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(" class goo {}");
 
            // get to the trivia node
            var trivia = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia()[0];
 
            // we get the trivia again
            var triviaAgain = tree.GetCompilationUnitRoot().Members[0].GetLeadingTrivia()[0];
 
            // should NOT return two distinct objects for trivia and triviaAgain - struct now.
            Assert.True(SyntaxTrivia.Equals(trivia, triviaAgain));
        }
 
        [Fact]
        public void TestGetFirstToken()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            var first = tree.GetCompilationUnitRoot().GetFirstToken();
            Assert.Equal(SyntaxKind.PublicKeyword, first.Kind());
        }
 
        [Fact]
        public void TestGetFirstTokenIncludingZeroWidth()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            var first = tree.GetCompilationUnitRoot().GetFirstToken(includeZeroWidth: true);
            Assert.Equal(SyntaxKind.PublicKeyword, first.Kind());
        }
 
        [Fact]
        public void TestGetLastToken()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            var last = tree.GetCompilationUnitRoot().GetLastToken();
            Assert.Equal(SyntaxKind.CloseBraceToken, last.Kind());
        }
 
        [Fact]
        public void TestGetLastTokenIncludingZeroWidth()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { ");
            var last = tree.GetCompilationUnitRoot().GetLastToken(includeZeroWidth: true);
            Assert.Equal(SyntaxKind.EndOfFileToken, last.Kind());
 
            last = tree.GetCompilationUnitRoot().Members[0].GetLastToken(includeZeroWidth: true);
            Assert.Equal(SyntaxKind.CloseBraceToken, last.Kind());
            Assert.True(last.IsMissing);
            Assert.Equal(26, last.FullSpan.Start);
        }
 
        [Fact]
        public void TestReverseChildSyntaxList()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class A {} public class B {} public static class C {}");
            var root1 = tree1.GetCompilationUnitRoot();
            TestReverse(root1.ChildNodesAndTokens());
            TestReverse(root1.Members[0].ChildNodesAndTokens());
            TestReverse(root1.Members[1].ChildNodesAndTokens());
            TestReverse(root1.Members[2].ChildNodesAndTokens());
        }
 
        private void TestReverse(ChildSyntaxList children)
        {
            var list1 = children.AsEnumerable().Reverse().ToList();
            var list2 = children.Reverse().ToList();
            Assert.Equal(list1.Count, list2.Count);
            for (int i = 0; i < list1.Count; i++)
            {
                Assert.Equal(list1[i], list2[i]);
                Assert.Equal(list1[i].FullSpan.Start, list2[i].FullSpan.Start);
            }
        }
 
        [Fact]
        public void TestGetNextToken()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            var tokens = tree.GetCompilationUnitRoot().DescendantTokens().ToList();
 
            var list = new List<SyntaxToken>();
            var token = tree.GetCompilationUnitRoot().GetFirstToken(includeSkipped: true);
            while (token.Kind() != SyntaxKind.None)
            {
                list.Add(token);
                token = token.GetNextToken(includeSkipped: true);
            }
 
            // descendant tokens include EOF
            Assert.Equal(tokens.Count - 1, list.Count);
            for (int i = 0; i < list.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetNextTokenIncludingSkippedTokens()
        {
            var text =
@"garbage
using goo.bar;
";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString());
 
            var tokens = tree.GetCompilationUnitRoot().DescendantTokens(descendIntoTrivia: true).Where(SyntaxToken.NonZeroWidth).ToList();
            Assert.Equal(6, tokens.Count);
            Assert.Equal("garbage", tokens[0].Text);
 
            var list = new List<SyntaxToken>(tokens.Count);
            var token = tree.GetCompilationUnitRoot().GetFirstToken(includeSkipped: true);
            while (token.Kind() != SyntaxKind.None)
            {
                list.Add(token);
                token = token.GetNextToken(includeSkipped: true);
            }
 
            Assert.Equal(tokens.Count, list.Count);
            for (int i = 0; i < tokens.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetNextTokenExcludingSkippedTokens()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"garbage
using goo.bar;
");
            var tokens = tree.GetCompilationUnitRoot().DescendantTokens().ToList();
            Assert.Equal(6, tokens.Count);
 
            var list = new List<SyntaxToken>(tokens.Count);
            var token = tree.GetCompilationUnitRoot().GetFirstToken(includeSkipped: false);
            while (token.Kind() != SyntaxKind.None)
            {
                list.Add(token);
                token = token.GetNextToken(includeSkipped: false);
            }
 
            // descendant tokens includes EOF
            Assert.Equal(tokens.Count - 1, list.Count);
            for (int i = 0; i < list.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetNextTokenCommon()
        {
            SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            List<SyntaxToken> tokens = syntaxTree.GetRoot().DescendantTokens().ToList();
 
            List<SyntaxToken> list = new List<SyntaxToken>();
            SyntaxToken token = syntaxTree.GetRoot().GetFirstToken();
            while (token.RawKind != 0)
            {
                list.Add(token);
                token = token.GetNextToken();
            }
 
            // descendant tokens includes EOF
            Assert.Equal(tokens.Count - 1, list.Count);
            for (int i = 0; i < list.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetPreviousToken()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            var tokens = tree.GetCompilationUnitRoot().DescendantTokens().ToList();
 
            var list = new List<SyntaxToken>();
            var token = tree.GetCompilationUnitRoot().GetLastToken(); // skip EOF
            while (token.Kind() != SyntaxKind.None)
            {
                list.Add(token);
                token = token.GetPreviousToken();
            }
            list.Reverse();
 
            // descendant tokens includes EOF
            Assert.Equal(tokens.Count - 1, list.Count);
            for (int i = 0; i < list.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetPreviousTokenIncludingSkippedTokens()
        {
            var text =
@"garbage
using goo.bar;
";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString());
 
            var tokens = tree.GetCompilationUnitRoot().DescendantTokens(descendIntoTrivia: true).Where(SyntaxToken.NonZeroWidth).ToList();
            Assert.Equal(6, tokens.Count);
            Assert.Equal("garbage", tokens[0].Text);
 
            var list = new List<SyntaxToken>(tokens.Count);
            var token = tree.GetCompilationUnitRoot().GetLastToken(includeSkipped: true);
            while (token.Kind() != SyntaxKind.None)
            {
                list.Add(token);
                token = token.GetPreviousToken(includeSkipped: true);
            }
            list.Reverse();
 
            Assert.Equal(tokens.Count, list.Count);
            for (int i = 0; i < tokens.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetPreviousTokenExcludingSkippedTokens()
        {
            var text =
@"garbage
using goo.bar;
";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            Assert.Equal(text, tree.GetCompilationUnitRoot().ToFullString());
 
            var tokens = tree.GetCompilationUnitRoot().DescendantTokens().ToList();
            Assert.Equal(6, tokens.Count);
 
            var list = new List<SyntaxToken>(tokens.Count);
            var token = tree.GetCompilationUnitRoot().GetLastToken(includeSkipped: false);
            while (token.Kind() != SyntaxKind.None)
            {
                list.Add(token);
                token = token.GetPreviousToken(includeSkipped: false);
            }
            list.Reverse();
 
            // descendant tokens includes EOF
            Assert.Equal(tokens.Count, list.Count + 1);
            for (int i = 0; i < list.Count; i++)
            {
                Assert.Equal(tokens[i], list[i]);
            }
        }
 
        [Fact]
        public void TestGetPreviousTokenCommon()
        {
            SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            List<SyntaxToken> tokens = syntaxTree.GetRoot().DescendantTokens().ToList();
 
            List<SyntaxToken> list = new List<SyntaxToken>();
            var token = syntaxTree.GetRoot().GetLastToken(includeZeroWidth: false); // skip EOF
 
            while (token.RawKind != 0)
            {
                list.Add(token);
                token = token.GetPreviousToken();
            }
            list.Reverse();
 
            // descendant tokens include EOF
            Assert.Equal(tokens.Count - 1, list.Count);
            for (int i = 0; i < list.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetNextTokenIncludingZeroWidth()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo {");
            var tokens = tree.GetCompilationUnitRoot().DescendantTokens().ToList();
 
            var list = new List<SyntaxToken>();
            var token = tree.GetCompilationUnitRoot().GetFirstToken(includeZeroWidth: true);
            while (token.Kind() != SyntaxKind.None)
            {
                list.Add(token);
                token = token.GetNextToken(includeZeroWidth: true);
            }
 
            Assert.Equal(tokens.Count, list.Count);
            for (int i = 0; i < tokens.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetNextTokenIncludingZeroWidthCommon()
        {
            SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree("public static class goo {");
            List<SyntaxToken> tokens = syntaxTree.GetRoot().DescendantTokens().ToList();
 
            List<SyntaxToken> list = new List<SyntaxToken>();
            SyntaxToken token = syntaxTree.GetRoot().GetFirstToken(includeZeroWidth: true);
            while (token.RawKind != 0)
            {
                list.Add(token);
                token = token.GetNextToken(includeZeroWidth: true);
            }
 
            Assert.Equal(tokens.Count, list.Count);
            for (int i = 0; i < tokens.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetPreviousTokenIncludingZeroWidth()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo {");
            var tokens = tree.GetCompilationUnitRoot().DescendantTokens().ToList();
 
            var list = new List<SyntaxToken>();
            var token = tree.GetCompilationUnitRoot().EndOfFileToken.GetPreviousToken(includeZeroWidth: true);
            while (token.Kind() != SyntaxKind.None)
            {
                list.Add(token);
                token = token.GetPreviousToken(includeZeroWidth: true);
            }
 
            list.Reverse();
 
            // descendant tokens include EOF
            Assert.Equal(tokens.Count - 1, list.Count);
            for (int i = 0; i < list.Count; i++)
            {
                Assert.Equal(list[i], tokens[i]);
            }
        }
 
        [Fact]
        public void TestGetPreviousTokenIncludingZeroWidthCommon()
        {
            SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree("public static class goo {");
            List<SyntaxToken> tokens = syntaxTree.GetRoot().DescendantTokens().ToList();
 
            List<SyntaxToken> list = new List<SyntaxToken>();
            SyntaxToken token = syntaxTree.GetCompilationUnitRoot().EndOfFileToken.GetPreviousToken(includeZeroWidth: true);
            while (token.RawKind != 0)
            {
                list.Add(token);
                token = token.GetPreviousToken(includeZeroWidth: true);
            }
            list.Reverse();
 
            // descendant tokens includes EOF
            Assert.Equal(tokens.Count - 1, list.Count);
            for (int i = 0; i < list.Count; i++)
            {
                Assert.Equal(tokens[i], list[i]);
            }
        }
 
        [Fact]
        public void TestGetNextSibling()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            var children = tree.GetCompilationUnitRoot().Members[0].ChildNodesAndTokens().ToList();
            var list = new List<SyntaxNodeOrToken>();
            for (var child = children[0]; child.Kind() != SyntaxKind.None; child = child.GetNextSibling())
            {
                list.Add(child);
            }
 
            Assert.Equal(children.Count, list.Count);
            for (int i = 0; i < children.Count; i++)
            {
                Assert.Equal(list[i], children[i]);
            }
        }
 
        [Fact]
        public void TestGetPreviousSibling()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            var children = tree.GetCompilationUnitRoot().Members[0].ChildNodesAndTokens().ToList();
            var reversed = children.AsEnumerable().Reverse().ToList();
            var list = new List<SyntaxNodeOrToken>();
            for (var child = children[children.Count - 1]; child.Kind() != SyntaxKind.None; child = child.GetPreviousSibling())
            {
                list.Add(child);
            }
 
            Assert.Equal(children.Count, list.Count);
            for (int i = 0; i < reversed.Count; i++)
            {
                Assert.Equal(list[i], reversed[i]);
            }
        }
 
        [Fact]
        public void TestSyntaxNodeOrTokenEquality()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("public static class goo { }");
            var child = tree.GetCompilationUnitRoot().ChildNodesAndTokens()[0];
            var member = (TypeDeclarationSyntax)tree.GetCompilationUnitRoot().Members[0];
            Assert.Equal((SyntaxNodeOrToken)member, child);
 
            var name = member.Identifier;
            var nameChild = member.ChildNodesAndTokens()[3];
            Assert.Equal((SyntaxNodeOrToken)name, nameChild);
 
            var closeBraceToken = member.CloseBraceToken;
            var closeBraceChild = member.GetLastToken();
            Assert.Equal((SyntaxNodeOrToken)closeBraceToken, closeBraceChild);
        }
 
        [Fact]
        public void TestStructuredTriviaHasNoParent()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("#define GOO");
            var trivia = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia()[0];
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, trivia.Kind());
            Assert.True(trivia.HasStructure);
            Assert.NotNull(trivia.GetStructure());
            Assert.Null(trivia.GetStructure().Parent);
        }
 
        [Fact]
        public void TestStructuredTriviaHasParentTrivia()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("#define GOO");
            var trivia = tree.GetCompilationUnitRoot().EndOfFileToken.GetLeadingTrivia()[0];
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, trivia.Kind());
            Assert.True(trivia.HasStructure);
            Assert.NotNull(trivia.GetStructure());
            var parentTrivia = trivia.GetStructure().ParentTrivia;
            Assert.NotEqual(SyntaxKind.None, parentTrivia.Kind());
            Assert.Equal(trivia, parentTrivia);
        }
 
        [Fact]
        public void TestStructuredTriviaParentTrivia()
        {
            var def = SyntaxFactory.DefineDirectiveTrivia(SyntaxFactory.Identifier("GOO"), false);
 
            // unrooted structured trivia should report parent trivia as default 
            Assert.Equal(default(SyntaxTrivia), def.ParentTrivia);
 
            var trivia = SyntaxFactory.Trivia(def);
            var structure = trivia.GetStructure();
            Assert.NotEqual(def, structure);  // these should not be identity equals
            Assert.True(def.IsEquivalentTo(structure)); // they should be equivalent though
            Assert.Equal(trivia, structure.ParentTrivia); // parent trivia should be equal to original trivia
 
            // attach trivia to token and walk down to structured trivia and back up again
            var token = SyntaxFactory.Identifier(default(SyntaxTriviaList), "x", SyntaxTriviaList.Create(trivia));
            var tokenTrivia = token.TrailingTrivia[0];
            var tokenStructuredTrivia = tokenTrivia.GetStructure();
            var tokenStructuredParentTrivia = tokenStructuredTrivia.ParentTrivia;
            Assert.Equal(tokenTrivia, tokenStructuredParentTrivia);
            Assert.Equal(token, tokenStructuredParentTrivia.Token);
        }
 
        [Fact]
        public void TestGetFirstDirective()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("#define GOO");
            var d = tree.GetCompilationUnitRoot().GetFirstDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d.Kind());
        }
 
        [Fact]
        public void TestGetLastDirective()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#undef GOO
");
            var d = tree.GetCompilationUnitRoot().GetLastDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.UndefDirectiveTrivia, d.Kind());
        }
 
        [Fact]
        public void TestGetNextDirective()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#define BAR
class C {
#if GOO
   void M() { }
#endif
}
");
            var d1 = tree.GetCompilationUnitRoot().GetFirstDirective();
            Assert.NotNull(d1);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d1.Kind());
            var d2 = d1.GetNextDirective();
            Assert.NotNull(d2);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d2.Kind());
            var d3 = d2.GetNextDirective();
            Assert.NotNull(d3);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, d3.Kind());
            var d4 = d3.GetNextDirective();
            Assert.NotNull(d4);
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, d4.Kind());
            var d5 = d4.GetNextDirective();
            Assert.Null(d5);
        }
 
        [Fact]
        public void TestGetPreviousDirective()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#define BAR
class C {
#if GOO
   void M() { }
#endif
}
");
            var d1 = tree.GetCompilationUnitRoot().GetLastDirective();
            Assert.NotNull(d1);
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, d1.Kind());
            var d2 = d1.GetPreviousDirective();
            Assert.NotNull(d2);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, d2.Kind());
            var d3 = d2.GetPreviousDirective();
            Assert.NotNull(d3);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d3.Kind());
            var d4 = d3.GetPreviousDirective();
            Assert.NotNull(d4);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d4.Kind());
            var d5 = d4.GetPreviousDirective();
            Assert.Null(d5);
        }
 
        [Fact]
        public void TestGetNextAndPreviousDirectiveWithDuplicateTrivia()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#region R
class C {
}
");
            var c = tree.GetCompilationUnitRoot().Members[0];
 
            // Duplicate the leading trivia on the class
            c = c.WithLeadingTrivia(c.GetLeadingTrivia().Concat(c.GetLeadingTrivia()));
 
            var leadingTriviaWithDuplicate = c.GetLeadingTrivia();
            Assert.Equal(2, leadingTriviaWithDuplicate.Count);
 
            var firstDirective = Assert.IsType<RegionDirectiveTriviaSyntax>(leadingTriviaWithDuplicate[0].GetStructure());
            var secondDirective = Assert.IsType<RegionDirectiveTriviaSyntax>(leadingTriviaWithDuplicate[1].GetStructure());
 
            // Test GetNextDirective works correctly
            Assert.Same(secondDirective, firstDirective.GetNextDirective());
            Assert.Null(secondDirective.GetNextDirective());
 
            // Test GetPreviousDirective works correctly
            Assert.Null(firstDirective.GetPreviousDirective());
            Assert.Same(secondDirective.GetPreviousDirective(), firstDirective);
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToIf()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#if GOO
class A { }
#elif BAR
class B { }
#elif BAZ
class B { }
#else 
class C { }
#endif
");
            var d = tree.GetCompilationUnitRoot().GetFirstDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d.Kind());
            d = d.GetNextDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(5, related.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[1].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[2].Kind());
            Assert.Equal(SyntaxKind.ElseDirectiveTrivia, related[3].Kind());
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, related[4].Kind());
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToIfElements()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#if GOO
class A { }
#elif BAR
class B { }
#elif BAZ
class B { }
#else 
class C { }
#endif
");
            var d = tree.GetCompilationUnitRoot().GetFirstDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d.Kind());
            d = d.GetNextDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(5, related.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[1].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[2].Kind());
            Assert.Equal(SyntaxKind.ElseDirectiveTrivia, related[3].Kind());
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, related[4].Kind());
 
            // get directives related to elif
            var related2 = related[1].GetRelatedDirectives();
            Assert.True(related.SequenceEqual(related2));
 
            // get directives related to else
            var related3 = related[3].GetRelatedDirectives();
            Assert.True(related.SequenceEqual(related3));
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToEndIf()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#if GOO
class A { }
#elif BAR
class B { }
#elif BAZ
class B { }
#else 
class C { }
#endif
");
            var d = tree.GetCompilationUnitRoot().GetLastDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(5, related.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[1].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[2].Kind());
            Assert.Equal(SyntaxKind.ElseDirectiveTrivia, related[3].Kind());
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, related[4].Kind());
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToIfWithNestedIfEndIF()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#if GOO
class A { }
#if ZED
  class A1 { }
#endif
#elif BAR
class B { }
#elif BAZ
class B { }
#else 
class C { }
#endif
");
            var d = tree.GetCompilationUnitRoot().GetFirstDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d.Kind());
            d = d.GetNextDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(5, related.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[1].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[2].Kind());
            Assert.Equal(SyntaxKind.ElseDirectiveTrivia, related[3].Kind());
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, related[4].Kind());
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToIfWithNestedRegionEndRegion()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#if GOO
class A { }
#region some region
  class A1 { }
#endregion
#elif BAR
class B { }
#elif BAZ
class B { }
#else 
class C { }
#endif
");
            var d = tree.GetCompilationUnitRoot().GetFirstDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.DefineDirectiveTrivia, d.Kind());
            d = d.GetNextDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(5, related.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[1].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[2].Kind());
            Assert.Equal(SyntaxKind.ElseDirectiveTrivia, related[3].Kind());
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, related[4].Kind());
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToEndIfWithNestedIfEndIf()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#if GOO
class A { }
#if ZED
  class A1 { }
#endif
#elif BAR
class B { }
#elif BAZ
class B { }
#else 
class C { }
#endif
");
            var d = tree.GetCompilationUnitRoot().GetLastDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(5, related.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[1].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[2].Kind());
            Assert.Equal(SyntaxKind.ElseDirectiveTrivia, related[3].Kind());
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, related[4].Kind());
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToEndIfWithNestedRegionEndRegion()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#define GOO
#if GOO
#region some region
class A { }
#endregion
#elif BAR
class B { }
#elif BAZ
class B { }
#else 
class C { }
#endif
");
            var d = tree.GetCompilationUnitRoot().GetLastDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(5, related.Count);
            Assert.Equal(SyntaxKind.IfDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[1].Kind());
            Assert.Equal(SyntaxKind.ElifDirectiveTrivia, related[2].Kind());
            Assert.Equal(SyntaxKind.ElseDirectiveTrivia, related[3].Kind());
            Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, related[4].Kind());
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToRegion()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"#region Some Region
class A { }
#endregion
#if GOO
#endif
");
            var d = tree.GetCompilationUnitRoot().GetFirstDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.RegionDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(2, related.Count);
            Assert.Equal(SyntaxKind.RegionDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.EndRegionDirectiveTrivia, related[1].Kind());
        }
 
        [Fact]
        public void TestGetDirectivesRelatedToEndRegion()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"
#if GOO
#endif
#region Some Region
class A { }
#endregion
");
            var d = tree.GetCompilationUnitRoot().GetLastDirective();
            Assert.NotNull(d);
            Assert.Equal(SyntaxKind.EndRegionDirectiveTrivia, d.Kind());
            var related = d.GetRelatedDirectives();
            Assert.NotNull(related);
            Assert.Equal(2, related.Count);
            Assert.Equal(SyntaxKind.RegionDirectiveTrivia, related[0].Kind());
            Assert.Equal(SyntaxKind.EndRegionDirectiveTrivia, related[1].Kind());
        }
 
        [WorkItem(536995, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536995")]
        [Fact]
        public void TestTextAndSpanWithTrivia1()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"/*START*/namespace Microsoft.CSharp.Test
{
}/*END*/");
            var rootNode = tree.GetCompilationUnitRoot();
 
            Assert.Equal(rootNode.FullSpan.Length, rootNode.ToFullString().Length);
            Assert.Equal(rootNode.Span.Length, rootNode.ToString().Length);
            Assert.True(rootNode.ToString().Contains("/*END*/"));
            Assert.False(rootNode.ToString().Contains("/*START*/"));
        }
 
        [WorkItem(536996, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536996")]
        [Fact]
        public void TestTextAndSpanWithTrivia2()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"/*START*/
namespace Microsoft.CSharp.Test
{
}
/*END*/");
            var rootNode = tree.GetCompilationUnitRoot();
 
            Assert.Equal(rootNode.FullSpan.Length, rootNode.ToFullString().Length);
            Assert.Equal(rootNode.Span.Length, rootNode.ToString().Length);
            Assert.True(rootNode.ToString().Contains("/*END*/"));
            Assert.False(rootNode.ToString().Contains("/*START*/"));
        }
 
        [Fact]
        public void TestCreateCommonSyntaxNode()
        {
            var rootNode = SyntaxFactory.ParseSyntaxTree("using X; namespace Y { }").GetCompilationUnitRoot();
            var namespaceNode = rootNode.ChildNodesAndTokens()[1].AsNode();
            var nodeOrToken = (SyntaxNodeOrToken)namespaceNode;
            Assert.True(nodeOrToken.IsNode);
            Assert.Equal(namespaceNode, nodeOrToken.AsNode());
            Assert.Equal(rootNode, nodeOrToken.Parent);
            Assert.Equal(namespaceNode.FullSpan, nodeOrToken.FullSpan);
            Assert.Equal(namespaceNode.Span, nodeOrToken.Span);
        }
 
        [Fact, WorkItem(537070, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537070")]
        public void TestTraversalUsingCommonSyntaxNodeOrToken()
        {
            SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(@"class c1
{
}");
            var nodeOrToken = (SyntaxNodeOrToken)syntaxTree.GetRoot();
            Assert.Equal(0, syntaxTree.GetDiagnostics().Count());
            Action<SyntaxNodeOrToken> walk = null;
            walk = (SyntaxNodeOrToken nOrT) =>
            {
                Assert.Equal(0, syntaxTree.GetDiagnostics(nOrT).Count());
                foreach (var child in nOrT.ChildNodesAndTokens())
                {
                    walk(child);
                }
            };
            walk(nodeOrToken);
        }
 
        [WorkItem(537747, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537747")]
        [Fact]
        public void SyntaxTriviaDefaultIsDirective()
        {
            SyntaxTrivia trivia = new SyntaxTrivia();
            Assert.False(trivia.IsDirective);
        }
 
        [Theory, CombinatorialData]
        public void SyntaxNames(bool collectionExpression)
        {
            var cc = SyntaxFactory.Token(SyntaxKind.ColonColonToken);
            var lt = SyntaxFactory.Token(SyntaxKind.LessThanToken);
            var gt = SyntaxFactory.Token(SyntaxKind.GreaterThanToken);
            var dot = SyntaxFactory.Token(SyntaxKind.DotToken);
            var gp = collectionExpression
                ? [SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword))]
                : SyntaxFactory.SingletonSeparatedList<TypeSyntax>(SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)));
 
            var externAlias = SyntaxFactory.IdentifierName("alias");
            var goo = SyntaxFactory.IdentifierName("Goo");
            var bar = SyntaxFactory.IdentifierName("Bar");
 
            // Goo.Bar
            var qualified = SyntaxFactory.QualifiedName(goo, dot, bar);
            Assert.Equal("Goo.Bar", qualified.ToString());
            Assert.Equal("Bar", qualified.GetUnqualifiedName().Identifier.ValueText);
 
            // Bar<int>
            var generic = SyntaxFactory.GenericName(bar.Identifier, SyntaxFactory.TypeArgumentList(lt, gp, gt));
            Assert.Equal("Bar<int>", generic.ToString());
            Assert.Equal("Bar", generic.GetUnqualifiedName().Identifier.ValueText);
 
            // Goo.Bar<int>
            var qualifiedGeneric = SyntaxFactory.QualifiedName(goo, dot, generic);
            Assert.Equal("Goo.Bar<int>", qualifiedGeneric.ToString());
            Assert.Equal("Bar", qualifiedGeneric.GetUnqualifiedName().Identifier.ValueText);
 
            // alias::Goo
            var alias = SyntaxFactory.AliasQualifiedName(externAlias, cc, goo);
            Assert.Equal("alias::Goo", alias.ToString());
            Assert.Equal("Goo", alias.GetUnqualifiedName().Identifier.ValueText);
 
            // alias::Bar<int>
            var aliasGeneric = SyntaxFactory.AliasQualifiedName(externAlias, cc, generic);
            Assert.Equal("alias::Bar<int>", aliasGeneric.ToString());
            Assert.Equal("Bar", aliasGeneric.GetUnqualifiedName().Identifier.ValueText);
 
            // alias::Goo.Bar
            var aliasQualified = SyntaxFactory.QualifiedName(alias, dot, bar);
            Assert.Equal("alias::Goo.Bar", aliasQualified.ToString());
            Assert.Equal("Bar", aliasQualified.GetUnqualifiedName().Identifier.ValueText);
 
            // alias::Goo.Bar<int>
            var aliasQualifiedGeneric = SyntaxFactory.QualifiedName(alias, dot, generic);
            Assert.Equal("alias::Goo.Bar<int>", aliasQualifiedGeneric.ToString());
            Assert.Equal("Bar", aliasQualifiedGeneric.GetUnqualifiedName().Identifier.ValueText);
        }
 
        [Theory, CombinatorialData]
        public void ZeroWidthTokensInListAreUnique1(bool collectionExpression)
        {
            var someToken = SyntaxFactory.MissingToken(SyntaxKind.IntKeyword);
            var list = collectionExpression
                ? [someToken, someToken]
                : SyntaxFactory.TokenList(someToken, someToken);
            Assert.Equal(someToken, someToken);
            Assert.NotEqual(list[0], list[1]);
        }
 
        [Fact]
        public void ZeroWidthTokensInParentAreUnique()
        {
            var missingComma = SyntaxFactory.MissingToken(SyntaxKind.CommaToken);
            var omittedArraySize = SyntaxFactory.OmittedArraySizeExpression(SyntaxFactory.Token(SyntaxKind.OmittedArraySizeExpressionToken));
            var spec = SyntaxFactory.ArrayRankSpecifier(
                SyntaxFactory.Token(SyntaxKind.OpenBracketToken),
                SyntaxFactory.SeparatedList<ExpressionSyntax>(new SyntaxNodeOrToken[] { omittedArraySize, missingComma, omittedArraySize, missingComma, omittedArraySize, missingComma, omittedArraySize }),
                SyntaxFactory.Token(SyntaxKind.CloseBracketToken)
                );
 
            var sizes = spec.Sizes;
            Assert.Equal(4, sizes.Count);
            Assert.Equal(3, sizes.SeparatorCount);
 
            Assert.NotEqual(sizes[0], sizes[1]);
            Assert.NotEqual(sizes[0], sizes[2]);
            Assert.NotEqual(sizes[0], sizes[3]);
            Assert.NotEqual(sizes[1], sizes[2]);
            Assert.NotEqual(sizes[1], sizes[3]);
            Assert.NotEqual(sizes[2], sizes[3]);
 
            Assert.NotEqual(sizes.GetSeparator(0), sizes.GetSeparator(1));
            Assert.NotEqual(sizes.GetSeparator(0), sizes.GetSeparator(2));
            Assert.NotEqual(sizes.GetSeparator(1), sizes.GetSeparator(2));
        }
 
        [Theory, CombinatorialData]
        public void ZeroWidthStructuredTrivia(bool collectionExpression)
        {
            // create zero width structured trivia (not sure how these come about but its not impossible)
            var zeroWidth = SyntaxFactory.ElseDirectiveTrivia(SyntaxFactory.MissingToken(SyntaxKind.HashToken), SyntaxFactory.MissingToken(SyntaxKind.ElseKeyword), SyntaxFactory.MissingToken(SyntaxKind.EndOfDirectiveToken), false, false);
            Assert.Equal(0, zeroWidth.Width);
 
            // create token with more than one instance of same zero width structured trivia!
            var someToken = SyntaxFactory.Identifier(
                default(SyntaxTriviaList),
                "goo",
                collectionExpression
                    ? [SyntaxFactory.Trivia(zeroWidth), SyntaxFactory.Trivia(zeroWidth)]
                    : SyntaxFactory.TriviaList(SyntaxFactory.Trivia(zeroWidth), SyntaxFactory.Trivia(zeroWidth)));
 
            // create node with this token
            var someNode = SyntaxFactory.IdentifierName(someToken);
 
            Assert.Equal(2, someNode.Identifier.TrailingTrivia.Count);
            Assert.True(someNode.Identifier.TrailingTrivia[0].HasStructure);
            Assert.True(someNode.Identifier.TrailingTrivia[1].HasStructure);
 
            // prove that trivia have different identity
            Assert.False(someNode.Identifier.TrailingTrivia[0].Equals(someNode.Identifier.TrailingTrivia[1]));
 
            var tt0 = someNode.Identifier.TrailingTrivia[0];
            var tt1 = someNode.Identifier.TrailingTrivia[1];
 
            var str0 = tt0.GetStructure();
            var str1 = tt1.GetStructure();
 
            // prove that structures have different identity
            Assert.NotEqual(str0, str1);
 
            // prove that structured trivia can get back to original trivia with correct identity
            var tr0 = str0.ParentTrivia;
            Assert.Equal(tt0, tr0);
 
            var tr1 = str1.ParentTrivia;
            Assert.Equal(tt1, tr1);
        }
 
        [Fact]
        public void ZeroWidthStructuredTriviaOnZeroWidthToken()
        {
            // create zero width structured trivia (not sure how these come about but its not impossible)
            var zeroWidth = SyntaxFactory.ElseDirectiveTrivia(SyntaxFactory.MissingToken(SyntaxKind.HashToken), SyntaxFactory.MissingToken(SyntaxKind.ElseKeyword), SyntaxFactory.MissingToken(SyntaxKind.EndOfDirectiveToken), false, false);
            Assert.Equal(0, zeroWidth.Width);
 
            // create token with more than one instance of same zero width structured trivia!
            var someToken = SyntaxFactory.Identifier(default(SyntaxTriviaList), "", SyntaxFactory.TriviaList(SyntaxFactory.Trivia(zeroWidth), SyntaxFactory.Trivia(zeroWidth)));
 
            // create node with this token
            var someNode = SyntaxFactory.IdentifierName(someToken);
 
            Assert.Equal(2, someNode.Identifier.TrailingTrivia.Count);
            Assert.True(someNode.Identifier.TrailingTrivia[0].HasStructure);
            Assert.True(someNode.Identifier.TrailingTrivia[1].HasStructure);
 
            // prove that trivia have different identity
            Assert.False(someNode.Identifier.TrailingTrivia[0].Equals(someNode.Identifier.TrailingTrivia[1]));
 
            var tt0 = someNode.Identifier.TrailingTrivia[0];
            var tt1 = someNode.Identifier.TrailingTrivia[1];
 
            var str0 = tt0.GetStructure();
            var str1 = tt1.GetStructure();
 
            // prove that structures have different identity
            Assert.NotEqual(str0, str1);
 
            // prove that structured trivia can get back to original trivia with correct identity
            var tr0 = str0.ParentTrivia;
            Assert.Equal(tt0, tr0);
 
            var tr1 = str1.ParentTrivia;
            Assert.Equal(tt1, tr1);
        }
 
        [WorkItem(537059, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537059")]
        [Fact]
        public void TestIncompleteDeclWithDotToken()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(
@"
class Test
{
  int IX.GOO
");
 
            // Verify the kind of the CSharpSyntaxNode "int IX.GOO" is MethodDeclaration and NOT FieldDeclaration
            Assert.Equal(SyntaxKind.MethodDeclaration, tree.GetCompilationUnitRoot().ChildNodesAndTokens()[0].ChildNodesAndTokens()[3].Kind());
        }
 
        [WorkItem(538360, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538360")]
        [Fact]
        public void TestGetTokensLanguageAny()
        {
            SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree("class C {}");
 
            var actualTokens = syntaxTree.GetCompilationUnitRoot().DescendantTokens();
 
            var expectedTokenKinds = new SyntaxKind[]
            {
                SyntaxKind.ClassKeyword,
                SyntaxKind.IdentifierToken,
                SyntaxKind.OpenBraceToken,
                SyntaxKind.CloseBraceToken,
                SyntaxKind.EndOfFileToken,
            };
 
            Assert.Equal(expectedTokenKinds.Count(), actualTokens.Count()); //redundant but helps debug
            Assert.True(expectedTokenKinds.SequenceEqual(actualTokens.Select(t => t.Kind())));
        }
 
        [WorkItem(538360, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538360")]
        [Fact]
        public void TestGetTokensCommonAny()
        {
            SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree("class C {}");
 
            var actualTokens = syntaxTree.GetRoot().DescendantTokens(syntaxTree.GetRoot().FullSpan);
 
            var expectedTokenKinds = new SyntaxKind[]
            {
                SyntaxKind.ClassKeyword,
                SyntaxKind.IdentifierToken,
                SyntaxKind.OpenBraceToken,
                SyntaxKind.CloseBraceToken,
                SyntaxKind.EndOfFileToken,
            };
 
            Assert.Equal(expectedTokenKinds.Count(), actualTokens.Count()); //redundant but helps debug
            Assert.True(expectedTokenKinds.SequenceEqual(actualTokens.Select(t => (SyntaxKind)t.RawKind)));
        }
 
        [Fact]
        public void TestGetLocation()
        {
            var tree = SyntaxFactory.ParseSyntaxTree("class C { void F() { } }");
            dynamic root = tree.GetCompilationUnitRoot();
            MethodDeclarationSyntax method = root.Members[0].Members[0];
 
            var nodeLocation = method.GetLocation();
            Assert.True(nodeLocation.IsInSource);
            Assert.Equal(tree, nodeLocation.SourceTree);
            Assert.Equal(method.Span, nodeLocation.SourceSpan);
 
            var tokenLocation = method.Identifier.GetLocation();
            Assert.True(tokenLocation.IsInSource);
            Assert.Equal(tree, tokenLocation.SourceTree);
            Assert.Equal(method.Identifier.Span, tokenLocation.SourceSpan);
 
            var triviaLocation = method.ReturnType.GetLastToken().TrailingTrivia[0].GetLocation();
            Assert.True(triviaLocation.IsInSource);
            Assert.Equal(tree, triviaLocation.SourceTree);
            Assert.Equal(method.ReturnType.GetLastToken().TrailingTrivia[0].Span, triviaLocation.SourceSpan);
 
            var textSpan = new TextSpan(5, 10);
            var spanLocation = tree.GetLocation(textSpan);
            Assert.True(spanLocation.IsInSource);
            Assert.Equal(tree, spanLocation.SourceTree);
            Assert.Equal(textSpan, spanLocation.SourceSpan);
        }
 
        [Fact]
        public void TestReplaceNode()
        {
            var expr = SyntaxFactory.ParseExpression("a + b");
            var bex = (BinaryExpressionSyntax)expr;
            var expr2 = bex.ReplaceNode(bex.Right, SyntaxFactory.ParseExpression("c"));
            Assert.Equal("a + c", expr2.ToFullString());
        }
 
        [Fact]
        public void TestReplaceNodes()
        {
            var expr = SyntaxFactory.ParseExpression("a + b + c + d");
 
            // replace each expression with a parenthesized expression
            var replaced = expr.ReplaceNodes(
                expr.DescendantNodes().OfType<ExpressionSyntax>(),
                (node, rewritten) => SyntaxFactory.ParenthesizedExpression(rewritten));
 
            var replacedText = replaced.ToFullString();
            Assert.Equal("(((a )+ (b ))+ (c ))+ (d)", replacedText);
        }
 
        [Fact]
        public void TestReplaceNodeInListWithMultiple()
        {
            var invocation = (InvocationExpressionSyntax)SyntaxFactory.ParseExpression("m(a, b)");
            var argC = SyntaxFactory.Argument(SyntaxFactory.ParseExpression("c"));
            var argD = SyntaxFactory.Argument(SyntaxFactory.ParseExpression("d"));
 
            // replace first with multiple
            var newNode = invocation.ReplaceNode(invocation.ArgumentList.Arguments[0], new SyntaxNode[] { argC, argD });
            Assert.Equal("m(c,d, b)", newNode.ToFullString());
 
            // replace last with multiple
            newNode = invocation.ReplaceNode(invocation.ArgumentList.Arguments[1], new SyntaxNode[] { argC, argD });
            Assert.Equal("m(a, c,d)", newNode.ToFullString());
 
            // replace first with empty list
            newNode = invocation.ReplaceNode(invocation.ArgumentList.Arguments[0], new SyntaxNode[] { });
            Assert.Equal("m(b)", newNode.ToFullString());
 
            // replace last with empty list
            newNode = invocation.ReplaceNode(invocation.ArgumentList.Arguments[1], new SyntaxNode[] { });
            Assert.Equal("m(a)", newNode.ToFullString());
        }
 
        [Fact]
        public void TestReplaceNonListNodeWithMultiple()
        {
            var ifstatement = (IfStatementSyntax)SyntaxFactory.ParseStatement("if (a < b) m(c)");
            var then = ifstatement.Statement;
 
            var stat1 = SyntaxFactory.ParseStatement("m1(x)");
            var stat2 = SyntaxFactory.ParseStatement("m2(y)");
 
            // you cannot replace a node that is a single node member with multiple nodes
            Assert.Throws<InvalidOperationException>(() => ifstatement.ReplaceNode(then, new[] { stat1, stat2 }));
 
            // you cannot replace a node that is a single node member with an empty list
            Assert.Throws<InvalidOperationException>(() => ifstatement.ReplaceNode(then, new StatementSyntax[] { }));
        }
 
        [Fact]
        public void TestInsertNodesInList()
        {
            var invocation = (InvocationExpressionSyntax)SyntaxFactory.ParseExpression("m(a, b)");
            var argC = SyntaxFactory.Argument(SyntaxFactory.ParseExpression("c"));
            var argD = SyntaxFactory.Argument(SyntaxFactory.ParseExpression("d"));
 
            // insert before first
            var newNode = invocation.InsertNodesBefore(invocation.ArgumentList.Arguments[0], new SyntaxNode[] { argC, argD });
            Assert.Equal("m(c,d,a, b)", newNode.ToFullString());
 
            // insert after first
            newNode = invocation.InsertNodesAfter(invocation.ArgumentList.Arguments[0], new SyntaxNode[] { argC, argD });
            Assert.Equal("m(a,c,d, b)", newNode.ToFullString());
 
            // insert before last
            newNode = invocation.InsertNodesBefore(invocation.ArgumentList.Arguments[1], new SyntaxNode[] { argC, argD });
            Assert.Equal("m(a,c,d, b)", newNode.ToFullString());
 
            // insert after last
            newNode = invocation.InsertNodesAfter(invocation.ArgumentList.Arguments[1], new SyntaxNode[] { argC, argD });
            Assert.Equal("m(a, b,c,d)", newNode.ToFullString());
        }
 
        [Fact]
        public void TestInsertNodesRelativeToNonListNode()
        {
            var ifstatement = (IfStatementSyntax)SyntaxFactory.ParseStatement("if (a < b) m(c)");
            var then = ifstatement.Statement;
 
            var stat1 = SyntaxFactory.ParseStatement("m1(x)");
            var stat2 = SyntaxFactory.ParseStatement("m2(y)");
 
            // you cannot insert nodes before/after a node that is not part of a list
            Assert.Throws<InvalidOperationException>(() => ifstatement.InsertNodesBefore(then, new[] { stat1, stat2 }));
 
            // you cannot insert nodes before/after a node that is not part of a list
            Assert.Throws<InvalidOperationException>(() => ifstatement.InsertNodesAfter(then, new StatementSyntax[] { }));
        }
 
        [Fact]
        public void TestReplaceStatementInListWithMultiple()
        {
            var block = (BlockSyntax)SyntaxFactory.ParseStatement("{ var x = 10; var y = 20; }");
            var stmt1 = SyntaxFactory.ParseStatement("var z = 30; ");
            var stmt2 = SyntaxFactory.ParseStatement("var q = 40; ");
 
            // replace first with multiple
            var newBlock = block.ReplaceNode(block.Statements[0], new[] { stmt1, stmt2 });
            Assert.Equal("{ var z = 30; var q = 40; var y = 20; }", newBlock.ToFullString());
 
            // replace second with multiple
            newBlock = block.ReplaceNode(block.Statements[1], new[] { stmt1, stmt2 });
            Assert.Equal("{ var x = 10; var z = 30; var q = 40; }", newBlock.ToFullString());
 
            // replace first with empty list
            newBlock = block.ReplaceNode(block.Statements[0], new SyntaxNode[] { });
            Assert.Equal("{ var y = 20; }", newBlock.ToFullString());
 
            // replace second with empty list
            newBlock = block.ReplaceNode(block.Statements[1], new SyntaxNode[] { });
            Assert.Equal("{ var x = 10; }", newBlock.ToFullString());
        }
 
        [Fact]
        public void TestInsertStatementsInList()
        {
            var block = (BlockSyntax)SyntaxFactory.ParseStatement("{ var x = 10; var y = 20; }");
            var stmt1 = SyntaxFactory.ParseStatement("var z = 30; ");
            var stmt2 = SyntaxFactory.ParseStatement("var q = 40; ");
 
            // insert before first
            var newBlock = block.InsertNodesBefore(block.Statements[0], new[] { stmt1, stmt2 });
            Assert.Equal("{ var z = 30; var q = 40; var x = 10; var y = 20; }", newBlock.ToFullString());
 
            // insert after first
            newBlock = block.InsertNodesAfter(block.Statements[0], new[] { stmt1, stmt2 });
            Assert.Equal("{ var x = 10; var z = 30; var q = 40; var y = 20; }", newBlock.ToFullString());
 
            // insert before last
            newBlock = block.InsertNodesBefore(block.Statements[1], new[] { stmt1, stmt2 });
            Assert.Equal("{ var x = 10; var z = 30; var q = 40; var y = 20; }", newBlock.ToFullString());
 
            // insert after last
            newBlock = block.InsertNodesAfter(block.Statements[1], new[] { stmt1, stmt2 });
            Assert.Equal("{ var x = 10; var y = 20; var z = 30; var q = 40; }", newBlock.ToFullString());
        }
 
        [Fact]
        public void TestReplaceSingleToken()
        {
            var expr = SyntaxFactory.ParseExpression("a + b");
            var bToken = expr.DescendantTokens().First(t => t.Text == "b");
            var expr2 = expr.ReplaceToken(bToken, SyntaxFactory.ParseToken("c"));
            Assert.Equal("a + c", expr2.ToString());
        }
 
        [Fact]
        public void TestReplaceMultipleTokens()
        {
            var expr = SyntaxFactory.ParseExpression("a + b + c");
            var d = SyntaxFactory.ParseToken("d ");
            var tokens = expr.DescendantTokens().Where(t => t.IsKind(SyntaxKind.IdentifierToken)).ToList();
            var replaced = expr.ReplaceTokens(tokens, (tok, tok2) => d);
            Assert.Equal("d + d + d ", replaced.ToFullString());
        }
 
        [Fact]
        public void TestReplaceSingleTokenWithMultipleTokens()
        {
            var cu = SyntaxFactory.ParseCompilationUnit("private class C { }");
            var privateToken = ((ClassDeclarationSyntax)cu.Members[0]).Modifiers[0];
            var publicToken = SyntaxFactory.ParseToken("public ");
            var partialToken = SyntaxFactory.ParseToken("partial ");
 
            var cu1 = cu.ReplaceToken(privateToken, publicToken);
            Assert.Equal("public class C { }", cu1.ToFullString());
 
            var cu2 = cu.ReplaceToken(privateToken, new[] { publicToken, partialToken });
            Assert.Equal("public partial class C { }", cu2.ToFullString());
 
            var cu3 = cu.ReplaceToken(privateToken, new SyntaxToken[] { });
            Assert.Equal("class C { }", cu3.ToFullString());
        }
 
        [Fact]
        public void TestReplaceNonListTokenWithMultipleTokensFails()
        {
            var cu = SyntaxFactory.ParseCompilationUnit("private class C { }");
            var identifierC = cu.DescendantTokens().First(t => t.Text == "C");
 
            var identifierA = SyntaxFactory.ParseToken("A");
            var identifierB = SyntaxFactory.ParseToken("B");
 
            // you cannot replace a token that is a single token member with multiple tokens
            Assert.Throws<InvalidOperationException>(() => cu.ReplaceToken(identifierC, new[] { identifierA, identifierB }));
 
            // you cannot replace a token that is a single token member with an empty list of tokens
            Assert.Throws<InvalidOperationException>(() => cu.ReplaceToken(identifierC, new SyntaxToken[] { }));
        }
 
        [Fact]
        public void TestInsertTokens()
        {
            var cu = SyntaxFactory.ParseCompilationUnit("public class C { }");
            var publicToken = ((ClassDeclarationSyntax)cu.Members[0]).Modifiers[0];
            var partialToken = SyntaxFactory.ParseToken("partial ");
            var staticToken = SyntaxFactory.ParseToken("static ");
 
            var cu1 = cu.InsertTokensBefore(publicToken, new[] { staticToken });
            Assert.Equal("static public class C { }", cu1.ToFullString());
 
            var cu2 = cu.InsertTokensAfter(publicToken, new[] { staticToken });
            Assert.Equal("public static class C { }", cu2.ToFullString());
        }
 
        [Fact]
        public void TestInsertTokensRelativeToNonListToken()
        {
            var cu = SyntaxFactory.ParseCompilationUnit("public class C { }");
            var identifierC = cu.DescendantTokens().First(t => t.Text == "C");
 
            var identifierA = SyntaxFactory.ParseToken("A");
            var identifierB = SyntaxFactory.ParseToken("B");
 
            // you cannot insert a token before/after a token that is not part of a list of tokens
            Assert.Throws<InvalidOperationException>(() => cu.InsertTokensBefore(identifierC, new[] { identifierA, identifierB }));
 
            // you cannot insert a token before/after a token that is not part of a list of tokens
            Assert.Throws<InvalidOperationException>(() => cu.InsertTokensAfter(identifierC, new[] { identifierA, identifierB }));
        }
 
        [Fact]
        public void ReplaceMissingToken()
        {
            var text = "return x";
            var expr = SyntaxFactory.ParseStatement(text);
 
            var token = expr.DescendantTokens().First(t => t.IsMissing);
 
            var expr2 = expr.ReplaceToken(token, SyntaxFactory.Token(token.Kind()));
            var text2 = expr2.ToFullString();
 
            Assert.Equal("return x;", text2);
        }
 
        [Fact]
        public void ReplaceEndOfCommentToken()
        {
            var text = "/// Goo\r\n return x;";
            var expr = SyntaxFactory.ParseStatement(text);
 
            var tokens = expr.DescendantTokens(descendIntoTrivia: true).ToList();
            var token = tokens.First(t => t.Kind() == SyntaxKind.EndOfDocumentationCommentToken);
 
            var expr2 = expr.ReplaceToken(token, SyntaxFactory.Token(SyntaxTriviaList.Create(SyntaxFactory.Whitespace("garbage")), token.Kind(), default(SyntaxTriviaList)));
            var text2 = expr2.ToFullString();
 
            Assert.Equal("/// Goo\r\ngarbage return x;", text2);
        }
 
        [Fact]
        public void ReplaceEndOfFileToken()
        {
            var text = "";
            var cu = SyntaxFactory.ParseCompilationUnit(text);
            var token = cu.DescendantTokens().Single(t => t.Kind() == SyntaxKind.EndOfFileToken);
 
            var cu2 = cu.ReplaceToken(token, SyntaxFactory.Token(SyntaxTriviaList.Create(SyntaxFactory.Whitespace("  ")), token.Kind(), default(SyntaxTriviaList)));
            var text2 = cu2.ToFullString();
 
            Assert.Equal("  ", text2);
        }
 
        [Fact]
        public void TestReplaceTriviaDeep()
        {
            var expr = SyntaxFactory.ParseExpression("#if true\r\na + \r\n#endif\r\n + b");
 
            // get whitespace trivia inside structured directive trivia
            var deepTrivia = expr.GetDirectives().SelectMany(d => d.DescendantTrivia().Where(tr => tr.Kind() == SyntaxKind.WhitespaceTrivia)).ToList();
 
            // replace deep trivia with double-whitespace trivia
            var twoSpace = SyntaxFactory.Whitespace("  ");
            var expr2 = expr.ReplaceTrivia(deepTrivia, (tr, tr2) => twoSpace);
 
            Assert.Equal("#if  true\r\na + \r\n#endif\r\n + b", expr2.ToFullString());
        }
 
        [Fact]
        public void TestReplaceSingleTriviaInNode()
        {
            var expr = SyntaxFactory.ParseExpression("a + b");
            var trivia = expr.DescendantTokens().First(t => t.Text == "a").TrailingTrivia[0];
            var twoSpaces = SyntaxFactory.Whitespace("  ");
            var expr2 = expr.ReplaceTrivia(trivia, twoSpaces);
            Assert.Equal("a  + b", expr2.ToFullString());
        }
 
        [Fact]
        public void TestReplaceMultipleTriviaInNode()
        {
            var expr = SyntaxFactory.ParseExpression("a + b");
            var twoSpaces = SyntaxFactory.Whitespace("  ");
            var trivia = expr.DescendantTrivia().Where(tr => tr.IsKind(SyntaxKind.WhitespaceTrivia)).ToList();
            var replaced = expr.ReplaceTrivia(trivia, (tr, tr2) => twoSpaces);
            Assert.Equal("a  +  b", replaced.ToFullString());
        }
 
        [Fact]
        public void TestReplaceSingleTriviaWithMultipleTriviaInNode()
        {
            var ex = SyntaxFactory.ParseExpression("/* c */ identifier");
            var leadingTrivia = ex.GetLeadingTrivia();
            Assert.Equal(2, leadingTrivia.Count);
            var comment1 = leadingTrivia[0];
            Assert.Equal(SyntaxKind.MultiLineCommentTrivia, comment1.Kind());
 
            var newComment1 = SyntaxFactory.ParseLeadingTrivia("/* a */")[0];
            var newComment2 = SyntaxFactory.ParseLeadingTrivia("/* b */")[0];
 
            var ex1 = ex.ReplaceTrivia(comment1, newComment1);
            Assert.Equal("/* a */ identifier", ex1.ToFullString());
 
            var ex2 = ex.ReplaceTrivia(comment1, new[] { newComment1, newComment2 });
            Assert.Equal("/* a *//* b */ identifier", ex2.ToFullString());
 
            var ex3 = ex.ReplaceTrivia(comment1, new SyntaxTrivia[] { });
            Assert.Equal(" identifier", ex3.ToFullString());
        }
 
        [Fact]
        public void TestInsertTriviaInNode()
        {
            var ex = SyntaxFactory.ParseExpression("/* c */ identifier");
            var leadingTrivia = ex.GetLeadingTrivia();
            Assert.Equal(2, leadingTrivia.Count);
            var comment1 = leadingTrivia[0];
            Assert.Equal(SyntaxKind.MultiLineCommentTrivia, comment1.Kind());
 
            var newComment1 = SyntaxFactory.ParseLeadingTrivia("/* a */")[0];
            var newComment2 = SyntaxFactory.ParseLeadingTrivia("/* b */")[0];
 
            var ex1 = ex.InsertTriviaBefore(comment1, new[] { newComment1, newComment2 });
            Assert.Equal("/* a *//* b *//* c */ identifier", ex1.ToFullString());
 
            var ex2 = ex.InsertTriviaAfter(comment1, new[] { newComment1, newComment2 });
            Assert.Equal("/* c *//* a *//* b */ identifier", ex2.ToFullString());
        }
 
        [Fact]
        public void TestReplaceSingleTriviaInToken()
        {
            var id = SyntaxFactory.ParseToken("a ");
            var trivia = id.TrailingTrivia[0];
            var twoSpace = SyntaxFactory.Whitespace("  ");
            var id2 = id.ReplaceTrivia(trivia, twoSpace);
            Assert.Equal("a  ", id2.ToFullString());
        }
 
        [Fact]
        public void TestReplaceMultipleTriviaInToken()
        {
            var id = SyntaxFactory.ParseToken("a // goo\r\n");
 
            // replace each trivia with a single space
            var id2 = id.ReplaceTrivia(id.GetAllTrivia(), (tr, tr2) => SyntaxFactory.Space);
 
            // should be 3 spaces (one for original space, comment and end-of-line)
            Assert.Equal("a   ", id2.ToFullString());
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepExteriorTrivia()
        {
            var expr = SyntaxFactory.ParseExpression("m(a, b, /* trivia */ c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal("m(a , /* trivia */ c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepExteriorTrivia_2()
        {
            var expr = SyntaxFactory.ParseExpression(@"m(a, b, /* trivia */
c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal(@"m(a,  /* trivia */
c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepExteriorTrivia_3()
        {
            var expr = SyntaxFactory.ParseExpression(@"m(a, b,
/* trivia */ c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal(@"m(a, 
/* trivia */ c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepExteriorTrivia_4()
        {
            var expr = SyntaxFactory.ParseExpression(@"SomeMethod(/*arg1:*/ a,
    /*arg2:*/ b,
    /*arg3:*/ c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal(@"SomeMethod(/*arg1:*/ a,
    /*arg2:*/ 
    /*arg3:*/ c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepExteriorTrivia_5()
        {
            var expr = SyntaxFactory.ParseExpression(@"SomeMethod(// comment about a
           a,
           // some comment about b
           b,
           // some comment about c
           c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal(@"SomeMethod(// comment about a
           a,
           // some comment about b
           
           // some comment about c
           c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepNoTrivia()
        {
            var expr = SyntaxFactory.ParseExpression("m(a, b, /* trivia */ c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepNoTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal("m(a, /* trivia */ c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepNoTrivia_2()
        {
            var expr = SyntaxFactory.ParseExpression(
                @"m(a, b, /* trivia */ 
c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepNoTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal(@"m(a, c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepNoTrivia_3()
        {
            var expr = SyntaxFactory.ParseExpression(
                @"m(a, b,
/* trivia */ c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepNoTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal(@"m(a, /* trivia */ c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepNoTrivia_4()
        {
            var expr = SyntaxFactory.ParseExpression(@"SomeMethod(/*arg1:*/ a,
    /*arg2:*/ b,
    /*arg3:*/ c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepNoTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal(@"SomeMethod(/*arg1:*/ a,
    /*arg3:*/ c)", text);
        }
 
        [Fact]
        public void TestRemoveNodeInSeparatedList_KeepNoTrivia_5()
        {
            var expr = SyntaxFactory.ParseExpression(@"SomeMethod(// comment about a
           a,
           // some comment about b
           b,
           // some comment about c
           c)");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepNoTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal(@"SomeMethod(// comment about a
           a,
           // some comment about c
           c)", text);
        }
 
        [Fact]
        public void TestRemoveOnlyNodeInSeparatedList_KeepExteriorTrivia()
        {
            var expr = SyntaxFactory.ParseExpression("m(/* before */ a /* after */)");
 
            var n = expr.DescendantTokens().Where(t => t.Text == "a").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(n);
 
            var expr2 = expr.RemoveNode(n, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal("m(/* before */  /* after */)", text);
        }
 
        [Fact]
        public void TestRemoveFirstNodeInSeparatedList_KeepExteriorTrivia()
        {
            var expr = SyntaxFactory.ParseExpression("m(/* before */ a /* after */, b, c)");
 
            var n = expr.DescendantTokens().Where(t => t.Text == "a").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(n);
 
            var expr2 = expr.RemoveNode(n, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal("m(/* before */  /* after */ b, c)", text);
        }
 
        [Fact]
        public void TestRemoveLastNodeInSeparatedList_KeepExteriorTrivia()
        {
            var expr = SyntaxFactory.ParseExpression("m(a, b, /* before */ c /* after */)");
 
            var n = expr.DescendantTokens().Where(t => t.Text == "c").Select(t => t.Parent.FirstAncestorOrSelf<ArgumentSyntax>()).FirstOrDefault();
            Assert.NotNull(n);
 
            var expr2 = expr.RemoveNode(n, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal("m(a, b /* before */  /* after */)", text);
        }
 
        [Fact]
        public void TestRemoveNode_KeepNoTrivia()
        {
            var expr = SyntaxFactory.ParseStatement("{ a; b; /* trivia */ c }");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<StatementSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepNoTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal("{ a; c }", text);
        }
 
        [Fact]
        public void TestRemoveNode_KeepExteriorTrivia()
        {
            var expr = SyntaxFactory.ParseStatement("{ a; b; /* trivia */ c }");
 
            var b = expr.DescendantTokens().Where(t => t.Text == "b").Select(t => t.Parent.FirstAncestorOrSelf<StatementSyntax>()).FirstOrDefault();
            Assert.NotNull(b);
 
            var expr2 = expr.RemoveNode(b, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = expr2.ToFullString();
            Assert.Equal("{ a;  /* trivia */ c }", text);
        }
 
        [Fact]
        public void TestRemoveLastNode_KeepExteriorTrivia()
        {
            // this tests removing the last node in a non-terminal such that there is no token to the right of the removed
            // node to attach the kept trivia too.  The trivia must be attached to the previous token.
 
            var cu = SyntaxFactory.ParseCompilationUnit("class C { void M() { } /* trivia */ }");
 
            var m = cu.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault();
            Assert.NotNull(m);
 
            // remove the body block from the method syntax (since it can be set to null)
            var m2 = m.RemoveNode(m.Body, SyntaxRemoveOptions.KeepExteriorTrivia);
 
            var text = m2.ToFullString();
 
            Assert.Equal("void M()  /* trivia */ ", text);
        }
 
        [Fact]
        public void TestRemove_KeepExteriorTrivia_KeepUnbalancedDirectives()
        {
            var cu = SyntaxFactory.ParseCompilationUnit(@"
class C
{
// before
void M()
{
#region Fred
} // after
#endregion
}");
 
            var expectedText = @"
class C
{
// before
#region Fred
 // after
#endregion
}";
 
            var m = cu.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault();
            Assert.NotNull(m);
 
            var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepExteriorTrivia | SyntaxRemoveOptions.KeepUnbalancedDirectives);
 
            var text = cu2.ToFullString();
 
            Assert.Equal(expectedText, text);
        }
 
        [Fact]
        public void TestRemove_KeepUnbalancedDirectives()
        {
            var inputText = @"
class C
{
// before
#region Fred
// more before
void M()
{
} // after
#endregion
}";
 
            var expectedText = @"
class C
{
 
#region Fred
#endregion
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepUnbalancedDirectives);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/19613")]
        public void TestRemove_KeepUnbalancedDirectives_Indented()
        {
            var inputText = """
                class C
                {
                    // before
                    #region Fred
                    // more before
                    void M()
                    {
                    } // after
                    #endregion
                }
                """;
 
            var expectedText = """
                class C
                {
 
                    #region Fred
                    #endregion
                }
                """;
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepUnbalancedDirectives);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        public void TestRemove_KeepDirectives()
        {
            var inputText = @"
class C
{
// before
#region Fred
// more before
void M()
{
#if true
#endif
} // after
#endregion
}";
 
            var expectedText = @"
class C
{
 
#region Fred
#if true
#endif
#endregion
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepDirectives);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemove_KeepEndOfLine()
        {
            var inputText = @"
class C
{
// before
void M()
{
} // after
}";
 
            var expectedText = @"
class C
{
 
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveWithoutEOL_KeepEndOfLine()
        {
            var cu = SyntaxFactory.ParseCompilationUnit(@"class A { } class B { } // test");
 
            var m = cu.DescendantNodes().OfType<TypeDeclarationSyntax>().LastOrDefault();
            Assert.NotNull(m);
 
            var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine);
 
            var text = cu2.ToFullString();
 
            Assert.Equal("class A { } ", text);
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveBadDirectiveWithoutEOL_KeepEndOfLine_KeepDirectives()
        {
            var cu = SyntaxFactory.ParseCompilationUnit(@"class A { } class B { } #endregion");
 
            var m = cu.DescendantNodes().OfType<TypeDeclarationSyntax>().LastOrDefault();
            Assert.NotNull(m);
 
            var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine | SyntaxRemoveOptions.KeepDirectives);
 
            var text = cu2.ToFullString();
 
            Assert.Equal("class A { } ", text);
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveDocument_KeepEndOfLine()
        {
            var cu = SyntaxFactory.ParseCompilationUnit(@"
#region A
class A 
{ } 
#endregion");
 
            var cu2 = cu.RemoveNode(cu, SyntaxRemoveOptions.KeepEndOfLine);
 
            Assert.Null(cu2);
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveFirstParameterEOLCommaTokenTrailingTrivia_KeepEndOfLine()
        {
            // EOL should be found on CommaToken TrailingTrivia
            var inputText = @"
class C
{
void M(
// before a
int a,
// after a
// before b
int b
/* after b*/)
{
}
}";
 
            var expectedText = @"
class C
{
void M(
 
// after a
// before b
int b
/* after b*/)
{
}
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<ParameterSyntax>().FirstOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveFirstParameterEOLParameterSyntaxTrailingTrivia_KeepEndOfLine()
        {
            // EOL should be found on ParameterSyntax TrailingTrivia
            var inputText = @"
class C
{
void M(
// before a
int a
, /* after comma */ int b
/* after b*/)
{
}
}";
 
            var expectedText = @"
class C
{
void M(
 
int b
/* after b*/)
{
}
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<ParameterSyntax>().FirstOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveFirstParameterEOLCommaTokenLeadingTrivia_KeepEndOfLine()
        {
            // EOL should be found on CommaToken LeadingTrivia and also on ParameterSyntax TrailingTrivia
            // but only one will be added
            var inputText = @"
class C
{
void M(
// before a
int a
 
// before b
, /* after comma */ int b
/* after b*/)
{
}
}";
 
            var expectedText = @"
class C
{
void M(
 
int b
/* after b*/)
{
}
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<ParameterSyntax>().FirstOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveFirstParameter_KeepTrailingTrivia()
        {
            var cu = SyntaxFactory.ParseCompilationUnit(@"
class C
{
void M(
// before a
int a
 
// before b
, /* after comma */ int b
/* after b*/)
{
}
}");
 
            var expectedText = @"
class C
{
void M(
 
 
// before b
 /* after comma */ int b
/* after b*/)
{
}
}";
 
            var m = cu.DescendantNodes().OfType<ParameterSyntax>().FirstOrDefault();
            Assert.NotNull(m);
 
            var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepTrailingTrivia);
 
            var text = cu2.ToFullString();
 
            Assert.Equal(expectedText, text);
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveLastParameterEOLCommaTokenLeadingTrivia_KeepEndOfLine()
        {
            // EOL should be found on CommaToken LeadingTrivia
            var inputText = @"
class C
{
void M(
// before a
int a
 
// after a
, /* after comma*/ int b /* after b*/)
{
}
}";
 
            var expectedText = @"
class C
{
void M(
// before a
int a
 
)
{
}
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<ParameterSyntax>().LastOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveLastParameterEOLCommaTokenTrailingTrivia_KeepEndOfLine()
        {
            // EOL should be found on CommaToken TrailingTrivia
            var inputText = @"
class C
{
void M(
// before a
int a, /* after comma*/ 
int b /* after b*/)
{
}
}";
 
            var expectedText = @"
class C
{
void M(
// before a
int a
)
{
}
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<ParameterSyntax>().LastOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveLastParameterEOLParameterSyntaxLeadingTrivia_KeepEndOfLine()
        {
            // EOL should be found on ParameterSyntax LeadingTrivia and also on CommaToken TrailingTrivia
            // but only one will be added
            var inputText = @"
class C
{
void M(
// before a
int a, /* after comma */ 
 
// before b
int b /* after b*/)
{
}
}";
 
            var expectedText = @"
class C
{
void M(
// before a
int a
)
{
}
}";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<ParameterSyntax>().LastOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveLastParameter_KeepLeadingTrivia()
        {
            var cu = SyntaxFactory.ParseCompilationUnit(@"
class C
{
void M(
// before a
int a, /* after comma */ 
 
// before b
int b /* after b*/)
{
}
}");
 
            var expectedText = @"
class C
{
void M(
// before a
int a /* after comma */ 
 
// before b
)
{
}
}";
 
            var m = cu.DescendantNodes().OfType<ParameterSyntax>().LastOrDefault();
            Assert.NotNull(m);
 
            var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepLeadingTrivia);
 
            var text = cu2.ToFullString();
 
            Assert.Equal(expectedText, text);
        }
 
        [Fact]
        [WorkItem(22924, "https://github.com/dotnet/roslyn/issues/22924")]
        public void TestRemoveClassWithEndRegionDirectiveWithoutEOL_KeepEndOfLine_KeepDirectives()
        {
            var inputText = @"
#region A
class A { } #endregion";
 
            var expectedText = @"
#region A
";
 
            TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) =>
            {
                var m = cu.DescendantNodes().OfType<TypeDeclarationSyntax>().FirstOrDefault();
                Assert.NotNull(m);
 
                var cu2 = cu.RemoveNode(m, SyntaxRemoveOptions.KeepEndOfLine | SyntaxRemoveOptions.KeepDirectives);
 
                var text = cu2.ToFullString();
 
                Assert.Equal(expected, text);
            });
        }
 
        [Fact]
        public void SeparatorsOfSeparatedSyntaxLists()
        {
            var s1 = "int goo(int a, int b, int c) {}";
            var tree = SyntaxFactory.ParseSyntaxTree(s1);
 
            var root = tree.GetCompilationUnitRoot();
            var method = (LocalFunctionStatementSyntax)((GlobalStatementSyntax)root.Members[0]).Statement;
 
            var list = (SeparatedSyntaxList<ParameterSyntax>)method.ParameterList.Parameters;
 
            Assert.Equal(SyntaxKind.CommaToken, ((SyntaxToken)list.GetSeparator(0)).Kind());
            Assert.Equal(SyntaxKind.CommaToken, ((SyntaxToken)list.GetSeparator(1)).Kind());
 
            foreach (var index in new int[] { -1, 2 })
            {
                bool exceptionThrown = false;
                try
                {
                    var unused = list.GetSeparator(2);
                }
                catch (ArgumentOutOfRangeException)
                {
                    exceptionThrown = true;
                }
                Assert.True(exceptionThrown);
            }
 
            var internalParameterList = (InternalSyntax.ParameterListSyntax)method.ParameterList.Green;
            var internalParameters = internalParameterList.Parameters;
 
            Assert.Equal(2, internalParameters.SeparatorCount);
            Assert.Equal(SyntaxKind.CommaToken, (new SyntaxToken(internalParameters.GetSeparator(0))).Kind());
            Assert.Equal(SyntaxKind.CommaToken, (new SyntaxToken(internalParameters.GetSeparator(1))).Kind());
 
            Assert.Equal(3, internalParameters.Count);
            Assert.Equal("a", internalParameters[0].Identifier.ValueText);
            Assert.Equal("b", internalParameters[1].Identifier.ValueText);
            Assert.Equal("c", internalParameters[2].Identifier.ValueText);
        }
 
        [Fact]
        public void ThrowIfUnderlyingNodeIsNullForList()
        {
            var list = new SyntaxNodeOrTokenList();
            Assert.Equal(0, list.Count);
 
            foreach (var index in new int[] { -1, 0, 23 })
            {
                bool exceptionThrown = false;
                try
                {
                    var unused = list[0];
                }
                catch (ArgumentOutOfRangeException)
                {
                    exceptionThrown = true;
                }
                Assert.True(exceptionThrown);
            }
        }
 
        [WorkItem(541188, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541188")]
        [Fact]
        public void GetDiagnosticsOnMissingToken()
        {
            var syntaxTree = SyntaxFactory.ParseSyntaxTree(@"namespace n1 { c1<t");
            var token = syntaxTree.FindNodeOrTokenByKind(SyntaxKind.GreaterThanToken);
            var diag = syntaxTree.GetDiagnostics(token).ToList();
 
            Assert.True(token.IsMissing);
            Assert.Equal(1, diag.Count);
        }
 
        [WorkItem(541325, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541325")]
        [Fact]
        public void GetDiagnosticsOnMissingToken2()
        {
            var syntaxTree = SyntaxFactory.ParseSyntaxTree(@"
class Base<T>
{
    public virtual int Property
    {
        get { return 0; }
        // Note: Repro for bug 7990 requires a missing close brace token i.e. missing } below
        set { 
    }
    public virtual void Method()
    {
    }
}");
            foreach (var t in syntaxTree.GetCompilationUnitRoot().DescendantTokens())
            {
                // Bug 7990: Below for loop is an infinite loop.
                foreach (var e in syntaxTree.GetDiagnostics(t))
                {
                }
            }
 
            // TODO: Please add meaningful checks once the above deadlock issue is fixed.
        }
 
        [WorkItem(541648, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541648")]
        [Fact]
        public void GetDiagnosticsOnMissingToken4()
        {
            string code = @"
public class MyClass
{	
using Lib;
using Lib2;
 
public class Test1
{
}
}";
            var syntaxTree = SyntaxFactory.ParseSyntaxTree(code);
            var token = syntaxTree.GetCompilationUnitRoot().FindToken(code.IndexOf("using Lib;", StringComparison.Ordinal));
            var diag = syntaxTree.GetDiagnostics(token).ToList();
 
            Assert.True(token.IsMissing);
            Assert.Equal(3, diag.Count);
        }
 
        [WorkItem(541630, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541630")]
        [Fact]
        public void GetDiagnosticsOnBadReferenceDirective()
        {
            string code = @"class c1
{
    #r
    void m1()
    {
    }
}";
            var tree = SyntaxFactory.ParseSyntaxTree(code);
            var trivia = tree.GetCompilationUnitRoot().FindTrivia(code.IndexOf("#r", StringComparison.Ordinal)); // ReferenceDirective.
 
            foreach (var diag in tree.GetDiagnostics(trivia))
            {
                Assert.NotNull(diag);
                // TODO: Please add any additional validations if necessary.
            }
        }
 
        [WorkItem(528626, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528626")]
        [Fact]
        public void SpanOfNodeWithMissingChildren()
        {
            string code = @"delegate = 1;";
            var tree = SyntaxFactory.ParseSyntaxTree(code);
            var compilationUnit = tree.GetCompilationUnitRoot();
            var delegateDecl = (DelegateDeclarationSyntax)compilationUnit.Members[0];
            var paramList = delegateDecl.ParameterList;
 
            // For (non-EOF) tokens, IsMissing is true if and only if Width is 0.
            Assert.True(compilationUnit.DescendantTokens(node => true).
                Where(token => token.Kind() != SyntaxKind.EndOfFileToken).
                All(token => token.IsMissing == (token.Width == 0)));
 
            // For non-terminals, Is true if Width is 0, but the converse may not hold.
            Assert.True(paramList.IsMissing);
            Assert.NotEqual(0, paramList.Width);
            Assert.NotEqual(0, paramList.FullWidth);
        }
 
        [WorkItem(542457, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542457")]
        [Fact]
        public void AddMethodModifier()
        {
            var tree = SyntaxFactory.ParseSyntaxTree(@"
class Program
{
    static void Main(string[] args)
    {
    }
}");
            var compilationUnit = tree.GetCompilationUnitRoot();
            var @class = (ClassDeclarationSyntax)compilationUnit.Members.Single();
            var method = (MethodDeclarationSyntax)@class.Members.Single();
            var newModifiers = method.Modifiers.Add(SyntaxFactory.Token(default(SyntaxTriviaList), SyntaxKind.UnsafeKeyword, SyntaxFactory.TriviaList(SyntaxFactory.Space)));
            Assert.Equal("    static unsafe ", newModifiers.ToFullString());
            Assert.Equal(2, newModifiers.Count);
            Assert.Equal(SyntaxKind.StaticKeyword, newModifiers[0].Kind());
            Assert.Equal(SyntaxKind.UnsafeKeyword, newModifiers[1].Kind());
        }
 
        [Fact]
        public void SeparatedSyntaxListValidation()
        {
            var intType = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword));
            var commaToken = SyntaxFactory.Token(SyntaxKind.CommaToken);
 
            SyntaxFactory.SingletonSeparatedList<TypeSyntax>(intType);
            SyntaxFactory.SeparatedList<TypeSyntax>(new SyntaxNodeOrToken[] { intType, commaToken });
            SyntaxFactory.SeparatedList<TypeSyntax>(new SyntaxNodeOrToken[] { intType, commaToken, intType });
            SyntaxFactory.SeparatedList<TypeSyntax>(new SyntaxNodeOrToken[] { intType, commaToken, intType, commaToken });
 
            Assert.Throws<ArgumentException>(() => SyntaxFactory.SeparatedList<TypeSyntax>(new SyntaxNodeOrToken[] { commaToken }));
            Assert.Throws<ArgumentException>(() => SyntaxFactory.SeparatedList<TypeSyntax>(new SyntaxNodeOrToken[] { intType, commaToken, commaToken }));
            Assert.Throws<ArgumentException>(() => SyntaxFactory.SeparatedList<TypeSyntax>(new SyntaxNodeOrToken[] { intType, intType }));
        }
 
        [WorkItem(543310, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543310")]
        [Fact]
        public void SyntaxDotParseCompilationUnitContainingOnlyWhitespace()
        {
            var node = SyntaxFactory.ParseCompilationUnit("  ");
            Assert.True(node.HasLeadingTrivia);
            Assert.Equal(1, node.GetLeadingTrivia().Count);
            Assert.Equal(1, node.DescendantTrivia().Count());
            Assert.Equal("  ", node.GetLeadingTrivia().First().ToString());
        }
 
        [WorkItem(543310, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543310")]
        [Fact]
        public void SyntaxTreeDotParseCompilationUnitContainingOnlyWhitespace()
        {
            var node = SyntaxFactory.ParseSyntaxTree("  ").GetCompilationUnitRoot();
            Assert.True(node.HasLeadingTrivia);
            Assert.Equal(1, node.GetLeadingTrivia().Count);
            Assert.Equal(1, node.DescendantTrivia().Count());
            Assert.Equal("  ", node.GetLeadingTrivia().First().ToString());
        }
 
        [Fact]
        public void SyntaxNodeAndTokenToString()
        {
            var text = @"class A { }";
            var root = SyntaxFactory.ParseCompilationUnit(text);
            var children = root.DescendantNodesAndTokens();
 
            var nodeOrToken = children.First();
            Assert.Equal("class A { }", nodeOrToken.ToString());
            Assert.Equal(text, nodeOrToken.ToString());
 
            var node = (SyntaxNode)children.First(n => n.IsNode);
            Assert.Equal("class A { }", node.ToString());
            Assert.Equal(text, node.ToFullString());
 
            var token = (SyntaxToken)children.First(n => n.IsToken);
            Assert.Equal("class", token.ToString());
            Assert.Equal("class ", token.ToFullString());
 
            var trivia = root.DescendantTrivia().First();
            Assert.Equal(" ", trivia.ToString());
            Assert.Equal(" ", trivia.ToFullString());
        }
 
        [WorkItem(545116, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545116")]
        [Fact]
        public void FindTriviaOutsideNode()
        {
            var text = @"// This is trivia
class C
{
    static void Main()
    {
    }
}
";
            var root = SyntaxFactory.ParseCompilationUnit(text);
            Assert.InRange(0, root.FullSpan.Start, root.FullSpan.End);
            var rootTrivia = root.FindTrivia(0);
            Assert.Equal("// This is trivia", rootTrivia.ToString().Trim());
 
            var method = root.DescendantNodes().OfType<MethodDeclarationSyntax>().Single();
            Assert.NotInRange(0, method.FullSpan.Start, method.FullSpan.End);
            var methodTrivia = method.FindTrivia(0);
            Assert.Equal(default(SyntaxTrivia), methodTrivia);
        }
 
        [Fact]
        public void TestSyntaxTriviaListEquals()
        {
            var emptyWhitespace = SyntaxFactory.Whitespace("");
            var emptyToken = SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken).WithTrailingTrivia(emptyWhitespace, emptyWhitespace);
            var emptyTokenList = SyntaxFactory.TokenList(emptyToken, emptyToken);
 
            // elements should be not equal
            Assert.NotEqual(emptyTokenList[0].TrailingTrivia[0], emptyTokenList[1].TrailingTrivia[0]);
 
            // lists should be not equal
            Assert.NotEqual(emptyTokenList[0].TrailingTrivia, emptyTokenList[1].TrailingTrivia);
 
            // Two lists with the same parent node, but different indexes should NOT be the same.
            var emptyTriviaList = SyntaxFactory.TriviaList(emptyWhitespace, emptyWhitespace);
            emptyToken = emptyToken.WithLeadingTrivia(emptyTriviaList).WithTrailingTrivia(emptyTriviaList);
 
            // elements should be not equal
            Assert.NotEqual(emptyToken.LeadingTrivia[0], emptyToken.TrailingTrivia[0]);
 
            // lists should be not equal
            Assert.NotEqual(emptyToken.LeadingTrivia, emptyToken.TrailingTrivia);
        }
 
        [Fact]
        public void Test_SyntaxTree_ParseTextInvalidArguments()
        {
            // Invalid arguments - Validate Exceptions     
            Assert.Throws<System.ArgumentNullException>(delegate
            {
                SourceText st = null;
                var treeFromSource_invalid2 = SyntaxFactory.ParseSyntaxTree(st);
            });
        }
 
        [Fact]
        public void TestSyntaxTree_Changes()
        {
            string SourceText = @"using System;
using System.Linq;
using System.Collections;
 
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(""Hello, World!"");
        }
    }";
 
            SyntaxTree tree = SyntaxFactory.ParseSyntaxTree(SourceText);
            var root = (CompilationUnitSyntax)tree.GetRoot();
 
            // Get the Imports Clauses
            var FirstUsingClause = root.Usings[0];
            var SecondUsingClause = root.Usings[1];
            var ThirdUsingClause = root.Usings[2];
 
            var ChangesForDifferentTrees = FirstUsingClause.SyntaxTree.GetChanges(SecondUsingClause.SyntaxTree);
            Assert.Equal(0, ChangesForDifferentTrees.Count);
 
            // Do a transform to Replace and Existing Tree
            NameSyntax name = SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("System"), SyntaxFactory.IdentifierName("Collections.Generic"));
 
            UsingDirectiveSyntax newUsingClause = ThirdUsingClause.WithName(name);
 
            // Replace Node with a different Imports Clause
            root = root.ReplaceNode(ThirdUsingClause, newUsingClause);
 
            var ChangesFromTransform = ThirdUsingClause.SyntaxTree.GetChanges(newUsingClause.SyntaxTree);
            Assert.Equal(2, ChangesFromTransform.Count);
 
            // Using the Common Syntax Changes Method
            SyntaxTree x = ThirdUsingClause.SyntaxTree;
            SyntaxTree y = newUsingClause.SyntaxTree;
 
            var changes2UsingCommonSyntax = x.GetChanges(y);
            Assert.Equal(2, changes2UsingCommonSyntax.Count);
 
            // Verify Changes from CS Specific SyntaxTree and Common SyntaxTree are the same
            Assert.Equal(ChangesFromTransform, changes2UsingCommonSyntax);
        }
 
        [Fact, WorkItem(658329, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/658329")]
        public void TestSyntaxTree_GetChangesInvalid()
        {
            string SourceText = @"using System;
using System.Linq;
using System.Collections;
 
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(""Hello, World!"");
        }
    }";
 
            SyntaxTree tree = SyntaxFactory.ParseSyntaxTree(SourceText);
            var root = (CompilationUnitSyntax)tree.GetRoot();
 
            // Get the Imports Clauses
            var FirstUsingClause = root.Usings[0];
            var SecondUsingClause = root.Usings[1];
            var ThirdUsingClause = root.Usings[2];
 
            var ChangesForDifferentTrees = FirstUsingClause.SyntaxTree.GetChanges(SecondUsingClause.SyntaxTree);
            Assert.Equal(0, ChangesForDifferentTrees.Count);
 
            // With null tree
            SyntaxTree BlankTree = null;
            Assert.Throws<ArgumentNullException>(() => FirstUsingClause.SyntaxTree.GetChanges(BlankTree));
        }
 
        [Fact, WorkItem(658329, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/658329")]
        public void TestSyntaxTree_GetChangedSpansInvalid()
        {
            string SourceText = @"using System;
using System.Linq;
using System.Collections;
 
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(""Hello, World!"");
        }
    }";
 
            SyntaxTree tree = SyntaxFactory.ParseSyntaxTree(SourceText);
            var root = (CompilationUnitSyntax)tree.GetRoot();
 
            // Get the Imports Clauses
            var FirstUsingClause = root.Usings[0];
            var SecondUsingClause = root.Usings[1];
            var ThirdUsingClause = root.Usings[2];
 
            var ChangesForDifferentTrees = FirstUsingClause.SyntaxTree.GetChangedSpans(SecondUsingClause.SyntaxTree);
            Assert.Equal(0, ChangesForDifferentTrees.Count);
 
            // With null tree
            SyntaxTree BlankTree = null;
            Assert.Throws<ArgumentNullException>(() => FirstUsingClause.SyntaxTree.GetChangedSpans(BlankTree));
        }
 
        [Fact]
        public void TestTriviaExists()
        {
            // token constructed using factory w/o specifying trivia (should have zero-width elastic trivia)
            var idToken = SyntaxFactory.Identifier("goo");
            Assert.True(idToken.HasLeadingTrivia);
            Assert.Equal(1, idToken.LeadingTrivia.Count);
            Assert.Equal(0, idToken.LeadingTrivia.Span.Length); // zero-width elastic trivia
            Assert.True(idToken.HasTrailingTrivia);
            Assert.Equal(1, idToken.TrailingTrivia.Count);
            Assert.Equal(0, idToken.TrailingTrivia.Span.Length); // zero-width elastic trivia
 
            // token constructed by parser w/o trivia
            idToken = SyntaxFactory.ParseToken("x");
            Assert.False(idToken.HasLeadingTrivia);
            Assert.Equal(0, idToken.LeadingTrivia.Count);
            Assert.False(idToken.HasTrailingTrivia);
            Assert.Equal(0, idToken.TrailingTrivia.Count);
 
            // token constructed by parser with trivia
            idToken = SyntaxFactory.ParseToken(" x  ");
            Assert.True(idToken.HasLeadingTrivia);
            Assert.Equal(1, idToken.LeadingTrivia.Count);
            Assert.Equal(1, idToken.LeadingTrivia.Span.Length);
            Assert.True(idToken.HasTrailingTrivia);
            Assert.Equal(1, idToken.TrailingTrivia.Count);
            Assert.Equal(2, idToken.TrailingTrivia.Span.Length);
 
            // node constructed using factory w/o specifying trivia
            SyntaxNode namedNode = SyntaxFactory.IdentifierName("goo");
            Assert.True(namedNode.HasLeadingTrivia);
            Assert.Equal(1, namedNode.GetLeadingTrivia().Count);
            Assert.Equal(0, namedNode.GetLeadingTrivia().Span.Length);  // zero-width elastic trivia
            Assert.True(namedNode.HasTrailingTrivia);
            Assert.Equal(1, namedNode.GetTrailingTrivia().Count);
            Assert.Equal(0, namedNode.GetTrailingTrivia().Span.Length);  // zero-width elastic trivia
 
            // node constructed by parse w/o trivia
            namedNode = SyntaxFactory.ParseExpression("goo");
            Assert.False(namedNode.HasLeadingTrivia);
            Assert.Equal(0, namedNode.GetLeadingTrivia().Count);
            Assert.False(namedNode.HasTrailingTrivia);
            Assert.Equal(0, namedNode.GetTrailingTrivia().Count);
 
            // node constructed by parse with trivia
            namedNode = SyntaxFactory.ParseExpression(" goo  ");
            Assert.True(namedNode.HasLeadingTrivia);
            Assert.Equal(1, namedNode.GetLeadingTrivia().Count);
            Assert.Equal(1, namedNode.GetLeadingTrivia().Span.Length);
            Assert.True(namedNode.HasTrailingTrivia);
            Assert.Equal(1, namedNode.GetTrailingTrivia().Count);
            Assert.Equal(2, namedNode.GetTrailingTrivia().Span.Length);
 
            // nodeOrToken with token constructed from factory w/o specifying trivia
            SyntaxNodeOrToken nodeOrToken = SyntaxFactory.Identifier("goo");
            Assert.True(nodeOrToken.HasLeadingTrivia);
            Assert.Equal(1, nodeOrToken.GetLeadingTrivia().Count);
            Assert.Equal(0, nodeOrToken.GetLeadingTrivia().Span.Length); // zero-width elastic trivia
            Assert.True(nodeOrToken.HasTrailingTrivia);
            Assert.Equal(1, nodeOrToken.GetTrailingTrivia().Count);
            Assert.Equal(0, nodeOrToken.GetTrailingTrivia().Span.Length); // zero-width elastic trivia
 
            // nodeOrToken with node constructed from factory w/o specifying trivia
            nodeOrToken = SyntaxFactory.IdentifierName("goo");
            Assert.True(nodeOrToken.HasLeadingTrivia);
            Assert.Equal(1, nodeOrToken.GetLeadingTrivia().Count);
            Assert.Equal(0, nodeOrToken.GetLeadingTrivia().Span.Length); // zero-width elastic trivia
            Assert.True(nodeOrToken.HasTrailingTrivia);
            Assert.Equal(1, nodeOrToken.GetTrailingTrivia().Count);
            Assert.Equal(0, nodeOrToken.GetTrailingTrivia().Span.Length); // zero-width elastic trivia
 
            // nodeOrToken with token parsed from factory w/o trivia
            nodeOrToken = SyntaxFactory.ParseToken("goo");
            Assert.False(nodeOrToken.HasLeadingTrivia);
            Assert.Equal(0, nodeOrToken.GetLeadingTrivia().Count);
            Assert.False(nodeOrToken.HasTrailingTrivia);
            Assert.Equal(0, nodeOrToken.GetTrailingTrivia().Count);
 
            // nodeOrToken with node parsed from factory w/o trivia
            nodeOrToken = SyntaxFactory.ParseExpression("goo");
            Assert.False(nodeOrToken.HasLeadingTrivia);
            Assert.Equal(0, nodeOrToken.GetLeadingTrivia().Count);
            Assert.False(nodeOrToken.HasTrailingTrivia);
            Assert.Equal(0, nodeOrToken.GetTrailingTrivia().Count);
 
            // nodeOrToken with token parsed from factory with trivia
            nodeOrToken = SyntaxFactory.ParseToken(" goo  ");
            Assert.True(nodeOrToken.HasLeadingTrivia);
            Assert.Equal(1, nodeOrToken.GetLeadingTrivia().Count);
            Assert.Equal(1, nodeOrToken.GetLeadingTrivia().Span.Length); // zero-width elastic trivia
            Assert.True(nodeOrToken.HasTrailingTrivia);
            Assert.Equal(1, nodeOrToken.GetTrailingTrivia().Count);
            Assert.Equal(2, nodeOrToken.GetTrailingTrivia().Span.Length); // zero-width elastic trivia
 
            // nodeOrToken with node parsed from factory with trivia
            nodeOrToken = SyntaxFactory.ParseExpression(" goo  ");
            Assert.True(nodeOrToken.HasLeadingTrivia);
            Assert.Equal(1, nodeOrToken.GetLeadingTrivia().Count);
            Assert.Equal(1, nodeOrToken.GetLeadingTrivia().Span.Length); // zero-width elastic trivia
            Assert.True(nodeOrToken.HasTrailingTrivia);
            Assert.Equal(1, nodeOrToken.GetTrailingTrivia().Count);
            Assert.Equal(2, nodeOrToken.GetTrailingTrivia().Span.Length); // zero-width elastic trivia
        }
 
        [WorkItem(6536, "https://github.com/dotnet/roslyn/issues/6536")]
        [Fact]
        public void TestFindTrivia_NoStackOverflowOnLargeExpression()
        {
            StringBuilder code = new StringBuilder();
            code.Append(
@"class Goo
{
    void Bar()
    {
        string test = ");
            for (var i = 0; i < 3000; i++)
            {
                code.Append(@"""asdf"" + ");
            }
            code.Append(@"""last"";
    }
}");
            var tree = SyntaxFactory.ParseSyntaxTree(code.ToString());
            var position = 4000;
            var trivia = tree.GetCompilationUnitRoot().FindTrivia(position);
            // no stack overflow
        }
 
        [Fact, WorkItem(8625, "https://github.com/dotnet/roslyn/issues/8625")]
        public void SyntaxNodeContains()
        {
            var text = "a + (b - (c * (d / e)))";
            var expression = SyntaxFactory.ParseExpression(text);
            var a = expression.DescendantNodes().OfType<IdentifierNameSyntax>().First(n => n.Identifier.Text == "a");
            var e = expression.DescendantNodes().OfType<IdentifierNameSyntax>().First(n => n.Identifier.Text == "e");
 
            var firstParens = e.FirstAncestorOrSelf<ExpressionSyntax>(n => n.Kind() == SyntaxKind.ParenthesizedExpression);
 
            Assert.False(firstParens.Contains(a));  // fixing #8625 allows this to return quicker
            Assert.True(firstParens.Contains(e));
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_AnonymousMethodExpressionSyntax_AddAsync()
        {
            var text = "static delegate(int i) { }";
            var expression = (AnonymousMethodExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithTrailingTrivia(SyntaxFactory.Space)).ToString();
            Assert.Equal("static async delegate(int i) { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_ParenthesizedLambdaExpressionSyntax_AddAsync()
        {
            var text = "static (a) => { }";
            var expression = (ParenthesizedLambdaExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithTrailingTrivia(SyntaxFactory.Space)).ToString();
            Assert.Equal("static async (a) => { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_SimpleLambdaExpressionSyntax_AddAsync()
        {
            var text = "static a => { }";
            var expression = (SimpleLambdaExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithTrailingTrivia(SyntaxFactory.Space)).ToString();
            Assert.Equal("static async a => { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_AnonymousMethodExpressionSyntax_ReplaceAsync()
        {
            var text = "static async/**/delegate(int i) { }";
            var expression = (AnonymousMethodExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithTrailingTrivia(SyntaxFactory.Space)).ToString();
            Assert.Equal("static async delegate(int i) { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_ParenthesizedLambdaExpressionSyntax_ReplaceAsync()
        {
            var text = "static async/**/(a) => { }";
            var expression = (ParenthesizedLambdaExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithTrailingTrivia(SyntaxFactory.Space)).ToString();
            Assert.Equal("static async (a) => { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_SimpleLambdaExpressionSyntax_ReplaceAsync()
        {
            var text = "static async/**/a => { }";
            var expression = (SimpleLambdaExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithTrailingTrivia(SyntaxFactory.Space)).ToString();
            Assert.Equal("static async a => { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_AnonymousMethodExpressionSyntax_RemoveExistingAsync()
        {
            var text = "static async/**/delegate(int i) { }";
            var expression = (AnonymousMethodExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(default).ToString();
            Assert.Equal("static delegate(int i) { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_ParenthesizedLambdaExpressionSyntax_RemoveExistingAsync()
        {
            var text = "static async (a) => { }";
            var expression = (ParenthesizedLambdaExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(default).ToString();
            Assert.Equal("static (a) => { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_SimpleLambdaExpressionSyntax_RemoveExistingAsync()
        {
            var text = "static async/**/a => { }";
            var expression = (SimpleLambdaExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(default).ToString();
            Assert.Equal("static a => { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_AnonymousMethodExpressionSyntax_RemoveNonExistingAsync()
        {
            var text = "static delegate(int i) { }";
            var expression = (AnonymousMethodExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(default).ToString();
            Assert.Equal(text, withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_ParenthesizedLambdaExpressionSyntax_RemoveNonExistingAsync()
        {
            var text = "static (a) => { }";
            var expression = (ParenthesizedLambdaExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(default).ToString();
            Assert.Equal(text, withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_SimpleLambdaExpressionSyntax_RemoveNonExistingAsync()
        {
            var text = "static a => { }";
            var expression = (SimpleLambdaExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(default).ToString();
            Assert.Equal(text, withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_AnonymousMethodExpressionSyntax_ReplaceAsync_ExistingTwoKeywords()
        {
            var text = "static async/*async1*/ async/*async2*/delegate(int i) { }";
            var expression = (AnonymousMethodExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var newAsync = SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithTrailingTrivia(SyntaxFactory.Space);
            var withAsync = expression.WithAsyncKeyword(newAsync).ToString();
            Assert.Equal("static async async/*async2*/delegate(int i) { }", withAsync);
        }
 
        [Fact, WorkItem(54239, "https://github.com/dotnet/roslyn/issues/54239")]
        public void TestWithAsyncKeyword_AnonymousMethodExpressionSyntax_RemoveAllExistingAsync()
        {
            var text = "static async/*async1*/ async/*async2*/ delegate(int i) { }";
            var expression = (AnonymousMethodExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var withAsync = expression.WithAsyncKeyword(default);
            Assert.Equal("static delegate(int i) { }", withAsync.ToString());
            Assert.Equal(default, withAsync.AsyncKeyword);
        }
 
        private static void TestWithWindowsAndUnixEndOfLines(string inputText, string expectedText, Action<CompilationUnitSyntax, string> action)
        {
            inputText = inputText.NormalizeLineEndings();
            expectedText = expectedText.NormalizeLineEndings();
 
            var tests = new Dictionary<string, string>
            {
                {inputText, expectedText}, // Test CRLF (Windows)
                {inputText.Replace("\r", ""), expectedText.Replace("\r", "")}, // Test LF (Unix)
            };
 
            foreach (var test in tests)
            {
                action(SyntaxFactory.ParseCompilationUnit(test.Key), test.Value);
            }
        }
 
        [Fact]
        [WorkItem(56740, "https://github.com/dotnet/roslyn/issues/56740")]
        public void TestStackAllocKeywordUpdate()
        {
            var text = "stackalloc/**/int[50]";
            var expression = (StackAllocArrayCreationExpressionSyntax)SyntaxFactory.ParseExpression(text);
            var replacedKeyword = SyntaxFactory.Token(SyntaxKind.StackAllocKeyword).WithTrailingTrivia(SyntaxFactory.Space);
            var newExpression = expression.Update(replacedKeyword, expression.Type).ToString();
            Assert.Equal("stackalloc int[50]", newExpression);
        }
 
        [Fact]
        [WorkItem(58597, "https://github.com/dotnet/roslyn/issues/58597")]
        public void TestExclamationExclamationUpdate()
        {
            var text = "(string s!!)";
            var parameter = SyntaxFactory.ParseParameterList(text).Parameters[0];
            var newParameter = parameter.Update(parameter.AttributeLists, parameter.Modifiers, parameter.Type, parameter.Identifier, parameter.Default);
            Assert.Equal("string s!!", newParameter.ToFullString());
            Assert.Equal("string s", newParameter.ToString());
        }
    }
}