File: Utilities\SymbolEquivalenceComparerTests.cs
Web Access
Project: src\src\EditorFeatures\Test\Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.EditorFeatures.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using static Roslyn.Test.Utilities.SigningTestHelpers;
using CS = Microsoft.CodeAnalysis.CSharp;
using VB = Microsoft.CodeAnalysis.VisualBasic;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities;
 
[UseExportProvider]
public class SymbolEquivalenceComparerTests
{
    public static readonly CS.CSharpCompilationOptions CSharpDllOptions = new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
    public static readonly CS.CSharpCompilationOptions CSharpSignedDllOptions = new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).
        WithCryptoKeyFile(SigningTestHelpers.KeyPairFile).
        WithStrongNameProvider(DefaultDesktopStrongNameProvider);
 
    [Fact]
    public async Task TestArraysAreEquivalent()
    {
        var csharpCode =
@"class C
{
    int intField1;
    int[] intArrayField1;
    string[] stringArrayField1;
    int[][] intArrayArrayField1;
    int[,] intArrayRank2Field1;
    System.Int32 int32Field1;
 
    int intField2;
    int[] intArrayField2;
    string[] stringArrayField2;
    int[][] intArrayArrayField2;
    int[,] intArrayRank2Field2;
    System.Int32 int32Field2;
}";
 
        using var workspace = TestWorkspace.CreateCSharp(csharpCode);
        var type = (ITypeSymbol)(await workspace.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var intField1 = (IFieldSymbol)type.GetMembers("intField1").Single();
        var intArrayField1 = (IFieldSymbol)type.GetMembers("intArrayField1").Single();
        var stringArrayField1 = (IFieldSymbol)type.GetMembers("stringArrayField1").Single();
        var intArrayArrayField1 = (IFieldSymbol)type.GetMembers("intArrayArrayField1").Single();
        var intArrayRank2Field1 = (IFieldSymbol)type.GetMembers("intArrayRank2Field1").Single();
        var int32Field1 = (IFieldSymbol)type.GetMembers("int32Field1").Single();
 
        var intField2 = (IFieldSymbol)type.GetMembers("intField2").Single();
        var intArrayField2 = (IFieldSymbol)type.GetMembers("intArrayField2").Single();
        var stringArrayField2 = (IFieldSymbol)type.GetMembers("stringArrayField2").Single();
        var intArrayArrayField2 = (IFieldSymbol)type.GetMembers("intArrayArrayField2").Single();
        var intArrayRank2Field2 = (IFieldSymbol)type.GetMembers("intArrayRank2Field2").Single();
        var int32Field2 = (IFieldSymbol)type.GetMembers("int32Field2").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(intField1.Type, intField1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(intField1.Type, intField2.Type));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(intField1.Type),
                     SymbolEquivalenceComparer.Instance.GetHashCode(intField2.Type));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(intArrayField1.Type, intArrayField1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(intArrayField1.Type, intArrayField2.Type));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(intArrayField1.Type),
                     SymbolEquivalenceComparer.Instance.GetHashCode(intArrayField2.Type));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(stringArrayField1.Type, stringArrayField1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(stringArrayField1.Type, stringArrayField2.Type));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(intArrayArrayField1.Type, intArrayArrayField1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(intArrayArrayField1.Type, intArrayArrayField2.Type));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(intArrayRank2Field1.Type, intArrayRank2Field1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(intArrayRank2Field1.Type, intArrayRank2Field2.Type));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(int32Field1.Type, int32Field1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(int32Field1.Type, int32Field2.Type));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(intField1.Type, intArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(intArrayField1.Type, stringArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(stringArrayField1.Type, intArrayArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(intArrayArrayField1.Type, intArrayRank2Field1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(intArrayRank2Field1.Type, int32Field1.Type));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(int32Field1.Type, intField1.Type));
    }
 
    [Fact]
    public async Task TestArraysInDifferentLanguagesAreEquivalent()
    {
        var csharpCode =
@"class C
{
    int intField1;
    int[] intArrayField1;
    string[] stringArrayField1;
    int[][] intArrayArrayField1;
    int[,] intArrayRank2Field1;
    System.Int32 int32Field1;
}";
        var vbCode =
@"class C
    dim intField1 as Integer;
    dim intArrayField1 as Integer()
    dim stringArrayField1 as String()
    dim intArrayArrayField1 as Integer()()
    dim intArrayRank2Field1 as Integer(,)
    dim int32Field1 as System.Int32
end class";
 
        using var csharpWorkspace = TestWorkspace.CreateCSharp(csharpCode);
        using var vbWorkspace = TestWorkspace.CreateVisualBasic(vbCode);
        var csharpType = (ITypeSymbol)(await csharpWorkspace.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var vbType = (await vbWorkspace.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var csharpIntField1 = (IFieldSymbol)csharpType.GetMembers("intField1").Single();
        var csharpIntArrayField1 = (IFieldSymbol)csharpType.GetMembers("intArrayField1").Single();
        var csharpStringArrayField1 = (IFieldSymbol)csharpType.GetMembers("stringArrayField1").Single();
        var csharpIntArrayArrayField1 = (IFieldSymbol)csharpType.GetMembers("intArrayArrayField1").Single();
        var csharpIntArrayRank2Field1 = (IFieldSymbol)csharpType.GetMembers("intArrayRank2Field1").Single();
        var csharpInt32Field1 = (IFieldSymbol)csharpType.GetMembers("int32Field1").Single();
 
        var vbIntField1 = (IFieldSymbol)vbType.GetMembers("intField1").Single();
        var vbIntArrayField1 = (IFieldSymbol)vbType.GetMembers("intArrayField1").Single();
        var vbStringArrayField1 = (IFieldSymbol)vbType.GetMembers("stringArrayField1").Single();
        var vbIntArrayArrayField1 = (IFieldSymbol)vbType.GetMembers("intArrayArrayField1").Single();
        var vbIntArrayRank2Field1 = (IFieldSymbol)vbType.GetMembers("intArrayRank2Field1").Single();
        var vbInt32Field1 = (IFieldSymbol)vbType.GetMembers("int32Field1").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(csharpIntField1.Type, vbIntField1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(csharpIntArrayField1.Type, vbIntArrayField1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(csharpStringArrayField1.Type, vbStringArrayField1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(csharpIntArrayArrayField1.Type, vbIntArrayArrayField1.Type));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(csharpInt32Field1.Type, vbInt32Field1.Type));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpIntField1.Type, vbIntArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(vbIntArrayField1.Type, csharpStringArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpStringArrayField1.Type, vbIntArrayArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(vbIntArrayArrayField1.Type, csharpIntArrayRank2Field1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpIntArrayRank2Field1.Type, vbInt32Field1.Type));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(csharpInt32Field1.Type, vbIntField1.Type));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(vbIntField1.Type, csharpIntArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpIntArrayField1.Type, vbStringArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(vbStringArrayField1.Type, csharpIntArrayArrayField1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpIntArrayArrayField1.Type, vbIntArrayRank2Field1.Type));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(vbIntArrayRank2Field1.Type, csharpInt32Field1.Type));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(vbInt32Field1.Type, csharpIntField1.Type));
    }
 
    [Fact]
    public async Task TestFields()
    {
        var csharpCode1 =
@"class Type1
{
    int field1;
    string field2;
}
 
class Type2
{
    bool field3;
    short field4;
}";
 
        var csharpCode2 =
@"class Type1
{
    int field1;
    short field4;
}
 
class Type2
{
    bool field3;
    string field2;
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type2_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type2").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type2_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type2").Single();
 
        var field1_v1 = type1_v1.GetMembers("field1").Single();
        var field1_v2 = type1_v2.GetMembers("field1").Single();
        var field2_v1 = type1_v1.GetMembers("field2").Single();
        var field2_v2 = type2_v2.GetMembers("field2").Single();
        var field3_v1 = type2_v1.GetMembers("field3").Single();
        var field3_v2 = type2_v2.GetMembers("field3").Single();
        var field4_v1 = type2_v1.GetMembers("field4").Single();
        var field4_v2 = type1_v2.GetMembers("field4").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(field1_v1, field1_v2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(field1_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(field1_v2));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(field2_v1, field2_v2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(field3_v1, field3_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(field4_v1, field4_v2));
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538124")]
    public async Task TestFieldsAcrossLanguages()
    {
        var csharpCode1 =
@"class Type1
{
    int field1;
    string field2;
}
 
class Type2
{
    bool field3;
    short field4;
}";
 
        var vbCode1 =
@"class Type1
    dim field1 as Integer;
    dim field4 as Short;
end class
 
class Type2
    dim field3 as Boolean;
    dim field2 as String;
end class";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateVisualBasic(vbCode1);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type2_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type2").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type2_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type2").Single();
 
        var field1_v1 = type1_v1.GetMembers("field1").Single();
        var field1_v2 = type1_v2.GetMembers("field1").Single();
        var field2_v1 = type1_v1.GetMembers("field2").Single();
        var field2_v2 = type2_v2.GetMembers("field2").Single();
        var field3_v1 = type2_v1.GetMembers("field3").Single();
        var field3_v2 = type2_v2.GetMembers("field3").Single();
        var field4_v1 = type2_v1.GetMembers("field4").Single();
        var field4_v2 = type1_v2.GetMembers("field4").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(field1_v1, field1_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(field2_v1, field2_v2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(field3_v1, field3_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(field4_v1, field4_v2));
    }
 
    [Fact]
    public async Task TestFieldsInGenericTypes()
    {
        var code =
@"class C<T>
{
    int goo;
    C<int> intInstantiation1;
    C<string> stringInstantiation;
    C<T> instanceInstantiation;
}
 
class D
{
    C<int> intInstantiation2;
}
";
 
        using var workspace = TestWorkspace.CreateCSharp(code);
        var typeC = (await workspace.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var typeD = (await workspace.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("D").Single();
 
        var intInstantiation1 = (IFieldSymbol)typeC.GetMembers("intInstantiation1").Single();
        var stringInstantiation = (IFieldSymbol)typeC.GetMembers("stringInstantiation").Single();
        var instanceInstantiation = (IFieldSymbol)typeC.GetMembers("instanceInstantiation").Single();
        var intInstantiation2 = (IFieldSymbol)typeD.GetMembers("intInstantiation2").Single();
 
        var goo = typeC.GetMembers("goo").Single();
        var goo_intInstantiation1 = intInstantiation1.Type.GetMembers("goo").Single();
        var goo_stringInstantiation = stringInstantiation.Type.GetMembers("goo").Single();
        var goo_instanceInstantiation = instanceInstantiation.Type.GetMembers("goo").Single();
        var goo_intInstantiation2 = intInstantiation2.Type.GetMembers("goo").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(goo, goo_intInstantiation1));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(goo, goo_intInstantiation2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(goo, goo_stringInstantiation));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(goo_intInstantiation1, goo_stringInstantiation));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(goo, goo_instanceInstantiation));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(goo),
                     SymbolEquivalenceComparer.Instance.GetHashCode(goo_instanceInstantiation));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(goo_intInstantiation1, goo_intInstantiation2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(goo_intInstantiation1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(goo_intInstantiation2));
    }
 
    [Fact]
    public async Task TestMethodsWithDifferentReturnTypeNotEquivalent()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo() {}
}";
 
        var csharpCode2 =
@"class Type1
{
    int Goo() {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact]
    public async Task TestMethodsWithDifferentNamesAreNotEquivalent()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo() {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo1() {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo1").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact]
    public async Task TestMethodsWithDifferentAritiesAreNotEquivalent()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo() {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo<T>() {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact]
    public async Task TestMethodsWithDifferentParametersAreNotEquivalent()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo() {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo(int a) {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact]
    public async Task TestMethodsWithDifferentTypeParameters()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo<A>(A a) {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo<B>(B a) {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(method_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(method_v2));
    }
 
    [Fact]
    public async Task TestMethodsWithSameParameters()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo(int a) {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo(int a) {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(method_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(method_v2));
    }
 
    [Fact]
    public async Task TestMethodsWithDifferentParameterNames()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo(int a) {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo(int b) {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(method_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(method_v2));
    }
 
    [Fact]
    public async Task TestMethodsAreEquivalentOutToRef()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo(out int a) {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo(ref int a) {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact]
    public async Task TestMethodsNotEquivalentRemoveOut()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo(out int a) {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo(int a) {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact]
    public async Task TestMethodsAreEquivalentIgnoreParams()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo(params int[] a) {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo(int[] a) {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(method_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(method_v2));
    }
 
    [Fact]
    public async Task TestMethodsNotEquivalentDifferentParameterTypes()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo(int[] a) {}
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo(string[] a) {}
}";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact]
    public async Task TestMethodsAcrossLanguages()
    {
        var csharpCode1 =
@"
using System.Collections.Generic;
 
class Type1
{
    T Goo<T>(IList<T> list, int a) {}
    void Bar() { }
}";
 
        var vbCode1 =
@"
Imports System.Collections.Generic
 
class Type1
    function Goo(of U)(list as IList(of U), a as Integer) as U
    end function
    sub Quux()
    end sub
end class";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateVisualBasic(vbCode1);
        var csharpType1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var vbType1 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var csharpGooMethod = csharpType1.GetMembers("Goo").Single();
        var csharpBarMethod = csharpType1.GetMembers("Bar").Single();
        var vbGooMethod = vbType1.GetMembers("Goo").Single();
        var vbQuuxMethod = vbType1.GetMembers("Quux").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(csharpGooMethod, vbGooMethod));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(csharpGooMethod),
                     SymbolEquivalenceComparer.Instance.GetHashCode(vbGooMethod));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpGooMethod, csharpBarMethod));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpGooMethod, vbQuuxMethod));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpBarMethod, csharpGooMethod));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpBarMethod, vbGooMethod));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpBarMethod, vbQuuxMethod));
    }
 
    [Fact]
    public async Task TestMethodsInGenericTypesAcrossLanguages()
    {
        var csharpCode1 =
@"
using System.Collections.Generic;
 
class Type1<X>
{
    T Goo<T>(IList<T> list, X a) {}
    void Bar(X x) { }
}";
 
        var vbCode1 =
@"
Imports System.Collections.Generic
 
class Type1(of M)
    function Goo(of U)(list as IList(of U), a as M) as U
    end function
    sub Bar(x as Object)
    end sub
end class";
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateVisualBasic(vbCode1);
        var csharpType1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var vbType1 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var csharpGooMethod = csharpType1.GetMembers("Goo").Single();
        var csharpBarMethod = csharpType1.GetMembers("Bar").Single();
        var vbGooMethod = vbType1.GetMembers("Goo").Single();
        var vbBarMethod = vbType1.GetMembers("Bar").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(csharpGooMethod, vbGooMethod));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(csharpGooMethod),
                     SymbolEquivalenceComparer.Instance.GetHashCode(vbGooMethod));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpGooMethod, csharpBarMethod));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpGooMethod, vbBarMethod));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpBarMethod, csharpGooMethod));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpBarMethod, vbGooMethod));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(csharpBarMethod, vbBarMethod));
    }
 
    [Fact]
    public async Task TestObjectAndDynamicAreNotEqualNormally()
    {
        var csharpCode1 =
@"class Type1
{
    object field1;
    dynamic field2;
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var field1_v1 = type1_v1.GetMembers("field1").Single();
        var field2_v1 = type1_v1.GetMembers("field2").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(field1_v1, field2_v1));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(field2_v1, field1_v1));
    }
 
    [Fact]
    public async Task TestObjectAndDynamicAreEqualInSignatures()
    {
        var csharpCode1 =
@"class Type1
{
    void Goo(object o1) { }
}";
 
        var csharpCode2 =
@"class Type1
{
    void Goo(dynamic o1) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v2, method_v1));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(method_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(method_v2));
    }
 
    [Fact]
    public async Task TestUnequalGenericsInSignatures()
    {
        var csharpCode1 =
@"
using System.Collections.Generic;
 
class Type1
{
    void Goo(IList<int> o1) { }
}";
 
        var csharpCode2 =
@"
using System.Collections.Generic;
 
class Type1
{
    void Goo(IList<string> o1) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v2, method_v1));
    }
 
    [Fact]
    public async Task TestGenericsWithDynamicAndObjectInSignatures()
    {
        var csharpCode1 =
@"
using System.Collections.Generic;
 
class Type1
{
    void Goo(IList<object> o1) { }
}";
 
        var csharpCode2 =
@"
using System.Collections.Generic;
 
class Type1
{
    void Goo(IList<dynamic> o1) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v2, method_v1));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(method_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(method_v2));
    }
 
    [Fact]
    public async Task TestDynamicAndUnrelatedTypeInSignatures()
    {
        var csharpCode1 =
@"
using System.Collections.Generic;
 
class Type1
{
    void Goo(dynamic o1) { }
}";
 
        var csharpCode2 =
@"
using System.Collections.Generic;
 
class Type1
{
    void Goo(string o1) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v2, method_v1));
    }
 
    [Fact]
    public async Task TestNamespaces()
    {
        var csharpCode1 =
@"namespace Outer
{
    namespace Inner
    {
        class Type
        {
        }
    }
 
    class Type
    {
    }
}
";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode1);
        var outer1 = (INamespaceSymbol)(await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetMembers("Outer").Single();
        var outer2 = (INamespaceSymbol)(await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetMembers("Outer").Single();
 
        var inner1 = (INamespaceSymbol)outer1.GetMembers("Inner").Single();
        var inner2 = (INamespaceSymbol)outer2.GetMembers("Inner").Single();
 
        var outerType1 = outer1.GetTypeMembers("Type").Single();
        var outerType2 = outer2.GetTypeMembers("Type").Single();
 
        var innerType1 = inner1.GetTypeMembers("Type").Single();
        var innerType2 = inner2.GetTypeMembers("Type").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(outer1, outer2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(outer1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(outer2));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(inner1, inner2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(inner1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(inner2));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(outerType1, outerType2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(outerType1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(outerType2));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(innerType1, innerType2));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(innerType1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(innerType2));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(outer1, inner1));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(inner1, outerType1));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(outerType1, innerType1));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(innerType1, outer1));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(outer1, inner1.ContainingSymbol));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(outer1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(inner1.ContainingSymbol));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(outer1, innerType1.ContainingSymbol.ContainingSymbol));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(outer1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(innerType1.ContainingSymbol.ContainingSymbol));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(inner1, innerType1.ContainingSymbol));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(inner1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(innerType1.ContainingSymbol));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(outer1, outerType1.ContainingSymbol));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(outer1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(outerType1.ContainingSymbol));
    }
 
    [Fact]
    public async Task TestNamedTypesEquivalent()
    {
        var csharpCode1 =
@"
class Type1
{
}
 
class Type2<X>
{
}
";
 
        var csharpCode2 =
@"
class Type1
{
  void Goo();
}
 
class Type2<Y>
{
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type2_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type2").Single();
        var type2_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type2").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(type1_v1, type1_v2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(type1_v2, type1_v1));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(type1_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(type1_v2));
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(type2_v1, type2_v2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(type2_v2, type2_v1));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(type2_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(type2_v2));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v1, type2_v1));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type2_v1, type1_v1));
    }
 
    [Fact]
    public async Task TestNamedTypesDifferentIfNameChanges()
    {
        var csharpCode1 =
@"
class Type1
{
}";
 
        var csharpCode2 =
@"
class Type2
{
  void Goo();
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type2").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v1, type1_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v2, type1_v1));
    }
 
    [Fact]
    public async Task TestNamedTypesDifferentIfTypeKindChanges()
    {
        var csharpCode1 =
@"
struct Type1
{
}";
 
        var csharpCode2 =
@"
class Type1
{
  void Goo();
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v1, type1_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v2, type1_v1));
    }
 
    [Fact]
    public async Task TestNamedTypesDifferentIfArityChanges()
    {
        var csharpCode1 =
@"
class Type1
{
}";
 
        var csharpCode2 =
@"
class Type1<T>
{
  void Goo();
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v1, type1_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v2, type1_v1));
    }
 
    [Fact]
    public async Task TestNamedTypesDifferentIfContainerDifferent()
    {
        var csharpCode1 =
@"
class Outer
{
    class Type1
    {
    }
}";
 
        var csharpCode2 =
@"
class Other
{
    class Type1
    {
        void Goo();
    }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var outer = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Outer").Single();
        var other = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Other").Single();
        var type1_v1 = outer.GetTypeMembers("Type1").Single();
        var type1_v2 = other.GetTypeMembers("Type1").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v1, type1_v2));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(type1_v2, type1_v1));
    }
 
    [Fact]
    public async Task TestAliasedTypes1()
    {
        var csharpCode1 =
@"
using i = System.Int32;
 
class Type1
{
    void Goo(i o1) { }
}";
 
        var csharpCode2 =
@"
class Type1
{
    void Goo(int o1) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("Type1").Single();
 
        var method_v1 = type1_v1.GetMembers("Goo").Single();
        var method_v2 = type1_v2.GetMembers("Goo").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v2, method_v1));
        Assert.Equal(SymbolEquivalenceComparer.Instance.GetHashCode(method_v1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(method_v2));
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/599")]
    public async Task TestRefVersusOut()
    {
        var csharpCode1 =
@"
class C
{
    void M(out int i) { }
}";
 
        var csharpCode2 =
@"
class C
{
    void M(ref int i) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var method_v1 = type1_v1.GetMembers("M").Single();
        var method_v2 = type1_v2.GetMembers("M").Single();
 
        var trueComp = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: false, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
        var falseComp = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: false, tupleNamesMustMatch: false, ignoreNullableAnnotations: false, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
 
        Assert.False(trueComp.Equals(method_v1, method_v2));
        Assert.False(trueComp.Equals(method_v2, method_v1));
        // The hashcodes of distinct objects don't have to be distinct.
 
        Assert.True(falseComp.Equals(method_v1, method_v2));
        Assert.True(falseComp.Equals(method_v2, method_v1));
        Assert.Equal(falseComp.GetHashCode(method_v1),
                     falseComp.GetHashCode(method_v2));
    }
 
    [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1388780")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1391743")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1393352")]
    public async Task TestTuples1()
    {
        var csharpCode1 =
@"
class C
{
    void M((int, int) i) { }
}";
 
        var csharpCode2 =
@"
class C
{
    void M(int i) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var method_v1 = type1_v1.GetMembers("M").Single();
        var method_v2 = type1_v2.GetMembers("M").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1388780")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1391743")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1393352")]
    public async Task TestTuples2()
    {
        var csharpCode1 =
@"
class C
{
    void M((int, int) i) { }
}";
 
        var csharpCode2 =
@"
class C
{
    void M(System.ValueTuple<int> i) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var method_v1 = type1_v1.GetMembers("M").Single();
        var method_v2 = type1_v2.GetMembers("M").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1388780")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1391743")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1393352")]
    public async Task TestTuples3()
    {
        var csharpCode1 =
@"
class C
{
    void M((int, int) i) { }
}";
 
        var csharpCode2 =
@"
class C
{
    void M(System.ValueTuple<int, int> i) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var method_v1 = type1_v1.GetMembers("M").Single();
        var method_v2 = type1_v2.GetMembers("M").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.True(SymbolEquivalenceComparer.TupleNamesMustMatchInstance.Equals(method_v1, method_v2));
    }
 
    [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1388780")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1391743")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1393352")]
    public async Task TestTuples4()
    {
        var csharpCode1 =
@"
class C
{
    void M((int a, int b) i) { }
}";
 
        var csharpCode2 =
@"
class C
{
    void M(System.ValueTuple<int, int> i) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var method_v1 = type1_v1.GetMembers("M").Single();
        var method_v2 = type1_v2.GetMembers("M").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.False(SymbolEquivalenceComparer.TupleNamesMustMatchInstance.Equals(method_v1, method_v2));
    }
 
    [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1388780")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1391743")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1393352")]
    public async Task TestTuples5()
    {
        var csharpCode1 =
@"
class C
{
    void M((int a, int b) i) { }
}";
 
        var csharpCode2 =
@"
class C
{
    void M((int, int) i) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var method_v1 = type1_v1.GetMembers("M").Single();
        var method_v2 = type1_v2.GetMembers("M").Single();
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
        Assert.False(SymbolEquivalenceComparer.TupleNamesMustMatchInstance.Equals(method_v1, method_v2));
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56133")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1388780")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1391743")]
    [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1393352")]
    public async Task TestTuples6()
    {
        var csharpCode1 =
@"
class C
{
    void M((int a, int b) i) { }
}";
 
        var csharpCode2 =
@"
class C
{
    void M((int a, int b, int c) i) { }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var type1_v1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
        var type1_v2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("C").Single();
 
        var method_v1 = type1_v1.GetMembers("M").Single();
        var method_v2 = type1_v2.GetMembers("M").Single();
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(method_v1, method_v2));
    }
 
    [Fact]
    public async Task TestNullableBothEnabled()
    {
        // Both are nullable enabled.  So `T?` should not be equal to `T` unless we are ignoring nullability.
        var csharpCode1 =
@"
#nullable enable
class T
{
    string? A;
    string[]? B;
    dynamic? C;
    dynamic?[]? D;
}";
 
        var csharpCode2 =
@"
#nullable enable
class T
{
    string A;
    string[] B;
    dynamic C;
    dynamic[] D;
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var t1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single();
        var t2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single();
 
        var a1 = ((IFieldSymbol)t1.GetMembers("A").Single()).Type;
        var b1 = ((IFieldSymbol)t1.GetMembers("B").Single()).Type;
        var c1 = ((IFieldSymbol)t1.GetMembers("C").Single()).Type;
        var d1 = ((IFieldSymbol)t1.GetMembers("D").Single()).Type;
        var a2 = ((IFieldSymbol)t2.GetMembers("A").Single()).Type;
        var b2 = ((IFieldSymbol)t2.GetMembers("B").Single()).Type;
        var c2 = ((IFieldSymbol)t2.GetMembers("C").Single()).Type;
        var d2 = ((IFieldSymbol)t2.GetMembers("D").Single()).Type;
 
        Assert.Equal(NullableAnnotation.Annotated, a1.NullableAnnotation);
        Assert.Equal(NullableAnnotation.NotAnnotated, a2.NullableAnnotation);
 
        var ignoreComparer = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: true, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
        var notIgnoreComparer = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: false, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
 
        Assert.True(ignoreComparer.Equals(a1, a2));
        Assert.True(ignoreComparer.Equals(b1, b2));
        Assert.True(ignoreComparer.Equals(c1, c2));
        Assert.True(ignoreComparer.Equals(d1, d2));
        Assert.False(notIgnoreComparer.Equals(a1, a2));
        Assert.False(notIgnoreComparer.Equals(b1, b2));
        Assert.False(notIgnoreComparer.Equals(c1, c2));
        Assert.False(notIgnoreComparer.Equals(d1, d2));
 
        // The hashcodes of distinct objects don't have to be distinct.
        Assert.Equal(ignoreComparer.GetHashCode(a1), ignoreComparer.GetHashCode(a2));
        Assert.Equal(ignoreComparer.GetHashCode(b1), ignoreComparer.GetHashCode(b2));
        Assert.Equal(ignoreComparer.GetHashCode(c1), ignoreComparer.GetHashCode(c2));
        Assert.Equal(ignoreComparer.GetHashCode(d1), ignoreComparer.GetHashCode(d2));
    }
 
    [Fact]
    public async Task TestNullableDisableVsEnable1()
    {
        // One side is nullable disabled. Since we don't know the nullability, we should consider it equals to the
        // type without nullability.
 
        var csharpCode1 =
@"
#nullable disable
class T
{
    string A;
    string[] B;
    dynamic C;
    dynamic[] D;
}";
 
        var csharpCode2 =
@"
#nullable enable
class T
{
    string A;
    string[] B;
    dynamic C;
    dynamic[] D;
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var t1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single();
        var t2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single();
 
        var a1 = ((IFieldSymbol)t1.GetMembers("A").Single()).Type;
        var b1 = ((IFieldSymbol)t1.GetMembers("B").Single()).Type;
        var c1 = ((IFieldSymbol)t1.GetMembers("C").Single()).Type;
        var d1 = ((IFieldSymbol)t1.GetMembers("D").Single()).Type;
        var a2 = ((IFieldSymbol)t2.GetMembers("A").Single()).Type;
        var b2 = ((IFieldSymbol)t2.GetMembers("B").Single()).Type;
        var c2 = ((IFieldSymbol)t2.GetMembers("C").Single()).Type;
        var d2 = ((IFieldSymbol)t2.GetMembers("D").Single()).Type;
 
        Assert.Equal(NullableAnnotation.None, a1.NullableAnnotation);
        Assert.Equal(NullableAnnotation.NotAnnotated, a2.NullableAnnotation);
 
        var ignoreComparer = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: true, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
        var notIgnoreComparer = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: false, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
 
        Assert.True(ignoreComparer.Equals(a1, a2));
        Assert.True(ignoreComparer.Equals(b1, b2));
        Assert.True(ignoreComparer.Equals(c1, c2));
        Assert.True(ignoreComparer.Equals(d1, d2));
        Assert.True(notIgnoreComparer.Equals(a1, a2));
        Assert.True(notIgnoreComparer.Equals(b1, b2));
        Assert.True(notIgnoreComparer.Equals(c1, c2));
        Assert.True(notIgnoreComparer.Equals(d1, d2));
 
        // The hashcodes of distinct objects don't have to be distinct.
        Assert.Equal(ignoreComparer.GetHashCode(a1), ignoreComparer.GetHashCode(a2));
        Assert.Equal(ignoreComparer.GetHashCode(b1), ignoreComparer.GetHashCode(b2));
        Assert.Equal(ignoreComparer.GetHashCode(c1), ignoreComparer.GetHashCode(c2));
        Assert.Equal(ignoreComparer.GetHashCode(d1), ignoreComparer.GetHashCode(d2));
    }
 
    [Fact]
    public async Task TestNullableDisableVsEnable2()
    {
        // One side is nullable disabled. One side definitely annotated.  Treat as different.
 
        var csharpCode1 =
@"
#nullable disable
class T
{
    string A;
    string[] B;
    dynamic C;
    dynamic[] D;
}";
 
        var csharpCode2 =
@"
#nullable enable
class T
{
    string? A;
    string?[] B;
    dynamic? C;
    dynamic?[] D;
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var t1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single();
        var t2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single();
 
        var a1 = ((IFieldSymbol)t1.GetMembers("A").Single()).Type;
        var b1 = ((IFieldSymbol)t1.GetMembers("B").Single()).Type;
        var c1 = ((IFieldSymbol)t1.GetMembers("C").Single()).Type;
        var d1 = ((IFieldSymbol)t1.GetMembers("D").Single()).Type;
        var a2 = ((IFieldSymbol)t2.GetMembers("A").Single()).Type;
        var b2 = ((IFieldSymbol)t2.GetMembers("B").Single()).Type;
        var c2 = ((IFieldSymbol)t2.GetMembers("C").Single()).Type;
        var d2 = ((IFieldSymbol)t2.GetMembers("D").Single()).Type;
 
        Assert.Equal(NullableAnnotation.None, a1.NullableAnnotation);
        Assert.Equal(NullableAnnotation.Annotated, a2.NullableAnnotation);
 
        var ignoreComparer = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: true, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
        var notIgnoreComparer = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: false, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
 
        Assert.True(ignoreComparer.Equals(a1, a2));
        Assert.True(ignoreComparer.Equals(b1, b2));
        Assert.True(ignoreComparer.Equals(c1, c2));
        Assert.True(ignoreComparer.Equals(d1, d2));
        Assert.False(notIgnoreComparer.Equals(a1, a2));
        Assert.False(notIgnoreComparer.Equals(b1, b2));
        Assert.False(notIgnoreComparer.Equals(c1, c2));
        Assert.False(notIgnoreComparer.Equals(d1, d2));
 
        // The hashcodes of distinct objects don't have to be distinct.
        Assert.Equal(ignoreComparer.GetHashCode(a1), ignoreComparer.GetHashCode(a2));
        Assert.Equal(ignoreComparer.GetHashCode(b1), ignoreComparer.GetHashCode(b2));
        Assert.Equal(ignoreComparer.GetHashCode(c1), ignoreComparer.GetHashCode(c2));
        Assert.Equal(ignoreComparer.GetHashCode(d1), ignoreComparer.GetHashCode(d2));
    }
 
    [Fact]
    public async Task TestNullableDisableVsEnable3()
    {
        // One side is nullable disabled. One side definitely annotated.  Treat as different.
 
        var csharpCode1 =
@"
#nullable disable
class T
{
    string A;
    string[] B;
    dynamic C;
    dynamic[] D;
}";
 
        var csharpCode2 =
@"
#nullable enable
class T
{
    string? A;
    string[]? B;
    dynamic? C;
    dynamic[]? D;
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1);
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2);
        var t1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single();
        var t2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single();
 
        var a1 = ((IFieldSymbol)t1.GetMembers("A").Single()).Type;
        var b1 = ((IFieldSymbol)t1.GetMembers("B").Single()).Type;
        var c1 = ((IFieldSymbol)t1.GetMembers("C").Single()).Type;
        var d1 = ((IFieldSymbol)t1.GetMembers("D").Single()).Type;
        var a2 = ((IFieldSymbol)t2.GetMembers("A").Single()).Type;
        var b2 = ((IFieldSymbol)t2.GetMembers("B").Single()).Type;
        var c2 = ((IFieldSymbol)t2.GetMembers("C").Single()).Type;
        var d2 = ((IFieldSymbol)t2.GetMembers("D").Single()).Type;
 
        Assert.Equal(NullableAnnotation.None, a1.NullableAnnotation);
        Assert.Equal(NullableAnnotation.Annotated, a2.NullableAnnotation);
 
        var ignoreComparer = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: true, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
        var notIgnoreComparer = new SymbolEquivalenceComparer(assemblyComparer: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: false, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
 
        Assert.True(ignoreComparer.Equals(a1, a2));
        Assert.True(ignoreComparer.Equals(b1, b2));
        Assert.True(ignoreComparer.Equals(c1, c2));
        Assert.True(ignoreComparer.Equals(d1, d2));
        Assert.False(notIgnoreComparer.Equals(a1, a2));
        Assert.False(notIgnoreComparer.Equals(b1, b2));
        Assert.False(notIgnoreComparer.Equals(c1, c2));
        Assert.False(notIgnoreComparer.Equals(d1, d2));
 
        // The hashcodes of distinct objects don't have to be distinct.
        Assert.Equal(ignoreComparer.GetHashCode(a1), ignoreComparer.GetHashCode(a2));
        Assert.Equal(ignoreComparer.GetHashCode(b1), ignoreComparer.GetHashCode(b2));
        Assert.Equal(ignoreComparer.GetHashCode(c1), ignoreComparer.GetHashCode(c2));
        Assert.Equal(ignoreComparer.GetHashCode(d1), ignoreComparer.GetHashCode(d2));
    }
 
    [Fact]
    public async Task TestCSharpReducedExtensionMethodsAreEquivalent()
    {
        var code = @"
class Zed {}
 
public static class Extensions
{
   public static void NotGeneric(this Zed z, int data) { }
   public static void GenericThis<T>(this T me, int data) where T : Zed { }
   public static void GenericNotThis<T>(this Zed z, T data) { }
   public static void GenericThisAndMore<T,S>(this T me, S data) where T : Zed { }
   public static void GenericThisAndOther<T>(this T me, T data) where T : Zed { } 
}
 
class Test
{    
    void NotGeneric() 
    {
        Zed z;
        int n;
        z.NotGeneric(n);
    }
 
    void GenericThis() 
    {
        Zed z;
        int n;
        z.GenericThis(n);
    }
 
    void GenericNotThis() 
    {
        Zed z;
        int n;
        z.GenericNotThis(n);
    }
 
    void GenericThisAndMore() 
    {
        Zed z;
        int n;
        z.GenericThisAndMore(n);
    }
 
    void GenericThisAndOther() 
    {
        Zed z;
        z.GenericThisAndOther(z);
    } 
}
";
        using var workspace1 = TestWorkspace.CreateCSharp(code);
        using var workspace2 = TestWorkspace.CreateCSharp(code);
        var comp1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync());
        var comp2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync());
 
        TestReducedExtension<CS.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "NotGeneric");
        TestReducedExtension<CS.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "GenericThis");
        TestReducedExtension<CS.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "GenericNotThis");
        TestReducedExtension<CS.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "GenericThisAndMore");
        TestReducedExtension<CS.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "GenericThisAndOther");
    }
 
    [Fact]
    public async Task TestVisualBasicReducedExtensionMethodsAreEquivalent()
    {
        var code = @"
Imports System.Runtime.CompilerServices
 
Class Zed
End Class
 
Module Extensions
   <Extension>
   Public Sub NotGeneric(z As Zed, data As Integer) 
   End Sub
 
   <Extension>
   Public Sub GenericThis(Of T As Zed)(m As T, data as Integer) 
   End Sub
 
   <Extension>
   Public Sub GenericNotThis(Of T)(z As Zed, data As T)
   End Sub
 
   <Extension>
   Public Sub GenericThisAndMore(Of T As Zed, S)(m As T, data As S)
   End Sub
 
   <Extension>
   Public Sub GenericThisAndOther(Of T As Zed)(m As T, data As T)
   End Sub
End Module
 
Class Test
    Sub NotGeneric() 
        Dim z As Zed
        Dim n As Integer
        z.NotGeneric(n)
    End Sub
 
    Sub GenericThis() 
        Dim z As Zed
        Dim n As Integer
        z.GenericThis(n)
    End Sub
 
    Sub GenericNotThis() 
        Dim z As Zed
        Dim n As Integer
        z.GenericNotThis(n)
    End Sub
 
    Sub GenericThisAndMore() 
        Dim z As Zed
        Dim n As Integer
        z.GenericThisAndMore(n)
    End Sub
 
    Sub GenericThisAndOther() 
        Dim z As Zed
        z.GenericThisAndOther(z)
    End Sub
End Class
";
        using var workspace1 = TestWorkspace.CreateVisualBasic(code);
        using var workspace2 = TestWorkspace.CreateVisualBasic(code);
        var comp1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync());
        var comp2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync());
 
        TestReducedExtension<VB.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "NotGeneric");
        TestReducedExtension<VB.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "GenericThis");
        TestReducedExtension<VB.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "GenericNotThis");
        TestReducedExtension<VB.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "GenericThisAndMore");
        TestReducedExtension<VB.Syntax.InvocationExpressionSyntax>(comp1, comp2, "Test", "GenericThisAndOther");
    }
 
    [Fact]
    public async Task TestDifferentModules()
    {
        var csharpCode =
@"namespace N
{
    namespace M
    {
    }
}";
 
        using var workspace1 = TestWorkspace.CreateCSharp(csharpCode, compilationOptions: new CS.CSharpCompilationOptions(OutputKind.NetModule, moduleName: "GooModule"));
        using var workspace2 = TestWorkspace.CreateCSharp(csharpCode, compilationOptions: new CS.CSharpCompilationOptions(OutputKind.NetModule, moduleName: "BarModule"));
        var namespace1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetNamespaceMembers().Single(n => n.Name == "N").GetNamespaceMembers().Single(n => n.Name == "M");
        var namespace2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetNamespaceMembers().Single(n => n.Name == "N").GetNamespaceMembers().Single(n => n.Name == "M");
 
        Assert.True(SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(namespace1, namespace2));
        Assert.Equal(SymbolEquivalenceComparer.IgnoreAssembliesInstance.GetHashCode(namespace1),
                     SymbolEquivalenceComparer.IgnoreAssembliesInstance.GetHashCode(namespace2));
 
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(namespace1, namespace2));
        Assert.NotEqual(SymbolEquivalenceComparer.Instance.GetHashCode(namespace1),
                     SymbolEquivalenceComparer.Instance.GetHashCode(namespace2));
    }
 
    [Fact]
    public void AssemblyComparer1()
    {
        var references = new[] { NetFramework.mscorlib };
 
        var source = "public class T {}";
        var sourceV1 = "[assembly: System.Reflection.AssemblyVersion(\"1.0.0.0\")] public class T {}";
        var sourceV2 = "[assembly: System.Reflection.AssemblyVersion(\"2.0.0.0\")] public class T {}";
 
        var a1 = (Compilation)CS.CSharpCompilation.Create("a", [CS.SyntaxFactory.ParseSyntaxTree(source)], references, CSharpDllOptions);
        var a2 = (Compilation)CS.CSharpCompilation.Create("a", [CS.SyntaxFactory.ParseSyntaxTree(source)], references, CSharpDllOptions);
 
        var b1 = (Compilation)CS.CSharpCompilation.Create("b", [CS.SyntaxFactory.ParseSyntaxTree(sourceV1)], references, CSharpSignedDllOptions);
        var b2 = (Compilation)CS.CSharpCompilation.Create("b", [CS.SyntaxFactory.ParseSyntaxTree(sourceV2)], references, CSharpSignedDllOptions);
        var b3 = (Compilation)CS.CSharpCompilation.Create("b", [CS.SyntaxFactory.ParseSyntaxTree(sourceV2)], references, CSharpSignedDllOptions);
 
        var ta1 = (ITypeSymbol)a1.GlobalNamespace.GetMembers("T").Single();
        var ta2 = (ITypeSymbol)a2.GlobalNamespace.GetMembers("T").Single();
        var tb1 = (ITypeSymbol)b1.GlobalNamespace.GetMembers("T").Single();
        var tb2 = (ITypeSymbol)b2.GlobalNamespace.GetMembers("T").Single();
        var tb3 = (ITypeSymbol)b3.GlobalNamespace.GetMembers("T").Single();
 
        var identityComparer = new SymbolEquivalenceComparer(AssemblySymbolIdentityComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: false, ignoreNullableAnnotations: false, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
 
        // same name:
        Assert.True(SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(ta1, ta2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(ta1, ta2));
        Assert.True(identityComparer.Equals(ta1, ta2));
 
        // different name:
        Assert.True(SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(ta1, tb1));
        Assert.False(SymbolEquivalenceComparer.Instance.Equals(ta1, tb1));
        Assert.False(identityComparer.Equals(ta1, tb1));
 
        // different identity
        Assert.True(SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(tb1, tb2));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(tb1, tb2));
        Assert.False(identityComparer.Equals(tb1, tb2));
 
        // same identity
        Assert.True(SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(tb2, tb3));
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(tb2, tb3));
        Assert.True(identityComparer.Equals(tb2, tb3));
    }
 
    private sealed class AssemblySymbolIdentityComparer : IEqualityComparer<IAssemblySymbol>
    {
        public static readonly IEqualityComparer<IAssemblySymbol> Instance = new AssemblySymbolIdentityComparer();
 
        public bool Equals(IAssemblySymbol x, IAssemblySymbol y)
            => x.Identity.Equals(y.Identity);
 
        public int GetHashCode(IAssemblySymbol obj)
            => obj.Identity.GetHashCode();
    }
 
    [Fact]
    public void CustomModifiers_Methods1()
    {
        const string ilSource = @"
.class public C
{
  .method public instance int32 [] modopt([mscorlib]System.Int64) F(         // 0
      int32 modopt([mscorlib]System.Runtime.CompilerServices.IsConst) a, 
      int32 modopt([mscorlib]System.Runtime.CompilerServices.IsConst) b)
  {
      ldnull     
      throw
  }
 
  .method public instance int32 [] modopt([mscorlib]System.Boolean) F(       // 1
      int32 modopt([mscorlib]System.Runtime.CompilerServices.IsConst) a, 
      int32 modopt([mscorlib]System.Runtime.CompilerServices.IsConst) b)
  {
      ldnull     
      throw
 }
 
  .method public instance int32[] F(                                         // 2
      int32 a, 
      int32 modopt([mscorlib]System.Runtime.CompilerServices.IsConst) b)
  {
      ldnull     
      throw
  }
 
  .method public instance int32[] F(                                         // 3
      int32 a, 
      int32 b)
  {
      ldnull     
      throw
  }
}
";
        MetadataReference r1, r2;
        using (var tempAssembly = IlasmUtilities.CreateTempAssembly(ilSource))
        {
            var bytes = File.ReadAllBytes(tempAssembly.Path);
            r1 = MetadataReference.CreateFromImage(bytes);
            r2 = MetadataReference.CreateFromImage(bytes);
        }
 
        var c1 = (Compilation)CS.CSharpCompilation.Create("comp1", Array.Empty<SyntaxTree>(), [NetFramework.mscorlib, r1]);
        var c2 = (Compilation)CS.CSharpCompilation.Create("comp2", Array.Empty<SyntaxTree>(), [NetFramework.mscorlib, r2]);
        var type1 = (ITypeSymbol)c1.GlobalNamespace.GetMembers("C").Single();
        var type2 = (ITypeSymbol)c2.GlobalNamespace.GetMembers("C").Single();
 
        var identityComparer = new SymbolEquivalenceComparer(AssemblySymbolIdentityComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: false, ignoreNullableAnnotations: false, objectAndDynamicCompareEqually: true, arrayAndReadOnlySpanCompareEqually: false);
 
        var f1 = type1.GetMembers("F");
        var f2 = type2.GetMembers("F");
 
        Assert.True(identityComparer.Equals(f1[0], f2[0]));
        Assert.False(identityComparer.Equals(f1[0], f2[1]));
        Assert.False(identityComparer.Equals(f1[0], f2[2]));
        Assert.False(identityComparer.Equals(f1[0], f2[3]));
 
        Assert.False(identityComparer.Equals(f1[1], f2[0]));
        Assert.True(identityComparer.Equals(f1[1], f2[1]));
        Assert.False(identityComparer.Equals(f1[1], f2[2]));
        Assert.False(identityComparer.Equals(f1[1], f2[3]));
 
        Assert.False(identityComparer.Equals(f1[2], f2[0]));
        Assert.False(identityComparer.Equals(f1[2], f2[1]));
        Assert.True(identityComparer.Equals(f1[2], f2[2]));
        Assert.False(identityComparer.Equals(f1[2], f2[3]));
 
        Assert.False(identityComparer.Equals(f1[3], f2[0]));
        Assert.False(identityComparer.Equals(f1[3], f2[1]));
        Assert.False(identityComparer.Equals(f1[3], f2[2]));
        Assert.True(identityComparer.Equals(f1[3], f2[3]));
    }
 
    private static void TestReducedExtension<TInvocation>(Compilation comp1, Compilation comp2, string typeName, string methodName)
        where TInvocation : SyntaxNode
    {
        var method1 = GetInvokedSymbol<TInvocation>(comp1, typeName, methodName);
        var method2 = GetInvokedSymbol<TInvocation>(comp2, typeName, methodName);
 
        Assert.NotNull(method1);
        Assert.Equal(MethodKind.ReducedExtension, method1.MethodKind);
 
        Assert.NotNull(method2);
        Assert.Equal(MethodKind.ReducedExtension, method2.MethodKind);
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(method1, method2));
 
        var cfmethod1 = method1.ConstructedFrom;
        var cfmethod2 = method2.ConstructedFrom;
 
        Assert.True(SymbolEquivalenceComparer.Instance.Equals(cfmethod1, cfmethod2));
    }
 
    private static IMethodSymbol GetInvokedSymbol<TInvocation>(Compilation compilation, string typeName, string methodName)
        where TInvocation : SyntaxNode
    {
        var type1 = compilation.GlobalNamespace.GetTypeMembers(typeName).Single();
        var method = type1.GetMembers(methodName).Single();
        var method_root = method.DeclaringSyntaxReferences[0].GetSyntax();
 
        var invocation = method_root.DescendantNodes().OfType<TInvocation>().FirstOrDefault();
        // vb method root is statement, but we need block to find body with invocation
        invocation ??= method_root.Parent.DescendantNodes().OfType<TInvocation>().First();
 
        var model = compilation.GetSemanticModel(invocation.SyntaxTree);
        var info = model.GetSymbolInfo(invocation);
        return info.Symbol as IMethodSymbol;
    }
}