File: Symbols\MethodEqualityTests.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.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class MethodEqualityTests : CSharpTestBase
    {
        [Fact]
        public void NoGenerics()
        {
            var text = @"
class Class1
{
    void Method1() { }
    void Method2() { }
}
 
class Class2
{
    void Method1() { }
}
";
            var comp = CreateCompilation(text);
            var global = comp.GlobalNamespace;
 
            var class1 = global.GetTypeMembers("Class1").Single();
            var class1Method1 = (MethodSymbol)class1.GetMembers("Method1").Single();
            var class1Method2 = (MethodSymbol)class1.GetMembers("Method2").Single();
 
            var class2 = global.GetTypeMembers("Class2").Single();
            var class2Method1 = (MethodSymbol)class2.GetMembers("Method1").Single();
 
            Assert.Equal(class1Method1, class1Method1);
 
            //null
            Assert.NotNull(class1Method1);
            Assert.NotNull(class1Method1);
 
            //different type
            Assert.NotEqual<Symbol>(class1, class1Method1);
            Assert.NotEqual<Symbol>(class1Method1, class1);
 
            //different original definition
            Assert.NotEqual(class1Method1, class1Method2);
            Assert.NotEqual(class1Method2, class1Method1);
 
            //different containing type
            Assert.NotEqual(class1Method1, class2Method1);
            Assert.NotEqual(class2Method1, class1Method1);
        }
 
        [Fact]
        public void GenericsTypes()
        {
            var text = @"
class Base<T>
{
    void Method(T t) { }
    void Method(int i) { }
}
 
class Derived1<S> : Base<S>
{
}
 
class Derived2 : Base<int>
{
}
";
            var comp = CreateCompilation(text);
            var global = comp.GlobalNamespace;
 
            var baseClass = global.GetTypeMembers("Base").Single();
            var baseClassMethod1 = (MethodSymbol)baseClass.GetMembers("Method").First();
            var baseClassMethod2 = (MethodSymbol)baseClass.GetMembers("Method").Last();
 
            var derivedClass1 = global.GetTypeMembers("Derived1").Single();
            var substitutedBaseClass = derivedClass1.BaseType();
            var substitutedBaseClassMethod1 = (MethodSymbol)substitutedBaseClass.GetMembers("Method").First();
            var substitutedBaseClassMethod2 = (MethodSymbol)substitutedBaseClass.GetMembers("Method").Last();
 
            var derivedClass2 = global.GetTypeMembers("Derived2").Single();
            var constructedBaseClass = derivedClass2.BaseType();
            var constructedBaseClassMethod1 = (MethodSymbol)constructedBaseClass.GetMembers("Method").First();
            var constructedBaseClassMethod2 = (MethodSymbol)constructedBaseClass.GetMembers("Method").Last();
 
            //different type args
            Assert.NotEqual(baseClassMethod1, substitutedBaseClassMethod1);
            Assert.NotEqual(substitutedBaseClassMethod1, baseClassMethod1);
 
            //different type args
            Assert.NotEqual(baseClassMethod1, constructedBaseClassMethod1);
            Assert.NotEqual(constructedBaseClassMethod1, baseClassMethod1);
 
            //different type args
            Assert.NotEqual(substitutedBaseClassMethod1, constructedBaseClassMethod1);
            Assert.NotEqual(constructedBaseClassMethod1, substitutedBaseClassMethod1);
 
            //different original definitions
            Assert.NotEqual(baseClassMethod1, baseClassMethod2);
            Assert.NotEqual(baseClassMethod2, baseClassMethod1);
 
            //different original definitions
            Assert.NotEqual(substitutedBaseClassMethod1, substitutedBaseClassMethod2);
            Assert.NotEqual(substitutedBaseClassMethod2, substitutedBaseClassMethod1);
 
            //different original definitions (though signatures and bodies now match)
            Assert.NotEqual(constructedBaseClassMethod1, constructedBaseClassMethod2);
            Assert.NotEqual(constructedBaseClassMethod2, constructedBaseClassMethod1);
        }
 
        [Fact]
        public void GenericsTypesAndMethods()
        {
            var text = @"
class Base<T>
{
    U Method<U>(T t) { }
    V Method<V>(int i) { }
}
 
class Derived1<S> : Base<S>
{
}
 
class Derived2 : Base<int>
{
}
";
            var comp = CreateCompilation(text);
            var global = comp.GlobalNamespace;
 
            var baseClass = global.GetTypeMembers("Base").Single();
            var baseClassMethod1 = (MethodSymbol)baseClass.GetMembers("Method").First();
            var baseClassMethod2 = (MethodSymbol)baseClass.GetMembers("Method").Last();
 
            var derivedClass1 = global.GetTypeMembers("Derived1").Single();
            var substitutedBaseClass = derivedClass1.BaseType();
            var substitutedBaseClassMethod1 = (MethodSymbol)substitutedBaseClass.GetMembers("Method").First();
            var substitutedBaseClassMethod2 = (MethodSymbol)substitutedBaseClass.GetMembers("Method").Last();
 
            var derivedClass2 = global.GetTypeMembers("Derived2").Single();
            var constructedBaseClass = derivedClass2.BaseType();
            var constructedBaseClassMethod1 = (MethodSymbol)constructedBaseClass.GetMembers("Method").First();
            var constructedBaseClassMethod2 = (MethodSymbol)constructedBaseClass.GetMembers("Method").Last();
 
            //different type args
            Assert.NotEqual(baseClassMethod1, substitutedBaseClassMethod1);
            Assert.NotEqual(substitutedBaseClassMethod1, baseClassMethod1);
 
            //different type args
            Assert.NotEqual(baseClassMethod1, constructedBaseClassMethod1);
            Assert.NotEqual(constructedBaseClassMethod1, baseClassMethod1);
 
            //different type args
            Assert.NotEqual(substitutedBaseClassMethod1, constructedBaseClassMethod1);
            Assert.NotEqual(constructedBaseClassMethod1, substitutedBaseClassMethod1);
 
            //different original definitions
            Assert.NotEqual(baseClassMethod1, baseClassMethod2);
            Assert.NotEqual(baseClassMethod2, baseClassMethod1);
 
            //different original definitions
            Assert.NotEqual(substitutedBaseClassMethod1, substitutedBaseClassMethod2);
            Assert.NotEqual(substitutedBaseClassMethod2, substitutedBaseClassMethod1);
 
            //different original definitions (though signatures and bodies now match)
            Assert.NotEqual(constructedBaseClassMethod1, constructedBaseClassMethod2);
            Assert.NotEqual(constructedBaseClassMethod2, constructedBaseClassMethod1);
        }
 
        [Fact]
        public void SubstitutedGenericMethods()
        {
            var text = @"
class Class
{
    void Method<U, V>(U u, V v)
    {
        Method<char, int>('a', 1); //0
        Method<char, int>('b', 2); //1
        Method<int, char>(1, 'a'); //2
        Method<U, V>(u, v); //3
        Method<U, V>(u, v); //4
        Method<V, U>(v, u); //5
    }
}
";
            var comp = (Compilation)CreateCompilation(text);
            var global = comp.GlobalNamespace;
 
            var @class = global.GetTypeMembers("Class").Single();
            var classMethodDeclaration = (IMethodSymbol)@class.GetMembers("Method").Single();
 
            var tree = comp.SyntaxTrees.Single();
            var model = comp.GetSemanticModel(tree);
 
            var root = tree.GetCompilationUnitRoot();
            var cDecl = (TypeDeclarationSyntax)root.Members[0];
            var mDecl = (MethodDeclarationSyntax)cDecl.Members[0];
            var stmts = mDecl.Body.Statements;
 
            var invokedMethods = stmts.Select(stmt =>
            {
                var exprStmt = (ExpressionStatementSyntax)stmt;
                var semanticInfo = model.GetSymbolInfo(exprStmt.Expression);
                return (IMethodSymbol)semanticInfo.Symbol;
            }).ToArray();
 
            Assert.Equal(6, invokedMethods.Length);
 
            //invocations are equal to themselves
            Assert.True(invokedMethods.All(m => m.Equals(m)));
 
            //invocations may be equal to declarations if type arguments are the same
            Assert.NotEqual(invokedMethods[0], classMethodDeclaration);
            Assert.NotEqual(invokedMethods[1], classMethodDeclaration);
            Assert.NotEqual(invokedMethods[2], classMethodDeclaration);
            Assert.Equal(invokedMethods[3], classMethodDeclaration);
            Assert.Equal(invokedMethods[4], classMethodDeclaration);
            Assert.NotEqual(invokedMethods[5], classMethodDeclaration);
 
            //invocations are equal to other invocations with the same substitutions
            Assert.Equal(invokedMethods[0], invokedMethods[1]);
            Assert.Equal(invokedMethods[1], invokedMethods[0]);
 
            Assert.Equal(invokedMethods[3], invokedMethods[4]);
            Assert.Equal(invokedMethods[4], invokedMethods[3]);
 
            //invocations with different type args are not equal
            var pairWiseNotEqual = new IMethodSymbol[]
            {
                invokedMethods[0],
                invokedMethods[2],
                invokedMethods[3],
                invokedMethods[5],
            };
 
            foreach (var method1 in pairWiseNotEqual)
            {
                foreach (var method2 in pairWiseNotEqual)
                {
                    if (ReferenceEquals(method1, method2))
                    {
                        continue;
                    }
 
                    Assert.NotEqual(method1, method2);
                    Assert.NotEqual(method2, method1);
                }
            }
        }
    }
}