File: Completion\CompletionProviders\ExplicitInterfaceMemberCompletionProviderTests.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.Tasks;
using Microsoft.CodeAnalysis.CSharp.Completion.Providers;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders;
 
[Trait(Traits.Feature, Traits.Features.Completion)]
public class ExplicitInterfaceMemberCompletionProviderTests : AbstractCSharpCompletionProviderTests
{
    internal override Type GetCompletionProviderType()
        => typeof(ExplicitInterfaceMemberCompletionProvider);
 
    [Fact]
    public async Task ExplicitInterfaceMember_01()
    {
        var markup = """
            interface IGoo
            {
                void Goo();
                void Goo(int x);
                int Prop { get; }
                int Generic<K, V>(K key, V value);
                string this[int i] { get; }
                void With_Underscore();
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemExistsAsync(markup, "Goo", displayTextSuffix: "()");
        await VerifyItemExistsAsync(markup, "Goo", displayTextSuffix: "(int x)");
        await VerifyItemExistsAsync(markup, "Prop");
        await VerifyItemExistsAsync(markup, "Generic", displayTextSuffix: "<K, V>(K key, V value)");
        await VerifyItemExistsAsync(markup, "this", displayTextSuffix: "[int i]");
        await VerifyItemExistsAsync(markup, "With_Underscore", displayTextSuffix: "()");
    }
 
    [Fact]
    public async Task ExplicitInterfaceMember_02()
    {
        var markup = """
            interface IGoo
            {
                void Goo();
                void Goo(int x);
                int Prop { get; }
            }
 
            interface IBar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemExistsAsync(markup, "Goo", displayTextSuffix: "()");
        await VerifyItemExistsAsync(markup, "Goo", displayTextSuffix: "(int x)");
        await VerifyItemExistsAsync(markup, "Prop");
    }
 
