|
// 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');
}
}
|