﻿// 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.

using Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Debugger.Evaluation;
using Roslyn.Test.Utilities;
using System;
using System.Collections.Immutable;
using Xunit;

namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests
{
    public class CSharpFunctionResolverTests : FunctionResolverTestBase
    {
        [Fact]
        public void OnLoad()
        {
            var source =
@"class C
{
    static void F(object o) { }
    object F() => null;
}";
            var compilation = CreateCompilation(source);
            var module = new Module(compilation.EmitToArray());
            using (var process = new Process())
            {
                var resolver = Resolver.CSharpResolver;
                var request = new Request(null, MemberSignatureParser.Parse("C.F"));
                resolver.EnableResolution(process, request);
                VerifySignatures(request);
                process.AddModule(module);
                resolver.OnModuleLoad(process, module);
                VerifySignatures(request, "C.F(System.Object)", "C.F()");
            }
        }

        /// <summary>
        /// ShouldEnableFunctionResolver should not be called
        /// until the first module is available for binding.
        /// </summary>
        [Fact]
        public void ShouldEnableFunctionResolver()
        {
            var sourceA =
@"class A
{
    static void F() { }
    static void G() { }
}";
            var sourceB =
@"class B
{
    static void F() { }
    static void G() { }
}";
            var bytesA = CreateCompilation(sourceA).EmitToArray();
            var bytesB = CreateCompilation(sourceB).EmitToArray();
            var resolver = Resolver.CSharpResolver;

            // Two modules loaded before two global requests,
            // ... resolver enabled.
            var moduleA = new Module(bytesA, name: "A.dll");
            var moduleB = new Module(bytesB, name: "B.dll");
            using (var process = new Process(shouldEnable: true, modules: new[] { moduleA, moduleB }))
            {
                var requestF = new Request(null, MemberSignatureParser.Parse("F"));
                var requestG = new Request(null, MemberSignatureParser.Parse("G"));
                Assert.Equal(0, process.ShouldEnableRequests);
                resolver.EnableResolution(process, requestF);
                Assert.Equal(1, process.ShouldEnableRequests);
                resolver.EnableResolution(process, requestG);
                Assert.Equal(2, process.ShouldEnableRequests);
                VerifySignatures(requestF, "A.F()", "B.F()");
                VerifySignatures(requestG, "A.G()", "B.G()");
            }

            // ... resolver disabled.
            moduleA = new Module(bytesA, name: "A.dll");
            moduleB = new Module(bytesB, name: "B.dll");
            using (var process = new Process(shouldEnable: false, modules: new[] { moduleA, moduleB }))
            {
                var requestF = new Request(null, MemberSignatureParser.Parse("F"));
                var requestG = new Request(null, MemberSignatureParser.Parse("G"));
                Assert.Equal(0, process.ShouldEnableRequests);
                resolver.EnableResolution(process, requestF);
                Assert.Equal(1, process.ShouldEnableRequests);
                resolver.EnableResolution(process, requestG);
                Assert.Equal(2, process.ShouldEnableRequests);
                VerifySignatures(requestF);
                VerifySignatures(requestG);
            }

            // Two modules loaded before two requests for same module,
            // ... resolver enabled.
            moduleA = new Module(bytesA, name: "A.dll");
            moduleB = new Module(bytesB, name: "B.dll");
            using (var process = new Process(shouldEnable: true, modules: new[] { moduleA, moduleB }))
            {
                var requestF = new Request("B.dll", MemberSignatureParser.Parse("F"));
                var requestG = new Request("B.dll", MemberSignatureParser.Parse("G"));
                Assert.Equal(0, process.ShouldEnableRequests);
                resolver.EnableResolution(process, requestF);
                Assert.Equal(1, process.ShouldEnableRequests);
                resolver.EnableResolution(process, requestG);
                Assert.Equal(2, process.ShouldEnableRequests);
                VerifySignatures(requestF, "B.F()");
                VerifySignatures(requestG, "B.G()");
            }

            // ... resolver disabled.
            moduleA = new Module(bytesA, name: "A.dll");
            moduleB = new Module(bytesB, name: "B.dll");
            using (var process = new Process(shouldEnable: false, modules: new[] { moduleA, moduleB }))
            {
                var requestF = new Request("B.dll", MemberSignatureParser.Parse("F"));
                var requestG = new Request("B.dll", MemberSignatureParser.Parse("G"));
                Assert.Equal(0, process.ShouldEnableRequests);
                resolver.EnableResolution(process, requestF);
                Assert.Equal(1, process.ShouldEnableRequests);
                resolver.EnableResolution(process, requestG);
                Assert.Equal(2, process.ShouldEnableRequests);
                VerifySignatures(requestF);
                VerifySignatures(requestG);
            }

            // Two modules loaded after two global requests,
            // ... resolver enabled.
            moduleA = new Module(bytesA, name: "A.dll");
            moduleB = new Module(bytesB, name: "B.dll");
            using (var process = new Process(shouldEnable: true))
            {
                var requestF = new Request(null, MemberSignatureParser.Parse("F"));
                var requestG = new Request(null, MemberSignatureParser.Parse("G"));
                resolver.EnableResolution(process, requestF);
                resolver.EnableResolution(process, requestG);
                Assert.Equal(0, process.ShouldEnableRequests);
                process.AddModule(moduleA);
                resolver.OnModuleLoad(process, moduleA);
                Assert.Equal(1, process.ShouldEnableRequests);
                VerifySignatures(requestF, "A.F()");
                VerifySignatures(requestG, "A.G()");
                process.AddModule(moduleB);
                resolver.OnModuleLoad(process, moduleB);
                Assert.Equal(2, process.ShouldEnableRequests);
                VerifySignatures(requestF, "A.F()", "B.F()");
                VerifySignatures(requestG, "A.G()", "B.G()");
            }

            // ... resolver enabled.
            moduleA = new Module(bytesA, name: "A.dll");
            moduleB = new Module(bytesB, name: "B.dll");
            using (var process = new Process(shouldEnable: false))
            {
                var requestF = new Request(null, MemberSignatureParser.Parse("F"));
                var requestG = new Request(null, MemberSignatureParser.Parse("G"));
                resolver.EnableResolution(process, requestF);
                resolver.EnableResolution(process, requestG);
                Assert.Equal(0, process.ShouldEnableRequests);
                process.AddModule(moduleA);
                resolver.OnModuleLoad(process, moduleA);
                Assert.Equal(1, process.ShouldEnableRequests);
                VerifySignatures(requestF);
                VerifySignatures(requestG);
                process.AddModule(moduleB);
                resolver.OnModuleLoad(process, moduleB);
                Assert.Equal(2, process.ShouldEnableRequests);
                VerifySignatures(requestF);
                VerifySignatures(requestG);
            }

            // Two modules after two requests for same module,
            // ... resolver enabled.
            moduleA = new Module(bytesA, name: "A.dll");
            moduleB = new Module(bytesB, name: "B.dll");
            using (var process = new Process(shouldEnable: true))
            {
                var requestF = new Request("A.dll", MemberSignatureParser.Parse("F"));
                var requestG = new Request("A.dll", MemberSignatureParser.Parse("G"));
                resolver.EnableResolution(process, requestF);
                resolver.EnableResolution(process, requestG);
                Assert.Equal(0, process.ShouldEnableRequests);
                process.AddModule(moduleA);
                resolver.OnModuleLoad(process, moduleA);
                Assert.Equal(1, process.ShouldEnableRequests);
                VerifySignatures(requestF, "A.F()");
                VerifySignatures(requestG, "A.G()");
                process.AddModule(moduleB);
                resolver.OnModuleLoad(process, moduleB);
                Assert.Equal(2, process.ShouldEnableRequests);
                VerifySignatures(requestF, "A.F()");
                VerifySignatures(requestG, "A.G()");
            }

            // ... resolver enabled.
            moduleA = new Module(bytesA, name: "A.dll");
            moduleB = new Module(bytesB, name: "B.dll");
            using (var process = new Process(shouldEnable: false))
            {
                var requestF = new Request("A.dll", MemberSignatureParser.Parse("F"));
                var requestG = new Request("A.dll", MemberSignatureParser.Parse("G"));
                resolver.EnableResolution(process, requestF);
                resolver.EnableResolution(process, requestG);
                Assert.Equal(0, process.ShouldEnableRequests);
                process.AddModule(moduleA);
                resolver.OnModuleLoad(process, moduleA);
                Assert.Equal(1, process.ShouldEnableRequests);
                VerifySignatures(requestF);
                VerifySignatures(requestG);
                process.AddModule(moduleB);
                resolver.OnModuleLoad(process, moduleB);
                Assert.Equal(2, process.ShouldEnableRequests);
                VerifySignatures(requestF);
                VerifySignatures(requestG);
            }
        }

        /// <summary>
        /// Should only handle requests with expected language id or
        /// default language id or causality breakpoints.
        /// </summary>
        [WorkItem(15119, "https://github.com/dotnet/roslyn/issues/15119")]
        [Fact]
        public void LanguageId()
        {
            var source =
@"class C
{
    static void F() { }
}";
            var bytes = CreateCompilation(source).EmitToArray();
            var resolver = Resolver.CSharpResolver;
            var unknownId = Guid.Parse("F02FB87B-64EC-486E-B039-D4A97F48858C");
            var csharpLanguageId = Guid.Parse("3f5162f8-07c6-11d3-9053-00c04fa302a1");
            var vbLanguageId = Guid.Parse("3a12d0b8-c26c-11d0-b442-00a0244a1dd2");
            var cppLanguageId = Guid.Parse("3a12d0b7-c26c-11d0-b442-00a0244a1dd2");

            // Module loaded before requests.
            var module = new Module(bytes);
            using (var process = new Process(module))
            {
                var requestDefaultId = new Request(null, MemberSignatureParser.Parse("F"), Guid.Empty);
                var requestUnknown = new Request(null, MemberSignatureParser.Parse("F"), unknownId);
                var requestCausalityBreakpoint = new Request(null, MemberSignatureParser.Parse("F"), DkmLanguageId.CausalityBreakpoint);
                var requestMethodId = new Request(null, MemberSignatureParser.Parse("F"), DkmLanguageId.MethodId);
                var requestCSharp = new Request(null, MemberSignatureParser.Parse("F"), csharpLanguageId);
                var requestVB = new Request(null, MemberSignatureParser.Parse("F"), vbLanguageId);
                var requestCPP = new Request(null, MemberSignatureParser.Parse("F"), cppLanguageId);
                resolver.EnableResolution(process, requestDefaultId);
                VerifySignatures(requestDefaultId, "C.F()");
                resolver.EnableResolution(process, requestUnknown);
                VerifySignatures(requestUnknown);
                resolver.EnableResolution(process, requestCausalityBreakpoint);
                VerifySignatures(requestCausalityBreakpoint, "C.F()");
                resolver.EnableResolution(process, requestMethodId);
                VerifySignatures(requestMethodId);
                resolver.EnableResolution(process, requestCSharp);
                VerifySignatures(requestCSharp, "C.F()");
                resolver.EnableResolution(process, requestVB);
                VerifySignatures(requestVB);
                resolver.EnableResolution(process, requestCPP);
                VerifySignatures(requestCPP);
            }

            // Module loaded after requests.
            module = new Module(bytes);
            using (var process = new Process())
            {
                var requestDefaultId = new Request(null, MemberSignatureParser.Parse("F"), Guid.Empty);
                var requestUnknown = new Request(null, MemberSignatureParser.Parse("F"), unknownId);
                var requestCausalityBreakpoint = new Request(null, MemberSignatureParser.Parse("F"), DkmLanguageId.CausalityBreakpoint);
                var requestMethodId = new Request(null, MemberSignatureParser.Parse("F"), DkmLanguageId.MethodId);
                var requestCSharp = new Request(null, MemberSignatureParser.Parse("F"), csharpLanguageId);
                var requestVB = new Request(null, MemberSignatureParser.Parse("F"), vbLanguageId);
                var requestCPP = new Request(null, MemberSignatureParser.Parse("F"), cppLanguageId);
                resolver.EnableResolution(process, requestCPP);
                resolver.EnableResolution(process, requestVB);
                resolver.EnableResolution(process, requestCSharp);
                resolver.EnableResolution(process, requestMethodId);
                resolver.EnableResolution(process, requestCausalityBreakpoint);
                resolver.EnableResolution(process, requestUnknown);
                resolver.EnableResolution(process, requestDefaultId);
                process.AddModule(module);
                resolver.OnModuleLoad(process, module);
                VerifySignatures(requestDefaultId, "C.F()");
                VerifySignatures(requestUnknown);
                VerifySignatures(requestCausalityBreakpoint, "C.F()");
                VerifySignatures(requestMethodId);
                VerifySignatures(requestCSharp, "C.F()");
                VerifySignatures(requestVB);
                VerifySignatures(requestCPP);
            }
        }

        [Fact]
        public void MissingMetadata()
        {
            var sourceA =
@"class A
{
    static void F1() { }
    static void F2() { }
    static void F3() { }
    static void F4() { }
}";
            var sourceC =
@"class C
{
    static void F1() { }
    static void F2() { }
    static void F3() { }
    static void F4() { }
}";
            var moduleA = new Module(CreateCompilation(sourceA).EmitToArray(), name: "A.dll");
            var moduleB = new Module(default(ImmutableArray<byte>), name: "B.dll");
            var moduleC = new Module(CreateCompilation(sourceC).EmitToArray(), name: "C.dll");

            using (var process = new Process())
            {
                var resolver = Resolver.CSharpResolver;
                var requestAll = new Request(null, MemberSignatureParser.Parse("F1"));
                var requestA = new Request("A.dll", MemberSignatureParser.Parse("F2"));
                var requestB = new Request("B.dll", MemberSignatureParser.Parse("F3"));
                var requestC = new Request("C.dll", MemberSignatureParser.Parse("F4"));

                // Request to all modules.
                resolver.EnableResolution(process, requestAll);
                Assert.Equal(0, moduleA.GetMetadataCount);
                Assert.Equal(0, moduleB.GetMetadataCount);
                Assert.Equal(0, moduleC.GetMetadataCount);

                // Load module A (available).
                process.AddModule(moduleA);
                resolver.OnModuleLoad(process, moduleA);
                Assert.Equal(1, moduleA.GetMetadataCount);
                Assert.Equal(0, moduleB.GetMetadataCount);
                Assert.Equal(0, moduleC.GetMetadataCount);
                VerifySignatures(requestAll, "A.F1()");
                VerifySignatures(requestA);
                VerifySignatures(requestB);
                VerifySignatures(requestC);

                // Load module B (missing).
                process.AddModule(moduleB);
                resolver.OnModuleLoad(process, moduleB);
                Assert.Equal(1, moduleA.GetMetadataCount);
                Assert.Equal(1, moduleB.GetMetadataCount);
                Assert.Equal(0, moduleC.GetMetadataCount);
                VerifySignatures(requestAll, "A.F1()");
                VerifySignatures(requestA);
                VerifySignatures(requestB);
                VerifySignatures(requestC);

                // Load module C (available).
                process.AddModule(moduleC);
                resolver.OnModuleLoad(process, moduleC);
                Assert.Equal(1, moduleA.GetMetadataCount);
                Assert.Equal(1, moduleB.GetMetadataCount);
                Assert.Equal(1, moduleC.GetMetadataCount);
                VerifySignatures(requestAll, "A.F1()", "C.F1()");
                VerifySignatures(requestA);
                VerifySignatures(requestB);
                VerifySignatures(requestC);

                // Request to module A (available).
                resolver.EnableResolution(process, requestA);
                Assert.Equal(2, moduleA.GetMetadataCount);
                Assert.Equal(1, moduleB.GetMetadataCount);
                Assert.Equal(1, moduleC.GetMetadataCount);
                VerifySignatures(requestAll, "A.F1()", "C.F1()");
                VerifySignatures(requestA, "A.F2()");
                VerifySignatures(requestB);
                VerifySignatures(requestC);

                // Request to module B (missing).
                resolver.EnableResolution(process, requestB);
                Assert.Equal(2, moduleA.GetMetadataCount);
                Assert.Equal(2, moduleB.GetMetadataCount);
                Assert.Equal(1, moduleC.GetMetadataCount);
                VerifySignatures(requestAll, "A.F1()", "C.F1()");
                VerifySignatures(requestA, "A.F2()");
                VerifySignatures(requestB);
                VerifySignatures(requestC);

                // Request to module C (available).
                resolver.EnableResolution(process, requestC);
                Assert.Equal(2, moduleA.GetMetadataCount);
                Assert.Equal(2, moduleB.GetMetadataCount);
                Assert.Equal(2, moduleC.GetMetadataCount);
                VerifySignatures(requestAll, "A.F1()", "C.F1()");
                VerifySignatures(requestA, "A.F2()");
                VerifySignatures(requestB);
                VerifySignatures(requestC, "C.F4()");
            }
        }

        [Fact]
        public void ModuleName()
        {
            var sourceA =
@"public struct A
{
    static void F() { }
}";
            var nameA = GetUniqueName();
            var compilationA = CreateCompilation(sourceA, assemblyName: nameA);
            var imageA = compilationA.EmitToArray();
            var refA = AssemblyMetadata.CreateFromImage(imageA).GetReference();
            var sourceB =
@"class B
{
    static void F(A a) { }
    static void Main() { }
}";
            var nameB = GetUniqueName();
            var compilationB = CreateCompilation(sourceB, assemblyName: nameB, options: TestOptions.DebugExe, references: new[] { refA });
            var imageB = compilationB.EmitToArray();
            using (var process = new Process(new Module(imageA, nameA + ".dll"), new Module(imageB, nameB + ".exe")))
            {
                var signature = MemberSignatureParser.Parse("F");
                var resolver = Resolver.CSharpResolver;

                // No module name.
                var request = new Request("", signature);
                resolver.EnableResolution(process, request);
                VerifySignatures(request, "A.F()", "B.F(A)");

                // DLL module name, uppercase.
                request = new Request(nameA.ToUpper() + ".DLL", signature);
                resolver.EnableResolution(process, request);
                VerifySignatures(request, "A.F()");

                // EXE module name.
                request = new Request(nameB + ".EXE", signature);
                resolver.EnableResolution(process, request);
                VerifySignatures(request, "B.F(A)");

                // EXE module name, lowercase.
                request = new Request(nameB.ToLower() + ".exe", signature);
                resolver.EnableResolution(process, request);
                VerifySignatures(request, "B.F(A)");

                // EXE module name, no extension.
                request = new Request(nameB, signature);
                resolver.EnableResolution(process, request);
                VerifySignatures(request);
            }
        }

        [Fact]
        public void Arrays()
        {
            var source =
@"class A
{
}
class B
{
    static void F(A o) { }
    static void F(A[] o) { }
    static void F(A[,,] o) { }
    static void F(A[,,][] o) { }
    static void F(A[][,,] o) { }
    static void F(A[][][] o) { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "B.F(A)", "B.F(A[])", "B.F(A[,,])", "B.F(A[][,,])", "B.F(A[,,][])", "B.F(A[][][])");
                Resolve(process, resolver, "F(A)", "B.F(A)");
                Resolve(process, resolver, "F(A[])", "B.F(A[])");
                Resolve(process, resolver, "F(A[][])");
                Resolve(process, resolver, "F(A[,])");
                Resolve(process, resolver, "F(A[,,])", "B.F(A[,,])");
                Resolve(process, resolver, "F(A[,,][])", "B.F(A[,,][])");
                Resolve(process, resolver, "F(A[][,,])", "B.F(A[][,,])");
                Resolve(process, resolver, "F(A[,][,,])");
                Resolve(process, resolver, "F(A[][][])", "B.F(A[][][])");
                Resolve(process, resolver, "F(A[][][][])");
            }
        }

        [Fact]
        public void Pointers()
        {
            var source =
@"class C
{
    static unsafe void F(int*[] p) { }
    static unsafe void F(int** q) { }
}";
            var compilation = CreateCompilation(source, options: TestOptions.UnsafeDebugDll);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "C.F(System.Int32*[])", "C.F(System.Int32**)");
                Resolve(process, resolver, "F(int)");
                Resolve(process, resolver, "F(int*)");
                Resolve(process, resolver, "F(int[])");
                Resolve(process, resolver, "F(int*[])", "C.F(System.Int32*[])");
                Resolve(process, resolver, "F(int**)", "C.F(System.Int32**)");
                Resolve(process, resolver, "F(Int32**)", "C.F(System.Int32**)");
                Resolve(process, resolver, "F(C<int*>)");
                Resolve(process, resolver, "F(C<int>*)");
            }
        }

        [Fact]
        public void Nullable()
        {
            var source =
@"struct S
{
    void F(S? o) { }
    static void F(int?[] o) { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "S.F(System.Nullable<S>)", "S.F(System.Nullable<System.Int32>[])");
                Resolve(process, resolver, "F(S)");
                Resolve(process, resolver, "F(S?)", "S.F(System.Nullable<S>)");
                Resolve(process, resolver, "F(S??)");
                Resolve(process, resolver, "F(int?[])", "S.F(System.Nullable<System.Int32>[])");
            }
        }

        [Fact]
        public void ByRef()
        {
            var source =
@"class @ref { }
class @out { }
class C
{
    static void F(@out a, @ref b) { }
    static void F(ref @out a, out @ref b) { b = null; }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "C.F(out, ref)", "C.F(ref out, ref ref)");
                Assert.Null(MemberSignatureParser.Parse("F(ref, out)"));
                Assert.Null(MemberSignatureParser.Parse("F(ref ref, out out)"));
                Resolve(process, resolver, "F(@out, @ref)", "C.F(out, ref)");
                Resolve(process, resolver, "F(@out, out @ref)");
                Resolve(process, resolver, "F(ref @out, @ref)");
                Resolve(process, resolver, "F(ref @out, out @ref)", "C.F(ref out, ref ref)");
                Resolve(process, resolver, "F(out @out, ref @ref)", "C.F(ref out, ref ref)");
            }
        }

        [Fact]
        public void Methods()
        {
            var source =
@"abstract class A
{
    abstract internal object F();
}
class B
{
    object F() => null;
}
class C
{
    static object F() => null;
}
interface I
{
    object F();
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "B.F()", "C.F()");
                Resolve(process, resolver, "A.F");
                Resolve(process, resolver, "B.F", "B.F()");
                Resolve(process, resolver, "B.F()", "B.F()");
                Resolve(process, resolver, "B.F(object)");
                Resolve(process, resolver, "B.F<T>");
                Resolve(process, resolver, "C.F", "C.F()");
            }
        }

        [Fact]
        public void Properties()
        {
            var source =
@"abstract class A
{
    abstract internal object P { get; set; }
}
class B
{
    object P { get; set; }
}
class C
{
    static object P { get; }
}
class D
{
    int P { set { } }
}
interface I
{
    object P { get; set; }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "P", "B.get_P()", "B.set_P(System.Object)", "C.get_P()", "D.set_P(System.Int32)");
                Resolve(process, resolver, "A.P");
                Resolve(process, resolver, "B.P", "B.get_P()", "B.set_P(System.Object)");
                Resolve(process, resolver, "B.P()");
                Resolve(process, resolver, "B.P(object)");
                Resolve(process, resolver, "B.P<T>");
                Resolve(process, resolver, "C.P", "C.get_P()");
                Resolve(process, resolver, "C.P()");
                Resolve(process, resolver, "D.P", "D.set_P(System.Int32)");
                Resolve(process, resolver, "D.P()");
                Resolve(process, resolver, "D.P(object)");
                Resolve(process, resolver, "get_P", "B.get_P()", "C.get_P()");
                Resolve(process, resolver, "set_P", "B.set_P(System.Object)", "D.set_P(System.Int32)");
                Resolve(process, resolver, "B.get_P()", "B.get_P()");
                Resolve(process, resolver, "B.set_P", "B.set_P(System.Object)");
            }
        }

        [Fact]
        public void Constructors()
        {
            var source =
@"class A
{
    static A() { }
    A() { }
    A(object o) { }
}
class B
{
}
class C<T>
{
}
class D
{
    static object A => null;
    static void B<T>() { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "A", "A..ctor()", "A..ctor(System.Object)", "D.get_A()");
                Resolve(process, resolver, "A.A", "A..ctor()", "A..ctor(System.Object)");
                Resolve(process, resolver, "B", "B..ctor()");
                Resolve(process, resolver, "B<T>", "D.B<T>()");
                Resolve(process, resolver, "C", "C<T>..ctor()");
                Resolve(process, resolver, "C<T>");
                Resolve(process, resolver, "C<T>.C", "C<T>..ctor()");
                Assert.Null(MemberSignatureParser.Parse(".ctor"));
                Assert.Null(MemberSignatureParser.Parse("A..ctor"));
            }
        }

        [Fact]
        public void GenericMethods()
        {
            var source =
@"class A
{
    static void F() { }
    void F<T, U>() { }
    static void F<T>() { }
}
class A<T>
{
    static void F() { }
    void F<U, V>() { }
}
class B : A<int>
{
    static void F<T>() { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                // Note, Dev14 matches type ignoring type parameters. For instance,
                // "A<T>.F" will bind to A.F() and A<T>.F(), and "A.F" will bind to A.F()
                // and A<T>.F. However, Dev14 does expect method type parameters to
                // match. Here, we expect both type and method parameters to match.
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "A.F", "A.F()");
                Resolve(process, resolver, "A.F<U>()", "A.F<T>()");
                Resolve(process, resolver, "A.F<T>", "A.F<T>()");
                Resolve(process, resolver, "A.F<T, T>", "A.F<T, U>()");
                Assert.Null(MemberSignatureParser.Parse("A.F<>()"));
                Assert.Null(MemberSignatureParser.Parse("A.F<,>()"));
                Resolve(process, resolver, "A<T>.F", "A<T>.F()");
                Resolve(process, resolver, "A<_>.F<_>");
                Resolve(process, resolver, "A<_>.F<_, _>", "A<T>.F<U, V>()");
                Resolve(process, resolver, "B.F()");
                Resolve(process, resolver, "B.F<T>()", "B.F<T>()");
                Resolve(process, resolver, "B.F<T, U>()");
            }
        }

        [Fact]
        public void Namespaces()
        {
            var source =
@"namespace N
{
    namespace M
    {
        class A<T>
        {
            static void F() { }
        }
    }
}
namespace N
{
    class B
    {
        static void F() { }
    }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "N.B.F()", "N.M.A<T>.F()");
                Resolve(process, resolver, "A<T>.F", "N.M.A<T>.F()");
                Resolve(process, resolver, "N.A<T>.F");
                Resolve(process, resolver, "M.A<T>.F", "N.M.A<T>.F()");
                Resolve(process, resolver, "N.M.A<T>.F", "N.M.A<T>.F()");
                Resolve(process, resolver, "N.B.F", "N.B.F()");
            }
        }

        [Fact]
        public void NestedTypes()
        {
            var source =
@"class A
{
    class B
    {
        static void F() { }
    }
    class B<T>
    {
        static void F<U>() { }
    }
}
class B
{
    static void F() { }
}
namespace N
{
    class A<T>
    {
        class B
        {
            static void F<U>() { }
        }
        class B<U>
        {
            static void F() { }
        }
    }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                // See comment in GenericMethods regarding differences with Dev14.
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "B.F()", "A.B.F()", "N.A<T>.B<U>.F()");
                Resolve(process, resolver, "F<T>", "A.B<T>.F<U>()", "N.A<T>.B.F<U>()");
                Resolve(process, resolver, "A.F");
                Resolve(process, resolver, "A.B.F", "A.B.F()");
                Resolve(process, resolver, "A.B.F<T>");
                Resolve(process, resolver, "A.B<T>.F<U>", "A.B<T>.F<U>()");
                Resolve(process, resolver, "A<T>.B.F<U>", "N.A<T>.B.F<U>()");
                Resolve(process, resolver, "A<T>.B<U>.F", "N.A<T>.B<U>.F()");
                Resolve(process, resolver, "B.F", "B.F()", "A.B.F()");
                Resolve(process, resolver, "B.F<T>", "N.A<T>.B.F<U>()");
                Resolve(process, resolver, "B<T>.F", "N.A<T>.B<U>.F()");
                Resolve(process, resolver, "B<T>.F<U>", "A.B<T>.F<U>()");
                Assert.Null(MemberSignatureParser.Parse("A+B.F"));
                Assert.Null(MemberSignatureParser.Parse("A.B`1.F<T>"));
            }
        }

        [Fact]
        public void NamespacesAndTypes()
        {
            var source =
@"namespace A.B
{
    class T { }
    class C
    {
        static void F(C c) { }
        static void F(A.B<T>.C c) { }
    }
}
namespace A
{
    class B<T>
    {
        internal class C
        {
            static void F(C c) { }
            static void F(A.B.C c) { }
        }
    }
}
class A<T>
{
    internal class B
    {
        internal class C
        {
            static void F(C c) { }
            static void F(A.B<T>.C c) { }
        }
    }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "A.B.C.F(A.B.C)", "A.B.C.F(A.B<A.B.T>.C)", "A.B<T>.C.F(A.B<T>.C)", "A.B<T>.C.F(A.B.C)", "A<T>.B.C.F(A<T>.B.C)", "A<T>.B.C.F(A.B<T>.C)");
                Resolve(process, resolver, "F(C)", "A.B.C.F(A.B.C)", "A.B.C.F(A.B<A.B.T>.C)", "A.B<T>.C.F(A.B.C)");
                Resolve(process, resolver, "F(B.C)", "A.B.C.F(A.B.C)", "A.B<T>.C.F(A.B.C)");
                Resolve(process, resolver, "F(B<T>.C)", "A.B.C.F(A.B<A.B.T>.C)");
                Resolve(process, resolver, "A.B.C.F", "A.B.C.F(A.B.C)", "A.B.C.F(A.B<A.B.T>.C)");
                Resolve(process, resolver, "A<T>.B.C.F", "A<T>.B.C.F(A<T>.B.C)", "A<T>.B.C.F(A.B<T>.C)");
                Resolve(process, resolver, "A.B<T>.C.F", "A.B<T>.C.F(A.B<T>.C)", "A.B<T>.C.F(A.B.C)");
                Resolve(process, resolver, "B<T>.C.F(B<T>.C)", "A.B<T>.C.F(A.B<T>.C)");
            }
        }

        [Fact]
        public void NamespacesAndTypes_More()
        {
            var source =
@"namespace A1.B
{
    class C
    {
        static void F(C c) { }
    }
}
namespace A2
{
    class B
    {
        internal class C
        {
            static void F(C c) { }
            static void F(A1.B.C c) { }
        }
    }
}
class A3
{
    internal class B
    {
        internal class C
        {
            static void F(C c) { }
            static void F(A2.B.C c) { }
        }
    }
}
namespace B
{
    class C
    {
        static void F(C c) { }
        static void F(A3.B.C c) { }
    }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F(C)", "B.C.F(B.C)", "B.C.F(A3.B.C)", "A1.B.C.F(A1.B.C)", "A2.B.C.F(A2.B.C)", "A2.B.C.F(A1.B.C)", "A3.B.C.F(A3.B.C)", "A3.B.C.F(A2.B.C)");
                Resolve(process, resolver, "B.C.F(B.C)", "B.C.F(B.C)", "B.C.F(A3.B.C)", "A1.B.C.F(A1.B.C)", "A2.B.C.F(A2.B.C)", "A2.B.C.F(A1.B.C)", "A3.B.C.F(A3.B.C)", "A3.B.C.F(A2.B.C)");
                Resolve(process, resolver, "B.C.F(A1.B.C)", "A1.B.C.F(A1.B.C)", "A2.B.C.F(A1.B.C)");
                Resolve(process, resolver, "B.C.F(A2.B.C)", "A2.B.C.F(A2.B.C)", "A3.B.C.F(A2.B.C)");
                Resolve(process, resolver, "B.C.F(A3.B.C)", "B.C.F(A3.B.C)", "A3.B.C.F(A3.B.C)");
            }
        }

        [Fact]
        public void TypeParameters()
        {
            var source =
@"class A
{
    class B<T>
    {
        static void F<U>(B<T> t) { }
        static void F<U>(B<U> u) { }
    }
}
class A<T>
{
    class B<U>
    {
        static void F<V>(T t) { }
        static void F<V>(A<U> u) { }
        static void F<V>(B<V> v) { }
    }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F<T>", "A.B<T>.F<U>(A.B<T>)", "A.B<T>.F<U>(A.B<U>)", "A<T>.B<U>.F<V>(T)", "A<T>.B<U>.F<V>(A<U>)", "A<T>.B<U>.F<V>(A<T>.B<V>)");
                Resolve(process, resolver, "B<T>.F<U>", "A.B<T>.F<U>(A.B<T>)", "A.B<T>.F<U>(A.B<U>)", "A<T>.B<U>.F<V>(T)", "A<T>.B<U>.F<V>(A<U>)", "A<T>.B<U>.F<V>(A<T>.B<V>)");
                Resolve(process, resolver, "F<T>(B<T>)", "A.B<T>.F<U>(A.B<U>)");
                Resolve(process, resolver, "F<U>(B<T>)"); // No T in signature to bind to.
                Resolve(process, resolver, "F<T>(B<U>)"); // No U in signature to bind to.
                Resolve(process, resolver, "B<X>.F<Y>(B<X>)", "A.B<T>.F<U>(A.B<T>)");
                Resolve(process, resolver, "B<X>.F<Y>(B<Y>)", "A.B<T>.F<U>(A.B<U>)");
                Resolve(process, resolver, "B<U>.F<V>(T)"); // No T in signature to bind to.
                Resolve(process, resolver, "B<U>.F<V>(A<U>)", "A<T>.B<U>.F<V>(A<U>)");
                Resolve(process, resolver, "B<U>.F<V>(B<V>)", "A.B<T>.F<U>(A.B<U>)");
                Resolve(process, resolver, "B<V>.F<U>(B<U>)", "A.B<T>.F<U>(A.B<U>)");
                Resolve(process, resolver, "A<X>.B<Y>.F<Z>(X)", "A<T>.B<U>.F<V>(T)");
                Resolve(process, resolver, "A<X>.B<Y>.F<Z>(B<Z>)", "A<T>.B<U>.F<V>(A<T>.B<V>)");
            }
        }

        [Fact]
        public void DifferentCase_MethodsAndProperties()
        {
            var source =
@"class A
{
    static void method() { }
    static void Method(object o) { }
    object property => null;
}
class B
{
    static void Method() { }
    object Property { get; set; }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "method", "A.method()");
                Resolve(process, resolver, "Method", "A.Method(System.Object)", "B.Method()");
                Resolve(process, resolver, "property", "A.get_property()");
                Resolve(process, resolver, "Property", "B.get_Property()", "B.set_Property(System.Object)");
                Resolve(process, resolver, "PROPERTY");
                Resolve(process, resolver, "get_property", "A.get_property()");
                Resolve(process, resolver, "GET_PROPERTY");
            }
        }

        [Fact]
        public void DifferentCase_NamespacesAndTypes()
        {
            var source =
@"namespace one.two
{
    class THREE
    {
        static void Method(THREE t) { }
    }
}
namespace One.Two
{
    class Three
    {
        static void Method(Three t) { }
    }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "Method", "One.Two.Three.Method(One.Two.Three)", "one.two.THREE.Method(one.two.THREE)");
                Resolve(process, resolver, "Three.Method", "One.Two.Three.Method(One.Two.Three)");
                Resolve(process, resolver, "three.Method");
                Resolve(process, resolver, "Method(three)");
                Resolve(process, resolver, "THREE.Method(THREE)", "one.two.THREE.Method(one.two.THREE)");
                Resolve(process, resolver, "One.Two.Three.Method", "One.Two.Three.Method(One.Two.Three)");
                Resolve(process, resolver, "ONE.TWO.THREE.Method");
                Resolve(process, resolver, "Method(One.Two.Three)", "One.Two.Three.Method(One.Two.Three)");
                Resolve(process, resolver, "Method(one.two.THREE)", "one.two.THREE.Method(one.two.THREE)");
                Resolve(process, resolver, "Method(one.two.Three)");
                Resolve(process, resolver, "THREE", "one.two.THREE..ctor()");
            }
        }

        [Fact]
        public void TypeReferences()
        {
            var sourceA =
@"public class A<T>
{
    public class B<U>
    {
        static void F<V, W>() { }
    }
}
namespace N
{
    public class C<T>
    {
    }
}";
            var compilationA = CreateCompilation(sourceA);
            var bytesA = compilationA.EmitToArray();
            var refA = AssemblyMetadata.CreateFromImage(bytesA).GetReference();
            var sourceB =
@"class D<T>
{
    static void F<U, V>(N.C<A<U>.B<V>[]> b) { }
}";
            var compilationB = CreateCompilation(sourceB, references: new[] { refA });
            var bytesB = compilationB.EmitToArray();
            using (var process = new Process(new Module(bytesA), new Module(bytesB)))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F<T, U>", "A<T>.B<U>.F<V, W>()", "D<T>.F<U, V>(N.C<A<U>.B<V>[]>)");
                Resolve(process, resolver, "F<T, U>(C)"); // No type argument for C<>
                Resolve(process, resolver, "F<T, U>(C<T>)"); // Incorrect type argument for C<>
                Resolve(process, resolver, "F<T, U>(C<B<U>>)"); // No array qualifier
                Resolve(process, resolver, "F<T, U>(C<B<T>[]>)"); // Incorrect type argument for B<>
                Resolve(process, resolver, "F<T, U>(C<B<U>[]>)", "D<T>.F<U, V>(N.C<A<U>.B<V>[]>)");
                Resolve(process, resolver, "F<T, U>(N.C<B<U>[]>)", "D<T>.F<U, V>(N.C<A<U>.B<V>[]>)");
                Resolve(process, resolver, "D<X>.F<Y, Z>", "D<T>.F<U, V>(N.C<A<U>.B<V>[]>)");
                Resolve(process, resolver, "D<X>.F<Y, Z>(C<A<Y>[]>)"); // No nested type B
                Resolve(process, resolver, "D<X>.F<Y, Z>(C<A<Y>.B<Z>[]>)", "D<T>.F<U, V>(N.C<A<U>.B<V>[]>)");
                Resolve(process, resolver, "D<X>.F<Y, Z>(C<A<Y>.B<Y>[]>)"); // Incorrect type argument for B<>.
            }
        }

        [Fact]
        public void Keywords_MethodName()
        {
            var source =
@"namespace @namespace
{
    struct @struct
    {
        object @public => 1;
    }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Assert.Null(MemberSignatureParser.Parse("public"));
                Assert.Null(MemberSignatureParser.Parse("namespace.@struct.@public"));
                Assert.Null(MemberSignatureParser.Parse("@namespace.struct.@public"));
                Assert.Null(MemberSignatureParser.Parse("@namespace.@struct.public"));
                Resolve(process, resolver, "@public", "namespace.struct.get_public()");
                Resolve(process, resolver, "@namespace.@struct.@public", "namespace.struct.get_public()");
            }
        }

        [Fact]
        public void Keywords_MethodTypeParameter()
        {
            var source =
@"class @class<@in>
{
    static void F<@out>(@in i, @out o) { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Assert.Null(MemberSignatureParser.Parse("F<out>"));
                Assert.Null(MemberSignatureParser.Parse("F<in>"));
                Assert.Null(MemberSignatureParser.Parse("class<@in>.F<@out>"));
                Assert.Null(MemberSignatureParser.Parse("@class<in>.F<@out>"));
                Assert.Null(MemberSignatureParser.Parse("@class<@in>.F<out>"));
                Resolve(process, resolver, "F<@out>", "class<in>.F<out>(in, out)");
                Resolve(process, resolver, "F<@in>", "class<in>.F<out>(in, out)");
                Resolve(process, resolver, "@class<@in>.F<@out>", "class<in>.F<out>(in, out)");
                Resolve(process, resolver, "@class<@this>.F<@base>", "class<in>.F<out>(in, out)");
                Resolve(process, resolver, "@class<T>.F<U>", "class<in>.F<out>(in, out)");
            }
        }

        [Fact]
        public void Keywords_ParameterName()
        {
            var source =
@"namespace @namespace
{
    struct @struct
    {
    }
}
class C
{
    static void F(@namespace.@struct s) { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Assert.Null(MemberSignatureParser.Parse("F(struct)"));
                Assert.Null(MemberSignatureParser.Parse("F(namespace.@struct)"));
                Resolve(process, resolver, "F(@struct)", "C.F(namespace.struct)");
                Resolve(process, resolver, "F(@namespace.@struct)", "C.F(namespace.struct)");
            }
        }

        [Fact]
        public void Keywords_ParameterTypeArgument()
        {
            var source =
@"class @this
{
    internal class @base
    {
    }
}
class @class<T>
{
}
class C
{
    static void F(@class<@this.@base> c) { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Assert.Null(MemberSignatureParser.Parse("F(@class<base>)"));
                Assert.Null(MemberSignatureParser.Parse("F(@class<this.@base>)"));
                Assert.Null(MemberSignatureParser.Parse("F(@class<@this.base>)"));
                Resolve(process, resolver, "F(@class<@base>)", "C.F(class<this.base>)");
                Resolve(process, resolver, "F(@class<@this.@base>)", "C.F(class<this.base>)");
            }
        }

        [Fact]
        public void EscapedNames()
        {
            var source =
@"class @object { }
class Object { }
class C
{
    static void F(@object o) { }
    static void F(Object o) { }
    static void F(object o) { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "C.F(object)", "C.F(Object)", "C.F(System.Object)");
                Resolve(process, resolver, "F(object)", "C.F(System.Object)");
                Resolve(process, resolver, "F(Object)", "C.F(Object)", "C.F(System.Object)");
                Resolve(process, resolver, "F(System.Object)", "C.F(System.Object)");
                Resolve(process, resolver, "F(@object)", "C.F(object)");
                Resolve(process, resolver, "F(@Object)", "C.F(Object)", "C.F(System.Object)");
            }
        }

        [Fact]
        public void SpecialTypes()
        {
            var source =
@"class C<T1, T2, T3, T4>
{
}
class C
{
    static void F(bool a, char b, sbyte c, byte d) { }
    static void F(short a, ushort b, int c, uint d) { }
    static void F(C<uint, long, ulong, float> o) { }
    static void F(C<double, string, object, decimal> o) { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F(bool, char, sbyte, byte)", "C.F(System.Boolean, System.Char, System.SByte, System.Byte)");
                Resolve(process, resolver, "F(System.Int16, System.UInt16, System.Int32, System.UInt32)", "C.F(System.Int16, System.UInt16, System.Int32, System.UInt32)");
                Resolve(process, resolver, "F(C<UInt32, Int64, UInt64, Single>)", "C.F(C<System.UInt32, System.Int64, System.UInt64, System.Single>)");
                Resolve(process, resolver, "F(C<double, string, object, decimal>)", "C.F(C<System.Double, System.String, System.Object, System.Decimal>)");
                Resolve(process, resolver, "F(bool, char, sbyte)");
                Resolve(process, resolver, "F(C<double, string, object, decimal, bool>)");
            }
        }

        [Fact]
        public void SpecialTypes_More()
        {
            var source =
@"class C
{
    static void F(System.IntPtr p) { }
    static void F(System.UIntPtr p) { }
    static void F(System.TypedReference r) { }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F(object)");
                Resolve(process, resolver, "F(IntPtr)", "C.F(System.IntPtr)");
                Resolve(process, resolver, "F(UIntPtr)", "C.F(System.UIntPtr)");
                Resolve(process, resolver, "F(TypedReference)", "C.F(System.TypedReference)");
            }
        }

        // Binding to "dynamic" type refs is not supported.
        // This is consistent with Dev14.
        [Fact]
        public void Dynamic()
        {
            var source =
@"class C<T>
{
}
class C
{
    static void F(dynamic d) { }
    static void F(C<dynamic[]> d) { }
}";
            var compilation = CreateCompilation(source, references: new[] { CSharpRef });
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "C.F(System.Object)", "C.F(C<System.Object[]>)");
                Resolve(process, resolver, "F(object)", "C.F(System.Object)");
                Resolve(process, resolver, "F(C<object[]>)", "C.F(C<System.Object[]>)");
                Resolve(process, resolver, "F(dynamic)");
                Resolve(process, resolver, "F(C<dynamic[]>)");
            }
        }

        [Fact]
        public void Iterator()
        {
            var source =
@"class C
{
    static System.Collections.IEnumerable F() { yield break; }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "C.F()");
            }
        }

        [Fact]
        public void Async()
        {
            var source =
@"using System.Threading.Tasks;
class C
{
    static async Task F()
    {
        await Task.Delay(0);
    }
}";
            var compilation = CreateCompilationWithMscorlib46(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "C.F()");
            }
        }

        [Fact(Skip = "global:: not supported")]
        public void Global()
        {
            var source =
@"class C
{
    static void F(N.C o) { }
    static void F(global::C o) { }
}
namespace N
{
    class C
    {
        static void F(N.C o) { }
        static void F(global::C o) { }
    }
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "C.F(C)", "C.F(N.C)", "C.F(C)", "N.C.F(N.C)", "N.C.F(C)");
                Resolve(process, resolver, "C.F(N.C)", "C.F(N.C)", "N.C.F(N.C)");
                Resolve(process, resolver, "global::C.F(C)", "C.F(N.C)", "C.F(C)"); // Dev14 does not bind global::
                Resolve(process, resolver, "C.F(global::C)", "C.F(C)", "N.C.F(C)"); // Dev14 does not bind global::
            }
        }

        // Since MetadataDecoder does not load referenced
        // assemblies, the arity or a type reference is determined
        // by the type name: e.g.: "C`2". If the arity from the name is
        // different from the number of generic type arguments, the
        // method signature containing the type reference is ignored.
        [Fact]
        public void UnexpectedArity()
        {
            var sourceA =
@".class public A<T> { }";
            var sourceB =
@"class B
{
    static void F(object o) { }
    static void F(A<object> a) { }
}";
            ImmutableArray<byte> bytesA;
            ImmutableArray<byte> pdbA;
            EmitILToArray(sourceA, appendDefaultHeader: true, includePdb: false, assemblyBytes: out bytesA, pdbBytes: out pdbA);
            var refA = AssemblyMetadata.CreateFromImage(bytesA).GetReference();
            var compilationB = CreateCompilation(sourceB, references: new[] { refA });
            var bytesB = compilationB.EmitToArray();
            using (var process = new Process(new Module(bytesB)))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "B.F(System.Object)", "B.F([notsupported])");
                Resolve(process, resolver, "F(A<object>)");
            }
        }

        /// <summary>
        /// Should not resolve to P/Invoke methods.
        /// </summary>
        [Fact]
        public void PInvoke()
        {
            var source =
@"using System.Runtime.InteropServices;
class A
{
    [DllImport(""extern.dll"")]
    public static extern int F();
}
class B
{
    public static int F() => 0;
}";
            var compilation = CreateCompilation(source);
            using (var process = new Process(new Module(compilation.EmitToArray())))
            {
                var resolver = Resolver.CSharpResolver;
                Resolve(process, resolver, "F", "B.F()");
            }
        }

        private static void Resolve(Process process, Resolver resolver, string str, params string[] expectedSignatures)
        {
            var signature = MemberSignatureParser.Parse(str);
            Assert.NotNull(signature);
            Resolve(process, resolver, signature, expectedSignatures);
        }
    }
}
