File: Symbols\Retargeting\RetargetExplicitInterfaceImplementation.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.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class RetargetExplicitInterfaceImplementation : CSharpTestBase
    {
        [Fact]
        public void ExplicitInterfaceImplementationRetargeting()
        {
            var comp1 = CreateCompilation(
                new AssemblyIdentity("Assembly1"),
                new string[]
                { @"
public class C : Interface1
{
    void Interface1.Method1() { }
    void Interface1.Method2() { }
    void Interface1.Method3(bool b) { }
    void Interface1.Method4(Class1 c) { }
    string Interface1.Property1 { get; set; } 
    string Interface1.Property2 { get; set; } 
    string Interface1.Property3 { get; set; } 
    Class1 Interface1.Property4 { get; set; } 
    string Interface1.this[string x] { get { return null; } set { } }
    string Interface1.this[string x, string y] { get { return null; } set { } }
    string Interface1.this[string x, string y, string z] { get { return null; } set { } }
    Class1 Interface1.this[Class1 x, Class1 y, Class1 z, Class1 w] { get { return null; } set { } }
    event System.Action Interface1.Event1 { add { } remove { } } 
    event System.Action Interface1.Event2 { add { } remove { } } 
    event System.Action Interface1.Event3 { add { } remove { } } 
    event Delegate1 Interface1.Event4 { add { } remove { } } 
}
"
                },
                new[]
                    {
                        NetFramework.mscorlib,
                        TestReferences.SymbolsTests.V1.MTTestLib1.dll,
                    });
 
            comp1.VerifyDiagnostics();
 
            var globalNamespace1 = comp1.GlobalNamespace;
            var classC = globalNamespace1.GetTypeMembers("C").Single();
 
            var interfaceV1 = globalNamespace1.GetTypeMembers("Interface1").Single();
 
            var interfaceV1Method1 = (MethodSymbol)interfaceV1.GetMembers("Method1").Single();
            var interfaceV1Method2 = (MethodSymbol)interfaceV1.GetMembers("Method2").Single();
            var interfaceV1Method3 = (MethodSymbol)interfaceV1.GetMembers("Method3").Single();
            var interfaceV1Method4 = (MethodSymbol)interfaceV1.GetMembers("Method4").Single();
 
            var interfaceV1Property1 = (PropertySymbol)interfaceV1.GetMembers("Property1").Single();
            var interfaceV1Property2 = (PropertySymbol)interfaceV1.GetMembers("Property2").Single();
            var interfaceV1Property3 = (PropertySymbol)interfaceV1.GetMembers("Property3").Single();
            var interfaceV1Property4 = (PropertySymbol)interfaceV1.GetMembers("Property4").Single();
 
            var interfaceV1Indexer1 = FindIndexerWithParameterCount(interfaceV1, 1);
            var interfaceV1Indexer2 = FindIndexerWithParameterCount(interfaceV1, 2);
            var interfaceV1Indexer3 = FindIndexerWithParameterCount(interfaceV1, 3);
            var interfaceV1Indexer4 = FindIndexerWithParameterCount(interfaceV1, 4);
 
            var interfaceV1Event1 = (EventSymbol)interfaceV1.GetMembers("Event1").Single();
            var interfaceV1Event2 = (EventSymbol)interfaceV1.GetMembers("Event2").Single();
            var interfaceV1Event3 = (EventSymbol)interfaceV1.GetMembers("Event3").Single();
            var interfaceV1Event4 = (EventSymbol)interfaceV1.GetMembers("Event4").Single();
 
            foreach (var member in classC.GetMembers())
            {
                switch (member.Kind)
                {
                    case SymbolKind.Method:
                        var method = (MethodSymbol)member;
                        if (method.MethodKind == MethodKind.ExplicitInterfaceImplementation)
                        {
                            Assert.Equal(interfaceV1, method.ExplicitInterfaceImplementations.Single().ContainingType);
                        }
                        break;
                    case SymbolKind.Property:
                        Assert.Equal(interfaceV1, ((PropertySymbol)member).ExplicitInterfaceImplementations.Single().ContainingType);
                        break;
                    case SymbolKind.Event:
                        Assert.Equal(interfaceV1, ((EventSymbol)member).ExplicitInterfaceImplementations.Single().ContainingType);
                        break;
                    case SymbolKind.ErrorType:
                        Assert.True(false);
                        break;
                }
            }
 
            var comp2 = CreateCompilation(
                new AssemblyIdentity("Assembly2"),
                new string[]
                {@"
public  class D : C
{
}
"
                },
                new MetadataReference[]
                {
                        NetFramework.mscorlib,
                        TestReferences.SymbolsTests.V2.MTTestLib1.dll,
                        new CSharpCompilationReference(comp1)
                });
 
            Assert.False(comp2.GetDiagnostics().Any());
 
            var globalNamespace2 = comp2.GlobalNamespace;
 
            var interfaceV2 = globalNamespace2.GetTypeMembers("Interface1").Single();
            Assert.NotSame(interfaceV1, interfaceV2);
 
            var interfaceV2Method1 = (MethodSymbol)interfaceV2.GetMembers("Method1").Single();
            //Method2 deleted
            var interfaceV2Method3 = (MethodSymbol)interfaceV2.GetMembers("Method3").Single();
            var interfaceV2Method4 = (MethodSymbol)interfaceV2.GetMembers("Method4").Single();
 
            var interfaceV2Property1 = (PropertySymbol)interfaceV2.GetMembers("Property1").Single();
            //Property2 deleted
            var interfaceV2Property3 = (PropertySymbol)interfaceV2.GetMembers("Property3").Single();
            var interfaceV2Property4 = (PropertySymbol)interfaceV2.GetMembers("Property4").Single();
 
            var interfaceV2Indexer1 = FindIndexerWithParameterCount(interfaceV2, 1);
            //Two-parameter indexer deleted
            var interfaceV2Indexer3 = FindIndexerWithParameterCount(interfaceV2, 3);
            var interfaceV2Indexer4 = FindIndexerWithParameterCount(interfaceV2, 4);
 
            var interfaceV2Event1 = (EventSymbol)interfaceV2.GetMembers("Event1").Single();
            //Event2 deleted
            var interfaceV2Event3 = (EventSymbol)interfaceV2.GetMembers("Event3").Single();
            var interfaceV2Event4 = (EventSymbol)interfaceV2.GetMembers("Event4").Single();
 
            var classD = globalNamespace2.GetTypeMembers("D").Single();
            var retargetedClassC = classD.BaseType();
 
            Assert.IsType<RetargetingNamedTypeSymbol>(retargetedClassC);
 
            Assert.Equal(interfaceV2, retargetedClassC.Interfaces().Single());
 
            var retargetedClassCMethod1 = (MethodSymbol)retargetedClassC.GetMembers("Interface1.Method1").Single();
            {
                Assert.IsType<RetargetingMethodSymbol>(retargetedClassCMethod1);
                Assert.Equal(MethodKind.ExplicitInterfaceImplementation, retargetedClassCMethod1.MethodKind);
 
                var retargetedClassCMethod1Impl = retargetedClassCMethod1.ExplicitInterfaceImplementations.Single();
                Assert.Same(interfaceV2Method1, retargetedClassCMethod1Impl);
                Assert.NotSame(interfaceV1Method1, retargetedClassCMethod1Impl);
                Assert.Equal(retargetedClassCMethod1Impl.ToTestDisplayString(), interfaceV1Method1.ToTestDisplayString());
            }
 
            var retargetedClassCMethod2 = (MethodSymbol)retargetedClassC.GetMembers("Interface1.Method2").Single();
            {
                Assert.IsType<RetargetingMethodSymbol>(retargetedClassCMethod2);
                Assert.Equal(MethodKind.ExplicitInterfaceImplementation, retargetedClassCMethod2.MethodKind);
 
                //since the method is missing from V2 of the interface
                Assert.False(retargetedClassCMethod2.ExplicitInterfaceImplementations.Any());
            }
 
            var retargetedClassCMethod3 = (MethodSymbol)retargetedClassC.GetMembers("Interface1.Method3").Single();
            {
                Assert.IsType<RetargetingMethodSymbol>(retargetedClassCMethod3);
                Assert.Equal(MethodKind.ExplicitInterfaceImplementation, retargetedClassCMethod3.MethodKind);
 
                //since the method has a different signature in V2 of the interface
                Assert.False(retargetedClassCMethod3.ExplicitInterfaceImplementations.Any());
            }
 
            var retargetedClassCMethod4 = (MethodSymbol)retargetedClassC.GetMembers("Interface1.Method4").Single();
            {
                Assert.IsType<RetargetingMethodSymbol>(retargetedClassCMethod4);
                Assert.Equal(MethodKind.ExplicitInterfaceImplementation, retargetedClassCMethod4.MethodKind);
 
                var retargetedClassCMethod4Impl = retargetedClassCMethod4.ExplicitInterfaceImplementations.Single();
                Assert.Same(interfaceV2Method4, retargetedClassCMethod4Impl);
                Assert.NotSame(interfaceV1Method4, retargetedClassCMethod4Impl);
                Assert.Equal(retargetedClassCMethod4Impl.ToTestDisplayString(), interfaceV1Method4.ToTestDisplayString());
            }
 
            var retargetedClassCProperty1 = (PropertySymbol)retargetedClassC.GetMembers("Interface1.Property1").Single();
            {
                Assert.IsType<RetargetingPropertySymbol>(retargetedClassCProperty1);
 
                var retargetedClassCProperty1Impl = retargetedClassCProperty1.ExplicitInterfaceImplementations.Single();
                Assert.Same(interfaceV2Property1, retargetedClassCProperty1Impl);
                Assert.NotSame(interfaceV1Property1, retargetedClassCProperty1Impl);
                Assert.Equal(retargetedClassCProperty1Impl.Name, interfaceV1Property1.Name);
                Assert.Equal(retargetedClassCProperty1Impl.Type.ToTestDisplayString(), interfaceV1Property1.Type.ToTestDisplayString());
            }
 
            var retargetedClassCProperty2 = (PropertySymbol)retargetedClassC.GetMembers("Interface1.Property2").Single();
            {
                Assert.IsType<RetargetingPropertySymbol>(retargetedClassCProperty2);
 
                //since the property is missing from V2 of the interface
                Assert.False(retargetedClassCProperty2.ExplicitInterfaceImplementations.Any());
            }
 
            var retargetedClassCProperty3 = (PropertySymbol)retargetedClassC.GetMembers("Interface1.Property3").Single();
            {
                Assert.IsType<RetargetingPropertySymbol>(retargetedClassCProperty3);
 
                //since the property has a different type in V2 of the interface
                Assert.False(retargetedClassCProperty3.ExplicitInterfaceImplementations.Any());
            }
 
            var retargetedClassCProperty4 = (PropertySymbol)retargetedClassC.GetMembers("Interface1.Property4").Single();
            {
                Assert.IsType<RetargetingPropertySymbol>(retargetedClassCProperty4);
 
                var retargetedClassCProperty4Impl = retargetedClassCProperty4.ExplicitInterfaceImplementations.Single();
                Assert.Same(interfaceV2Property4, retargetedClassCProperty4Impl);
                Assert.NotSame(interfaceV1Property4, retargetedClassCProperty4Impl);
                Assert.Equal(retargetedClassCProperty4Impl.Name, interfaceV1Property4.Name);
                Assert.Equal(retargetedClassCProperty4Impl.Type.ToTestDisplayString(), interfaceV1Property4.Type.ToTestDisplayString());
            }
 
            var retargetedClassCIndexer1 = FindIndexerWithParameterCount(retargetedClassC, 1);
            {
                Assert.IsType<RetargetingPropertySymbol>(retargetedClassCIndexer1);
 
                var retargetedClassCIndexer1Impl = retargetedClassCIndexer1.ExplicitInterfaceImplementations.Single();
                Assert.Same(interfaceV2Indexer1, retargetedClassCIndexer1Impl);
                Assert.NotSame(interfaceV1Indexer1, retargetedClassCIndexer1Impl);
                Assert.Equal(retargetedClassCIndexer1Impl.Name, interfaceV1Indexer1.Name);
                Assert.Equal(retargetedClassCIndexer1Impl.Type.ToTestDisplayString(), interfaceV1Indexer1.Type.ToTestDisplayString());
            }
 
            var retargetedClassCIndexer2 = FindIndexerWithParameterCount(retargetedClassC, 2);
            {
                Assert.IsType<RetargetingPropertySymbol>(retargetedClassCIndexer2);
 
                //since the property is missing from V2 of the interface
                Assert.False(retargetedClassCIndexer2.ExplicitInterfaceImplementations.Any());
            }
 
            var retargetedClassCIndexer3 = FindIndexerWithParameterCount(retargetedClassC, 3);
            {
                Assert.IsType<RetargetingPropertySymbol>(retargetedClassCIndexer3);
 
                //since the property has a different type in V2 of the interface
                Assert.False(retargetedClassCIndexer3.ExplicitInterfaceImplementations.Any());
            }
 
            var retargetedClassCIndexer4 = FindIndexerWithParameterCount(retargetedClassC, 4);
            {
                Assert.IsType<RetargetingPropertySymbol>(retargetedClassCIndexer4);
 
                var retargetedClassCIndexer4Impl = retargetedClassCIndexer4.ExplicitInterfaceImplementations.Single();
                Assert.Same(interfaceV2Indexer4, retargetedClassCIndexer4Impl);
                Assert.NotSame(interfaceV1Indexer4, retargetedClassCIndexer4Impl);
                Assert.Equal(retargetedClassCIndexer4Impl.Name, interfaceV1Indexer4.Name);
                Assert.Equal(retargetedClassCIndexer4Impl.Type.ToTestDisplayString(), interfaceV1Indexer4.Type.ToTestDisplayString());
            }
 
            var retargetedClassCEvent1 = (EventSymbol)retargetedClassC.GetMembers("Interface1.Event1").Single();
            {
                Assert.IsType<RetargetingEventSymbol>(retargetedClassCEvent1);
 
                var retargetedClassCEvent1Impl = retargetedClassCEvent1.ExplicitInterfaceImplementations.Single();
                Assert.Same(interfaceV2Event1, retargetedClassCEvent1Impl);
                Assert.NotSame(interfaceV1Event1, retargetedClassCEvent1Impl);
                Assert.Equal(retargetedClassCEvent1Impl.Name, interfaceV1Event1.Name);
                Assert.Equal(retargetedClassCEvent1Impl.Type.ToTestDisplayString(), interfaceV1Event1.Type.ToTestDisplayString());
            }
 
            var retargetedClassCEvent2 = (EventSymbol)retargetedClassC.GetMembers("Interface1.Event2").Single();
            {
                Assert.IsType<RetargetingEventSymbol>(retargetedClassCEvent2);
 
                //since the event is missing from V2 of the interface
                Assert.False(retargetedClassCEvent2.ExplicitInterfaceImplementations.Any());
            }
 
            var retargetedClassCEvent3 = (EventSymbol)retargetedClassC.GetMembers("Interface1.Event3").Single();
            {
                Assert.IsType<RetargetingEventSymbol>(retargetedClassCEvent3);
 
                //since the event has a different type in V2 of the interface
                Assert.False(retargetedClassCEvent3.ExplicitInterfaceImplementations.Any());
            }
 
            var retargetedClassCEvent4 = (EventSymbol)retargetedClassC.GetMembers("Interface1.Event4").Single();
            {
                Assert.IsType<RetargetingEventSymbol>(retargetedClassCEvent4);
 
                var retargetedClassCEvent4Impl = retargetedClassCEvent4.ExplicitInterfaceImplementations.Single();
                Assert.Same(interfaceV2Event4, retargetedClassCEvent4Impl);
                Assert.NotSame(interfaceV1Event4, retargetedClassCEvent4Impl);
                Assert.Equal(retargetedClassCEvent4Impl.Name, interfaceV1Event4.Name);
                Assert.Equal(retargetedClassCEvent4Impl.Type.ToTestDisplayString(), interfaceV1Event4.Type.ToTestDisplayString());
            }
        }
 
        private static PropertySymbol FindIndexerWithParameterCount(NamedTypeSymbol type, int parameterCount)
        {
            return type.GetMembers().Where(s => s.Kind == SymbolKind.Property).Cast<PropertySymbol>().Single(p => p.Parameters.Length == parameterCount);
        }
 
        [Fact]
        public void ExplicitInterfaceImplementationRetargetingGeneric()
        {
            var comp1 = CreateCompilation(
                new AssemblyIdentity("Assembly1"),
                new string[]
                { @"
public class C1<S> : Interface2<S>
{
    void Interface2<S>.Method1(S s) { }
    S Interface2<S>.Property1 { get; set; } 
    event System.Action<S> Interface2<S>.Event1 { add { } remove { } }
}
 
public class C2 : Interface2<int>
{
    void Interface2<int>.Method1(int i) { }
    int Interface2<int>.Property1 { get; set; } 
    event System.Action<int> Interface2<int>.Event1 { add { } remove { } }
}
 
public class C3 : Interface2<Class1>
{
    void Interface2<Class1>.Method1(Class1 c) { }
    Class1 Interface2<Class1>.Property1 { get; set; } 
    event System.Action<Class1> Interface2<Class1>.Event1 { add { } remove { } }
}
"
                },
                new[]
                    {
                        NetFramework.mscorlib,
                        TestReferences.SymbolsTests.V1.MTTestLib1.dll,
                    });
 
            var d = comp1.GetDiagnostics();
            Assert.False(comp1.GetDiagnostics().Any());
 
            var globalNamespace1 = comp1.GlobalNamespace;
            var classC1 = globalNamespace1.GetTypeMembers("C1").Single();
            var classC2 = globalNamespace1.GetTypeMembers("C2").Single();
            var classC3 = globalNamespace1.GetTypeMembers("C3").Single();
 
            foreach (var diag in comp1.GetDiagnostics())
            {
                Console.WriteLine(diag);
            }
 
            var comp2 = CreateCompilation(
                new AssemblyIdentity("Assembly2"),
                new string[]
                {@"
public  class D1<R> : C1<R>
{
}
public  class D2 : C2
{
}
public  class D3 : C3
{
}
"
                },
                new MetadataReference[]
                {
                        NetFramework.mscorlib,
                        TestReferences.SymbolsTests.V2.MTTestLib1.dll,
                        new CSharpCompilationReference(comp1)
                });
 
            Assert.False(comp2.GetDiagnostics().Any());
 
            var globalNamespace2 = comp2.GlobalNamespace;
 
            var interfaceV2 = globalNamespace2.GetTypeMembers("Interface2").Single();
            var interfaceV2Method1 = interfaceV2.GetMembers("Method1").Single();
            var interfaceV2Property1 = interfaceV2.GetMembers("Property1").Single();
            var interfaceV2Event1 = interfaceV2.GetMembers("Event1").Single();
 
            var classD1 = globalNamespace2.GetTypeMembers("D1").Single();
            var classD2 = globalNamespace2.GetTypeMembers("D2").Single();
            var classD3 = globalNamespace2.GetTypeMembers("D3").Single();
 
            var retargetedClassC1 = classD1.BaseType();
            var retargetedClassC2 = classD2.BaseType();
            var retargetedClassC3 = classD3.BaseType();
 
            var retargetedClassC1Method1 = (MethodSymbol)retargetedClassC1.GetMembers("Interface2<S>.Method1").Single();
            var retargetedClassC1Method1Impl = retargetedClassC1Method1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Method1, retargetedClassC1Method1Impl.OriginalDefinition);
 
            var retargetedClassC2Method1 = (MethodSymbol)retargetedClassC2.GetMembers("Interface2<System.Int32>.Method1").Single();
            var retargetedClassC2Method1Impl = retargetedClassC2Method1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Method1, retargetedClassC2Method1Impl.OriginalDefinition);
 
            var retargetedClassC3Method1 = (MethodSymbol)retargetedClassC3.GetMembers("Interface2<Class1>.Method1").Single();
            var retargetedClassC3Method1Impl = retargetedClassC3Method1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Method1, retargetedClassC3Method1Impl.OriginalDefinition);
 
            var retargetedClassC1Property1 = (PropertySymbol)retargetedClassC1.GetMembers("Interface2<S>.Property1").Single();
            var retargetedClassC1Property1Impl = retargetedClassC1Property1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Property1, retargetedClassC1Property1Impl.OriginalDefinition);
 
            var retargetedClassC2Property1 = (PropertySymbol)retargetedClassC2.GetMembers("Interface2<System.Int32>.Property1").Single();
            var retargetedClassC2Property1Impl = retargetedClassC2Property1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Property1, retargetedClassC2Property1Impl.OriginalDefinition);
 
            var retargetedClassC3Property1 = (PropertySymbol)retargetedClassC3.GetMembers("Interface2<Class1>.Property1").Single();
            var retargetedClassC3Property1Impl = retargetedClassC3Property1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Property1, retargetedClassC3Property1Impl.OriginalDefinition);
 
            var retargetedClassC1Event1 = (EventSymbol)retargetedClassC1.GetMembers("Interface2<S>.Event1").Single();
            var retargetedClassC1Event1Impl = retargetedClassC1Event1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Event1, retargetedClassC1Event1Impl.OriginalDefinition);
 
            var retargetedClassC2Event1 = (EventSymbol)retargetedClassC2.GetMembers("Interface2<System.Int32>.Event1").Single();
            var retargetedClassC2Event1Impl = retargetedClassC2Event1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Event1, retargetedClassC2Event1Impl.OriginalDefinition);
 
            var retargetedClassC3Event1 = (EventSymbol)retargetedClassC3.GetMembers("Interface2<Class1>.Event1").Single();
            var retargetedClassC3Event1Impl = retargetedClassC3Event1.ExplicitInterfaceImplementations.Single();
            Assert.Same(interfaceV2Event1, retargetedClassC3Event1Impl.OriginalDefinition);
        }
 
        [Fact]
        public void ExplicitInterfaceImplementationRetargetingGenericType()
        {
            var source1 = @"
public class C1<T>
{
    public interface I1
    {
        void M(T x);
    }
}
";
            var ref1 = CreateEmptyCompilation("").ToMetadataReference();
            var compilation1 = CreateCompilation(source1, references: new[] { ref1 });
 
            var source2 = @"
public class C2<U> : C1<U>.I1
{
    void C1<U>.I1.M(U x) {}
}
";
            var compilation2 = CreateCompilation(source2, references: new[] { compilation1.ToMetadataReference(), ref1, CreateEmptyCompilation("").ToMetadataReference() });
 
            var compilation3 = CreateCompilation("", references: new[] { compilation1.ToMetadataReference(), compilation2.ToMetadataReference() });
 
            Assert.NotSame(compilation2.GetTypeByMetadataName("C1`1"), compilation3.GetTypeByMetadataName("C1`1"));
 
            var c2 = compilation3.GetTypeByMetadataName("C2`1");
            Assert.IsType<RetargetingNamedTypeSymbol>(c2);
 
            var m = c2.GetMethod("C1<U>.I1.M");
 
            Assert.Equal(c2.Interfaces().Single().GetMethod("M"), m.ExplicitInterfaceImplementations.Single());
        }
    }
}