File: SymbolKey\SymbolKeyErrorTypeTests.cs
Web Access
Project: src\src\EditorFeatures\CSharpTest\Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.UnitTests;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SymbolId;
 
public class SymbolKeyErrorTypeTests : SymbolKeyTestBase
{
    [Fact]
    public void GenericType_NotMissingWithMissingTypeArgument()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public void M(D<string> x)
                    {
                    }
                }
 
                public class D<T>
                {
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void GenericType_MissingWithNonMissingTypeArgument()
    {
        var source = """
            using System.Collections.Generic;
 
            namespace N
            {
                public class C
                {
                    public void M(List<D> x)
                    {
                    }
                }
 
                public class D
                {
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void GenericType_MissingWithMissingTypeArgument()
    {
        var source = """
            using System.Collections.Generic;
 
            namespace N
            {
                public class C
                {
                    public void M(List<string> x)
                    {
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void Tuple_MissingTypes()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public void M((string, int) x)
                    {
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void Tuple_NonMissingTypes()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public void M((C, D) x)
                    {
                    }
                }
 
                public class D
                {
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void Array_MissingElementType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public void M(string[] x)
                    {
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void Array_NonMissingElementType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public void M(D[] x)
                    {
                    }
                }
 
                public class D
                {
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void Pointer_MissingType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public unsafe void M(int *x)
                    {
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void Pointer_NonMissingType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public unsafe void M(S *x)
                    {
                    }
                }
 
                public struct S
                {
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void NestedType_MissingInGenericContainer()
    {
        var source = """
            using System.Collections.Generic;
 
            namespace N
            {
                public class C
                {
                    public void M(List<int>.Enumerator x)
                    {
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void NestedType_MissingInNonGenericContainer()
    {
        var source = """
            using System.Diagnostics;
 
            namespace N
            {
                public class C
                {
                    public void M(DebuggableAttribute.DebuggingModes x)
                    {
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.M"));
    }
 
    [Fact]
    public void Method_MissingReturnType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public string Create()
                    {
                        return new string('c', 1);
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.Create"));
    }
 
    [Fact]
    public void Method_MissingParameterType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public C Create(string x)
                    {
                        return new C();
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.Create"));
    }
 
    [Fact]
    public void Constructor_MissingParameterType()
    {
        var source = """
            public class C
            {
                public C(string x)
                {
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("C..ctor"));
    }
 
    [Fact]
    public void Indexer_MissingParameterType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public C this[string x]
                    {
                        get { return null; }
                        set { }
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.this[]"));
    }
 
    [Fact]
    public void Indexer_MissingReturnType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public string this[C x]
                    {
                        get { return null; }
                        set { }
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.this[]"));
    }
 
    [Fact]
    public void Property_MissingReturnType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public string P
                    {
                        get { return null; }
                        set { }
                    }
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.P"));
    }
 
    [Fact]
    public void EventField_MissingReturnType()
    {
        var source = """
            namespace N
            {
                public class C
                {
                    public event System.EventHandler E;
                }
            }
            """;
 
        VerifyResolution(source, c => c.GetMember("N.C.E"));
    }
 
    private static void VerifyResolution(string source, Func<Compilation, ISymbol> symbolToResolve)
    {
        var sourceCompilation = (Compilation)CreateCompilation(source, options: new(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true));
        var symbol = symbolToResolve(sourceCompilation);
 
        Assert.NotNull(symbol);
 
        var symbolKey = SymbolKey.CreateString(symbol);
 
        // Create a compilation that references our library, but doesn't reference types needed by the library.
        // This emulates the experience in Go To Definition when navigating to metadata from the .NET runtime when
        // implementations are split over multiple assemblies with various type forwards in play.
        // For example:
        //   System.Uri exists in System.Private.Uri.dll, but System.String exists in System.Private.CoreLib.dll
        //   and we want to allow a symbol for System.Uri.Create(System.String) to resolve correctly even when the
        //   System.Private.CoreLib reference is missing.
        var emptyCompilation = CSharpCompilation.Create("empty", options: new(OutputKind.DynamicallyLinkedLibrary, concurrentBuild: false))
            .AddReferences(sourceCompilation.EmitToImageReference());
 
        var resolution = SymbolKey.ResolveString(symbolKey, emptyCompilation, ignoreAssemblyKey: true, out var failureReason, CancellationToken.None);
 
        Assert.Null(failureReason);
        Assert.NotNull(resolution.Symbol);
 
        // Since we expect some types to be error types, we just use display string to make sure we found the right
        // symbol.
        Assert.Equal(symbol.ToDisplayString(), resolution.Symbol!.ToDisplayString());
    }
}