File: Emit\EditAndContinue\SymbolMatcherTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit2\Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit2.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.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests
{
    public class SymbolMatcherTests : EditAndContinueTestBase
    {
        private static PEAssemblySymbol CreatePEAssemblySymbol(string source)
        {
            var compilation = CreateCompilation(source, options: TestOptions.DebugDll);
            var reference = compilation.EmitToImageReference();
            return (PEAssemblySymbol)CreateCompilation("", new[] { reference }).GetReferencedAssemblySymbol(reference);
        }
 
        private static CSharpSymbolMatcher CreateMatcher(CSharpCompilation fromCompilation, CSharpCompilation toCompilation)
            => new CSharpSymbolMatcher(
                fromCompilation.SourceAssembly,
                toCompilation.SourceAssembly,
                SynthesizedTypeMaps.Empty,
                otherSynthesizedMembers: null,
                otherDeletedMembers: null);
 
        private static CSharpSymbolMatcher CreateMatcher(CSharpCompilation fromCompilation, PEAssemblySymbol peAssemblySymbol)
            => new CSharpSymbolMatcher(
                SynthesizedTypeMaps.Empty,
                fromCompilation.SourceAssembly,
                peAssemblySymbol);
 
        private static IEnumerable<string> Inspect(ImmutableSegmentedDictionary<AnonymousDelegateWithIndexedNamePartialKey, ImmutableArray<AnonymousTypeValue>> anonymousDelegatesWithIndexedNames)
            => from entry in anonymousDelegatesWithIndexedNames
               from value in entry.Value
               select $"({entry.Key.GenericArity},{entry.Key.ParameterCount}): {value.Type}";
 
        [Fact]
        public void ConcurrentAccess()
        {
            var source =
@"class A
{
    B F;
    D P { get; set; }
    void M(A a, B b, S s, I i) { }
    delegate void D(S s);
    class B { }
    struct S { }
    interface I { }
}
class B
{
    A M<T, U>() where T : A where U : T, I { return null; }
    event D E;
    delegate void D(S s);
    struct S { }
    interface I { }
}";
 
            var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source);
 
            var builder = new List<Symbol>();
            var type = compilation1.GetMember<NamedTypeSymbol>("A");
            builder.Add(type);
            builder.AddRange(type.GetMembers());
            type = compilation1.GetMember<NamedTypeSymbol>("B");
            builder.Add(type);
            builder.AddRange(type.GetMembers());
            var members = builder.ToImmutableArray();
            Assert.True(members.Length > 10);
 
            for (int i = 0; i < 10; i++)
            {
                var matcher = CreateMatcher(compilation1, compilation0);
 
                var tasks = new Task[10];
                for (int j = 0; j < tasks.Length; j++)
                {
                    int startAt = i + j + 1;
                    tasks[j] = Task.Run(() =>
                    {
                        MatchAll(matcher, members, startAt);
                        Thread.Sleep(10);
                    });
                }
                Task.WaitAll(tasks);
            }
        }
 
        private static void MatchAll(CSharpSymbolMatcher matcher, ImmutableArray<Symbol> members, int startAt)
        {
            int n = members.Length;
            for (int i = 0; i < n; i++)
            {
                var member = members[(i + startAt) % n];
                var other = matcher.MapDefinition((Cci.IDefinition)member.GetCciAdapter());
                Assert.NotNull(other);
            }
        }
 
        [Fact]
        public void TypeArguments()
        {
            const string source =
@"class A<T>
{
    class B<U>
    {
        static A<V> M<V>(A<U>.B<T> x, A<object>.S y)
        {
            return null;
        }
        static A<V> M<V>(A<U>.B<T> x, A<V>.S y)
        {
            return null;
        }
    }
    struct S
    {
    }
}";
            var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var members = compilation1.GetMember<NamedTypeSymbol>("A.B").GetMembers("M");
            Assert.Equal(2, members.Length);
            foreach (var member in members)
            {
                var other = matcher.MapDefinition((Cci.IMethodDefinition)member.GetCciAdapter());
                Assert.NotNull(other);
            }
        }
 
        [Fact]
        public void Constraints()
        {
            const string source =
@"interface I<T> where T : I<T>
{
}
class C
{
    static void M<T>(I<T> o) where T : I<T>
    {
    }
}";
            var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var member = compilation1.GetMember<MethodSymbol>("C.M");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            Assert.NotNull(other);
        }
 
        [Fact]
        public void CustomModifiers()
        {
            var ilSource =
@".class public abstract A
{
  .method public hidebysig specialname rtspecialname instance void .ctor() { ret }
  .method public abstract virtual instance object modopt(A) [] F(int32 modopt(object) *p) { }
}";
            var metadataRef = CompileIL(ilSource);
            var source =
@"unsafe class B : A
{
    public override object[] F(int* p) { return null; }
}";
            var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll, references: new[] { metadataRef });
            var compilation1 = compilation0.WithSource(source);
 
            var member1 = compilation1.GetMember<MethodSymbol>("B.F");
            Assert.Equal(1, ((PointerTypeSymbol)member1.Parameters[0].Type).PointedAtTypeWithAnnotations.CustomModifiers.Length);
            Assert.Equal(1, ((ArrayTypeSymbol)member1.ReturnType).ElementTypeWithAnnotations.CustomModifiers.Length);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var other = (MethodSymbol)matcher.MapDefinition(member1.GetCciAdapter()).GetInternalSymbol();
            Assert.NotNull(other);
            Assert.Equal(1, ((PointerTypeSymbol)other.Parameters[0].Type).PointedAtTypeWithAnnotations.CustomModifiers.Length);
            Assert.Equal(1, ((ArrayTypeSymbol)other.ReturnType).ElementTypeWithAnnotations.CustomModifiers.Length);
        }
 
        [Fact]
        public void CustomModifiers_InAttribute_Source()
        {
            // The parameter is emitted as
            // int32& modreq([mscorlib]System.Runtime.InteropServices.InAttribute)
 
            var source0 = @"
abstract class C
{  
    // matching
    public abstract void F(in int x);
    public virtual void G(in int x) => throw null;
 
    // non-matching
    public void H(in int x) => throw null;
}";
            var source1 = @"
abstract class C
{
    // matching
    public abstract void F(in int x);
    public virtual void G(in int x) => throw null;
 
    // non-matching
    public void H(int x) => throw null;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var f0 = compilation0.GetMember<MethodSymbol>("C.F");
            var g0 = compilation0.GetMember<MethodSymbol>("C.G");
            var f1 = compilation1.GetMember<MethodSymbol>("C.F");
            var g1 = compilation1.GetMember<MethodSymbol>("C.G");
            var h1 = compilation1.GetMember<MethodSymbol>("C.H");
 
            Assert.Same(f0, (MethodSymbol)matcher.MapDefinition(f1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(g0, (MethodSymbol)matcher.MapDefinition(g1.GetCciAdapter()).GetInternalSymbol());
            Assert.Null(matcher.MapDefinition(h1.GetCciAdapter()));
        }
 
        [Fact]
        public void CustomModifiers_InAttribute_Metadata()
        {
            // The parameter is emitted as
            // int32& modreq([mscorlib]System.Runtime.InteropServices.InAttribute)
 
            var source0 = @"
abstract class C
{  
    // matching
    public abstract void F(in int x);
    public virtual void G(in int x) => throw null;
 
    // non-matching
    public void H(in int x) => throw null;
}";
            var source1 = @"
abstract class C
{
    // matching
    public abstract void F(in int x);
    public virtual void G(in int x) => throw null;
 
    // non-matching
    public void H(int x) => throw null;
}";
 
            var peAssemblySymbol = CreatePEAssemblySymbol(source0);
 
            var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll).WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, peAssemblySymbol);
 
            var f0 = peAssemblySymbol.GlobalNamespace.GetMember<NamedTypeSymbol>("C").GetMember("F");
            var g0 = peAssemblySymbol.GlobalNamespace.GetMember<NamedTypeSymbol>("C").GetMember("G");
            var f1 = compilation1.GetMember<MethodSymbol>("C.F");
            var g1 = compilation1.GetMember<MethodSymbol>("C.G");
            var h1 = compilation1.GetMember<MethodSymbol>("C.H");
 
            Assert.Equal(f0, (MethodSymbol)matcher.MapDefinition(f1.GetCciAdapter()).GetInternalSymbol());
            Assert.Equal(g0, (MethodSymbol)matcher.MapDefinition(g1.GetCciAdapter()).GetInternalSymbol());
            Assert.Null(matcher.MapDefinition(h1.GetCciAdapter()));
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void VaryingCompilationReferences()
        {
            string libSource = @"
public class D { }
";
 
            string source = @"
public class C
{
    public void F(D a) {}
}
";
            var lib0 = CreateCompilation(libSource, options: TestOptions.DebugDll, assemblyName: "Lib");
            var lib1 = CreateCompilation(libSource, options: TestOptions.DebugDll, assemblyName: "Lib");
 
            var compilation0 = CreateCompilation(source, new[] { lib0.ToMetadataReference() }, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source).WithReferences(MscorlibRef, lib1.ToMetadataReference());
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var f0 = compilation0.GetMember<MethodSymbol>("C.F");
            var f1 = compilation1.GetMember<MethodSymbol>("C.F");
 
            var mf1 = matcher.MapDefinition(f1.GetCciAdapter());
            Assert.Equal(f0, mf1.GetInternalSymbol());
        }
 
        [WorkItem(1533, "https://github.com/dotnet/roslyn/issues/1533")]
        [Fact]
        public void PreviousType_ArrayType()
        {
            var source0 = @"
class C
{  
    static void M()
    {
        int x = 0;
    }
    class D {}
}";
            var source1 = @"
class C
{
    static void M()
    {
        D[] x = null;
    }
    class D {}
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var elementType = compilation1.GetMember<TypeSymbol>("C.D");
            var member = compilation1.CreateArrayTypeSymbol(elementType);
            var other = matcher.MapReference(member.GetCciAdapter());
            Assert.NotNull(other);
        }
 
        [WorkItem(1533, "https://github.com/dotnet/roslyn/issues/1533")]
        [Fact]
        public void NoPreviousType_ArrayType()
        {
            var source0 = @"
class C
{  
    static void M()
    {
        int x = 0;
    }
}";
            var source1 = @"
class C
{
    static void M()
    {
        D[] x = null;
    }
    class D {}
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var elementType = compilation1.GetMember<TypeSymbol>("C.D");
            var member = compilation1.CreateArrayTypeSymbol(elementType);
            var other = matcher.MapReference(member.GetCciAdapter());
            // For a newly added type, there is no match in the previous generation.
            Assert.Null(other);
        }
 
        [WorkItem(1533, "https://github.com/dotnet/roslyn/issues/1533")]
        [Fact]
        public void NoPreviousType_PointerType()
        {
            var source0 = @"
class C
{  
    static void M()
    {
        int x = 0;
    }
}";
            var source1 = @"
class C
{
    static unsafe void M()
    {
        D* x = null;
    }
    struct D {}
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var elementType = compilation1.GetMember<TypeSymbol>("C.D");
            var member = compilation1.CreatePointerTypeSymbol(elementType);
            var other = matcher.MapReference(member.GetCciAdapter());
            // For a newly added type, there is no match in the previous generation.
            Assert.Null(other);
        }
 
        [WorkItem(1533, "https://github.com/dotnet/roslyn/issues/1533")]
        [Fact]
        public void NoPreviousType_GenericType()
        {
            var source0 = @"
using System.Collections.Generic;
class C
{  
    static void M()
    {
        int x = 0;
    }
}";
            var source1 = @"
using System.Collections.Generic;
class C
{
    static void M()
    {
        List<D> x = null;
    }
    class D {}
    List<D> y;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var member = compilation1.GetMember<FieldSymbol>("C.y");
            var other = matcher.MapReference((Cci.ITypeReference)member.Type.GetCciAdapter());
            // For a newly added type, there is no match in the previous generation.
            Assert.Null(other);
        }
 
        [Fact]
        public void HoistedAnonymousTypes()
        {
            var source0 = @"
using System;
 
class C
{
    static void F()
    {
        var x1 = new { A = 1 };
        var x2 = new { B = 1 };
        var y = new Func<int>(() => x1.A + x2.B);
    }
}
";
            var source1 = @"
using System;
 
class C
{
    static void F()
    {
        var x1 = new { A = 1 };
        var x2 = new { b = 1 };
        var y = new Func<int>(() => x1.A + x2.b);
    }
}";
 
            var peAssemblySymbol0 = CreatePEAssemblySymbol(source0);
            var peModule0 = (PEModuleSymbol)peAssemblySymbol0.Modules[0];
 
            var reader0 = peModule0.Module.MetadataReader;
            var decoder0 = new MetadataDecoder(peModule0);
 
            var synthesizedTypes0 = PEDeltaAssemblyBuilder.GetSynthesizedTypesFromMetadata(reader0, decoder0);
            var anonymousTypeMap0 = synthesizedTypes0.AnonymousTypes;
            Assert.Equal("<>f__AnonymousType0", anonymousTypeMap0[new AnonymousTypeKey(ImmutableArray.Create(new AnonymousTypeKeyField("A", isKey: false, ignoreCase: false)))].Name);
            Assert.Equal("<>f__AnonymousType1", anonymousTypeMap0[new AnonymousTypeKey(ImmutableArray.Create(new AnonymousTypeKeyField("B", isKey: false, ignoreCase: false)))].Name);
            Assert.Equal(2, anonymousTypeMap0.Count);
 
            var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll);
 
            var testData = new CompilationTestData();
            compilation1.EmitToArray(testData: testData);
            var peAssemblyBuilder = (PEAssemblyBuilder)testData.Module;
 
            var c = compilation1.GetMember<NamedTypeSymbol>("C");
            var displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single();
            Assert.Equal("<>c__DisplayClass0_0", displayClass.Name);
 
            var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);
 
            var fields = displayClass.GetFields(emitContext).ToArray();
            var x1 = fields[0];
            var x2 = fields[1];
            Assert.Equal("x1", x1.Name);
            Assert.Equal("x2", x2.Name);
 
            var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0);
 
            var mappedX1 = (Cci.IFieldDefinition)matcher.MapDefinition(x1);
            var mappedX2 = (Cci.IFieldDefinition)matcher.MapDefinition(x2);
 
            Assert.Equal("x1", mappedX1.Name);
            Assert.Null(mappedX2);
        }
 
        [Fact]
        public void HoistedAnonymousTypes_Complex()
        {
            var source0 = @"
using System;
 
class C
{
    static void F()
    {
        var x1 = new[] { new { A = new { X = 1 } } };
        var x2 = new[] { new { A = new { Y = 1 } } };
        var y = new Func<int>(() => x1[0].A.X + x2[0].A.Y);
    }
}
";
            var source1 = @"
using System;
 
class C
{
    static void F()
    {
        var x1 = new[] { new { A = new { X = 1 } } };
        var x2 = new[] { new { A = new { Z = 1 } } };
        var y = new Func<int>(() => x1[0].A.X + x2[0].A.Z);
    }
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
 
            var peRef0 = compilation0.EmitToImageReference();
            var peAssemblySymbol0 = (PEAssemblySymbol)CreateCompilation("", new[] { peRef0 }).GetReferencedAssemblySymbol(peRef0);
            var peModule0 = (PEModuleSymbol)peAssemblySymbol0.Modules[0];
 
            var reader0 = peModule0.Module.MetadataReader;
            var decoder0 = new MetadataDecoder(peModule0);
 
            var synthesizedTypes0 = PEDeltaAssemblyBuilder.GetSynthesizedTypesFromMetadata(reader0, decoder0);
            var anonymousTypeMap0 = synthesizedTypes0.AnonymousTypes;
            Assert.Equal("<>f__AnonymousType0", anonymousTypeMap0[new AnonymousTypeKey(ImmutableArray.Create(new AnonymousTypeKeyField("A", isKey: false, ignoreCase: false)))].Name);
            Assert.Equal("<>f__AnonymousType1", anonymousTypeMap0[new AnonymousTypeKey(ImmutableArray.Create(new AnonymousTypeKeyField("X", isKey: false, ignoreCase: false)))].Name);
            Assert.Equal("<>f__AnonymousType2", anonymousTypeMap0[new AnonymousTypeKey(ImmutableArray.Create(new AnonymousTypeKeyField("Y", isKey: false, ignoreCase: false)))].Name);
            Assert.Equal(3, anonymousTypeMap0.Count);
 
            var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll);
 
            var testData = new CompilationTestData();
            compilation1.EmitToArray(testData: testData);
            var peAssemblyBuilder = (PEAssemblyBuilder)testData.Module;
 
            var c = compilation1.GetMember<NamedTypeSymbol>("C");
            var displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single();
            Assert.Equal("<>c__DisplayClass0_0", displayClass.Name);
 
            var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);
 
            var fields = displayClass.GetFields(emitContext).ToArray();
            AssertEx.SetEqual(fields.Select(f => f.Name), new[] { "x1", "x2" });
            var x1 = fields.Where(f => f.Name == "x1").Single();
            var x2 = fields.Where(f => f.Name == "x2").Single();
 
            var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0);
 
            var mappedX1 = (Cci.IFieldDefinition)matcher.MapDefinition(x1);
            var mappedX2 = (Cci.IFieldDefinition)matcher.MapDefinition(x2);
 
            Assert.Equal("x1", mappedX1.Name);
            Assert.Null(mappedX2);
        }
 
        [Fact]
        public void TupleField_TypeChange()
        {
            var source0 = @"
class C
{  
    public (int a, int b) x;
}";
            var source1 = @"
class C
{
    public (int a, bool b) x;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<FieldSymbol>("C.x");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // If a type changes within a tuple, we do not expect types to match.
            Assert.Null(other);
        }
 
        [Fact]
        public void TupleField_NameChange()
        {
            var source0 = @"
class C
{  
    public (int a, int b) x;
}";
            var source1 = @"
class C
{
    public (int a, int c) x;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<FieldSymbol>("C.x");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // Types must match because just an element name was changed.
            Assert.NotNull(other);
        }
 
        [Fact]
        public void TupleMethod_TypeChange()
        {
            var source0 = @"
class C
{  
    public (int a, int b) X() { return null };
}";
            var source1 = @"
class C
{
    public (int a, bool b) X() { return null };
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<MethodSymbol>("C.X");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // If a type changes within a tuple, we do not expect types to match.
            Assert.Null(other);
        }
 
        [Fact]
        public void TupleMethod_NameChange()
        {
            var source0 = @"
class C
{  
    public (int a, int b) X() { return null };
}";
            var source1 = @"
class C
{
    public (int a, int c) X() { return null };
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<MethodSymbol>("C.X");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // Types must match because just an element name was changed.
            Assert.NotNull(other);
        }
 
        [Fact]
        public void TupleProperty_TypeChange()
        {
            var source0 = @"
class C
{  
    public (int a, int b) X { get { return null; } };
}";
            var source1 = @"
class C
{
    public (int a, bool b) X { get { return null; } };
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<PropertySymbol>("C.X");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // If a type changes within a tuple, we do not expect types to match.
            Assert.Null(other);
        }
 
        [Fact]
        public void TupleProperty_NameChange()
        {
            var source0 = @"
class C
{  
    public (int a, int b) X { get { return null; } };
}";
            var source1 = @"
class C
{
    public (int a, int c) X { get { return null; } };
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<PropertySymbol>("C.X");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // Types must match because just an element name was changed.
            Assert.NotNull(other);
        }
 
        [Fact]
        public void TupleStructField_TypeChange()
        {
            var source0 = @"
public struct Vector
{
    public (int x, int y) Coordinates;
}";
            var source1 = @"
public struct Vector
{
    public (int x, int y, int z) Coordinates;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<FieldSymbol>("Vector.Coordinates");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // If a type changes within a tuple, we do not expect types to match.
            Assert.Null(other);
        }
 
        [Fact]
        public void TupleStructField_NameChange()
        {
            var source0 = @"
public struct Vector
{
    public (int x, int y) Coordinates;
}";
            var source1 = @"
public struct Vector
{
    public (int x, int z) Coordinates;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<FieldSymbol>("Vector.Coordinates");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // Types must match because just an element name was changed.
            Assert.NotNull(other);
        }
 
        [Fact]
        public void TupleDelegate_TypeChange()
        {
            var source0 = @"
public class C
{
    public delegate (int, int) F();
}";
            var source1 = @"
public class C
{
    public delegate (int, bool) F();
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<SourceNamedTypeSymbol>("C.F");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // Tuple delegate defines a type. We should be able to match old and new types by name.
            Assert.NotNull(other);
        }
 
        [Fact]
        public void TupleDelegate_NameChange()
        {
            var source0 = @"
public class C
{
    public delegate (int, int) F();
}";
            var source1 = @"
public class C
{
    public delegate (int x, int y) F();
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<SourceNamedTypeSymbol>("C.F");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            // Types must match because just an element name was changed.
            Assert.NotNull(other);
        }
 
        [Fact]
        public void RefReturn_Method()
        {
            var source0 = @"
struct C
{
    // non-matching
    public ref int P() => throw null;
    public ref readonly int Q() => throw null;
    public int R() => throw null;
 
    // matching
    public ref readonly int S() => throw null;
    public ref int T() => throw null;
}";
            var source1 = @"
struct C
{
    // non-matching
    public ref bool P() => throw null;
    public ref int Q() => throw null;
    public ref int R() => throw null;
 
    // matching
    public ref readonly int S() => throw null;
    public ref int T() => throw null;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var s0 = compilation0.GetMember<MethodSymbol>("C.S");
            var t0 = compilation0.GetMember<MethodSymbol>("C.T");
            var p1 = compilation1.GetMember<MethodSymbol>("C.P");
            var q1 = compilation1.GetMember<MethodSymbol>("C.Q");
            var r1 = compilation1.GetMember<MethodSymbol>("C.R");
            var s1 = compilation1.GetMember<MethodSymbol>("C.S");
            var t1 = compilation1.GetMember<MethodSymbol>("C.T");
 
            Assert.Null(matcher.MapDefinition(p1.GetCciAdapter()));
            Assert.Null(matcher.MapDefinition(q1.GetCciAdapter()));
            Assert.Null(matcher.MapDefinition(r1.GetCciAdapter()));
 
            Assert.Same(s0, matcher.MapDefinition(s1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(t0, matcher.MapDefinition(t1.GetCciAdapter()).GetInternalSymbol());
        }
 
        [Fact]
        public void RefReturn_Property()
        {
            var source0 = @"
struct C
{
    // non-matching
    public ref int P => throw null;
    public ref readonly int Q => throw null;
    public int R => throw null;
 
    // matching
    public ref readonly int S => throw null;
    public ref int T => throw null;
}";
            var source1 = @"
struct C
{
    // non-matching
    public ref bool P => throw null;
    public ref int Q => throw null;
    public ref int R => throw null;
 
    // matching
    public ref readonly int S => throw null;
    public ref int T => throw null;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var s0 = compilation0.GetMember<PropertySymbol>("C.S");
            var t0 = compilation0.GetMember<PropertySymbol>("C.T");
            var p1 = compilation1.GetMember<PropertySymbol>("C.P");
            var q1 = compilation1.GetMember<PropertySymbol>("C.Q");
            var r1 = compilation1.GetMember<PropertySymbol>("C.R");
            var s1 = compilation1.GetMember<PropertySymbol>("C.S");
            var t1 = compilation1.GetMember<PropertySymbol>("C.T");
 
            Assert.Null(matcher.MapDefinition(p1.GetCciAdapter()));
            Assert.Null(matcher.MapDefinition(q1.GetCciAdapter()));
            Assert.Null(matcher.MapDefinition(r1.GetCciAdapter()));
 
            Assert.Same(s0, matcher.MapDefinition(s1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(t0, matcher.MapDefinition(t1.GetCciAdapter()).GetInternalSymbol());
        }
 
        [Fact]
        public void Property_CompilationVsPE()
        {
            var source = @"
using System;
 
interface I<T, S>
{
	int this[int index] { set; }
}
 
class C : I<int, bool>
{
    int _current;
	int I<int, bool>.this[int anotherIndex] 
	{
		set { _current = anotherIndex + value; }
	}
}";
 
            var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll);
 
            var peRef0 = compilation0.EmitToImageReference();
            var peAssemblySymbol0 = (PEAssemblySymbol)CreateCompilation("", new[] { peRef0 }).GetReferencedAssemblySymbol(peRef0);
 
            var compilation1 = CreateCompilation(source, options: TestOptions.DebugDll);
 
            var testData = new CompilationTestData();
            compilation1.EmitToArray(testData: testData);
 
            var c = compilation1.GetMember<NamedTypeSymbol>("C");
            var property = c.GetMember<PropertySymbol>("I<System.Int32,System.Boolean>.this[]");
            var parameters = property.GetParameters().ToArray();
            Assert.Equal(1, parameters.Length);
            Assert.Equal("anotherIndex", parameters[0].Name);
 
            var matcher = CreateMatcher(compilation1, peAssemblySymbol0);
 
            var mappedProperty = (Cci.IPropertyDefinition)matcher.MapDefinition(property.GetCciAdapter());
 
            Assert.Equal("I<System.Int32,System.Boolean>.Item", ((PropertySymbol)mappedProperty.GetInternalSymbol()).MetadataName);
        }
 
        [Fact]
        public void Method_ParameterNullableChange()
        {
            var source0 = @"
using System.Collections.Generic;
class C
{
    string c;
    ref string M(string? s, (string a, dynamic? b) tuple, List<string?> list) => ref c;
}";
            var source1 = @"
using System.Collections.Generic;
class C
{
    string c;
    ref string? M(string s, (string? a, dynamic b) tuple, List<string> list) => ref c;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<MethodSymbol>("C.M");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            Assert.NotNull(other);
        }
 
        [Fact]
        public void Method_ParameterRename()
        {
            var source0 = @"
using System.Collections.Generic;
class C
{
    string M(string s) => s.ToString();
}";
            var source1 = @"
using System.Collections.Generic;
class C
{
     string M(string m) => m.ToString();
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<MethodSymbol>("C.M");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            Assert.NotNull(other);
        }
 
        [Fact]
        public void Method_ParameterRenameToDiscard()
        {
            var source0 = @"
using System.Collections.Generic;
class C
{
    string M(string s) => s.ToString();
}";
            var source1 = @"
using System.Collections.Generic;
class C
{
     string M(string _) => ""Hello"";
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<MethodSymbol>("C.M");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            Assert.NotNull(other);
        }
 
        [Fact]
        public void Field_NullableChange()
        {
            var source0 = @"
class C
{
    string S;
}";
            var source1 = @"
class C
{
    string? S;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<FieldSymbol>("C.S");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            Assert.NotNull(other);
        }
 
        [Fact]
        public void AnonymousTypesWithNullables()
        {
            var source0 = @"
using System;
 
class C
{
    static T id<T>(T t) => t;
    static T F<T>(Func<T> f) => f();
 
    static void M(string? x)
    {
        var y1 = new { A = id(x) };
        var y2 = F(() => new { B = id(x) });
        var z = new Func<string>(() => y1.A + y2.B);
    }
}";
            var source1 = @"
using System;
 
class C
{
    static T id<T>(T t) => t;
    static T F<T>(Func<T> f) => f();
 
    static void M(string? x)
    {
        if (x is null) throw new Exception();
        var y1 = new { A = id(x) };
        var y2 = F(() => new { B = id(x) });
        var z = new Func<string>(() => y1.A + y2.B);
    }
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
 
            var peRef0 = compilation0.EmitToImageReference();
            var peAssemblySymbol0 = (PEAssemblySymbol)CreateCompilation("", new[] { peRef0 }).GetReferencedAssemblySymbol(peRef0);
            var peModule0 = (PEModuleSymbol)peAssemblySymbol0.Modules[0];
 
            var reader0 = peModule0.Module.MetadataReader;
            var decoder0 = new MetadataDecoder(peModule0);
 
            var synthesizedTypes0 = PEDeltaAssemblyBuilder.GetSynthesizedTypesFromMetadata(reader0, decoder0);
            var anonymousTypeMap0 = synthesizedTypes0.AnonymousTypes;
            Assert.Equal("<>f__AnonymousType0", anonymousTypeMap0[new AnonymousTypeKey(ImmutableArray.Create(new AnonymousTypeKeyField("A", isKey: false, ignoreCase: false)))].Name);
            Assert.Equal("<>f__AnonymousType1", anonymousTypeMap0[new AnonymousTypeKey(ImmutableArray.Create(new AnonymousTypeKeyField("B", isKey: false, ignoreCase: false)))].Name);
            Assert.Equal(2, anonymousTypeMap0.Count);
 
            var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll);
 
            var testData = new CompilationTestData();
            compilation1.EmitToArray(testData: testData);
            var peAssemblyBuilder = (PEAssemblyBuilder)testData.Module;
 
            var c = compilation1.GetMember<NamedTypeSymbol>("C");
            var displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single();
            Assert.Equal("<>c__DisplayClass2_0", displayClass.Name);
 
            var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);
 
            var fields = displayClass.GetFields(emitContext).ToArray();
            AssertEx.SetEqual(fields.Select(f => f.Name), new[] { "x", "y1", "y2" });
            var y1 = fields.Where(f => f.Name == "y1").Single();
            var y2 = fields.Where(f => f.Name == "y2").Single();
 
            var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0);
 
            var mappedY1 = (Cci.IFieldDefinition)matcher.MapDefinition(y1);
            var mappedY2 = (Cci.IFieldDefinition)matcher.MapDefinition(y2);
 
            Assert.Equal("y1", mappedY1.Name);
            Assert.Equal("y2", mappedY2.Name);
        }
 
        [Fact]
        public void InterfaceMembers()
        {
            var source = @"
using System;
 
interface I
{
    static int X = 1;
    static event Action Y;
 
    static void M() { }
    void N() { }
 
    static int P { get => 1; set { } }
    int Q { get => 1; set { } }
 
    static event Action E { add { } remove { } }
    event Action F { add { } remove { } }
}
";
            var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var x0 = compilation0.GetMember<FieldSymbol>("I.X");
            var y0 = compilation0.GetMember<EventSymbol>("I.Y");
            var m0 = compilation0.GetMember<MethodSymbol>("I.M");
            var n0 = compilation0.GetMember<MethodSymbol>("I.N");
            var p0 = compilation0.GetMember<PropertySymbol>("I.P");
            var q0 = compilation0.GetMember<PropertySymbol>("I.Q");
            var e0 = compilation0.GetMember<EventSymbol>("I.E");
            var f0 = compilation0.GetMember<EventSymbol>("I.F");
 
            var x1 = compilation1.GetMember<FieldSymbol>("I.X");
            var y1 = compilation1.GetMember<EventSymbol>("I.Y");
            var m1 = compilation1.GetMember<MethodSymbol>("I.M");
            var n1 = compilation1.GetMember<MethodSymbol>("I.N");
            var p1 = compilation1.GetMember<PropertySymbol>("I.P");
            var q1 = compilation1.GetMember<PropertySymbol>("I.Q");
            var e1 = compilation1.GetMember<EventSymbol>("I.E");
            var f1 = compilation1.GetMember<EventSymbol>("I.F");
 
            Assert.Same(x0, matcher.MapDefinition(x1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(y0, matcher.MapDefinition(y1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(m0, matcher.MapDefinition(m1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(n0, matcher.MapDefinition(n1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(p0, matcher.MapDefinition(p1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(q0, matcher.MapDefinition(q1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(e0, matcher.MapDefinition(e1.GetCciAdapter()).GetInternalSymbol());
            Assert.Same(f0, matcher.MapDefinition(f1.GetCciAdapter()).GetInternalSymbol());
        }
 
        [Fact]
        public void FunctionPointerMembersTranslated()
        {
            var source = @"
unsafe class C
{
    delegate*<void> f1;
    delegate*<C, C, C> f2;
    delegate*<ref C> f3;
    delegate*<ref readonly C> f4;
    delegate*<ref C, void> f5;
    delegate*<in C, void> f6;
    delegate*<out C, void> f7;
}
";
 
            var compilation0 = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.Regular9);
            var compilation1 = compilation0.WithSource(source);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            for (int i = 1; i <= 7; i++)
            {
                var f_0 = compilation0.GetMember<FieldSymbol>($"C.f{i}");
                var f_1 = compilation1.GetMember<FieldSymbol>($"C.f{i}");
 
                Assert.Same(f_0, matcher.MapDefinition(f_1.GetCciAdapter()).GetInternalSymbol());
            }
        }
 
        [Theory]
        [InlineData("C", "void")]
        [InlineData("C", "object")]
        [InlineData("C", "ref C")]
        [InlineData("C", "ref readonly C")]
        [InlineData("ref C", "ref readonly C")]
        public void FunctionPointerMembers_ReturnMismatch(string return1, string return2)
        {
            var source1 = $@"
unsafe class C
{{
    delegate*<C, {return1}> f1;
}}";
 
            var source2 = $@"
unsafe class C
{{
    delegate*<C, {return2}> f1;
}}";
 
            var compilation0 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.Regular9);
            var compilation1 = compilation0.WithSource(source2);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var f_1 = compilation1.GetMember<FieldSymbol>($"C.f1");
 
            Assert.Null(matcher.MapDefinition(f_1.GetCciAdapter()));
        }
 
        [Theory]
        [InlineData("C", "object")]
        [InlineData("C", "ref C")]
        [InlineData("C", "out C")]
        [InlineData("C", "in C")]
        [InlineData("ref C", "out C")]
        [InlineData("ref C", "in C")]
        [InlineData("out C", "in C")]
        [InlineData("C, C", "C")]
        public void FunctionPointerMembers_ParamMismatch(string param1, string param2)
        {
            var source1 = $@"
unsafe class C
{{
    delegate*<{param1}, C, void>* f1;
}}";
 
            var source2 = $@"
unsafe class C
{{
    delegate*<{param2}, C, void>* f1;
}}";
 
            verify(source1, source2);
 
            source1 = $@"
unsafe class C
{{
    delegate*<C, {param1}, void> f1;
}}";
 
            source2 = $@"
unsafe class C
{{
    delegate*<C, {param2}, void> f1;
}}";
 
            verify(source1, source2);
 
            static void verify(string source1, string source2)
            {
                var compilation0 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.Regular9);
                var compilation1 = compilation0.WithSource(source2);
 
                var matcher = CreateMatcher(compilation1, compilation0);
 
                var f_1 = compilation1.GetMember<FieldSymbol>($"C.f1");
 
                Assert.Null(matcher.MapDefinition(f_1.GetCciAdapter()));
            }
        }
 
        [Fact]
        public void Record_ImplementSynthesizedMember_ToString()
        {
            var source0 = @"
public record R
{
}";
            var source1 = @"
public record R
{
    public override string ToString() => ""R"";
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member = compilation1.GetMember<SourceOrdinaryMethodSymbol>("R.ToString");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            Assert.NotNull(other);
        }
 
        [Fact]
        public void Record_ImplementSynthesizedMember_PrintMembers()
        {
            var source0 = @"
public record R
{
}";
            var source1 = @"
public record R
{
    protected virtual bool PrintMembers(System.Text.StringBuilder builder) => true;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member0 = compilation0.GetMember<SynthesizedRecordPrintMembers>("R.PrintMembers");
            var member1 = compilation1.GetMember<SourceOrdinaryMethodSymbol>("R.PrintMembers");
 
            Assert.Equal(member0, (MethodSymbol)matcher.MapDefinition(member1.GetCciAdapter()).GetInternalSymbol());
        }
 
        [Fact]
        public void Record_RemoveSynthesizedMember_PrintMembers()
        {
            var source0 = @"
public record R
{
    protected virtual bool PrintMembers(System.Text.StringBuilder builder) => true;
}";
            var source1 = @"
public record R
{
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member0 = compilation0.GetMember<SourceOrdinaryMethodSymbol>("R.PrintMembers");
            var member1 = compilation1.GetMember<SynthesizedRecordPrintMembers>("R.PrintMembers");
 
            Assert.Equal(member0, (MethodSymbol)matcher.MapDefinition(member1.GetCciAdapter()).GetInternalSymbol());
        }
 
        [Fact]
        public void Record_ImplementSynthesizedMember_Property()
        {
            var source0 = @"
public record R(int X);";
            var source1 = @"
public record R(int X)
{
    public int X { get; init; } = this.X;
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var member0 = compilation0.GetMember<SynthesizedRecordPropertySymbol>("R.X");
            var member1 = compilation1.GetMember<SourcePropertySymbol>("R.X");
 
            Assert.Equal(member0, (PropertySymbol)matcher.MapDefinition(member1.GetCciAdapter()).GetInternalSymbol());
        }
 
        [Fact]
        public void Record_ImplementSynthesizedMember_Constructor()
        {
            var source0 = @"
public record R(int X);";
            var source1 = @"
public record R
{
    public R(int X)
    {
        this.X = X;
    }
 
    public int X { get; init; }
}";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
 
            var members = compilation1.GetMembers("R..ctor");
            // There are two, one is the copy constructor
            Assert.Equal(2, members.Length);
 
            var member = (SourceConstructorSymbol)members.Single(m => m.ToString() == "R.R(int)");
            var other = matcher.MapDefinition(member.GetCciAdapter());
            Assert.NotNull(other);
        }
 
        [Fact]
        public void SynthesizedDelegates_01()
        {
            var source0 = @"
using System;
 
class C
{
    static void F()
    {
        var _1 = (int a, ref int b) => a;
        var _2 = (in int a, int b) => { };
 
        var _3 = (int _1, int _2, int _3, int _4, int _5, int _6, int _7, int _8, int _9, int _10, int _11, int _12, int _13, int _14, int _15, int _16, int _17, int _18, int _19, int _20, int _21, int _22, int _23, int _24, int _25, int _26, int _27, int _28, int _29, int _30, int _31, int _32, ref int _33) => { };
    }
}
";
            var source1 = @"
using System;
 
class C
{
    static void F()
    {
        var _1 = (int c, ref int d) => c;
        var _2 = (in int c, int d) => { d.ToString(); };
 
        var _3 = (int _1, int _2, int _3, int _4, int _5, int _6, int _7, int _8, int _9, int _10, int _11, int _12, int _13, int _14, int _15, int _16, int _17, int _18, int _19, int _20, int _21, int _22, int _23, int _24, int _25, int _26, int _27, int _28, int _29, int _30, int _31, int _32, ref int _33) => { _1.ToString(); };
    }
}";
 
            var peAssemblySymbol0 = CreatePEAssemblySymbol(source0);
            var peModule0 = (PEModuleSymbol)peAssemblySymbol0.Modules[0];
 
            var reader0 = peModule0.Module.MetadataReader;
            var decoder0 = new MetadataDecoder(peModule0);
 
            var synthesizedTypes0 = PEDeltaAssemblyBuilder.GetSynthesizedTypesFromMetadata(reader0, decoder0);
            var synthesizedDelegates0 = synthesizedTypes0.AnonymousDelegates;
            Assert.Contains(new SynthesizedDelegateKey("<>F{00000008}`3"), synthesizedDelegates0.Keys);
            Assert.Contains(new SynthesizedDelegateKey("<>A{00000003}`2"), synthesizedDelegates0.Keys);
            Assert.Contains(new SynthesizedDelegateKey("<>A{00000000,100000000}`33"), synthesizedDelegates0.Keys);
            Assert.Equal(3, synthesizedDelegates0.Count);
 
            var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll);
 
            var testData = new CompilationTestData();
            compilation1.EmitToArray(testData: testData);
            var peAssemblyBuilder = (PEAssemblyBuilder)testData.Module;
 
            var c = compilation1.GetMember<NamedTypeSymbol>("C");
            var displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single();
            Assert.Equal("<>c", displayClass.Name);
 
            var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);
 
            var fields = displayClass.GetFields(emitContext).ToArray();
            var field1 = fields[1];
            var field2 = fields[2];
            var field3 = fields[3];
            Assert.Equal("<>9__0_0", field1.Name);
            Assert.Equal("<>9__0_1", field2.Name);
            Assert.Equal("<>9__0_2", field3.Name);
 
            var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0);
 
            var mappedField1 = (Cci.IFieldDefinition)matcher.MapDefinition(field1);
            var mappedField2 = (Cci.IFieldDefinition)matcher.MapDefinition(field2);
            var mappedField3 = (Cci.IFieldDefinition)matcher.MapDefinition(field3);
 
            Assert.Equal("<>9__0_0", mappedField1.Name);
            Assert.Equal("<>9__0_1", mappedField2.Name);
            Assert.Equal("<>9__0_2", mappedField3.Name);
        }
 
        [Fact]
        public void SynthesizedDelegates_02()
        {
            var source0 =
@"unsafe class Program
{
    static void Main()
    {
        var d1 = (int* p) => *p;
    }
}";
            var source1 =
@"unsafe class Program
{
    static void Main()
    {
        var d1 = (int* p) => 1;
    }
}";
 
            var compilation0 = CreateCompilation(source0, options: TestOptions.UnsafeDebugDll);
            var compilation1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll);
 
            var peRef0 = compilation0.EmitToImageReference();
            var peAssemblySymbol0 = (PEAssemblySymbol)CreateCompilation("", new[] { peRef0 }).GetReferencedAssemblySymbol(peRef0);
            var peModule0 = (PEModuleSymbol)peAssemblySymbol0.Modules[0];
 
            var reader0 = peModule0.Module.MetadataReader;
            var decoder0 = new MetadataDecoder(peModule0);
 
            var synthesizedTypes0 = PEDeltaAssemblyBuilder.GetSynthesizedTypesFromMetadata(reader0, decoder0);
            var anonymousDelegates0 = synthesizedTypes0.AnonymousDelegatesWithIndexedNames;
            Assert.Equal("<>f__AnonymousDelegate0", anonymousDelegates0.Single().Value.Single().Name);
            Assert.Equal(1, anonymousDelegates0.Count);
 
            var testData = new CompilationTestData();
            compilation1.EmitToArray(testData: testData);
            var peAssemblyBuilder = (PEAssemblyBuilder)testData.Module;
 
            var type = compilation1.GetMember<NamedTypeSymbol>("Program");
            var displayClass = peAssemblyBuilder.GetSynthesizedTypes(type).Single();
            Assert.Equal("<>c", displayClass.Name);
 
            var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);
            var field0 = displayClass.GetFields(emitContext).Single(f => f.Name == "<>9__0_0");
            Assert.Equal("<>f__AnonymousDelegate0", field0.GetType(emitContext).ToString());
 
            var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0);
            var field1 = (Cci.IFieldDefinition)matcher.MapDefinition(field0);
            Assert.Equal("<>9__0_0", field1.Name);
        }
 
        [Fact]
        public void SynthesizedDelegates_03()
        {
            // This test checks that lambdas between source0 and source1 are not the same,
            // as they differ in default parameter value or `params` modifier.
            var source0 = """
                class C
                {
                    unsafe static void F()
                    {
                        var _1 = (int a, int b = 1) => a;
                        var _2 = (int* x, int[] c) => c;
                        var _3 = (int* x, int b) => b;
                    }
                }
                """;
            var source1 = """
                class C
                {
                    unsafe static void F()
                    {
                        var _1 = (int a, int b = 2) => a;
                        var _2 = (int* x, params int[] c) => c;
                        var _3 = (int* x, int b = 1) => b;
                    }
                }
                """;
 
            var compilation0 = CreateCompilation(source0, options: TestOptions.UnsafeDebugDll);
            var compilation1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll);
 
            var peRef0 = compilation0.EmitToImageReference();
            var peAssemblySymbol0 = (PEAssemblySymbol)CreateCompilation("", new[] { peRef0 }).GetReferencedAssemblySymbol(peRef0);
            var peModule0 = (PEModuleSymbol)peAssemblySymbol0.Modules[0];
 
            var reader0 = peModule0.Module.MetadataReader;
            var decoder0 = new MetadataDecoder(peModule0);
 
            var synthesizedTypes0 = PEDeltaAssemblyBuilder.GetSynthesizedTypesFromMetadata(reader0, decoder0);
 
            AssertEx.SetEqual(new[]
            {
                "(3,2): <>f__AnonymousDelegate0<T1, T2, TResult>",
                "(0,2): <>f__AnonymousDelegate1",
                "(0,2): <>f__AnonymousDelegate2"
            }, Inspect(synthesizedTypes0.AnonymousDelegatesWithIndexedNames));
 
            var testData = new CompilationTestData();
            compilation1.EmitToArray(testData: testData);
            var peAssemblyBuilder = (PEAssemblyBuilder)testData.Module;
 
            var c = compilation1.GetMember<NamedTypeSymbol>("C");
            var displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single();
            Assert.Equal("<>c", displayClass.Name);
 
            var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);
 
            var fields = displayClass.GetFields(emitContext).ToArray();
            var field1 = fields[1];
            var field2 = fields[2];
            var field3 = fields[3];
            Assert.Equal("<>9__0_0", field1.Name);
            Assert.Equal("<>9__0_1", field2.Name);
            Assert.Equal("<>9__0_2", field3.Name);
 
            var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0);
 
            Assert.Null(matcher.MapDefinition(field1));
            Assert.Null(matcher.MapDefinition(field2));
            Assert.Null(matcher.MapDefinition(field3));
        }
 
        [Fact]
        public void SynthesizedDelegates_04()
        {
            var source0 = """
                class C
                {
                    unsafe static void F()
                    {
                        var _1 = (int a, int b = 1) => a;
                        var _2 = (int* x, params int[] c) => c;
                        var _3 = (int* x, int b) => b;
                    }
                }
                """;
            var source1 = """
                class C
                {
                    unsafe static void F()
                    {
                        var _1 = (int m, int n = 1) => m;
                        var _2 = (int* y, params int[] p) => p;
                        var _3 = (int* y, int c) => c;
                    }
                }
                """;
 
            var compilation0 = CreateCompilation(source0, options: TestOptions.UnsafeDebugDll);
            var compilation1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll);
 
            var peRef0 = compilation0.EmitToImageReference();
            var peAssemblySymbol0 = (PEAssemblySymbol)CreateCompilation("", new[] { peRef0 }).GetReferencedAssemblySymbol(peRef0);
            var peModule0 = (PEModuleSymbol)peAssemblySymbol0.Modules[0];
 
            var reader0 = peModule0.Module.MetadataReader;
            var decoder0 = new MetadataDecoder(peModule0);
 
            var synthesizedTypes0 = PEDeltaAssemblyBuilder.GetSynthesizedTypesFromMetadata(reader0, decoder0);
 
            AssertEx.SetEqual(new[]
            {
                "(3,2): <>f__AnonymousDelegate0<T1, T2, TResult>",
                "(0,2): <>f__AnonymousDelegate1",
                "(0,2): <>f__AnonymousDelegate2"
            }, Inspect(synthesizedTypes0.AnonymousDelegatesWithIndexedNames));
 
            var testData = new CompilationTestData();
            compilation1.EmitToArray(testData: testData);
            var peAssemblyBuilder = (PEAssemblyBuilder)testData.Module;
 
            var c = compilation1.GetMember<NamedTypeSymbol>("C");
            var displayClass = peAssemblyBuilder.GetSynthesizedTypes(c).Single();
            Assert.Equal("<>c", displayClass.Name);
 
            var emitContext = new EmitContext(peAssemblyBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);
 
            var fields = displayClass.GetFields(emitContext).ToArray();
            var field1 = fields[1];
            var field2 = fields[2];
            var field3 = fields[3];
            Assert.Equal("<>9__0_0", field1.Name);
            Assert.Equal("<>9__0_1", field2.Name);
            Assert.Equal("<>9__0_2", field3.Name);
 
            var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0);
 
            var mappedField1 = (Cci.IFieldDefinition)matcher.MapDefinition(field1);
            var mappedField2 = (Cci.IFieldDefinition)matcher.MapDefinition(field2);
            var mappedField3 = (Cci.IFieldDefinition)matcher.MapDefinition(field3);
 
            Assert.Equal("<>9__0_0", mappedField1.Name);
            Assert.Equal("<>9__0_1", mappedField2.Name);
            Assert.Equal("<>9__0_2", mappedField3.Name);
        }
 
        [Fact]
        public void CheckedUserDefinedOperators_01()
        {
            const string source =
@"
class A 
{
    public static A operator -(A c) => c;
    public static A operator checked -(A c) => c;
 
    public static A operator +(A c, A d) => c;
    public static A operator checked +(A c, A d) => c;
 
    public static explicit operator int(A c) => 0;
    public static explicit operator checked int(A c) => 0;
}
";
            var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var members1 = compilation1.GetMember<NamedTypeSymbol>("A").GetMembers().OfType<MethodSymbol>().Where(m => m.MethodKind is (MethodKind.Conversion or MethodKind.UserDefinedOperator)).ToArray();
            var members0 = compilation0.GetMember<NamedTypeSymbol>("A").GetMembers().OfType<MethodSymbol>().Where(m => m.MethodKind is (MethodKind.Conversion or MethodKind.UserDefinedOperator)).ToArray();
            Assert.Equal(6, members1.Length);
            Assert.Equal(members1.Length, members0.Length);
 
            for (int i = 0; i < members0.Length; i++)
            {
                Assert.Same(members0[i], matcher.MapDefinition(members1[i].GetCciAdapter()).GetInternalSymbol());
            }
        }
 
        [Fact]
        public void CheckedUserDefinedOperators_02()
        {
            const string source0 =
@"
class A 
{
    public static A operator -(A c) => c;
    public static A operator +(A c, A d) => c;
    public static explicit operator int(A c) => 0;
}
";
 
            const string source1 =
@"
class A 
{
    public static A operator -(A c) => c;
    public static A operator checked -(A c) => c;
 
    public static A operator +(A c, A d) => c;
    public static A operator checked +(A c, A d) => c;
 
    public static explicit operator int(A c) => 0;
    public static explicit operator checked int(A c) => 0;
}
";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var members1 = compilation1.GetMember<NamedTypeSymbol>("A").GetMembers().OfType<MethodSymbol>().Where(m => m.MethodKind is (MethodKind.Conversion or MethodKind.UserDefinedOperator)).ToArray();
            var members0 = compilation0.GetMember<NamedTypeSymbol>("A").GetMembers().OfType<MethodSymbol>().Where(m => m.MethodKind is (MethodKind.Conversion or MethodKind.UserDefinedOperator)).ToArray();
            Assert.Equal(6, members1.Length);
            Assert.Equal(3, members0.Length);
 
            for (int i = 0; i < members0.Length; i++)
            {
                Assert.Same(members0[i], matcher.MapDefinition(members1[i * 2].GetCciAdapter()).GetInternalSymbol());
            }
 
            for (int i = 0; i < members1.Length; i++)
            {
                if (i % 2 == 0)
                {
                    continue;
                }
 
                Assert.Null(matcher.MapDefinition(members1[i].GetCciAdapter()));
            }
        }
 
        [Fact]
        public void CheckedUserDefinedOperators_03()
        {
            const string source0 =
@"
class A 
{
    public static A operator -(A c) => c;
    public static A operator checked -(A c) => c;
 
    public static A operator +(A c, A d) => c;
    public static A operator checked +(A c, A d) => c;
 
    public static explicit operator int(A c) => 0;
    public static explicit operator checked int(A c) => 0;
}
";
 
            const string source1 =
@"
class A 
{
    public static A operator -(A c) => c;
    public static A operator +(A c, A d) => c;
    public static explicit operator int(A c) => 0;
}
";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var members1 = compilation1.GetMember<NamedTypeSymbol>("A").GetMembers().OfType<MethodSymbol>().Where(m => m.MethodKind is (MethodKind.Conversion or MethodKind.UserDefinedOperator)).ToArray();
            var members0 = compilation0.GetMember<NamedTypeSymbol>("A").GetMembers().OfType<MethodSymbol>().Where(m => m.MethodKind is (MethodKind.Conversion or MethodKind.UserDefinedOperator)).ToArray();
            Assert.Equal(3, members1.Length);
            Assert.Equal(6, members0.Length);
 
            for (int i = 0; i < members1.Length; i++)
            {
                Assert.Same(members0[i * 2], matcher.MapDefinition(members1[i].GetCciAdapter()).GetInternalSymbol());
            }
        }
 
        [Fact]
        public void CheckedUserDefinedOperators_04()
        {
            const string source0 =
@"
class A 
{
    public static A operator -(A c) => default;
    public static A operator checked -(A c) => default;
 
    public static A operator +(A c, A d) => default;
    public static A operator checked +(A c, A d) => default;
 
    public static explicit operator int(A c) => default;
    public static explicit operator checked int(A c) => default;
}
";
 
            const string source1 =
@"
class A 
{
    public static int operator -(A c) => default;
    public static int operator checked -(A c) => default;
 
    public static int operator +(A c, A d) => default;
    public static int operator checked +(A c, A d) => default;
 
    public static explicit operator long(A c) => default;
    public static explicit operator checked long(A c) => default;
}
";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var members1 = compilation1.GetMember<NamedTypeSymbol>("A").GetMembers().OfType<MethodSymbol>().Where(m => m.MethodKind is (MethodKind.Conversion or MethodKind.UserDefinedOperator)).ToArray();
            Assert.Equal(6, members1.Length);
 
            foreach (var member in members1)
            {
                Assert.Null(matcher.MapDefinition(member.GetCciAdapter()));
            }
        }
 
        [Fact]
        public void CheckedUserDefinedOperators_05()
        {
            const string source0 =
@"
class A 
{
    public static A operator -(A c) => default;
    public static A operator checked -(A c) => default;
 
    public static A operator +(A c, A d) => default;
    public static A operator checked +(A c, A d) => default;
 
    public static explicit operator int(A c) => default;
    public static explicit operator checked int(A c) => default;
}
";
 
            const string source1 =
@"
class A 
{
    public static A operator -(int c) => default;
    public static A operator checked -(int c) => default;
 
    public static A operator +(A c, int d) => default;
    public static A operator checked +(A c, int d) => default;
 
    public static explicit operator int(int c) => default;
    public static explicit operator checked int(int c) => default;
}
";
            var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
            var compilation1 = compilation0.WithSource(source1);
 
            var matcher = CreateMatcher(compilation1, compilation0);
            var members1 = compilation1.GetMember<NamedTypeSymbol>("A").GetMembers().OfType<MethodSymbol>().Where(m => m.MethodKind is (MethodKind.Conversion or MethodKind.UserDefinedOperator)).ToArray();
            Assert.Equal(6, members1.Length);
 
            foreach (var member in members1)
            {
                Assert.Null(matcher.MapDefinition(member.GetCciAdapter()));
            }
        }
    }
}