File: Syntax\SyntaxEquivalenceTests.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.Linq;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class SyntaxEquivalenceTests
    {
        private void VerifyEquivalent(SyntaxTree tree1, SyntaxTree tree2, bool topLevel)
        {
            Assert.True(SyntaxFactory.AreEquivalent(tree1, tree2, topLevel));
 
            // now try as if the second tree were created from scratch.
            var tree3 = SyntaxFactory.ParseSyntaxTree(tree2.GetText().ToString());
            Assert.True(SyntaxFactory.AreEquivalent(tree1, tree3, topLevel));
        }
 
        private void VerifyNotEquivalent(SyntaxTree tree1, SyntaxTree tree2, bool topLevel)
        {
            Assert.False(SyntaxFactory.AreEquivalent(tree1, tree2, topLevel));
 
            // now try as if the second tree were created from scratch.
            var tree3 = SyntaxFactory.ParseSyntaxTree(tree2.GetText().ToString());
            Assert.False(SyntaxFactory.AreEquivalent(tree1, tree3, topLevel));
        }
 
        private void VerifyEquivalent(SyntaxNode node1, SyntaxNode node2, Func<SyntaxKind, bool> ignoreChildNode)
        {
            Assert.True(SyntaxFactory.AreEquivalent(node1, node2, ignoreChildNode));
 
            // now try as if the second tree were created from scratch.
            var tree3 = SyntaxFactory.ParseSyntaxTree(node2.GetText().ToString());
            Assert.True(SyntaxFactory.AreEquivalent(node1, tree3.GetRoot(), ignoreChildNode));
        }
 
        [Fact]
        public void TestEmptyTrees()
        {
            var text = "";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = SyntaxFactory.ParseSyntaxTree(text);
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddingComment()
        {
            var text = "";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = tree1.WithInsertAt(0, "/* goo */");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddingActivePPDirective()
        {
            var text = "";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = tree1.WithInsertAt(0, "#if true \r\n\r\n#endif");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddingInactivePPDirective()
        {
            var text = "";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = tree1.WithInsertAt(0, "#if false \r\n\r\n#endif");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingEmpty()
        {
            var text = "";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = tree1.WithInsertAt(0, "namespace N { }");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddingClass()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { }");
            var tree2 = tree1.WithInsertBefore("}", "class C { }");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRenameOuter()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { }");
            var tree2 = tree1.WithReplaceFirst("N", "N1");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRenameInner()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { void Goo() { int z = 0; } } }");
            var tree2 = tree1.WithReplaceFirst("z", "y");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRenameOuterToSamename()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { }");
            var tree2 = tree1.WithReplaceFirst("N", "N");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRenameInnerToSameName()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { void Goo() { int z = 0; } } }");
            var tree2 = tree1.WithReplaceFirst("z", "z");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddingMethod()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { } }");
            var tree2 = tree1.WithInsertBefore("}", "void Goo() { } ");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddingLocal()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { void Goo() { } } }");
            var tree2 = tree1.WithInsertBefore("}", "int i; ");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRemovingLocal()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { void Goo() { int i; } } }");
            var tree2 = tree1.WithRemoveFirst("int i;");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRemovingField1()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { int i = 5; int j = 6; } }");
            var tree2 = tree1.WithRemoveFirst("int i = 5;");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRemovingField2()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { int i = 5; int j = 6; } }");
            var tree2 = tree1.WithRemoveFirst("int j = 6;");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingField()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { int i = 5; } }");
            var tree2 = tree1.WithReplaceFirst("5", "6");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingField2()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { int i = 5, j = 7; } }");
            var tree2 = tree1.WithReplaceFirst("7", "8");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            tree2 = tree1.WithReplaceFirst("5", "6");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingConstField()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { const int i = 5; } }");
            var tree2 = tree1.WithReplaceFirst("5", "6");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingConstField2()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { const int i = 5, j = 7; } }");
            var tree2 = tree1.WithReplaceFirst("5", "6");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
 
            tree2 = tree1.WithReplaceFirst("7", "8");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingConstLocal()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { void Goo() { const int i = 5; } } }");
            var tree2 = tree1.WithReplaceFirst("5", "6");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingEnumMember()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("enum E { i = 5 }");
            var tree2 = tree1.WithReplaceFirst("5", "6");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingAttribute()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { [Obsolete(true)]class C { const int i = 5; } }");
            var tree2 = tree1.WithReplaceFirst("true", "false");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingMethodCall()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { void Goo() { Console.Write(0); } } }");
            var tree2 = tree1.WithReplaceFirst("Write", "WriteLine");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingUsing()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("using System; namespace N { class C { void Goo() { Console.Write(0); } } }");
            var tree2 = tree1.WithReplaceFirst("System", "System.Linq");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingBaseType()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { Console.Write(0); } }");
            var tree2 = tree1.WithInsertBefore("{", ": B ");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangingMethodType()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { Console.Write(0); } }");
            var tree2 = tree1.WithReplaceFirst("void", "int");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddComment()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { Console.Write(0); } }");
            var tree2 = tree1.WithInsertBefore("class", "// Comment\r\n");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestCommentOutCode()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { Console.Write(0); } }");
            var tree2 = tree1.WithInsertBefore("class", "// ");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddDocComment()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { Console.Write(0); } }");
            var tree2 = tree1.WithInsertBefore("class", "/// Comment\r\n");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestCommentOutMethodCode()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { Console.Write(0); } }");
            var tree2 = tree1.WithReplaceFirst("Console.Write(0);", "/* Console.Write(0); */");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestCommentOutMethod()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { } }");
            var tree2 = tree1.WithReplaceFirst("void Goo() { }", "/* void Goo() { } */");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestSurroundMethodWithActivePPRegion()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { } }");
            var tree2 = tree1.WithReplaceFirst("void Goo() { }", "\r\n#if true\r\n void Goo() { }\r\n#endif\r\n");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestSurroundMethodWithInactivePPRegion()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { } }");
            var tree2 = tree1.WithReplaceFirst("void Goo() { }", "\r\n#if false\r\n void Goo() { }\r\n#endif\r\n");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestSurroundStatementWithActivePPRegion()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { int i; } }");
            var tree2 = tree1.WithReplaceFirst("int i;", "\r\n#if true\r\n int i;\r\n#endif\r\n");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestSurroundStatementWithInactivePPRegion()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { int i; } }");
            var tree2 = tree1.WithReplaceFirst("int i;", "\r\n#if false\r\n int i;\r\n#endif\r\n");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestAddLotsOfComments()
        {
            var text = "class C { void Goo() { int i; } }";
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { int i; } }");
            var tree2 = SyntaxFactory.ParseSyntaxTree(text.Replace(" ", " /**/ "));
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestChangeWhitespace()
        {
            var text = "class C { void Goo() { int i; } }";
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void Goo() { int i; } }");
            var tree2 = SyntaxFactory.ParseSyntaxTree(text.Replace(" ", "  "));
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestSkippedTest()
        {
            var text = "abc using";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text);
            var tree2 = SyntaxFactory.ParseSyntaxTree(text.Replace("abc", "hello"));
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUpdateInterpolatedString()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("namespace N { class C { void Goo() { Console.Write($\"Hello{123:N1}\"); } } }");
            var tree2 = tree1.WithReplaceFirst("N1", "N2");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
 
            tree2 = tree1.WithReplaceFirst("Hello", "World");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact, WorkItem(7380, "https://github.com/dotnet/roslyn/issues/7380")]
        public void TestExpressionBodiedMethod()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree("class C { void M() => 1; }");
            var tree2 = SyntaxFactory.ParseSyntaxTree("class C { void M() => 2; }");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Theory, WorkItem(38694, "https://github.com/dotnet/roslyn/issues/38694")]
        [InlineData("#nullable enable", "#nullable disable")]
        [InlineData("#nullable enable", "#nullable restore")]
        [InlineData("#nullable disable", "#nullable restore")]
        [InlineData("#nullable enable", "#nullable enable warnings")]
        [InlineData("#nullable enable", "#nullable enable annotations")]
        [InlineData("#nullable enable annotations", "#nullable enable warnings")]
        [InlineData("", "#nullable disable")]
        [InlineData("", "#nullable enable")]
        [InlineData("", "#nullable restore")]
        [InlineData("#nullable disable", "")]
        [InlineData("#nullable enable", "")]
        [InlineData("#nullable restore", "")]
        public void TestNullableDirectives_DifferentDirectives(string firstDirective, string secondDirective)
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree($@"
{firstDirective}
class C
{{
}}");
            var tree2 = SyntaxFactory.ParseSyntaxTree($@"
{secondDirective}
class C
{{
}}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1.GetRoot(), tree2.GetRoot(), ignoreChildNode: k => k == SyntaxKind.NullableDirectiveTrivia);
 
            var tree3 = SyntaxFactory.ParseSyntaxTree($@"
class C
{{
    void M()
    {{
{firstDirective}
    }}
}}");
            var tree4 = SyntaxFactory.ParseSyntaxTree($@"
class C
{{
    void M()
    {{
{secondDirective}
    }}
}}");
 
            VerifyNotEquivalent(tree3, tree4, topLevel: true);
            VerifyNotEquivalent(tree3, tree4, topLevel: false);
            VerifyEquivalent(tree3.GetRoot(), tree4.GetRoot(), ignoreChildNode: k => k == SyntaxKind.NullableDirectiveTrivia);
        }
 
        [Theory, WorkItem(38694, "https://github.com/dotnet/roslyn/issues/38694")]
        [InlineData("#nullable enable")]
        [InlineData("#nullable disable")]
        [InlineData("#nullable restore")]
        [InlineData("#nullable enable warnings")]
        public void TestNullableDirectives_TopLevelIdentical(string directive)
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree($@"
class C
{{
    void M()
    {{
{directive}
        Console.WriteLine(1234);
    }}
}}");
            var tree2 = SyntaxFactory.ParseSyntaxTree($@"
class C
{{
    void M()
    {{
{directive}
    }}
}}");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact, WorkItem(38694, "https://github.com/dotnet/roslyn/issues/38694")]
        public void TestNullableDirectives_InvalidDirective()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
#nullable invalid
    }
}");
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact, WorkItem(38694, "https://github.com/dotnet/roslyn/issues/38694")]
        public void TestNullableDirectives_DifferentNumberOfDirectives()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
#nullable enable
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
#nullable enable
#nullable disable
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRawStringLiteral1()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc"""""";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc"""""";
    }
}");
 
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRawStringLiteral2()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc"""""";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abcd"""""";
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestRawStringLiteral3()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc"""""";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc"";
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestRawStringLiteral4()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc"""""";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""""abc"""""""";
    }
}");
 
            VerifyEquivalent(tree1, tree2, topLevel: true);
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestStringLiteral_01()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc"";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc"";
    }
}");
 
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestStringLiteral_02()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc"";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = @""abc"";
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestStringLiteral_03()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc"";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abcd"";
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestStringLiteral_04()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc"";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = @""abcd"";
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8StringLiteral_01()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc""u8;
    }
}");
 
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestUtf8StringLiteral_02()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = @""abc""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8StringLiteral_03()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abcd""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8StringLiteral_04()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc""U8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8StringLiteral_05()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc"";
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8StringLiteral_06()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc"";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = ""abc""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8SingleLineRawStringLiteral_01()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""u8;
    }
}");
 
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestUtf8SingleLineRawStringLiteral_02()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""""abc""""""""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8SingleLineRawStringLiteral_03()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abcd""""""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8SingleLineRawStringLiteral_04()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""U8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8SingleLineRawStringLiteral_05()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc"""""";
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8SingleLineRawStringLiteral_06()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc"""""";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8MultiLineRawStringLiteral_01()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""u8;
    }
}");
 
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact]
        public void TestUtf8MultiLineRawStringLiteral_02()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""""
