|
// 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 System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols
{
public class InterfaceImplementationTests : CSharpTestBase
{
/// <summary>
/// Test the pre-checks performed by TypeSymbol.FindImplementationForInterfaceMember to
/// short-circuit interface mapping. Should be the same for methods, properties, and
/// events since it never gets to the interface mapping code.
/// </summary>
[Fact]
public void TestObviousNulls()
{
var text = @"
class Base
{
public void Method() { }
public int Property { get { return 0; } }
public int this[int x] { get { return 0; } }
public event Delegate Event;
public int Field;
public interface Interface { }
public class Class { }
public struct Struct { }
public enum Enum { Element }
public delegate void Delegate();
}
interface Interface
{
void Method();
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @base = (NamedTypeSymbol)global.GetMembers("Base").Single();
var baseMethod = @base.GetMembers("Method").Single();
Assert.Null(@base.FindImplementationForInterfaceMember(baseMethod)); //containing type is not an interface
var baseProperty = @base.GetMember<PropertySymbol>("Property");
Assert.Null(@base.FindImplementationForInterfaceMember(baseProperty)); //containing type is not an interface
var baseIndexer = @base.Indexers.Single();
Assert.Null(@base.FindImplementationForInterfaceMember(baseIndexer)); //containing type is not an interface
var baseEvent = @base.GetMember<EventSymbol>("Event");
Assert.Null(@base.FindImplementationForInterfaceMember(baseEvent)); //containing type is not an interface
var baseField = @base.GetMembers("Field").Single();
Assert.Null(@base.FindImplementationForInterfaceMember(baseField)); //not a method/property/event
var baseNestedInterface = @base.GetMembers("Interface").Single();
Assert.Null(@base.FindImplementationForInterfaceMember(baseNestedInterface)); //not a method/property/event
var baseNestedClass = @base.GetMembers("Class").Single();
Assert.Null(@base.FindImplementationForInterfaceMember(baseNestedClass)); //not a method/property/event
var baseNestedStruct = @base.GetMembers("Struct").Single();
Assert.Null(@base.FindImplementationForInterfaceMember(baseNestedStruct)); //not a method/property/event
var baseNestedEnum = @base.GetMembers("Enum").Single();
Assert.Null(@base.FindImplementationForInterfaceMember(baseNestedEnum)); //not a method/property/event
var baseNestedDelegate = @base.GetMembers("Delegate").Single();
Assert.Null(@base.FindImplementationForInterfaceMember(baseNestedDelegate)); //not a method/property/event
Assert.Throws<ArgumentNullException>(() => @base.FindImplementationForInterfaceMember(null)); //not a method/property/event
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @base.GetMembers("Method").Single();
Assert.Null(@interface.FindImplementationForInterfaceMember(interfaceMethod)); //type is not a class or struct
Assert.Null(@interface.FindImplementationForInterfaceMember(@interface)); //symbol containing type is null
}
/// <summary>
/// 1) Explicit implementation beats implicit implementation.
/// 2) Explicit implementation of a declared interface's base interface.
/// 3) Explicit implementation of a hidden interface method.
/// </summary>
[Fact]
public void TestExplicitMethodImplementation()
{
var text = @"
interface BaseInterface
{
void Method();
}
interface Interface : BaseInterface
{
new void Method();
}
class Class : Interface
{
void BaseInterface.Method() { }
void Interface.Method() { }
public void Method() { }
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var baseInterface = (NamedTypeSymbol)global.GetMembers("BaseInterface").Single();
var baseInterfaceMethod = baseInterface.GetMembers("Method").Single();
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @interface.GetMembers("Method").Single();
var @class = (NamedTypeSymbol)global.GetMembers("Class").Single();
var classExplicitImplementationBase = (MethodSymbol)@class.GetMembers("BaseInterface.Method").Single();
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, classExplicitImplementationBase.MethodKind);
var classExplicitImplementation = (MethodSymbol)@class.GetMembers("Interface.Method").Single();
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, classExplicitImplementation.MethodKind);
var classImplicitImplementation = (MethodSymbol)@class.GetMembers("Method").Single();
Assert.Equal(MethodKind.Ordinary, classImplicitImplementation.MethodKind);
Assert.NotSame(classImplicitImplementation, classExplicitImplementation);
Assert.NotSame(classImplicitImplementation, classExplicitImplementationBase);
var implementingMethodBase = @class.FindImplementationForInterfaceMember(baseInterfaceMethod);
Assert.Same(classExplicitImplementationBase, implementingMethodBase);
var implementingMethod = @class.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(classExplicitImplementation, implementingMethod);
}
/// <summary>
/// 1) Explicit implementation beats implicit implementation.
/// 2) Explicit implementation of a declared interface's base interface.
/// 3) Explicit implementation of a hidden interface method.
/// </summary>
[Fact]
public void TestExplicitIndexerImplementation()
{
var text = @"
interface BaseInterface
{
int this[int x] { get; }
}
interface Interface : BaseInterface
{
new int this[int x] { get; }
}
class Class : Interface
{
int BaseInterface.this[int x] { get { return 0; } }
int Interface.this[int x] { get { return 0; } }
public int this[int x] { get { return 0; } }
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var baseInterface = (NamedTypeSymbol)global.GetMembers("BaseInterface").Single();
var baseInterfaceIndexer = baseInterface.Indexers.Single();
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceIndexer = @interface.Indexers.Single();
var @class = (NamedTypeSymbol)global.GetMembers("Class").Single();
var classExplicitImplementationBase = @class.GetProperty("BaseInterface.this[]");
var classExplicitImplementation = @class.GetProperty("Interface.this[]");
var classImplicitImplementation = @class.GetProperty("this[]");
Assert.NotSame(classImplicitImplementation, classExplicitImplementation);
Assert.NotSame(classImplicitImplementation, classExplicitImplementationBase);
var implementingMethodBase = @class.FindImplementationForInterfaceMember(baseInterfaceIndexer);
Assert.Same(classExplicitImplementationBase, implementingMethodBase);
var implementingMethod = @class.FindImplementationForInterfaceMember(interfaceIndexer);
Assert.Same(classExplicitImplementation, implementingMethod);
}
/// <summary>
/// 1) Implicit implementation of a declared interface.
/// 2) Implicit implementation of a declared interface's base interface.
/// 3) Implicit implementation of more than one interface method.
/// </summary>
[Fact]
public void TestImplicitMethodImplementation()
{
var text1 = @"
public interface BaseInterface1
{
void BaseMethod();
}
public interface BaseInterface2
{
void BaseMethod();
}
";
var text2 = @"
public interface Interface : BaseInterface1, BaseInterface2
{
void Method();
}
";
var text3 = @"
class Class : Interface
{
public void Method() { }
public void BaseMethod() { }
}
";
var comp1 = CreateCompilation(text1);
var comp1ref = new CSharpCompilationReference(comp1);
var refs = new System.Collections.Generic.List<MetadataReference>() { comp1ref };
var comp2 = CreateCompilation(text2, references: refs, assemblyName: "Test2");
var comp2ref = new CSharpCompilationReference(comp2);
refs.Add(comp2ref);
var comp = CreateCompilation(text3, refs, assemblyName: "Test3");
var global = comp.GlobalNamespace;
var baseInterface1 = (NamedTypeSymbol)global.GetMembers("BaseInterface1").Single();
var baseInterface1Method = baseInterface1.GetMembers("BaseMethod").Single();
var baseInterface2 = (NamedTypeSymbol)global.GetMembers("BaseInterface2").Single();
var baseInterface2Method = baseInterface2.GetMembers("BaseMethod").Single();
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @interface.GetMembers("Method").Single();
var @class = (NamedTypeSymbol)global.GetMembers("Class").Single();
var classImplicitImplementation = (MethodSymbol)@class.GetMembers("Method").Single();
Assert.Equal(MethodKind.Ordinary, classImplicitImplementation.MethodKind);
var classImplicitImplementationBase = (MethodSymbol)@class.GetMembers("BaseMethod").Single();
Assert.Equal(MethodKind.Ordinary, classImplicitImplementationBase.MethodKind);
var implementingMethod = @class.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(classImplicitImplementation, implementingMethod);
var implementingMethodBase1 = @class.FindImplementationForInterfaceMember(baseInterface1Method);
Assert.Same(classImplicitImplementationBase, implementingMethodBase1);
var implementingMethodBase2 = @class.FindImplementationForInterfaceMember(baseInterface2Method);
Assert.Same(classImplicitImplementationBase, implementingMethodBase2);
}
/// <summary>
/// 1) Implicit implementation of a declared interface.
/// 2) Implicit implementation of a declared interface's base interface.
/// 3) Implicit implementation of more than one interface indexer.
/// </summary>
[Fact]
public void TestImplicitIndexerImplementation()
{
var text1 = @"
public interface BaseInterface1
{
int this[int x] { get; }
}
public interface BaseInterface2
{
int this[int x] { get; }
}
";
var text2 = @"
public interface Interface : BaseInterface1, BaseInterface2
{
int this[int x, int y] { get; }
}
";
var text3 = @"
class Class : Interface
{
public int this[int x] { get { return 0; } }
public int this[int x, int y] { get { return 0; } }
}
";
var comp1 = CreateCompilation(text1);
var comp1ref = new CSharpCompilationReference(comp1);
var refs = new System.Collections.Generic.List<MetadataReference>() { comp1ref };
var comp2 = CreateCompilation(text2, references: refs, assemblyName: "Test2");
var comp2ref = new CSharpCompilationReference(comp2);
refs.Add(comp2ref);
var comp = CreateCompilation(text3, refs, assemblyName: "Test3");
var global = comp.GlobalNamespace;
var baseInterface1 = (NamedTypeSymbol)global.GetMembers("BaseInterface1").Single();
var baseInterface1Indexer = baseInterface1.Indexers.Single();
var baseInterface2 = (NamedTypeSymbol)global.GetMembers("BaseInterface2").Single();
var baseInterface2Indexer = baseInterface2.Indexers.Single();
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceIndexer = @interface.Indexers.Single();
var @class = (NamedTypeSymbol)global.GetMembers("Class").Single();
var classImplicitImplementation = @class.Indexers.Single(p => p.Parameters.Length == 2);
var classImplicitImplementationBase = @class.Indexers.Single(p => p.Parameters.Length == 1);
var implementingIndexer = @class.FindImplementationForInterfaceMember(interfaceIndexer);
Assert.Same(classImplicitImplementation, implementingIndexer);
var implementingIndexerBase1 = @class.FindImplementationForInterfaceMember(baseInterface1Indexer);
Assert.Same(classImplicitImplementationBase, implementingIndexerBase1);
var implementingIndexerBase2 = @class.FindImplementationForInterfaceMember(baseInterface2Indexer);
Assert.Same(classImplicitImplementationBase, implementingIndexerBase2);
}
/// <summary>
/// Tests classes that nearly, but do not actually implement interface methods.
/// </summary>
[Fact]
public void TestImplicitMethodImplementationMismatches()
{
//UNDONE: type constraint mismatch
var text = @"
interface Interface
{
void Method<T>(long l, int i);
}
class Class1 : Interface
{
private void Method<T>(long l, int i) { } //non-public methods don't participate
public void Method<T, U>(long l, int i) { } //wrong arity
public void Method(long l, int i) { } //wrong arity
public int Method<T>(long l, int i) { } //wrong return type
public void Method1<T>(long l, int i) { } //wrong name
public void Method<T>(long l) { } //wrong parameter count
public void Method<T>(int i, long l) { } //wrong parameter types
public void Method<T>(long l, ref int i) { } //wrong parameter ref kind
}
class Class2 : Interface
{
public static void Method<T>(T t, int i) { } //static methods don't participate
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @interface.GetMembers("Method").Single();
var class1 = (NamedTypeSymbol)global.GetMembers("Class1").Single();
Assert.Null(class1.FindImplementationForInterfaceMember(interfaceMethod));
var class2 = (NamedTypeSymbol)global.GetMembers("Class2").Single();
Assert.Null(class2.FindImplementationForInterfaceMember(interfaceMethod));
}
/// <summary>
/// Class implements interface method via explicit implementation in base class.
/// </summary>
[Fact]
public void TestExplicitMethodImplementationInBase()
{
var text1 = @"
public interface BaseInterface
{
void Method();
}
public interface Interface : BaseInterface
{
new void Method();
}
";
var text2 = @"
public class BaseClass : Interface
{
void BaseInterface.Method() { }
void Interface.Method() { }
public void Method() { }
}
";
var text3 = @"
class Class1 : BaseClass, Interface //declares Interface
{
}
class Class2 : BaseClass //does not declare interface
{
}
";
var comp1 = CreateCompilation(text1);
var comp1ref = new CSharpCompilationReference(comp1);
var refs = new System.Collections.Generic.List<MetadataReference>() { comp1ref };
var comp2 = CreateCompilation(text2, references: refs, assemblyName: "Test2");
var comp2ref = new CSharpCompilationReference(comp2);
refs.Add(comp2ref);
var comp = CreateCompilation(text3, refs, assemblyName: "Test3");
var global = comp.GlobalNamespace;
var baseInterface = (NamedTypeSymbol)global.GetMembers("BaseInterface").Single();
var baseInterfaceMethod = baseInterface.GetMembers("Method").Single();
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @interface.GetMembers("Method").Single();
var baseClass = (NamedTypeSymbol)global.GetMembers("BaseClass").Single();
var baseClassExplicitImplementationBase = (MethodSymbol)baseClass.GetMembers("BaseInterface.Method").Single();
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, baseClassExplicitImplementationBase.MethodKind);
var baseClassExplicitImplementation = (MethodSymbol)baseClass.GetMembers("Interface.Method").Single();
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, baseClassExplicitImplementation.MethodKind);
var baseClassImplicitImplementation = (MethodSymbol)baseClass.GetMembers("Method").Single();
Assert.Equal(MethodKind.Ordinary, baseClassImplicitImplementation.MethodKind);
Assert.NotSame(baseClassImplicitImplementation, baseClassExplicitImplementation);
Assert.NotSame(baseClassImplicitImplementation, baseClassExplicitImplementationBase);
var class1 = (NamedTypeSymbol)global.GetMembers("Class1").Single();
var class1ImplementingMethodBase = class1.FindImplementationForInterfaceMember(baseInterfaceMethod);
Assert.Same(baseClassExplicitImplementationBase, class1ImplementingMethodBase);
var class1ImplementingMethod = class1.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(baseClassExplicitImplementation, class1ImplementingMethod);
var class2 = (NamedTypeSymbol)global.GetMembers("Class2").Single();
var class2ImplementingMethodBase = class2.FindImplementationForInterfaceMember(baseInterfaceMethod);
Assert.Same(baseClassExplicitImplementationBase, class2ImplementingMethodBase);
var class2ImplementingMethod = class2.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(baseClassExplicitImplementation, class2ImplementingMethod);
}
/// <summary>
/// Class implements interface method via implicit implementation in base class.
/// </summary>
[Fact]
public void TestImplicitMethodImplementationInBase()
{
var text = @"
interface BaseInterface1
{
void BaseMethod();
}
interface BaseInterface2
{
void BaseMethod();
}
interface Interface : BaseInterface1, BaseInterface2
{
void Method();
}
class BaseClass : Interface
{
public void Method() { }
public void BaseMethod() { }
}
class Class1 : BaseClass, Interface //declares Interface
{
}
class Class2 : BaseClass //does not declare interface
{
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var baseInterface1 = (NamedTypeSymbol)global.GetMembers("BaseInterface1").Single();
var baseInterface1Method = baseInterface1.GetMembers("BaseMethod").Single();
var baseInterface2 = (NamedTypeSymbol)global.GetMembers("BaseInterface2").Single();
var baseInterface2Method = baseInterface2.GetMembers("BaseMethod").Single();
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @interface.GetMembers("Method").Single();
var baseClass = (NamedTypeSymbol)global.GetMembers("BaseClass").Single();
var baseClassImplicitImplementation = (MethodSymbol)baseClass.GetMembers("Method").Single();
Assert.Equal(MethodKind.Ordinary, baseClassImplicitImplementation.MethodKind);
var baseClassImplicitImplementationBase = (MethodSymbol)baseClass.GetMembers("BaseMethod").Single();
Assert.Equal(MethodKind.Ordinary, baseClassImplicitImplementationBase.MethodKind);
var class1 = (NamedTypeSymbol)global.GetMembers("Class1").Single();
var class1ImplementingMethod = class1.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(baseClassImplicitImplementation, class1ImplementingMethod);
var class1ImplementingMethodBase1 = class1.FindImplementationForInterfaceMember(baseInterface1Method);
Assert.Same(baseClassImplicitImplementationBase, class1ImplementingMethodBase1);
var class1ImplementingMethodBase2 = class1.FindImplementationForInterfaceMember(baseInterface2Method);
Assert.Same(baseClassImplicitImplementationBase, class1ImplementingMethodBase2);
var class2 = (NamedTypeSymbol)global.GetMembers("Class2").Single();
var class2ImplementingMethod = class2.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(baseClassImplicitImplementation, class2ImplementingMethod);
var class2ImplementingMethodBase1 = class2.FindImplementationForInterfaceMember(baseInterface1Method);
Assert.Same(baseClassImplicitImplementationBase, class2ImplementingMethodBase1);
var class2ImplementingMethodBase2 = class2.FindImplementationForInterfaceMember(baseInterface2Method);
Assert.Same(baseClassImplicitImplementationBase, class2ImplementingMethodBase2);
}
/// <summary>
/// Class implements interface method via implicit implementation in base class (which does not implement the interface).
/// </summary>
[Fact]
public void TestImplicitMethodImplementationViaBase()
{
var text = @"
interface Interface
{
void Method();
}
class BaseClass
{
public void Method() { }
}
class Class1 : BaseClass, Interface //declares Interface
{
}
class Class2 : BaseClass //does not declare interface
{
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @interface.GetMembers("Method").Single();
var baseClass = (NamedTypeSymbol)global.GetMembers("BaseClass").Single();
Assert.False(baseClass.AllInterfaces().Contains(@interface));
var baseClassMethod = (MethodSymbol)baseClass.GetMembers("Method").Single();
Assert.Equal(MethodKind.Ordinary, baseClassMethod.MethodKind);
var class1 = (NamedTypeSymbol)global.GetMembers("Class1").Single();
Assert.Same(baseClass, class1.BaseType());
Assert.True(class1.Interfaces().Contains(@interface));
var class2 = (NamedTypeSymbol)global.GetMembers("Class2").Single();
Assert.Same(baseClass, class2.BaseType());
Assert.False(class2.AllInterfaces().Contains(@interface));
Assert.Null(baseClass.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Same(baseClassMethod, class1.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Null(class2.FindImplementationForInterfaceMember(interfaceMethod));
}
/// <summary>
/// Class implements interface indexer via implicit implementation in base class (which does not implement the interface).
/// </summary>
[Fact]
public void TestImplicitIndexerImplementationViaBase()
{
var text = @"
interface Interface
{
int this[int x] { get; }
}
class BaseClass
{
public int this[int x] { get { return 0; } }
}
class Class1 : BaseClass, Interface //declares Interface
{
}
class Class2 : BaseClass //does not declare interface
{
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceIndexer = @interface.Indexers.Single();
var baseClass = (NamedTypeSymbol)global.GetMembers("BaseClass").Single();
Assert.False(baseClass.AllInterfaces().Contains(@interface));
var baseClassIndexer = baseClass.Indexers.Single();
var class1 = (NamedTypeSymbol)global.GetMembers("Class1").Single();
Assert.Same(baseClass, class1.BaseType());
Assert.True(class1.Interfaces().Contains(@interface));
var class2 = (NamedTypeSymbol)global.GetMembers("Class2").Single();
Assert.Same(baseClass, class2.BaseType());
Assert.False(class2.AllInterfaces().Contains(@interface));
Assert.Null(baseClass.FindImplementationForInterfaceMember(interfaceIndexer));
Assert.Same(baseClassIndexer, class1.FindImplementationForInterfaceMember(interfaceIndexer));
Assert.Null(class2.FindImplementationForInterfaceMember(interfaceIndexer));
}
/// <summary>
/// Test remapping of an explicitly implemented interface.
/// </summary>
[Fact]
public void TestExplicitMethodImplementationRemapping()
{
var text = @"
interface Interface
{
void Method();
}
class BaseClass : Interface
{
void Interface.Method() { }
}
class Class1 : BaseClass, Interface //declares Interface
{
public void Method() { }
}
class Class2 : BaseClass //does not declare interface
{
public void Method() { }
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @interface.GetMembers("Method").Single();
var baseClass = (NamedTypeSymbol)global.GetMembers("BaseClass").Single();
var baseClassMethod = (MethodSymbol)baseClass.GetMembers("Interface.Method").Single();
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, baseClassMethod.MethodKind);
var baseClassImplementingMethod = baseClass.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(baseClassMethod, baseClassImplementingMethod);
var class1 = (NamedTypeSymbol)global.GetMembers("Class1").Single();
var class1Method = (MethodSymbol)class1.GetMembers("Method").Single();
Assert.Equal(MethodKind.Ordinary, class1Method.MethodKind);
var class1ImplementingMethod = class1.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(class1Method, class1ImplementingMethod);
Assert.NotSame(baseClassMethod, class1ImplementingMethod);
var class2 = (NamedTypeSymbol)global.GetMembers("Class2").Single();
var class2Method = (MethodSymbol)class2.GetMembers("Method").Single();
Assert.Equal(MethodKind.Ordinary, class2Method.MethodKind);
var class2ImplementingMethod = class2.FindImplementationForInterfaceMember(interfaceMethod);
Assert.Same(baseClassMethod, class2ImplementingMethod);
Assert.NotSame(class2Method, class1ImplementingMethod);
}
/// <summary>
/// Test remapping of an implicitly implemented interface.
/// </summary>
[Fact]
public void TestImplicitMethodImplementationRemapping()
{
var text = @"
interface Interface
{
void Virtual();
void NonVirtual();
}
class BaseClass : Interface
{
public virtual void Virtual() { }
public void NonVirtual() { }
}
class Class1 : BaseClass, Interface //declares Interface
{
public override void Virtual() { }
public new void NonVirtual() { }
}
class Class2 : BaseClass //does not declare interface
{
public override void Virtual() { }
public new void NonVirtual() { }
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethodVirtual = @interface.GetMembers("Virtual").Single();
var interfaceMethodNonVirtual = @interface.GetMembers("NonVirtual").Single();
var baseClass = (NamedTypeSymbol)global.GetMembers("BaseClass").Single();
var baseClassMethodVirtual = (MethodSymbol)baseClass.GetMembers("Virtual").Single();
Assert.Equal(MethodKind.Ordinary, baseClassMethodVirtual.MethodKind);
Assert.True(baseClassMethodVirtual.IsVirtual);
var baseClassMethodNonVirtual = (MethodSymbol)baseClass.GetMembers("NonVirtual").Single();
Assert.Equal(MethodKind.Ordinary, baseClassMethodNonVirtual.MethodKind);
Assert.False(baseClassMethodNonVirtual.IsVirtual);
var baseClassImplementingMethodVirtual = baseClass.FindImplementationForInterfaceMember(interfaceMethodVirtual);
Assert.Same(baseClassMethodVirtual, baseClassImplementingMethodVirtual);
var baseClassImplementingMethodNonVirtual = baseClass.FindImplementationForInterfaceMember(interfaceMethodNonVirtual);
Assert.Same(baseClassMethodNonVirtual, baseClassImplementingMethodNonVirtual);
var class1 = (NamedTypeSymbol)global.GetMembers("Class1").Single();
var class1MethodVirtual = (MethodSymbol)class1.GetMembers("Virtual").Single();
Assert.Equal(MethodKind.Ordinary, class1MethodVirtual.MethodKind);
Assert.True(class1MethodVirtual.IsOverride);
var class1MethodNonVirtual = (MethodSymbol)class1.GetMembers("NonVirtual").Single();
Assert.Equal(MethodKind.Ordinary, class1MethodNonVirtual.MethodKind);
Assert.False(class1MethodNonVirtual.IsOverride);
var class1ImplementingMethodVirtual = class1.FindImplementationForInterfaceMember(interfaceMethodVirtual);
Assert.Same(class1MethodVirtual, class1ImplementingMethodVirtual);
Assert.NotSame(baseClassMethodVirtual, class1ImplementingMethodVirtual);
var class1ImplementingMethodNonVirtual = class1.FindImplementationForInterfaceMember(interfaceMethodNonVirtual);
Assert.Same(class1MethodNonVirtual, class1ImplementingMethodNonVirtual);
Assert.NotSame(baseClassMethodNonVirtual, class1ImplementingMethodNonVirtual);
var class2 = (NamedTypeSymbol)global.GetMembers("Class2").Single();
var class2MethodVirtual = (MethodSymbol)class2.GetMembers("Virtual").Single();
Assert.Equal(MethodKind.Ordinary, class2MethodVirtual.MethodKind);
Assert.True(class2MethodVirtual.IsOverride);
var class2MethodNonVirtual = (MethodSymbol)class2.GetMembers("NonVirtual").Single();
Assert.Equal(MethodKind.Ordinary, class2MethodNonVirtual.MethodKind);
Assert.False(class2MethodNonVirtual.IsOverride);
var class2ImplementingMethodVirtual = class2.FindImplementationForInterfaceMember(interfaceMethodVirtual);
Assert.Same(baseClassMethodVirtual, class2ImplementingMethodVirtual);
Assert.NotSame(class2MethodVirtual, class2ImplementingMethodVirtual);
var class2ImplementingMethodNonVirtual = class2.FindImplementationForInterfaceMember(interfaceMethodNonVirtual);
Assert.Same(baseClassMethodNonVirtual, class2ImplementingMethodNonVirtual);
Assert.NotSame(class2MethodNonVirtual, class2ImplementingMethodNonVirtual);
}
/// <summary>
/// Test remapping of an implicitly implemented interface in a longer chain of types.
/// </summary>
[Fact]
public void TestImplicitMethodImplementationRemapping2()
{
var text = @"
interface Interface
{
void Method();
}
class NonDeclaringClass1
{
public void Method() { }
}
class DeclaringClass1 : NonDeclaringClass1, Interface
{
}
class NonDeclaringClass2 : DeclaringClass1
{
public new void Method() { }
}
class DeclaringClass2 : NonDeclaringClass2, Interface
{
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = @interface.GetMembers("Method").Single();
var nonDeclaring1 = (NamedTypeSymbol)global.GetMembers("NonDeclaringClass1").Single();
Assert.False(nonDeclaring1.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.ContainsKey(@interface));
var nonDeclaring1Method = nonDeclaring1.GetMembers("Method").Single();
var declaring1 = (NamedTypeSymbol)global.GetMembers("DeclaringClass1").Single();
Assert.True(declaring1.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.ContainsKey(@interface));
Assert.Equal(nonDeclaring1, declaring1.BaseType());
var nonDeclaring2 = (NamedTypeSymbol)global.GetMembers("NonDeclaringClass2").Single();
Assert.False(nonDeclaring2.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.ContainsKey(@interface));
Assert.Equal(declaring1, nonDeclaring2.BaseType());
var nonDeclaring2Method = nonDeclaring2.GetMembers("Method").Single();
var declaring2 = (NamedTypeSymbol)global.GetMembers("DeclaringClass2").Single();
Assert.True(declaring2.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.ContainsKey(@interface));
Assert.Equal(nonDeclaring2, declaring2.BaseType());
Assert.Null(nonDeclaring1.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Equal(nonDeclaring1Method, declaring1.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Equal(nonDeclaring1Method, nonDeclaring2.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Equal(nonDeclaring2Method, declaring2.FindImplementationForInterfaceMember(interfaceMethod));
}
/// <summary>
/// In metadata, it is possible for a type to explicitly implement a method of an interface
/// declared by its base type (even if it does not declare the interface itself).
/// </summary>
[Fact]
public void TestExplicitMethodImplementationOnNonDeclaringType()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.ExplicitInterfaceImplementation.Methods.IL,
});
var global = assemblies.ElementAt(1).GlobalNamespace;
var @interface = (NamedTypeSymbol)global.GetMembers("I1").Single();
var interfaceMethod = @interface.GetMembers("Method1").Single();
var baseClass = (NamedTypeSymbol)global.GetMembers("BaseDeclaresInterface").Single();
Assert.True(baseClass.Interfaces().Contains(@interface));
Assert.Null(baseClass.FindImplementationForInterfaceMember(interfaceMethod));
var derivedClass = (NamedTypeSymbol)global.GetMembers("DerivedExplicitlyImplementsInterface").Single();
Assert.False(derivedClass.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.ContainsKey(@interface));
Assert.True(derivedClass.AllInterfaces().Contains(@interface));
var derivedClassMethod = derivedClass.GetMembers("I1.Method1").Single();
Assert.Same(derivedClassMethod, derivedClass.FindImplementationForInterfaceMember(interfaceMethod));
}
[Fact]
public void TestNonVirtualImplicitImplementationSameAssembly()
{
var text = @"
public interface Interface
{
void Method();
int Property { get; set; }
}
public class Base
{
public void Method() { }
public int Property { get; set; }
}
public class Derived : Base, Interface
{
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
Assert.False(comp.GetDiagnostics().Any(), string.Join("\n", comp.GetDiagnostics()));
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = (MethodSymbol)@interface.GetMembers("Method").Single();
var interfaceProperty = (PropertySymbol)@interface.GetMembers("Property").Single();
var interfacePropertyGetter = interfaceProperty.GetMethod;
var interfacePropertySetter = interfaceProperty.SetMethod;
var baseClass = (NamedTypeSymbol)global.GetMembers("Base").Single();
Assert.False(baseClass.AllInterfaces().Contains(@interface));
var baseClassMethod = (MethodSymbol)baseClass.GetMembers("Method").Single();
var baseClassProperty = (PropertySymbol)baseClass.GetMembers("Property").Single();
var baseClassPropertyGetter = baseClassProperty.GetMethod;
var baseClassPropertySetter = baseClassProperty.SetMethod;
Assert.False(baseClassMethod.IsVirtual);
Assert.False(baseClassProperty.IsVirtual);
Assert.False(baseClassPropertyGetter.IsVirtual);
Assert.False(baseClassPropertySetter.IsVirtual);
var derivedClass = (SourceNamedTypeSymbol)global.GetMembers("Derived").Single();
Assert.True(derivedClass.Interfaces().Contains(@interface));
Assert.Same(baseClassMethod, derivedClass.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Same(baseClassProperty, derivedClass.FindImplementationForInterfaceMember(interfaceProperty));
Assert.Same(baseClassPropertyGetter, derivedClass.FindImplementationForInterfaceMember(interfacePropertyGetter));
Assert.Same(baseClassPropertySetter, derivedClass.FindImplementationForInterfaceMember(interfacePropertySetter));
Assert.True(((Cci.IMethodDefinition)baseClassMethod.GetCciAdapter()).IsVirtual);
Assert.True(((Cci.IMethodDefinition)baseClassPropertyGetter.GetCciAdapter()).IsVirtual);
Assert.True(((Cci.IMethodDefinition)baseClassPropertySetter.GetCciAdapter()).IsVirtual);
Assert.False(derivedClass.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Any());
}
[Fact]
public void TestNonVirtualImplicitImplementationOtherAssembly()
{
var text1 = @"
public interface Interface
{
void Method();
int Property { get; set; }
}
public class Base
{
public void Method() { }
public int Property { get; set; }
}
";
var text2 = @"
public class Derived : Base, Interface
{
}
";
var comp1 = CreateCompilation(text1,
assemblyName: "OtherAssembly",
options: TestOptions.ReleaseDll);
Assert.False(comp1.GetDiagnostics().Any(), string.Join("\n", comp1.GetDiagnostics()));
var comp2 = CreateCompilation(text2,
references: new MetadataReference[] { new CSharpCompilationReference(comp1) },
assemblyName: "SourceAssembly",
options: TestOptions.ReleaseDll);
Assert.False(comp2.GetDiagnostics().Any(), string.Join("\n", comp2.GetDiagnostics()));
var global = comp2.GlobalNamespace;
var @interface = (NamedTypeSymbol)global.GetMembers("Interface").Single();
var interfaceMethod = (MethodSymbol)@interface.GetMembers("Method").Single();
var interfaceProperty = (PropertySymbol)@interface.GetMembers("Property").Single();
var interfacePropertyGetter = interfaceProperty.GetMethod;
var interfacePropertySetter = interfaceProperty.SetMethod;
var baseClass = (NamedTypeSymbol)global.GetMembers("Base").Single();
Assert.False(baseClass.AllInterfaces().Contains(@interface));
var baseClassMethod = (MethodSymbol)baseClass.GetMembers("Method").Single();
var baseClassProperty = (PropertySymbol)baseClass.GetMembers("Property").Single();
var baseClassPropertyGetter = baseClassProperty.GetMethod;
var baseClassPropertySetter = baseClassProperty.SetMethod;
Assert.False(baseClassMethod.IsVirtual);
Assert.False(baseClassProperty.IsVirtual);
Assert.False(baseClassPropertyGetter.IsVirtual);
Assert.False(baseClassPropertySetter.IsVirtual);
var derivedClass = (SourceNamedTypeSymbol)global.GetMembers("Derived").Single();
Assert.True(derivedClass.Interfaces().Contains(@interface));
Assert.Same(baseClassMethod, derivedClass.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Same(baseClassProperty, derivedClass.FindImplementationForInterfaceMember(interfaceProperty));
Assert.Same(baseClassPropertyGetter, derivedClass.FindImplementationForInterfaceMember(interfacePropertyGetter));
Assert.Same(baseClassPropertySetter, derivedClass.FindImplementationForInterfaceMember(interfacePropertySetter));
Assert.False(((Cci.IMethodDefinition)baseClassMethod.GetCciAdapter()).IsVirtual);
Assert.False(((Cci.IMethodDefinition)baseClassPropertyGetter.GetCciAdapter()).IsVirtual);
Assert.False(((Cci.IMethodDefinition)baseClassPropertySetter.GetCciAdapter()).IsVirtual);
// GetSynthesizedExplicitImplementations doesn't guarantee order, so sort to make the asserts easier to write.
var synthesizedExplicitImpls = (from m in derivedClass.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods orderby m.MethodKind select m).ToArray();
Assert.Equal(3, synthesizedExplicitImpls.Length);
Assert.True(synthesizedExplicitImpls.All(s => ReferenceEquals(derivedClass, s.ContainingType)));
Assert.Same(interfaceMethod, synthesizedExplicitImpls[0].ExplicitInterfaceImplementations.Single());
Assert.Same(baseClassMethod, synthesizedExplicitImpls[0].ImplementingMethod);
Assert.Same(interfacePropertyGetter, synthesizedExplicitImpls[1].ExplicitInterfaceImplementations.Single());
Assert.Same(baseClassPropertyGetter, synthesizedExplicitImpls[1].ImplementingMethod);
Assert.Equal(MethodKind.PropertyGet, synthesizedExplicitImpls[1].MethodKind);
Assert.Same(interfacePropertySetter, synthesizedExplicitImpls[2].ExplicitInterfaceImplementations.Single());
Assert.Same(baseClassPropertySetter, synthesizedExplicitImpls[2].ImplementingMethod);
Assert.Equal(MethodKind.PropertySet, synthesizedExplicitImpls[2].MethodKind);
}
/// <summary>
/// Layout:
/// D : C : B : A
/// All have virtual Method1 and Method2 with the same signatures (modulo custom modifiers)
/// D has 2 custom modifiers
/// C has 1 custom modifier
/// B has 2 custom modifiers, but not the same as D
/// A has 1 custom modifier for Method1, but not the same as C, and 0 custom modifiers for Method2
/// </summary>
[Fact]
public void TestCustomModifierImplicitImplementation()
{
var text = @"
interface Interface
{
void Method1(int[] x);
void Method2(int[] x);
}
class Class : CustomModifierOverridingD, Interface
{
}
";
var ilAssemblyReference = TestReferences.SymbolsTests.CustomModifiers.Modifiers.dll;
var comp = CreateCompilation(text, new MetadataReference[] { ilAssemblyReference });
var global = comp.GlobalNamespace;
Assert.False(comp.GetDiagnostics().Any());
//IL
var classD = global.GetTypeMembers("CustomModifierOverridingD").Single();
var classDMethod1 = (MethodSymbol)classD.GetMembers("Method1").Single();
var classDMethod2 = (MethodSymbol)classD.GetMembers("Method2").Single();
//Source
var @interface = global.GetTypeMembers("Interface").Single();
var interfaceMethod1 = (MethodSymbol)@interface.GetMembers("Method1").Single();
var interfaceMethod2 = (MethodSymbol)@interface.GetMembers("Method2").Single();
var @class = (SourceNamedTypeSymbol)global.GetTypeMembers("Class").Single();
//ignore custom modifiers (chooses most derived, even though less derived has fewer)
var classMethod1Impl = @class.FindImplementationForInterfaceMember(interfaceMethod1);
Assert.Same(classDMethod1, classMethod1Impl);
//ignore custom modifiers (chooses most derived, even though less derived is exact)
var classMethod2Impl = @class.FindImplementationForInterfaceMember(interfaceMethod2);
Assert.Same(classDMethod2, classMethod2Impl);
// GetSynthesizedExplicitImplementations doesn't guarantee order, so sort to make the asserts easier to write.
var synthesizedExplicitImpls = (from m in @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods orderby m.Name select m).ToArray();
Assert.Equal(2, synthesizedExplicitImpls.Length);
var synthesizedExplicitMethod1Impl = synthesizedExplicitImpls[0];
Assert.Same(interfaceMethod1, synthesizedExplicitMethod1Impl.ExplicitInterfaceImplementations.Single());
Assert.Same(classDMethod1, synthesizedExplicitMethod1Impl.ImplementingMethod);
Assert.Same(@class, synthesizedExplicitMethod1Impl.ContainingType);
var synthesizedExplicitMethod2Impl = synthesizedExplicitImpls[1];
Assert.Same(interfaceMethod2, synthesizedExplicitMethod2Impl.ExplicitInterfaceImplementations.Single());
Assert.Same(classDMethod2, synthesizedExplicitMethod2Impl.ImplementingMethod);
Assert.Same(@class, synthesizedExplicitMethod2Impl.ContainingType);
}
[Fact]
public void TestExplicitImplementationOfStaticMethod()
{
var text = @"
class Class : ContainsStatic
{
void ContainsStatic.Bar() { }
void ContainsStatic.StaticMethod() { } //CS0539
}
";
var ilAssemblyReference = TestReferences.SymbolsTests.Interface.StaticMethodInInterface;
var comp = CreateCompilation(text, new[] { ilAssemblyReference });
comp.VerifyDiagnostics(
// (5,25): error CS0539: 'Class.StaticMethod()' in explicit interface declaration is not a member of interface
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "StaticMethod").WithArguments("Class.StaticMethod()"));
}
[Fact]
public void TestNoImplementationOfStaticMethod()
{
var text = @"
class Class : ContainsStatic
{
void ContainsStatic.Bar() { }
}
";
var ilAssemblyReference = TestReferences.SymbolsTests.Interface.StaticMethodInInterface;
var comp = CreateCompilation(text, new[] { ilAssemblyReference });
comp.VerifyDiagnostics();
}
[Fact]
public void MultiLevelPropertyImplementation()
{
var text = @"
interface I1
{
int bar { get; set; }
}
public class c1
{
public virtual int bar { get { return 1; } set { } }
}
public class c2 : c1, I1
{
public override int bar { get { return 2; } }
}
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var type = comp.GlobalNamespace.GetTypeMembers("c2").Single();
Assert.NotEmpty(type.Interfaces());
Assert.True(type.Interfaces().Any(@interface => @interface.Name == "I1"));
}
[Fact]
public void TestImplementInterfaceInpartialClass()
{
var text = @"
interface Interface
{
void Method1();
}
partial class Base : Interface
{
public void Method2() { }
}
partial class Base
{
public void Method1() { }
}
";
CreateCompilation(text).VerifyDiagnostics();
}
/// <summary>
/// I -> M(ref int)
/// B -> M(out int)
/// D : B, I
///
/// I source, B source, D source
/// </summary>
[Fact]
[WorkItem(540451, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540451")]
public void TestSourceMetadataImplicitImplementation1()
{
var csharp = @"
interface Interface
{
void M(ref int x);
}
class Base
{
void M(out int x)
{
x = 1;
}
}
class Derived : Base, Interface
{
}
class Program
{
static void Main()
{
Interface id = new Derived();
int x = 2;
id.M(ref x);
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (15,7): error CS0535: 'Derived' does not implement interface member 'Interface.M(ref int)'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Derived", "Interface.M(ref int)"));
var global = comp.GlobalNamespace;
Assert.Null(global.GetMember<NamedTypeSymbol>("Derived").FindImplementationForInterfaceMember(
global.GetMember<NamedTypeSymbol>("Interface").GetMember<MethodSymbol>("M")));
}
// I source, B source, D metadata - skip: metadata implementing source
// public void TestSourceMetadataImplicitImplementation2()
/// <summary>
/// I -> M(ref int)
/// B -> M(out int)
/// D : B, I
///
/// I source, B metadata, D source
/// </summary>
[Fact]
[WorkItem(540451, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540451")]
public void TestSourceMetadataImplicitImplementation3()
{
var csharp = @"
interface Interface
{
void M(ref int x);
}
class Derived : Base, Interface
{
}
class Program
{
static void Main()
{
Interface id = new Derived();
int x = 2;
id.M(ref x);
}
}
";
var il = @"
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
{
.method public hidebysig virtual instance void
M([out] int32& x) cil managed
{
// Code size 5 (0x5)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldc.i4.1
IL_0003: stind.i4
IL_0004: ret
} // end of method Base::M
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Base::.ctor
} // end of class Base";
var comp = CreateCompilationWithILAndMscorlib40(csharp, il);
comp.VerifyDiagnostics(
// (7,7): error CS0535: 'Derived' does not implement interface member 'Interface.M(ref int)'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Derived", "Interface.M(ref int)"));
var global = comp.GlobalNamespace;
Assert.Null(global.GetMember<NamedTypeSymbol>("Derived").FindImplementationForInterfaceMember(
global.GetMember<NamedTypeSymbol>("Interface").GetMember<MethodSymbol>("M")));
}
// I source, B metadata, D metadata - skip: metadata implementing source
// public void TestSourceMetadataImplicitImplementation4()
/// <summary>
/// I -> M(ref int)
/// B -> M(out int)
/// D : B, I
///
/// I metadata, B source, D source
/// </summary>
[Fact]
[WorkItem(540451, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540451")]
public void TestSourceMetadataImplicitImplementation5()
{
var csharp = @"
class Base
{
void M(out int x)
{
x = 1;
}
}
class Derived : Base, Interface
{
}
class Program
{
static void Main()
{
Interface id = new Derived();
int x = 2;
id.M(ref x);
}
}
";
var il = @"
.class interface public abstract auto ansi Interface
{
.method public hidebysig newslot abstract virtual
instance void M(int32& x) cil managed
{
} // end of method Interface::M
} // end of class Interface";
var comp = CreateCompilationWithILAndMscorlib40(csharp, il);
comp.VerifyDiagnostics(
// (10,7): error CS0535: 'Derived' does not implement interface member 'Interface.M(ref int)'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Derived", "Interface.M(ref int)"));
var global = comp.GlobalNamespace;
Assert.Null(global.GetMember<NamedTypeSymbol>("Derived").FindImplementationForInterfaceMember(
global.GetMember<NamedTypeSymbol>("Interface").GetMember<MethodSymbol>("M")));
}
// I metadata, B source, D metadata - skip: metadata extending source
// public void TestSourceMetadataImplicitImplementation6()
/// <summary>
/// I -> M(ref int)
/// B -> M(out int)
/// D : B, I
///
/// I metadata, B metadata, D source
/// </summary>
[Fact]
[WorkItem(540451, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540451")]
public void TestSourceMetadataImplicitImplementation7()
{
var csharp = @"
class Derived : Base, Interface
{
}
class Program
{
static void Main()
{
Interface id = new Derived();
int x = 2;
id.M(ref x);
}
}
";
var il = @"
.class interface public abstract auto ansi Interface
{
.method public hidebysig newslot abstract virtual
instance void M(int32& x) cil managed
{
} // end of method Interface::M
} // end of class Interface
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
{
.method public hidebysig virtual instance void
M([out] int32& x) cil managed
{
// Code size 5 (0x5)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldc.i4.1
IL_0003: stind.i4
IL_0004: ret
} // end of method Base::M
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Base::.ctor
} // end of class Base";
var comp = CreateCompilationWithILAndMscorlib40(csharp, il);
comp.VerifyDiagnostics(
// (2,7): error CS0535: 'Derived' does not implement interface member 'Interface.M(ref int)'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Derived", "Interface.M(ref int)"));
var global = comp.GlobalNamespace;
Assert.Null(global.GetMember<NamedTypeSymbol>("Derived").FindImplementationForInterfaceMember(
global.GetMember<NamedTypeSymbol>("Interface").GetMember<MethodSymbol>("M")));
}
[WorkItem(528858, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528858")]
[Fact(Skip = "528858")]
public void InconsistentTypeParameters()
{
var il = @"
.class interface public abstract I<T>
{
.class interface abstract auto ansi nested public I2 { }
}
";
var csharp = @"
class C : I<int>.I2 { }
";
var comp = CreateCompilationWithILAndMscorlib40(csharp, il);
comp.VerifyDiagnostics(
Diagnostic(ErrorCode.ERR_BogusType));
}
[WorkItem(528901, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528901")]
[Fact(Skip = "528901")]
public void BaseInterfacesWithWeirdNamesCanBeImplementedThroughInterfaceInheritance()
{
var il = @"
.class interface public abstract 'N..A' { }
.class interface public abstract B implements 'N..A' { }
";
var csharp = @"
class C : B { }
";
CompileWithCustomILSource(csharp, il);
}
/// <summary>
/// I -> M(ref int)
/// B -> M(out int)
/// D : B, I
///
/// I source, B source, D source
/// </summary>
[Fact]
[WorkItem(540451, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540451")]
public void TestSourceMetadataImplicitImplementation8()
{
var csharp = @"
class Program
{
static void Main()
{
Interface id = new Derived();
int x = 2;
id.M(ref x);
}
}
";
var il = @"
.class interface public abstract auto ansi Interface
{
.method public hidebysig newslot abstract virtual
instance void M(int32& x) cil managed
{
} // end of method Interface::M
} // end of class Interface
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
{
.method public hidebysig virtual instance void
M([out] int32& x) cil managed
{
// Code size 5 (0x5)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldc.i4.1
IL_0003: stind.i4
IL_0004: ret
} // end of method Base::M
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Base::.ctor
} // end of class Base
.class public auto ansi beforefieldinit Derived
extends Base
implements Interface
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void Base::.ctor()
IL_0006: ret
} // end of method Derived::.ctor
} // end of class Derived";
var comp = CreateCompilationWithILAndMscorlib40(csharp, il);
comp.VerifyDiagnostics();
// CONSIDER: dev10 probably regards the interface method as unimplemented,
// but it also doesn't give diagnostics for such cases and this is the behavior at runtime.
var global = comp.GlobalNamespace;
Assert.Equal(
global.GetMember<NamedTypeSymbol>("Base").GetMember<MethodSymbol>("M"),
global.GetMember<NamedTypeSymbol>("Derived").FindImplementationForInterfaceMember(
global.GetMember<NamedTypeSymbol>("Interface").GetMember<MethodSymbol>("M")));
}
[WorkItem(540451, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540451")]
[Fact]
public void TestImplementRefParamWithOutParam()
{
var text = @"
interface I1
{
void Goo(out int x);
}
class C1 : I1
{
public void Goo(ref int x) { }
}
";
CreateCompilation(text).VerifyDiagnostics(
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.Goo(out int)"));
}
[Fact]
public void TestSameInterfaceOverloadUsingGenericArgument()
{
var text = @"
interface I1<T>
{
void Goo(int x);
void Goo(T x);
}
class C1 : I1<int>
{
public void Goo(int x) { }
}
static class Program
{
static void Main()
{
I1<int> i = new C1();
i.Goo(0);
}
}
";
var comp = CreateCompilation(text);
comp.VerifyDiagnostics();
var typeSymbol = comp.GlobalNamespace.GetTypeMembers("C1").Single();
var interfaceSymbol = typeSymbol.Interfaces().First();
var gooMethod = typeSymbol.GetMember<MethodSymbol>("Goo");
var interfaceMembers = interfaceSymbol.GetMembers().OfType<MethodSymbol>();
var firstInterfaceMethod = interfaceMembers.First();
var secondInterfaceMethod = interfaceMembers.Last();
Assert.Equal(gooMethod, typeSymbol.FindImplementationForInterfaceMember(firstInterfaceMethod));
Assert.Equal(gooMethod, typeSymbol.FindImplementationForInterfaceMember(secondInterfaceMethod));
}
/// <summary>
/// In this case, C# thinks B.M implements I.M for C, but the CLR thinks A.M does. To make sure that we get the
/// desired behavior, we have to insert an explicit bridge method.
/// (See SourceNamedTypeSymbol.IsOverrideOfPossibleImplementationUnderRuntimeRules.)
/// </summary>
[Fact]
[WorkItem(540558, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540558")]
public void TestCSharpClrDisagreement_NonOverride()
{
var text = @"
interface I
{
void M();
}
class A : I
{
public virtual void M() { }
}
class B : A
{
public new virtual void M() { }
}
class C : B, I { }
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @interface = global.GetMember<NamedTypeSymbol>("I");
var interfaceMethod = @interface.GetMember<MethodSymbol>("M");
var classA = global.GetMember<NamedTypeSymbol>("A");
var classAMethod = classA.GetMember<MethodSymbol>("M");
var classB = global.GetMember<NamedTypeSymbol>("B");
var classBMethod = classB.GetMember<MethodSymbol>("M");
var classC = global.GetMember<SourceNamedTypeSymbol>("C");
Assert.Equal(@interface, classA.AllInterfaces().Single());
Assert.Equal(@interface, classB.AllInterfaces().Single());
Assert.Equal(@interface, classC.AllInterfaces().Single());
Assert.Equal(0, classB.Interfaces().Length);
Assert.Equal(classB, classC.BaseType());
Assert.Equal(classA, classB.BaseType());
Assert.Equal(classAMethod, classA.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Equal(classBMethod, classC.FindImplementationForInterfaceMember(interfaceMethod));
var synthesizedExplicitImpl = classC.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single();
Assert.Equal(classC, synthesizedExplicitImpl.ContainingType);
Assert.Equal(interfaceMethod, synthesizedExplicitImpl.ExplicitInterfaceImplementations.Single());
Assert.Equal(classBMethod, synthesizedExplicitImpl.ImplementingMethod);
}
/// <summary>
/// In this case, C# thinks B.M implements I.M for C, but the CLR thinks A.M does. However,
/// B.M overrides A.M, so there's no problem (distinguish from TestCSharpClrDisagreement_NonOverride).
/// (See SourceNamedTypeSymbol.IsOverrideOfPossibleImplementationUnderRuntimeRules.)
/// </summary>
[Fact]
[WorkItem(540558, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540558")]
public void TestCSharpClrDisagreement_Override()
{
var text = @"
interface I
{
void M();
}
class A : I
{
public virtual void M() { }
}
class B : A
{
public override void M() { }
}
class C : B, I { }
";
var comp = CreateCompilation(text);
var global = comp.GlobalNamespace;
var @interface = global.GetMember<NamedTypeSymbol>("I");
var interfaceMethod = @interface.GetMember<MethodSymbol>("M");
var classA = global.GetMember<NamedTypeSymbol>("A");
var classAMethod = classA.GetMember<MethodSymbol>("M");
var classB = global.GetMember<NamedTypeSymbol>("B");
var classBMethod = classB.GetMember<MethodSymbol>("M");
var classC = global.GetMember<SourceNamedTypeSymbol>("C");
Assert.Equal(@interface, classA.AllInterfaces().Single());
Assert.Equal(@interface, classB.AllInterfaces().Single());
Assert.Equal(@interface, classC.AllInterfaces().Single());
Assert.Equal(0, classB.Interfaces().Length);
Assert.Equal(classB, classC.BaseType());
Assert.Equal(classA, classB.BaseType());
Assert.Equal(classAMethod, classA.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Equal(classBMethod, classC.FindImplementationForInterfaceMember(interfaceMethod));
Assert.Equal(0, classC.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Length);
}
[Fact]
public void ExplicitlyImplementParameterizedProperty()
{
var il = @"
.class interface public abstract auto ansi I
{
.method public hidebysig newslot specialname abstract virtual
instance int32 get_Item(int32 x) cil managed
{
} // end of method I::get_Item
.method public hidebysig newslot specialname abstract virtual
instance void set_Item(int32 x,
int32 'value') cil managed
{
} // end of method I::set_Item
.property instance int32 Item(int32)
{
.get instance int32 I::get_Item(int32)
.set instance void I::set_Item(int32,
int32)
} // end of property I::Item
} // end of class I
";
var csharp = @"
class C : I
{
int I.get_Item(int x) { return 0; }
void I.set_Item(int x, int value) { }
}
class D : I
{
public int get_Item(int x) { return 0; }
public void set_Item(int x, int value) { }
}
";
var compilation = CreateCompilationWithILAndMscorlib40(csharp, il);
compilation.VerifyDiagnostics();
var globalNamespace = compilation.GlobalNamespace;
var @interface = globalNamespace.GetMember<NamedTypeSymbol>("I");
var classC = globalNamespace.GetMember<NamedTypeSymbol>("C");
var classD = globalNamespace.GetMember<NamedTypeSymbol>("D");
var interfaceProperty = @interface.GetMember<PropertySymbol>("Item");
Assert.False(interfaceProperty.IsIndexer);
Assert.True(interfaceProperty.MustCallMethodsDirectly);
var interfaceGetter = interfaceProperty.GetMethod;
var interfaceSetter = interfaceProperty.SetMethod;
Assert.Null(classC.FindImplementationForInterfaceMember(interfaceProperty));
Assert.Equal("System.Int32 C.I.get_Item(System.Int32 x)", classC.FindImplementationForInterfaceMember(interfaceGetter).ToTestDisplayString());
Assert.Equal("void C.I.set_Item(System.Int32 x, System.Int32 value)", classC.FindImplementationForInterfaceMember(interfaceSetter).ToTestDisplayString());
Assert.Null(classD.FindImplementationForInterfaceMember(interfaceProperty));
Assert.Equal("System.Int32 D.get_Item(System.Int32 x)", classD.FindImplementationForInterfaceMember(interfaceGetter).ToTestDisplayString());
Assert.Equal("void D.set_Item(System.Int32 x, System.Int32 value)", classD.FindImplementationForInterfaceMember(interfaceSetter).ToTestDisplayString());
}
[WorkItem(528898, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528898")]
[ClrOnlyFact]
public void GenericTypeWithObsoleteBangAritySuffixIsNotAvailable()
{
var ilSource =
@"
.class public A
{
.class interface nested public abstract I`1<T> { }
}
.class public B extends A
{
.class nested public 'I!1'<T> { }
}
";
var csharpSource =
@"
class C : object, B.I<string>
{
}
";
CompileWithCustomILSource(csharpSource, ilSource);
}
[WorkItem(528913, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528913")]
[Fact(Skip = "528913")]
public void StaticTypesCannotBeUsedAsTypeArgumentsInInterfacesImplementedThroughInterfaceInheritance()
{
var ilSource =
@"
.class public interface abstract A`1<T> { }
.class public abstract sealed B { }
.class public interface abstract C implements class A`1<class B> { }
";
var csharpSource =
@"
class D : C { }
";
CreateCompilationWithILAndMscorlib40(csharpSource, ilSource).VerifyDiagnostics(
Diagnostic(ErrorCode.ERR_GenericArgIsStaticClass, "B"));
}
[WorkItem(530224, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530224")]
[Fact]
public void IsMetadataVirtualBeforeForceComplete()
{
var source = @"
interface I
{
void Finalize();
}
class Derived : Base, I
{
~Derived()
{
}
static void Main()
{
}
}
class Base
{
public void Finalize()
{
}
}
";
var comp = CreateCompilation(source);
var global = comp.GlobalNamespace;
var @interface = global.GetMember<NamedTypeSymbol>("I");
var @base = global.GetMember<NamedTypeSymbol>("Base");
var derived = global.GetMember<NamedTypeSymbol>("Derived");
// Force completion of destructor symbol. Calls IsMetadataVirtual on Base.Finalize.
var returnType = derived.GetMember<MethodSymbol>(WellKnownMemberNames.DestructorName).ReturnTypeWithAnnotations;
Assert.Equal(SpecialType.System_Void, returnType.SpecialType);
// Force completion of entire symbol. Calls EnsureMetadataVirtual on Base.Finalize.
derived.ForceComplete(locationOpt: null, filter: null, cancellationToken: CancellationToken.None);
}
[Fact]
public void NoBridgeMethodForVirtualImplementation()
{
var source1 = @"
public interface I
{
void Virtual();
void NonVirtual();
}
public class B
{
public virtual void Virtual() { }
public void NonVirtual() { }
}
";
var source2 = @"
class D : B, I
{
}
";
var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, assemblyName: "asm1");
comp1.VerifyDiagnostics();
var ref1 = new CSharpCompilationReference(comp1);
var comp2 = CreateCompilation(source2, new[] { ref1 }, options: TestOptions.ReleaseDll, assemblyName: "asm2");
comp2.VerifyDiagnostics();
var derivedType = comp2.GlobalNamespace.GetMember<SourceNamedTypeSymbol>("D");
var bridgeMethod = derivedType.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single();
Assert.Equal("NonVirtual", bridgeMethod.ImplementingMethod.Name);
}
[WorkItem(530358, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530358")]
[Fact]
public void ExplicitImplementationWithoutInterfaceInName()
{
var il = @"
.class interface public abstract auto ansi I1
{
.method public hidebysig newslot abstract virtual
instance void M() cil managed
{
}
} // end of class I1
.class interface public abstract auto ansi I2
{
.method public hidebysig newslot abstract virtual
instance void M() cil managed
{
}
} // end of class I2
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
implements I1, I2
{
.method public hidebysig newslot virtual final
instance void M() cil managed
{
.override I1::M
ret
} // end of method Base::I1.M
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
} // end of method Base::.ctor
} // end of class Base
";
var source = @"
class Derived : Base, I2
{
}
";
var comp = CreateCompilationWithILAndMscorlib40(source, il);
comp.VerifyDiagnostics();
var global = comp.GlobalNamespace;
var interface1 = global.GetMember<NamedTypeSymbol>("I1");
var interface1Method = interface1.GetMember<MethodSymbol>("M");
var interface2 = global.GetMember<NamedTypeSymbol>("I2");
var interface2Method = interface2.GetMember<MethodSymbol>("M");
var baseType = global.GetMember<NamedTypeSymbol>("Base");
var baseTypeMethod = baseType.GetMember<MethodSymbol>("M");
var derivedType = global.GetMember<NamedTypeSymbol>("Derived");
// Both interface methods are implemented by a single base type method - one implicitly and
// the other explicitly.
Assert.Equal(baseTypeMethod, derivedType.FindImplementationForInterfaceMember(interface1Method));
Assert.Equal(baseTypeMethod, derivedType.FindImplementationForInterfaceMember(interface2Method));
}
[Fact]
[WorkItem(530164, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530164"), WorkItem(531642, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531642"), WorkItem(531643, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531643")]
public void ImplicitImplementationOfByRefReturn()
{
var il = @"
.class interface public abstract auto ansi I
{
.method public hidebysig newslot abstract virtual
instance int32& M() cil managed
{
}
} // end of class I
.class public auto ansi beforefieldinit B
extends [mscorlib]System.Object
{
.method public hidebysig instance int32&
M() cil managed
{
ldnull
throw
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
} // end of class B
";
var source = @"
public class D : B, I
{
}
";
var comp = CreateCompilationWithILAndMscorlib40(source, il);
comp.VerifyDiagnostics();
var global = comp.GlobalNamespace;
var interfaceType = global.GetMember<NamedTypeSymbol>("I");
var interfaceMethod = interfaceType.GetMember<MethodSymbol>("M");
var baseType = global.GetMember<NamedTypeSymbol>("B");
var baseMethod = baseType.GetMember<MethodSymbol>("M");
var derivedType = global.GetMember<SourceNamedTypeSymbol>("D");
// Interface implementation:
Assert.Equal(RefKind.Ref, interfaceMethod.RefKind);
Assert.Equal(RefKind.Ref, baseMethod.RefKind);
Assert.Equal(baseMethod, derivedType.FindImplementationForInterfaceMember(interfaceMethod));
var synthesized = derivedType.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single();
Assert.Equal(baseMethod, synthesized.ImplementingMethod);
Assert.Equal(interfaceMethod, synthesized.ExplicitInterfaceImplementations.Single());
// Still get a use site error if you actually call the method.
var source2 = @"
public class D : B, I
{
static void Main()
{
I i = new D();
var x = i.M();
}
}
";
CreateCompilationWithILAndMscorlib40(source2, il).VerifyDiagnostics();
}
[WorkItem(547149, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547149")]
[Fact]
public void BaseTypeDoesNotActuallyImplementInterface()
{
var il = @"
.class interface public abstract auto ansi Interface
{
.method public hidebysig newslot abstract virtual
instance void M() cil managed
{
}
} // end of class Interface
.class public auto ansi beforefieldinit Base1
extends [mscorlib]System.Object
implements Interface
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
} // end of class Base
";
var ilRef = CompileIL(il);
var source = @"
class Derived1 : Base1, Interface
{
}
class Base2 : Interface
{
}
class Derived2 : Base2, Interface
{
}
";
// Base1 is in metadata, so we just trust it when it claims to implement Interface.
// Base2 is identical, but in source. We produce error for Base2.
CreateCompilation(source, new[] { ilRef }).VerifyDiagnostics(
// (6,7): error CS0535: 'Base2' does not implement interface member 'Interface.M()'
// class Base2 : Interface
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Base2", "Interface.M()"));
}
[WorkItem(718115, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/718115")]
[ClrOnlyFact]
public void ExplicitlyImplementedAccessorsWithoutEvent()
{
var il = @"
.class interface public abstract auto ansi I
{
.method public hidebysig newslot specialname abstract virtual
instance void add_E(class [mscorlib]System.Action 'value') cil managed
{
}
.method public hidebysig newslot specialname abstract virtual
instance void remove_E(class [mscorlib]System.Action 'value') cil managed
{
}
.event [mscorlib]System.Action E
{
.addon instance void I::add_E(class [mscorlib]System.Action)
.removeon instance void I::remove_E(class [mscorlib]System.Action)
}
} // end of class I
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
implements I
{
.method private hidebysig newslot specialname virtual final
instance void I.add_E(class [mscorlib]System.Action 'value') cil managed
{
.override I::add_E
ldstr ""Explicit implementation""
call void [mscorlib]System.Console::WriteLine(string)
ret
}
.method private hidebysig newslot specialname virtual final
instance void I.remove_E(class [mscorlib]System.Action 'value') cil managed
{
.override I::remove_E
ret
}
.method family hidebysig specialname instance void
add_E(class [mscorlib]System.Action 'value') cil managed
{
ldstr ""Protected event""
call void [mscorlib]System.Console::WriteLine(string)
ret
}
.method family hidebysig specialname instance void
remove_E(class [mscorlib]System.Action 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
// NOTE: No event I.E
.event [mscorlib]System.Action E
{
.addon instance void Base::add_E(class [mscorlib]System.Action)
.removeon instance void Base::remove_E(class [mscorlib]System.Action)
}
} // end of class Base
";
var source = @"
public class Derived : Base, I
{
public void Test()
{
((I)this).E += null;
}
}
public class Program
{
static void Main()
{
Derived d = new Derived();
d.Test();
I id = new Derived();
id.E += null;
}
}
";
var comp = CreateCompilationWithILAndMscorlib40(source, il, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: @"
Explicit implementation
Explicit implementation
");
var global = comp.GlobalNamespace;
var @interface = global.GetMember<NamedTypeSymbol>("I");
var baseType = global.GetMember<NamedTypeSymbol>("Base");
var derivedType = global.GetMember<NamedTypeSymbol>("Derived");
var interfaceEvent = @interface.GetMember<EventSymbol>("E");
var interfaceAdder = interfaceEvent.AddMethod;
var baseAdder = baseType.GetMembers().OfType<MethodSymbol>().
Where(m => m.MethodKind == MethodKind.ExplicitInterfaceImplementation).
Single(m => m.ExplicitInterfaceImplementations.Single().MethodKind == MethodKind.EventAdd);
Assert.Equal(baseAdder, derivedType.FindImplementationForInterfaceMember(interfaceAdder));
Assert.Equal(baseAdder, baseType.FindImplementationForInterfaceMember(interfaceAdder));
Assert.Null(derivedType.FindImplementationForInterfaceMember(interfaceEvent));
Assert.Null(baseType.FindImplementationForInterfaceMember(interfaceEvent));
}
[WorkItem(718115, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/718115")]
[Fact]
public void ExplicitlyImplementedParameterizedPropertyAccessor()
{
var il = @"
.class interface public abstract auto ansi I
{
.method public hidebysig newslot specialname abstract virtual
instance int32 get_P(int32 x) cil managed
{
}
.property instance int32 P(int32)
{
.get instance int32 I::get_P(int32)
}
} // end of class I
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
implements I
{
.method public hidebysig newslot specialname virtual final
instance int32 get_P(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 P(int32)
{
.get instance int32 Base::get_P(int32)
}
} // end of class Base
";
var source = @"
public class Derived : Base, I
{
int I.get_P(int x) { return 0; }
}
";
var comp = CreateCompilationWithILAndMscorlib40(source, il);
comp.VerifyDiagnostics();
var global = comp.GlobalNamespace;
var @interface = global.GetMember<NamedTypeSymbol>("I");
var baseType = global.GetMember<NamedTypeSymbol>("Base");
var derivedType = global.GetMember<NamedTypeSymbol>("Derived");
var interfaceProperty = @interface.GetMember<PropertySymbol>("P");
var interfaceGetter = interfaceProperty.GetMethod;
var baseProperty = baseType.GetMember<PropertySymbol>("P");
var baseGetter = baseProperty.GetMethod;
var derivedGetter = derivedType.GetMembers().OfType<MethodSymbol>().
Single(m => m.MethodKind == MethodKind.ExplicitInterfaceImplementation);
Assert.Equal(baseProperty, baseType.FindImplementationForInterfaceMember(interfaceProperty));
Assert.Equal(baseGetter, baseType.FindImplementationForInterfaceMember(interfaceGetter));
Assert.Null(derivedType.FindImplementationForInterfaceMember(interfaceProperty)); // Used to return baseProperty, which seems wrong.
Assert.Equal(derivedGetter, derivedType.FindImplementationForInterfaceMember(interfaceGetter));
}
[WorkItem(943542, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/943542"), WorkItem(137, "CodePlex")]
[ClrOnlyFact]
public void Bug943542()
{
var il = @"
.class interface public abstract auto ansi IBase
{
.method public newslot specialname abstract strict virtual
instance object get_P1() cil managed
{
} // end of method IBase::get_P1
.method public newslot specialname abstract strict virtual
instance void set_P1(object Value) cil managed
{
} // end of method IBase::set_P1
.method public newslot specialname abstract strict virtual
instance object get_P2() cil managed
{
} // end of method IBase::get_P2
.method public newslot specialname abstract strict virtual
instance void set_P2(object Value) cil managed
{
} // end of method IBase::set_P2
.property instance object P1()
{
.get instance object IBase::get_P1()
.set instance void IBase::set_P1(object)
} // end of property IBase::P1
.property instance object P2()
{
.get instance object IBase::get_P2()
.set instance void IBase::set_P2(object)
} // end of property IBase::P2
} // end of class IBase
.class public auto ansi Base
extends [mscorlib]System.Object
implements IBase
{
.method public specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Base::.ctor
.method public newslot specialname strict virtual
instance object g_P1() cil managed
{
.override IBase::get_P1
// Code size 16 (0x10)
.maxstack 1
.locals init (object V_0)
IL_0000: ldstr ""g_P1""
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ldnull
IL_000b: stloc.0
IL_000c: br.s IL_000e
IL_000e: ldloc.0
IL_000f: ret
} // end of method Base::get_P1
.method public newslot specialname strict virtual
instance void set_P1(object 'value') cil managed
{
.override IBase::set_P1
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr ""set_P1""
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Base::set_P1
.method public newslot specialname strict virtual
instance object get_P2() cil managed
{
.override IBase::get_P2
// Code size 16 (0x10)
.maxstack 1
.locals init (object V_0)
IL_0000: ldstr ""get_P2""
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ldnull
IL_000b: stloc.0
IL_000c: br.s IL_000e
IL_000e: ldloc.0
IL_000f: ret
} // end of method Base::get_P2
.method public newslot specialname strict virtual
instance void s_P2(object 'value') cil managed
{
.override IBase::set_P2
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr ""s_P2""
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Base::set_P2
.property instance object P1()
{
.get instance object Base::g_P1()
.set instance void Base::set_P1(object)
} // end of property Base::P1
.property instance object P2()
{
.get instance object Base::get_P2()
.set instance void Base::s_P2(object)
} // end of property Base::P2
} // end of class Base
";
var source = @"
interface IDerived
{
object P1 {get;set;}
object P2 {get;set;}
}
class Derived : Base, IDerived
{
static void Main()
{
object val;
IDerived x = new Derived();
x.P1 = null;
val = x.P1;
x.P2 = null;
val = x.P2;
}
}";
var comp = CreateCompilationWithILAndMscorlib40(source, il, options: TestOptions.DebugExe);
CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: @"set_P1
g_P1
s_P2
get_P2");
}
[Fact]
[WorkItem(42340, "https://github.com/dotnet/roslyn/issues/42340")]
public void Issue42340()
{
var text = @"
#nullable enable
class StringComparer : System.Collections.Generic.IEqualityComparer<string?>
{
public bool Equals(string? x, string? y)
{
throw null;
}
public int GetHashCode(string obj)
{
throw null;
}
}
class OneToOneUnicodeComparer : StringComparer
{
}
";
var comp = CreateCompilation(text);
comp.VerifyDiagnostics(
// (7,15): warning CS8597: Thrown value may be null.
// throw null;
Diagnostic(ErrorCode.WRN_ThrowPossibleNull, "null").WithLocation(7, 15),
// (10,16): warning CS8767: Nullability of reference types in type of parameter 'obj' of 'int StringComparer.GetHashCode(string obj)' doesn't match implicitly implemented member 'int IEqualityComparer<string?>.GetHashCode(string? obj)' (possibly because of nullability attributes).
// public int GetHashCode(string obj)
Diagnostic(ErrorCode.WRN_TopLevelNullabilityMismatchInParameterTypeOnImplicitImplementation, "GetHashCode").WithArguments("obj", "int StringComparer.GetHashCode(string obj)", "int IEqualityComparer<string?>.GetHashCode(string? obj)").WithLocation(10, 16),
// (12,15): warning CS8597: Thrown value may be null.
// throw null;
Diagnostic(ErrorCode.WRN_ThrowPossibleNull, "null").WithLocation(12, 15)
);
var baseType = comp.GetTypeByMetadataName("StringComparer");
var derivedType = comp.GetTypeByMetadataName("OneToOneUnicodeComparer");
var implementation = derivedType.FindImplementationForInterfaceMember(baseType.Interfaces().Single().GetMember("GetHashCode"));
Assert.Equal("System.Int32 StringComparer.GetHashCode(System.String obj)", implementation.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
[WorkItem(46494, "https://github.com/dotnet/roslyn/issues/46494")]
public void ExplicitImplementationInBaseType_01(bool useCompilationReference)
{
var source0 =
@"#nullable enable
public struct S<T>
{
}
public interface I
{
S<object?> F();
}";
var source1 =
@"#nullable enable
public class A<T> : I where T : class
{
public S<T?> F() => throw null;
S<object?> I.F() => default;
}";
var source2A =
@"#nullable enable
class B<T> : A<T>, I where T : class
{
}";
var source2B =
@"#nullable disable
class B<T> : A<T>, I where T : class
{
}";
var source3 =
@"class Program
{
static void Main()
{
object o = ((I)new B<string>()).F();
System.Console.WriteLine(o);
}
}";
ExplicitImplementationInBaseType(useCompilationReference, source0, source1, source2A, source3, "B", "I.F", "S`1[System.Object]", "S<System.Object?> A<T>.I.F()");
ExplicitImplementationInBaseType(useCompilationReference, source0, source1, source2B, source3, "B", "I.F", "S`1[System.Object]", "S<System.Object?> A<T>.I.F()");
}
[WorkItem(46494, "https://github.com/dotnet/roslyn/issues/46494")]
[Theory]
[InlineData("dynamic", "dynamic", "dynamic", "System.Object", true)]
[InlineData("dynamic", "dynamic", "dynamic", "System.Object", false)]
[InlineData("dynamic", "object", "System.Object", "System.Object", true)]
[InlineData("dynamic", "object", "System.Object", "System.Object", false)]
[InlineData("object", "dynamic", "dynamic", "System.Object", true)]
[InlineData("object", "dynamic", "dynamic", "System.Object", false)]
[InlineData("(int X, int Y)", "(int X, int Y)", "(System.Int32 X, System.Int32 Y)", "System.ValueTuple`2[System.Int32,System.Int32]", true)]
[InlineData("(int X, int Y)", "(int X, int Y)", "(System.Int32 X, System.Int32 Y)", "System.ValueTuple`2[System.Int32,System.Int32]", false)]
[InlineData("nint", "nint", "nint", "System.IntPtr", true)]
[InlineData("nint", "nint", "nint", "System.IntPtr", false)]
[InlineData("nint", "System.IntPtr", "System.IntPtr", "System.IntPtr", true)]
[InlineData("nint", "System.IntPtr", "System.IntPtr", "System.IntPtr", false)]
[InlineData("System.IntPtr", "nint", "nint", "System.IntPtr", true)]
[InlineData("System.IntPtr", "nint", "nint", "System.IntPtr", false)]
public void ExplicitImplementationInBaseType_02(string interfaceTypeArg, string baseTypeArg, string expectedTypeArg, string expectedOutput, bool useCompilationReference)
{
var source0 =
$@"public struct S<T>
{{
}}
public interface I
{{
S<{interfaceTypeArg}> F();
}}";
var source1 =
$@"public class A<T> : I
{{
public S<T> F() => throw null;
S<{baseTypeArg}> I.F() => default;
}}";
var source2 =
@"class B<T> : A<T>, I
{
}";
var source3 =
@"class Program
{
static void Main()
{
object o = ((I)new B<string>()).F();
System.Console.WriteLine(o);
}
}";
ExplicitImplementationInBaseType(useCompilationReference, source0, source1, source2, source3, "B", "I.F", $"S`1[{expectedOutput}]", $"S<{expectedTypeArg}> A<T>.I.F()");
}
[Theory]
[CombinatorialData]
[WorkItem(46494, "https://github.com/dotnet/roslyn/issues/46494")]
public void ExplicitImplementationInBaseType_03(bool useCompilationReference)
{
var source0 =
@"#nullable enable
public struct S<T>
{
}
public interface I
{
void F(S<object?> s);
}";
var source1 =
@"#nullable enable
public class A<T> : I
{
public void F(S<T> s) => throw null;
void I.F(S<object?> s) { }
}";
var source2A =
@"#nullable enable
class B<T> : A<T>, I
{
}";
var source2B =
@"#nullable disable
class B<T> : A<T>, I
{
}";
var source3 =
@"class Program
{
static void Main()
{
((I)new B<string>()).F(default);
System.Console.WriteLine(1);
}
}";
ExplicitImplementationInBaseType(useCompilationReference, source0, source1, source2A, source3, "B", "I.F", "1", "void A<T>.I.F(S<System.Object?> s)");
ExplicitImplementationInBaseType(useCompilationReference, source0, source1, source2B, source3, "B", "I.F", "1", "void A<T>.I.F(S<System.Object?> s)");
}
[WorkItem(46494, "https://github.com/dotnet/roslyn/issues/46494")]
[Theory]
[InlineData("dynamic", "dynamic", "dynamic", true)]
[InlineData("dynamic", "dynamic", "dynamic", false)]
[InlineData("dynamic", "object", "System.Object", true)]
[InlineData("dynamic", "object", "System.Object", false)]
[InlineData("object", "dynamic", "dynamic", true)]
[InlineData("object", "dynamic", "dynamic", false)]
[InlineData("(int X, int Y)", "(int X, int Y)", "(System.Int32 X, System.Int32 Y)", true)]
[InlineData("(int X, int Y)", "(int X, int Y)", "(System.Int32 X, System.Int32 Y)", false)]
[InlineData("nint", "nint", "nint", true)]
[InlineData("nint", "nint", "nint", false)]
[InlineData("nint", "System.IntPtr", "System.IntPtr", true)]
[InlineData("nint", "System.IntPtr", "System.IntPtr", false)]
[InlineData("System.IntPtr", "nint", "nint", true)]
[InlineData("System.IntPtr", "nint", "nint", false)]
public void ExplicitImplementationInBaseType_04(string interfaceTypeArg, string baseTypeArg, string expectedTypeArg, bool useCompilationReference)
{
var source0 =
$@"public struct S<T>
{{
}}
public interface I
{{
void F(S<{interfaceTypeArg}> s);
}}";
var source1 =
$@"public class A<T> : I
{{
public void F(S<T> s) => throw null;
void I.F(S<{baseTypeArg}> s) {{ }}
}}";
var source2 =
@"class B<T> : A<T>, I
{
}";
var source3 =
@"class Program
{
static void Main()
{
((I)new B<string>()).F(default);
System.Console.WriteLine(1);
}
}";
ExplicitImplementationInBaseType(useCompilationReference, source0, source1, source2, source3, "B", "I.F", "1", $"void A<T>.I.F(S<{expectedTypeArg}> s)");
}
private void ExplicitImplementationInBaseType(
bool useCompilationReference,
string source0,
string source1,
string source2,
string source3,
string derivedTypeName,
string interfaceMemberName,
string expectedOutput,
string expectedImplementingMember)
{
var comp = CreateCompilation(source0);
var ref0 = AsReference(comp, useCompilationReference);
comp = CreateCompilation(source1, references: new[] { ref0 });
var ref1 = AsReference(comp, useCompilationReference);
comp = CreateCompilation(new[] { source2, source3 }, references: new[] { ref0, ref1 }, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: expectedOutput);
var derivedType = comp.GetMember<SourceNamedTypeSymbol>(derivedTypeName);
Assert.True(derivedType.GetSynthesizedExplicitImplementations(cancellationToken: default).ForwardingMethods.IsEmpty);
var interfaceMember = comp.GetMember<MethodSymbol>(interfaceMemberName);
var implementingMember = derivedType.FindImplementationForInterfaceMember(interfaceMember);
Assert.Equal(expectedImplementingMember, implementingMember.ToTestDisplayString());
}
[Fact]
[WorkItem(50713, "https://github.com/dotnet/roslyn/issues/50713")]
public void Issue50713_1()
{
var text1 = @"
public interface I1
{
void M();
}
public interface I2 : I1
{
new void M();
}
";
var comp1 = CreateCompilation(text1);
comp1.VerifyDiagnostics();
var i1M = comp1.GetMember("I1.M");
var i2 = comp1.GetMember<NamedTypeSymbol>("I2");
Assert.Null(i2.FindImplementationForInterfaceMember(i1M));
}
[Fact]
[WorkItem(50713, "https://github.com/dotnet/roslyn/issues/50713")]
public void Issue50713_2()
{
var text0 = @"
public interface I1
{
void M();
}
public interface I2 : I1
{
new void M();
}
";
var comp0 = CreateCompilation(text0);
var comp1 = CreateCompilation("", references: new[] { comp0.EmitToImageReference() });
var i1M = comp1.GetMember("I1.M");
var i2 = comp1.GetMember<NamedTypeSymbol>("I2");
Assert.Null(i2.FindImplementationForInterfaceMember(i1M));
}
}
}
|