File: DeclarationTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Symbol\Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Symbol.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.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
//test
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class DeclarationTests : CSharpTestBase
    {
        [Fact]
        public void TestSimpleDeclarations()
        {
            var text1 = @"
namespace NA.NB
{
  partial class C<T>
  { 
    partial class D
    {
      int F;
    }
  }
  class C { }
}
";
            var text2 = @"
namespace NA
{
  namespace NB
  {
    partial class C<T>
    { 
      partial class D
      {
        void G() {};
      }
    }
  }
}
";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text1);
            var tree2 = SyntaxFactory.ParseSyntaxTree(text2);
            Assert.NotNull(tree1);
            Assert.NotNull(tree2);
            var decl1 = DeclarationTreeBuilder.ForTree(tree1, TestOptions.DebugExe.ScriptClassName, isSubmission: false);
            var decl2 = DeclarationTreeBuilder.ForTree(tree2, TestOptions.DebugExe.ScriptClassName, isSubmission: false);
            Assert.NotNull(decl1);
            Assert.NotNull(decl2);
            Assert.Equal(string.Empty, decl1.Name);
            Assert.Equal(string.Empty, decl2.Name);
            Assert.Equal(1, decl1.Children.Length);
            Assert.Equal(1, decl2.Children.Length);
            var na1 = decl1.Children.Single();
            var na2 = decl2.Children.Single();
            Assert.NotNull(na1);
            Assert.NotNull(na2);
            Assert.Equal(DeclarationKind.Namespace, na1.Kind);
            Assert.Equal(DeclarationKind.Namespace, na2.Kind);
            Assert.Equal("NA", na1.Name);
            Assert.Equal("NA", na2.Name);
            Assert.Equal(1, na1.Children.Length);
            Assert.Equal(1, na2.Children.Length);
            var nb1 = na1.Children.Single();
            var nb2 = na2.Children.Single();
            Assert.NotNull(nb1);
            Assert.NotNull(nb2);
            Assert.Equal(DeclarationKind.Namespace, nb1.Kind);
            Assert.Equal(DeclarationKind.Namespace, nb2.Kind);
            Assert.Equal("NB", nb1.Name);
            Assert.Equal("NB", nb2.Name);
            Assert.Equal(2, nb1.Children.Length);
            Assert.Equal(1, nb2.Children.Length);
            var ct1 = (SingleTypeDeclaration)nb1.Children.First();
            var ct2 = (SingleTypeDeclaration)nb2.Children.Single();
            Assert.Equal(DeclarationKind.Class, ct1.Kind);
            Assert.Equal(DeclarationKind.Class, ct2.Kind);
            Assert.NotNull(ct1);
            Assert.NotNull(ct2);
            Assert.Equal("C", ct1.Name);
            Assert.Equal("C", ct2.Name);
            Assert.Equal(1, ct1.Arity);
            Assert.Equal(1, ct2.Arity);
            Assert.Equal(1, ct1.Children.Length);
            Assert.Equal(1, ct2.Children.Length);
            var c1 = (SingleTypeDeclaration)nb1.Children.Skip(1).Single();
            Assert.NotNull(c1);
            Assert.Equal(DeclarationKind.Class, c1.Kind);
            Assert.Equal("C", c1.Name);
            Assert.Equal(0, c1.Arity);
            var d1 = ct1.Children.Single();
            var d2 = ct2.Children.Single();
            Assert.NotNull(d1);
            Assert.NotNull(d2);
            Assert.Equal(DeclarationKind.Class, d1.Kind);
            Assert.Equal(DeclarationKind.Class, d2.Kind);
            Assert.Equal("D", d1.Name);
            Assert.Equal("D", d2.Name);
            Assert.Equal(0, d1.Arity);
            Assert.Equal(0, d2.Arity);
            Assert.Equal(0, d1.Children.Length);
            Assert.Equal(0, d2.Children.Length);
 
            var table = DeclarationTable.Empty;
            var mr = table.CalculateMergedRoot(null);
            Assert.NotNull(mr);
            Assert.True(mr.Declarations.IsEmpty);
            Assert.True(table.TypeNames.IsEmpty());
 
            table = table.AddRootDeclaration(Lazy(decl1));
            mr = table.CalculateMergedRoot(null);
 
            Assert.Equal(mr.Declarations, new[] { decl1 });
            Assert.True(table.TypeNames.OrderBy(s => s).SequenceEqual(new[] { "C", "D" }));
 
            Assert.Equal(DeclarationKind.Namespace, mr.Kind);
            Assert.Equal(string.Empty, mr.Name);
 
            var na = mr.Children.Single();
            Assert.Equal(DeclarationKind.Namespace, na.Kind);
            Assert.Equal("NA", na.Name);
 
            var nb = na.Children.Single();
            Assert.Equal(DeclarationKind.Namespace, nb.Kind);
            Assert.Equal("NB", nb.Name);
 
            var ct = nb.Children.OfType<MergedTypeDeclaration>().Single(x => x.Arity == 1);
            Assert.Equal(1, ct.Arity);
            Assert.Equal(DeclarationKind.Class, ct.Kind);
            Assert.Equal("C", ct.Name);
 
            var c = nb.Children.OfType<MergedTypeDeclaration>().Single(x => x.Arity == 0);
            Assert.Equal(0, c.Arity);
            Assert.Equal(DeclarationKind.Class, c.Kind);
            Assert.Equal("C", c.Name);
 
            var d = ct.Children.Single();
            Assert.Equal(0, d.Arity);
            Assert.Equal(DeclarationKind.Class, d.Kind);
            Assert.Equal("D", d.Name);
 
            table = table.AddRootDeclaration(Lazy(decl2));
            mr = table.CalculateMergedRoot(null);
 
            Assert.True(table.TypeNames.Distinct().OrderBy(s => s).SequenceEqual(new[] { "C", "D" }));
 
            Assert.Equal(mr.Declarations, new[] { decl1, decl2 });
 
            Assert.Equal(DeclarationKind.Namespace, mr.Kind);
            Assert.Equal(string.Empty, mr.Name);
 
            na = mr.Children.Single();
            Assert.Equal(DeclarationKind.Namespace, na.Kind);
            Assert.Equal("NA", na.Name);
 
            nb = na.Children.Single();
            Assert.Equal(DeclarationKind.Namespace, nb.Kind);
            Assert.Equal("NB", nb.Name);
 
            ct = nb.Children.OfType<MergedTypeDeclaration>().Single(x => x.Arity == 1);
            Assert.Equal(1, ct.Arity);
            Assert.Equal(DeclarationKind.Class, ct.Kind);
            Assert.Equal("C", ct.Name);
 
            c = nb.Children.OfType<MergedTypeDeclaration>().Single(x => x.Arity == 0);
            Assert.Equal(0, c.Arity);
            Assert.Equal(DeclarationKind.Class, c.Kind);
            Assert.Equal("C", c.Name);
 
            d = ct.Children.Single();
            Assert.Equal(0, d.Arity);
            Assert.Equal(DeclarationKind.Class, d.Kind);
            Assert.Equal("D", d.Name);
        }
 
        private Lazy<RootSingleNamespaceDeclaration> Lazy(RootSingleNamespaceDeclaration decl)
        {
            return new Lazy<RootSingleNamespaceDeclaration>(() => decl);
        }
 
        [Fact]
        public void TestTypeNames()
        {
            var text1 = @"
namespace NA.NB
{
  partial class A<T>
  { 
    partial class B
    {
      int F;
    }
  }
}
";
            var text2 = @"
namespace NA
{
  namespace NB
  {
    partial class C<T>
    { 
      partial class D
      {
        void G() {};
      }
    }
  }
}
";
            var tree1 = SyntaxFactory.ParseSyntaxTree(text1);
            var tree2 = SyntaxFactory.ParseSyntaxTree(text2);
            Assert.NotNull(tree1);
            Assert.NotNull(tree2);
            var decl1 = Lazy(DeclarationTreeBuilder.ForTree(tree1, TestOptions.DebugExe.ScriptClassName, isSubmission: false));
            var decl2 = Lazy(DeclarationTreeBuilder.ForTree(tree2, TestOptions.DebugExe.ScriptClassName, isSubmission: false));
 
            var table = DeclarationTable.Empty;
            table = table.AddRootDeclaration(decl1);
 
            Assert.True(table.TypeNames.OrderBy(s => s).SequenceEqual(new[] { "A", "B" }));
 
            table = table.AddRootDeclaration(decl2);
            Assert.True(table.TypeNames.OrderBy(s => s).SequenceEqual(new[] { "A", "B", "C", "D" }));
 
            table = table.RemoveRootDeclaration(decl2);
            Assert.True(table.TypeNames.OrderBy(s => s).SequenceEqual(new[] { "A", "B" }));
 
            table = table.AddRootDeclaration(decl2);
            Assert.True(table.TypeNames.OrderBy(s => s).SequenceEqual(new[] { "A", "B", "C", "D" }));
 
            table = table.RemoveRootDeclaration(decl1);
            Assert.True(table.TypeNames.OrderBy(s => s).SequenceEqual(new[] { "C", "D" }));
 
            table = table.RemoveRootDeclaration(decl2);
            Assert.True(table.TypeNames.IsEmpty());
        }
 
        [Fact]
        public void Bug2038()
        {
            string code = @"
                    public public interface testiface {}";
 
            var comp = CSharpCompilation.Create(
                "Test.dll",
                new[] { SyntaxFactory.ParseSyntaxTree(code) },
                options: TestOptions.ReleaseDll);
 
            Assert.Equal(SymbolKind.NamedType, comp.GlobalNamespace.GetMembers()[0].Kind);
        }
 
        [ConditionalFact(typeof(NoIOperationValidation), typeof(NoUsedAssembliesValidation))]
        public void OnlyOneParse()
        {
            var underlyingTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
class C
{
    public B X(B b) { return b; }
    C(){}
}
");
            var foreignType = SyntaxFactory.ParseSyntaxTree(@"
public class B
{
  public int member(string s) { return s.Length; }
  B(){}
}
");
 
            var countedTree = new CountedSyntaxTree(foreignType);
 
            var compilation = CreateCompilation(new SyntaxTree[] { underlyingTree, countedTree }, skipUsesIsNullable: true, options: TestOptions.ReleaseDll);
 
            var type = compilation.Assembly.GlobalNamespace.GetTypeMembers().First();
            Assert.Equal(1, countedTree.AccessCount);   // parse once to build the decl table
 
            // We shouldn't need to go back to syntax to get info about the member names.
            var memberNames = type.MemberNames;
            Assert.Equal(1, countedTree.AccessCount);
 
            // Getting the interfaces will cause us to do some more binding of the current type.
            var interfaces = type.Interfaces();
            Assert.Equal(1, countedTree.AccessCount);
 
            // Now bind the members.
            var method = (MethodSymbol)type.GetMembers().First();
            Assert.Equal(1, countedTree.AccessCount);
 
            // Once we have the method, we shouldn't need to go back to syntax again.
            var returnType = method.ReturnTypeWithAnnotations;
            Assert.Equal(1, countedTree.AccessCount);
 
            var parameterType = method.Parameters.Single();
            Assert.Equal(1, countedTree.AccessCount);
        }
 
        [ConditionalFact(typeof(NoIOperationValidation), typeof(NoUsedAssembliesValidation))]
        public void OnlyOneParse_WithReservedTypeName()
        {
            var underlyingTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
class c
{
    public b X(b b1) { return b1; }
    c(){}
}
");
            var foreignType = SyntaxFactory.ParseSyntaxTree(@"
public class b
{
  public int member(string s) { return s.Length; }
  b(){}
}
");
 
            var countedTree = new CountedSyntaxTree(foreignType);
 
            var compilation = CreateCompilation(new SyntaxTree[] { underlyingTree, countedTree }, skipUsesIsNullable: true, options: TestOptions.ReleaseDll);
 
            var type = compilation.Assembly.GlobalNamespace.GetTypeMembers().First();
            Assert.Equal(1, countedTree.AccessCount);
 
            var memberNames = type.MemberNames;
            Assert.Equal(1, countedTree.AccessCount);
 
            var interfaces = type.Interfaces();
            Assert.Equal(1, countedTree.AccessCount);
 
            var method = (MethodSymbol)type.GetMembers().First();
            Assert.Equal(1, countedTree.AccessCount);
 
            var returnType = method.ReturnTypeWithAnnotations;
            Assert.Equal(1, countedTree.AccessCount);
 
            var parameterType = method.Parameters.Single();
            Assert.Equal(1, countedTree.AccessCount);
        }
 
        [Fact]
        public void TestMemberNamesReused1()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        class C
        {
            int x, y, z;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        class C
        {
            int z, y, x;
        }
    }
}
");
            var thirdTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        class C
        {
            int z, y, x, w;
        }
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1 = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.C");
            Assert.True(type1.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2 = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.C");
            Assert.True(type2.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1.MergedDeclaration.Declarations[0].MemberNames, type2.MergedDeclaration.Declarations[0].MemberNames);
 
            compilation = compilation.ReplaceSyntaxTree(secondTree, thirdTree);
 
            var type3 = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.C");
            Assert.True(type3.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "w", "x", "y", "z" }));
 
            Assert.NotSame(type1.MergedDeclaration.Declarations[0].MemberNames, type3.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_UnaffectedByTypeNameChange()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        class C
        {
            int x, y, z;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        class D
        {
            int z, y, x;
        }
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1 = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.C");
            Assert.True(type1.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2 = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.D");
            Assert.True(type2.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1.MergedDeclaration.Declarations[0].MemberNames, type2.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_UnaffectedByNamespaceChange()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        class C
        {
            int x, y, z;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    class C
    {
        int z, y, x;
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1 = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.C");
            Assert.True(type1.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2 = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.C");
            Assert.True(type2.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1.MergedDeclaration.Declarations[0].MemberNames, type2.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_SupportsMultipleTypes()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        class C
        {
            int x, y, z;
        }
 
        class D
        {
            int a, b, c;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    class C
    {
        int z, y, x;
    }
 
    class D
    {
        int a, b, c;
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.C");
            Assert.True(type1a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            var type1b = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.D");
            Assert.True(type1b.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "a", "b", "c" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.C");
            Assert.True(type2a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            var type2b = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.D");
            Assert.True(type2b.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "a", "b", "c" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1a.MergedDeclaration.Declarations[0].MemberNames, type2a.MergedDeclaration.Declarations[0].MemberNames);
            Assert.Same(type1b.MergedDeclaration.Declarations[0].MemberNames, type2b.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_SupportsMultipleTypes_TypeRemoved()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        class C
        {
            int x, y, z;
        }
 
        class D
        {
            int a, b, c;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    class C
    {
        int z, y, x;
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.C");
            Assert.True(type1a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            var type1b = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.D");
            Assert.True(type1b.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "a", "b", "c" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.C");
            Assert.True(type2a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            var type2b = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.D");
            Assert.Null(type2b);
 
            // We should have the exact same set for the names.
            Assert.Same(type1a.MergedDeclaration.Declarations[0].MemberNames, type2a.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_SupportsEnums()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        enum E
        {
            x, y, z;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    enum E
    {
        z, y, x;
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.E");
            Assert.True(type1a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.E");
            Assert.True(type2a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1a.MergedDeclaration.Declarations[0].MemberNames, type2a.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_SupportsStructs()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        struct S
        {
            int x, y, z;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    struct S
    {
        int z, y, x;
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.S");
            Assert.True(type1a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.S");
            Assert.True(type2a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1a.MergedDeclaration.Declarations[0].MemberNames, type2a.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_SupportsInterfaces()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        interface I
        {
            int x { get; }
            int y { get; }
            int z { get; }
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    interface I
    {
        int z { get; }
        int y { get; }
        int x { get; }
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.I");
            Assert.True(type1a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.I");
            Assert.True(type2a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1a.MergedDeclaration.Declarations[0].MemberNames, type2a.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_UnaffectedByDelegate1()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        delegate void D();
 
        enum E
        {
            x, y, z;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    enum E
    {
        z, y, x;
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.E");
            Assert.True(type1a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.E");
            Assert.True(type2a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1a.MergedDeclaration.Declarations[0].MemberNames, type2a.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_UnaffectedByDelegate2()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        enum E
        {
            x, y, z;
        }
    }
}
");
            var secondTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    delegate void D();
 
    enum E
    {
        z, y, x;
    }
}
");
 
            var compilation = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.N2.N3.E");
            Assert.True(type1a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            compilation = compilation.ReplaceSyntaxTree(firstTree, secondTree);
 
            var type2a = (SourceNamedTypeSymbol)compilation.GetTypeByMetadataName("N1.E");
            Assert.True(type2a.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1a.MergedDeclaration.Declarations[0].MemberNames, type2a.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        [Fact]
        public void TestMemberNamesReused_SameTreeDifferentCompilations()
        {
            var firstTree = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
namespace N1
{
    namespace N2.N3
    {
        enum E
        {
            x, y, z;
        }
    }
}
");
 
            var compilation1 = CreateCompilation(new SyntaxTree[] { firstTree });
            var compilation2 = CreateCompilation(new SyntaxTree[] { firstTree });
 
            var type1 = (SourceNamedTypeSymbol)compilation1.GetTypeByMetadataName("N1.N2.N3.E");
            var type2 = (SourceNamedTypeSymbol)compilation2.GetTypeByMetadataName("N1.N2.N3.E");
            Assert.True(type1.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
            Assert.True(type2.MergedDeclaration.Declarations[0].MemberNames.Value.SetEquals(new[] { "x", "y", "z" }));
 
            // We should have the exact same set for the names.
            Assert.Same(type1.MergedDeclaration.Declarations[0].MemberNames, type2.MergedDeclaration.Declarations[0].MemberNames);
        }
 
        /// <remarks>
        /// When using this type, make sure to pass an explicit CompilationOptions to CreateCompilation, as the check
        /// to see whether the syntax tree has top-level statements will increment the counter.
        /// </remarks>
        private class CountedSyntaxTree : CSharpSyntaxTree
        {
            private class Reference : SyntaxReference
            {
                private readonly CountedSyntaxTree _countedSyntaxTree;
                private readonly SyntaxReference _underlyingSyntaxReference;
 
                public Reference(CountedSyntaxTree countedSyntaxTree, SyntaxReference syntaxReference)
                {
                    _countedSyntaxTree = countedSyntaxTree;
                    _underlyingSyntaxReference = syntaxReference;
                }
 
                public override SyntaxTree SyntaxTree
                {
                    get
                    {
                        return _countedSyntaxTree;
                    }
                }
 
                public override TextSpan Span
                {
                    get { return _underlyingSyntaxReference.Span; }
                }
 
                public override SyntaxNode GetSyntax(CancellationToken cancellationToken)
                {
                    // Note: It's important for us to maintain identity of nodes/trees, so we find
                    // the equivalent node in our CountedSyntaxTree.
                    var nodeInUnderlying = _underlyingSyntaxReference.GetSyntax(cancellationToken);
 
                    // Note: GetCompilationUnitRoot increments AccessCount
                    var token = _countedSyntaxTree.GetCompilationUnitRoot(cancellationToken).FindToken(nodeInUnderlying.SpanStart);
                    for (var node = token.Parent; node != null; node = node.Parent)
                    {
                        if (node.Span == nodeInUnderlying.Span && node.RawKind == nodeInUnderlying.RawKind)
                        {
                            return (CSharpSyntaxNode)node;
                        }
                    }
 
                    throw new Exception("Should have found the node");
                }
            }
 
            private readonly SyntaxTree _underlyingTree;
            private readonly CompilationUnitSyntax _root;
 
            public int AccessCount;
 
            public CountedSyntaxTree(SyntaxTree underlying)
            {
                Debug.Assert(underlying != null);
                Debug.Assert(underlying.HasCompilationUnitRoot);
 
                _underlyingTree = underlying;
                _root = CloneNodeAsRoot(_underlyingTree.GetCompilationUnitRoot(CancellationToken.None));
            }
 
            public override string FilePath
            {
                get { return _underlyingTree.FilePath; }
            }
 
            public override CSharpParseOptions Options
            {
                get { return (CSharpParseOptions)_underlyingTree.Options; }
            }
 
            public override CSharpSyntaxNode GetRoot(CancellationToken cancellationToken = default(CancellationToken))
            {
                AccessCount++;
                return _root;
            }
 
            public override bool TryGetRoot(out CSharpSyntaxNode root)
            {
                root = _root;
                AccessCount++;
                return true;
            }
 
            public override bool HasCompilationUnitRoot
            {
                get { return true; }
            }
 
            public override SourceText GetText(CancellationToken cancellationToken)
            {
                return _underlyingTree.GetText(cancellationToken);
            }
 
            public override bool TryGetText(out SourceText text)
            {
                return _underlyingTree.TryGetText(out text);
            }
 
            public override Encoding Encoding
            {
                get { return _underlyingTree.Encoding; }
            }
 
            public override int Length
            {
                get { return _underlyingTree.Length; }
            }
 
            [Obsolete]
            public override ImmutableDictionary<string, ReportDiagnostic> DiagnosticOptions => throw new NotImplementedException();
 
            public override SyntaxReference GetReference(SyntaxNode node)
            {
                return new Reference(this, _underlyingTree.GetReference(node));
            }
 
            public override SyntaxTree WithChangedText(SourceText newText)
            {
                return _underlyingTree.WithChangedText(newText);
            }
 
            public override SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions options)
            {
                throw new NotImplementedException();
            }
 
            public override SyntaxTree WithFilePath(string path)
            {
                throw new NotImplementedException();
            }
        }
    }
}