abc
""""""""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8MultiLineRawStringLiteral_03()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abcd
""""""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8MultiLineRawStringLiteral_04()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""U8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8MultiLineRawStringLiteral_05()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
"""""";
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8MultiLineRawStringLiteral_06()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
"""""";
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact]
        public void TestUtf8MultiLineRawStringLiteral_07()
        {
            var tree1 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""
abc
""""""u8;
    }
}");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    void M()
    {
        var v = """"""abc""""""u8;
    }
}");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
            VerifyEquivalent(tree1, tree2, topLevel: true);
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2018744")]
        public void TestDeeplyNested1()
        {
            var expr = string.Join(" + ", Enumerable.Range(0, 10000).Select(_ => "a"));
 
            var tree1 = SyntaxFactory.ParseSyntaxTree($$""""
                class C
                {
                    void M(int a, int b, int c)
                    {
                        var v = {{expr}} + b;
                    }
                }
                """");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree($$""""
                class C
                {
                    void M(int a, int b, int c)
                    {
                        var v = {{expr}} + c;
                    }
                }
                """");
 
            VerifyNotEquivalent(tree1, tree2, topLevel: false);
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2018744")]
        public void TestDeeplyNested2()
        {
            var expr = string.Join(" + ", Enumerable.Range(0, 10000).Select(_ => "a"));
 
            var tree1 = SyntaxFactory.ParseSyntaxTree($$""""
                class C
                {
                    void M(int a)
                    {
                        var v = {{expr}};
                    }
                }
                """");
 
            var tree2 = SyntaxFactory.ParseSyntaxTree($$""""
                class C
                {
                    void M(int a)
                    {
                        var v = {{expr}};
                    }
                }
                """");
 
            VerifyEquivalent(tree1, tree2, topLevel: false);
        }
    }
}