    [Fact]
    public async Task ExplicitInterfaceMember_03()
    {
        var markup = """
            interface IGoo
            {
                virtual void Goo() {}
                virtual void Goo(int x) {}
                virtual int Prop { get => 0; }
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemExistsAsync(markup, "Goo", displayTextSuffix: "()");
        await VerifyItemExistsAsync(markup, "Goo", displayTextSuffix: "(int x)");
        await VerifyItemExistsAsync(markup, "Prop");
    }
 
    [Fact]
    public async Task ExplicitInterfaceMember_04()
    {
        var markup = """
            interface IGoo
            {
                virtual void Goo() {}
                virtual void Goo(int x) {}
                virtual int Prop { get => 0; }
            }
 
            interface IBar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemExistsAsync(markup, "Goo", displayTextSuffix: "()");
        await VerifyItemExistsAsync(markup, "Goo", displayTextSuffix: "(int x)");
        await VerifyItemExistsAsync(markup, "Prop");
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/709988")]
    public async Task CommitOnNotParen()
    {
        var markup = """
            interface IGoo
            {
                void Goo();
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        var expected = """
            interface IGoo
            {
                void Goo();
            }
 
            class Bar : IGoo
            {
                 void IGoo.Goo()
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "Goo()", expected, null);
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/709988")]
    public async Task CommitOnParen()
    {
        var markup = """
            interface IGoo
            {
                void Goo();
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        var expected = """
            interface IGoo
            {
                void Goo();
            }
 
            class Bar : IGoo
            {
                 void IGoo.Goo(
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "Goo()", expected, '(');
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/19947")]
    public async Task ExplicitInterfaceMemberCompletionContainsOnlyValidValues()
    {
        var markup = """
            interface I1
            {
                void Goo();
            }
 
            interface I2 : I1
            {
                void Goo2();
                int Prop { get; }
            }
 
            class Bar : I2
            {
                 void I2.$$
            }
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Equals(object obj)");
        await VerifyItemIsAbsentAsync(markup, "Goo()");
        await VerifyItemIsAbsentAsync(markup, "GetHashCode()");
        await VerifyItemIsAbsentAsync(markup, "GetType()");
        await VerifyItemIsAbsentAsync(markup, "ToString()");
 
        await VerifyItemExistsAsync(markup, "Goo2", displayTextSuffix: "()");
        await VerifyItemExistsAsync(markup, "Prop");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26595")]
    public async Task ExplicitInterfaceMemberCompletionDoesNotContainAccessors()
    {
        var markup = """
            interface I1
            {
                void Foo();
                int Prop { get; }
                event EventHandler TestEvent;
            }
 
            class Bar : I1
            {
                 void I1.$$
            }
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Prop.get");
        await VerifyItemIsAbsentAsync(markup, "TestEvent.add");
        await VerifyItemIsAbsentAsync(markup, "TestEvent.remove");
 
        await VerifyItemExistsAsync(markup, "Foo", displayTextSuffix: "()");
        await VerifyItemExistsAsync(markup, "Prop");
        await VerifyItemExistsAsync(markup, "TestEvent");
    }
 
    [Fact]
    public async Task NotStaticMember_01()
    {
        var markup = """
            interface IGoo
            {
                static void Goo() {}
                static int Prop { get => 0; }
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Goo()");
        await VerifyItemIsAbsentAsync(markup, "Prop");
    }
 
    [Fact]
    public async Task NotStaticMember_02()
    {
        var markup = """
            interface IGoo
            {
                static void Goo() {}
                static int Prop { get => 0; }
            }
 
            interface IBar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Goo()");
        await VerifyItemIsAbsentAsync(markup, "Prop");
    }
 
    [Fact]
    public async Task NotSealedMember_01()
    {
        var markup = """
            interface IGoo
            {
                sealed void Goo() {}
                sealed int Prop { get => 0; }
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Goo()");
        await VerifyItemIsAbsentAsync(markup, "Prop");
    }
 
    [Fact]
    public async Task NotSealedMember_02()
    {
        var markup = """
            interface IGoo
            {
                sealed void Goo() {}
                sealed int Prop { get => 0; }
            }
 
            interface IBar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Goo()");
        await VerifyItemIsAbsentAsync(markup, "Prop");
    }
 
    [Fact]
    public async Task NotNestedType_01()
    {
        var markup = """
            interface IGoo
            {
                public abstract class Goo
                {
                }
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Goo");
    }
 
    [Fact]
    public async Task NotNestedType_02()
    {
        var markup = """
            interface IGoo
            {
                public abstract class Goo
                {
                }
            }
 
            interface IBar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Goo");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/34456")]
    public async Task NotInaccessibleMember_01()
    {
        var markup =
            """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <ProjectReference>Assembly2</ProjectReference>
                    <Document FilePath="Test1.cs">
            <![CDATA[
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            ]]>
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true" LanguageVersion="Preview">
                    <Document FilePath="Test2.cs">
            public interface IGoo
            {
                internal void Goo1() {}
                internal int Prop1 { get => 0; }
                protected void Goo2() {}
                protected int Prop2 { get => 0; }
            }
                    </Document>
                </Project>
            </Workspace>
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Goo1", displayTextSuffix: "()");
        await VerifyItemIsAbsentAsync(markup, "Prop1");
        await VerifyItemExistsAsync(markup, "Goo2", displayTextSuffix: "()");
        await VerifyItemExistsAsync(markup, "Prop2");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/34456")]
    public async Task NotInaccessibleMember_02()
    {
        var markup =
            """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <ProjectReference>Assembly2</ProjectReference>
                    <Document FilePath="Test1.cs">
            <![CDATA[
            interface IBar : IGoo
            {
                 void IGoo.$$
            }
            ]]>
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true" LanguageVersion="Preview">
                    <Document FilePath="Test2.cs">
            public interface IGoo
            {
                internal void Goo1() {}
                internal int Prop1 { get => 0; }
                protected void Goo2() {}
                protected int Prop2 { get => 0; }
            }
                    </Document>
                </Project>
            </Workspace>
            """;
 
        await VerifyItemIsAbsentAsync(markup, "Goo1", displayTextSuffix: "()");
        await VerifyItemIsAbsentAsync(markup, "Prop1");
        await VerifyItemExistsAsync(markup, "Goo2", displayTextSuffix: "()");
        await VerifyItemExistsAsync(markup, "Prop2");
    }
 
    [Fact]
    public async Task VerifySignatureCommit_Generic_Tab()
    {
        var markup = """
            interface IGoo
            {
                int Generic<K, V>(K key, V value);
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        var expected = """
            interface IGoo
            {
                int Generic<K, V>(K key, V value);
            }
 
            class Bar : IGoo
            {
                 void IGoo.Generic<K, V>(K key, V value)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "Generic<K, V>(K key, V value)", expected, '\t');
    }
 
    [Fact]
    public async Task VerifySignatureCommit_Generic_OpenBrace()
    {
        var markup = """
            interface IGoo
            {
                int Generic<K, V>(K key, V value);
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        var expected = """
            interface IGoo
            {
                int Generic<K, V>(K key, V value);
            }
 
            class Bar : IGoo
            {
                 void IGoo.Generic<
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "Generic<K, V>(K key, V value)", expected, '<');
    }
 
    [Fact]
    public async Task VerifySignatureCommit_Method_Tab()
    {
        var markup = """
            interface IGoo
            {
                int Generic(K key, V value);
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        var expected = """
            interface IGoo
            {
                int Generic(K key, V value);
            }
 
            class Bar : IGoo
            {
                 void IGoo.Generic(K key, V value)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "Generic(K key, V value)", expected, '\t');
    }
 
    [Fact]
    public async Task VerifySignatureCommit_Method_OpenBrace()
    {
        var markup = """
            interface IGoo
            {
                int Generic(K key, V value);
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        var expected = """
            interface IGoo
            {
                int Generic(K key, V value);
            }
 
            class Bar : IGoo
            {
                 void IGoo.Generic(
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "Generic(K key, V value)", expected, '(');
    }
 
    [Fact]
    public async Task VerifySignatureCommit_Indexer_Tab()
    {
        var markup = """
            interface IGoo
            {
                int this[K key, V value] { get; }
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        var expected = """
            interface IGoo
            {
                int this[K key, V value] { get; }
            }
 
            class Bar : IGoo
            {
                 void IGoo.this[K key, V value]
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "this[K key, V value]", expected, '\t');
    }
 
    [Fact]
    public async Task VerifySignatureCommit_Indexer_OpenBrace()
    {
        var markup = """
            interface IGoo
            {
                int this[K key, V value] { get; }
            }
 
            class Bar : IGoo
            {
                 void IGoo.$$
            }
            """;
 
        var expected = """
            interface IGoo
            {
                int this[K key, V value] { get; }
            }
 
            class Bar : IGoo
            {
                 void IGoo.this[
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "this[K key, V value]", expected, '[');
    }
 
    [Theory]
    [InlineData("ref")]
    [InlineData("in")]
    [InlineData("out")]
    public async Task TestWithRefKind(string refKind)
    {
        var markup = $@"
interface I
{{
    void M({refKind} string s);
}}
 
class C : I
{{
    void I.$$
}}
";
 
        var expected = $@"
interface I
{{
    void M({refKind} string s);
}}
 
class C : I
{{
    void I.M({refKind} string s)
}}
";
 
        await VerifyProviderCommitAsync(markup, $"M({refKind} string s)", expected, '\t');
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/53924")]
    public async Task TestStaticAbstractInterfaceMember()
    {
        var markup = """
            interface I2<T> where T : I2<T>
            {
                abstract static implicit operator int(T x);
            }
 
            class Test2 : I2<Test2>
            {
                static implicit I2<Test2>.$$
            }
            """;
 
        var expected = """
            interface I2<T> where T : I2<T>
            {
                abstract static implicit operator int(T x);
            }
 
            class Test2 : I2<Test2>
            {
                static implicit I2<Test2>.operator int(Test2 x)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "operator int(Test2 x)", expected, '\t');
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/53924")]
    public async Task TestStaticAbstractInterfaceMember_TrueOperator()
    {
        var markup = """
            interface I<T> where T : I<T>
            {
                abstract static bool operator true(T x);
                abstract static bool operator false(T x);
            }
 
            class C : I<C>
            {
                static bool I<C>.$$
            }
            """;
 
        var expected = """
            interface I<T> where T : I<T>
            {
                abstract static bool operator true(T x);
                abstract static bool operator false(T x);
            }
 
            class C : I<C>
            {
                static bool I<C>.operator true(C x)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "operator true(C x)", expected, '\t');
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/53924")]
    public async Task TestStaticAbstractInterfaceMember_UnaryPlusOperator()
    {
        var markup = """
            interface I<T> where T : I<T>
            {
                abstract static T operator +(T x);
            }
 
            class C : I<C>
            {
                static C I<C>.$$
            }
            """;
 
        var expected = """
            interface I<T> where T : I<T>
            {
                abstract static T operator +(T x);
            }
 
            class C : I<C>
            {
                static C I<C>.operator +(C x)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "operator +(C x)", expected, '\t');
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/53924")]
    public async Task TestStaticAbstractInterfaceMember_BinaryPlusOperator()
    {
        var markup = """
            interface I<T> where T : I<T>
            {
                abstract static T operator +(T x, T y);
            }
 
            class C : I<C>
            {
                static C I<C>.$$
            }
            """;
 
        var expected = """
            interface I<T> where T : I<T>
            {
                abstract static T operator +(T x, T y);
            }
 
            class C : I<C>
            {
                static C I<C>.operator +(C x, C y)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "operator +(C x, C y)", expected, '\t');
    }
 
    [Fact]
    public async Task TestWithParamsArrayParameter()
    {
        var markup = """
            interface I
            {
                void M(params string[] args);
            }
 
            class C : I
            {
                void I.$$
            }
            """;
 
        var expected = """
            interface I
            {
                void M(params string[] args);
            }
 
            class C : I
            {
                void I.M(params string[] args)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "M(params string[] args)", expected, '\t');
    }
 
    [Fact(Skip = "https://github.com/dotnet/roslyn/issues/72224")]
    [WorkItem("https://github.com/dotnet/roslyn/issues/72224")]
    public async Task TestWithParamsCollectionParameter()
    {
        var markup = """
            using System.Collections.Generic;
 
            interface I
            {
                void M(params IEnumerable<string> args);
            }
 
            class C : I
            {
                void I.$$
            }
            """;
 
        var expected = """
            using System.Collections.Generic;
 
            interface I
            {
                void M(params IEnumerable<string> args);
            }
 
            class C : I
            {
                void I.M(params IEnumerable<string> args)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "M(params IEnumerable<string> args)", expected, '\t');
    }
 
    [Fact]
    public async Task TestWithNullable()
    {
        var markup = """
            #nullable enable
 
            interface I
            {
                void M<T>(T? x);
            }
 
            class C : I
            {
                void I.$$
            }
            """;
 
        var expected = """
            #nullable enable
 
            interface I
            {
                void M<T>(T? x);
            }
 
            class C : I
            {
                void I.M<T>(T? x)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "M<T>(T? x)", expected, '\t');
    }
 
    [Fact]
    public async Task TestEscapeIdentifier()
    {
        var markup = """
            interface I
            {
                void M(string @class);
            }
 
            class C : I
            {
                void I.$$
            }
            """;
 
        var expected = """
            interface I
            {
                void M(string @class);
            }
 
            class C : I
            {
                void I.M(string @class)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "M(string @class)", expected, '\t');
    }
 
    [Fact]
    public async Task TestEscapeIdentifier2()
    {
        var markup = """
            interface I
            {
                void M<@class>();
            }
 
            class C : I
            {
                void I.$$
            }
            """;
 
        var expected = """
            interface I
            {
                void M<@class>();
            }
 
            class C : I
            {
                void I.M<@class>()
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "M<@class>()", expected, '\t');
    }
 
    [Fact]
    public async Task TestParameterWithDefaultValue()
    {
        var markup = """
            interface I
            {
                void M(int x = 10);
            }
 
            class C : I
            {
                void I.$$
            }
            """;
 
        var expected = """
            interface I
            {
                void M(int x = 10);
            }
 
            class C : I
            {
                void I.M(int x)
            }
            """;
        // TODO: Consider adding the default value too.
        await VerifyProviderCommitAsync(markup, "M(int x)", expected, '\t');
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60215")]
    public async Task TestStaticAbstractCheckedUnaryOperator()
    {
        var markup = """
            interface I1<T> where T : I1<T>
            {
                abstract static T operator checked -(T x);
 
                abstract static T operator -(T x);
            }
 
            class C : I1<C>
            {
                static C I1<C>.$$
            }
            """;
 
        var expected = """
            interface I1<T> where T : I1<T>
            {
                abstract static T operator checked -(T x);
 
                abstract static T operator -(T x);
            }
 
            class C : I1<C>
            {
                static C I1<C>.operator checked -(C x)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "operator checked -(C x)", expected, '\t');
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60215")]
    public async Task TestStaticAbstractCheckedBinaryOperator()
    {
        var markup = """
            interface I1<T> where T : I1<T>
            {
                abstract static T operator checked +(T x, T y);
 
                abstract static T operator +(T x, T y);
            }
 
            class C : I1<C>
            {
                static C I1<C>.$$
            }
            """;
 
        var expected = """
            interface I1<T> where T : I1<T>
            {
                abstract static T operator checked +(T x, T y);
 
                abstract static T operator +(T x, T y);
            }
 
            class C : I1<C>
            {
                static C I1<C>.operator checked +(C x, C y)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "operator checked +(C x, C y)", expected, '\t');
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60215")]
    public async Task TestStaticAbstractCheckedCastOperator()
    {
        var markup = """
            interface I1<T> where T : I1<T>
            {
                abstract static explicit operator checked string(T x);
                abstract static explicit operator string(T x);
            }
 
 
            class C3 : I1<C3>
            {
                static C3 I1<C3>.$$
            }
            """;
 
        var expected = """
            interface I1<T> where T : I1<T>
            {
                abstract static explicit operator checked string(T x);
                abstract static explicit operator string(T x);
            }
 
 
            class C3 : I1<C3>
            {
                static C3 I1<C3>.operator checked string(C3 x)
            }
            """;
 
        await VerifyProviderCommitAsync(markup, "operator checked string(C3 x)", expected, '\t');
    }
}