File: Completion\CompletionProviders\IndexerCompletionProviderTests.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.Editor.Implementation.IntelliSense.AsyncCompletion;
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 IndexerCompletionProviderTests : AbstractCSharpCompletionProviderTests
{
    internal override Type GetCompletionProviderType()
        => typeof(UnnamedSymbolCompletionProvider);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerIsSuggestedAfterDot()
    {
        await VerifyItemExistsAsync("""
            public class C
            {
                public int this[int i] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    var c = new C();
                    c.$$
                }
            }
            """, "this", displayTextSuffix: "[]", matchingFilters: [FilterSet.PropertyFilter]);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerIsSuggestedAfterDotForString()
    {
        await VerifyItemExistsAsync("""
            public class Program
            {
                public static void Main(string s)
                {
                    s.$$
                }
            }
            """, "this", displayTextSuffix: "[]", matchingFilters: [FilterSet.PropertyFilter]);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerIsNotSuggestedOnStaticAccess()
    {
        await VerifyNoItemsExistAsync("""
            public class C
            {
                public int this[int i] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    C.$$
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerIsNotSuggestedInNameOfContext()
    {
        await VerifyNoItemsExistAsync("""
            public class C
            {
                public int this[int i] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    var c = new C();
                    var name = nameof(c.$$
                }
            }
            """);
    }
 
    [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerSuggestionCommitsOpenAndClosingBraces()
    {
        await VerifyCustomCommitProviderAsync("""
            public class C
            {
                public int this[int i] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    var c = new C();
                    c.$$
                }
            }
            """, "this", """
            public class C
            {
                public int this[int i] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    var c = new C();
                    c[$$]
                }
            }
            """);
    }
 
    [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerWithTwoParametersSuggestionCommitsOpenAndClosingBraces()
    {
        await VerifyCustomCommitProviderAsync("""
            public class C
            {
                public int this[int x, int y] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    var c = new C();
                    c.$$
                }
            }
            """, "this", """
            public class C
            {
                public int this[int x, int y] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    var c = new C();
                    c[$$]
                }
            }
            """);
    }
 
    [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    [InlineData("c.$$",
                "c[$$]")]
    [InlineData("c. $$",
                "c[$$] ")]
    [InlineData("c.$$;",
                "c[$$];")]
    [InlineData("c.th$$",
                "c[$$]")]
    [InlineData("c.this$$",
                "c[$$]")]
    [InlineData("c.th$$;",
                "c[$$];")]
    [InlineData("var f = c.$$;",
                "var f = c[$$];")]
    [InlineData("var f = c.th$$;",
                "var f = c[$$];")]
    [InlineData("c?.$$",
                "c?[$$]")]
    [InlineData("c?.this$$",
                "c?[$$]")]
    [InlineData("((C)c).$$",
                "((C)c)[$$]")]
    [InlineData("(true ? c : c).$$",
                "(true ? c : c)[$$]")]
    public async Task IndexerCompletionForDifferentExpressions(string expression, string fixedCode)
    {
        await VerifyCustomCommitProviderAsync($@"
public class C
{{
    public int this[int i] => i;
}}
 
public class Program
{{
    public static void Main()
    {{
        var c = new C();
        {expression}
    }}
}}
", "this", @$"
public class C
{{
    public int this[int i] => i;
}}
 
public class Program
{{
    public static void Main()
    {{
        var c = new C();
        {fixedCode}
    }}
}}
");
    }
 
    [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    [InlineData("/* Leading trivia */c.$$",
                "/* Leading trivia */c[$$]")]
    [InlineData("c. $$ /* Trailing trivia */",
                "c[$$]  /* Trailing trivia */")]
    [InlineData("c./* Trivia in between */$$",
                "c[$$]/* Trivia in between */")]
    public async Task IndexerCompletionTriviaTest(string expression, string fixedCode)
    {
        await VerifyCustomCommitProviderAsync($@"
public class C
{{
    public int this[int i] => i;
}}
 
public class Program
{{
    public static void Main()
    {{
        var c = new C();
        {expression}
    }}
}}
", "this", @$"
public class C
{{
    public int this[int i] => i;
}}
 
public class Program
{{
    public static void Main()
    {{
        var c = new C();
        {fixedCode}
    }}
}}
");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerDescriptionIncludesDocCommentsAndOverloadsHint()
    {
        await VerifyItemExistsAsync("""
            public class C
            {
                /// <summary>
                /// Returns the index <paramref name="i"/>
                /// </summary>
                /// <param name="i">The index</param>
                /// <returns>Returns the index <paramref name="i"/></returns>
                public int this[int i] => i;
 
                /// <summary>
                /// Returns 1
                /// </summary>
                /// <param name="i">The index</param>
                /// <returns>Returns 1</returns>
                public int this[string s] => 1;
            }
 
            public class Program
            {
                public static void Main()
                {
                    var c = new C();
                    c.$$
                }
            }
            """, "this", displayTextSuffix: "[]", expectedDescriptionOrNull: @$"int C.this[int i] {{ get; }} (+ 1 {FeaturesResources.overload})
Returns the index i");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerOfBaseTypeIsSuggestedAfterDot()
    {
        await VerifyItemExistsAsync("""
            public class Base
            {
                public int this[int i] => i;
            }
            public class Derived : Base
            {
            }
 
            public class Program
            {
                public static void Main()
                {
                    var d = new Derived();
                    d.$$
                }
            }
            """, "this", displayTextSuffix: "[]");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerOfBaseTypeIsNotSuggestedIfNotAccessible()
    {
        await VerifyNoItemsExistAsync("""
            public class Base
            {
                protected int this[int i] => i;
            }
            public class Derived : Base
            {
            }
 
            public class Program
            {
                public static void Main()
                {
                    var d = new Derived();
                    d.$$
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerIsSuggestedOnString()
    {
        await VerifyItemExistsAsync("""
            public class Program
            {
                public static void Main()
                {
                    var s = "Test";
                    s.$$
                }
            }
            """, "this", displayTextSuffix: "[]");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task TestEditorBrowsableOnIndexerIsRespected_EditorBrowsableStateNever()
    {
        var markup = """
            namespace N
            {
                public class Program
                {
                    public static void Main()
                    {
                        var c = new C();
                        c.$$
                    }
                }
            }
            """;
        var referencedCode = """
            using System.ComponentModel;
 
            namespace N
            {
                public class C
                {
                    [EditorBrowsable(EditorBrowsableState.Never)]
                    public int this[int i] => i;
                }
            }
            """;
 
        await VerifyItemInEditorBrowsableContextsAsync(
            markup: markup,
            referencedCode: referencedCode,
            item: "this",
            expectedSymbolsSameSolution: 1,
            expectedSymbolsMetadataReference: 0,
            sourceLanguage: LanguageNames.CSharp,
            referencedLanguage: LanguageNames.CSharp);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task TestEditorBrowsableOnIndexerIsRespected_EditorBrowsableStateAdvanced()
    {
        var markup = """
            namespace N
            {
                public class Program
                {
                    public static void Main()
                    {
                        var c = new C();
                        c.$$
                    }
                }
            }
            """;
        var referencedCode = """
            using System.ComponentModel;
 
            namespace N
            {
                public class C
                {
                    [EditorBrowsable(EditorBrowsableState.Advanced)]
                    public int this[int i] => i;
                }
            }
            """;
        HideAdvancedMembers = true;
 
        await VerifyItemInEditorBrowsableContextsAsync(
            markup: markup,
            referencedCode: referencedCode,
            item: "this",
            expectedSymbolsSameSolution: 1,
            expectedSymbolsMetadataReference: 0,
            sourceLanguage: LanguageNames.CSharp,
            referencedLanguage: LanguageNames.CSharp);
 
        HideAdvancedMembers = false;
 
        await VerifyItemInEditorBrowsableContextsAsync(
            markup: markup,
            referencedCode: referencedCode,
            item: "this",
            expectedSymbolsSameSolution: 1,
            expectedSymbolsMetadataReference: 1,
            sourceLanguage: LanguageNames.CSharp,
            referencedLanguage: LanguageNames.CSharp);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task TestEditorBrowsableOnIndexerIsRespected_EditorBrowsableStateNever_InheritedMember()
    {
        var markup = """
            namespace N
            {
                public class Program
                {
                    public static void Main()
                    {
                        var d = new Derived();
                        d.$$
                    }
                }
            }
            """;
        var referencedCode = """
            using System.ComponentModel;
 
            namespace N
            {
                public class Base
                {
                    [EditorBrowsable(EditorBrowsableState.Never)]
                    public int this[int i] => i;
                }
 
                public class Derived: Base
                {
                }
            }
            """;
 
        await VerifyItemInEditorBrowsableContextsAsync(
            markup: markup,
            referencedCode: referencedCode,
            item: "this",
            expectedSymbolsSameSolution: 1,
            expectedSymbolsMetadataReference: 0,
            sourceLanguage: LanguageNames.CSharp,
            referencedLanguage: LanguageNames.CSharp);
    }
 
    [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/47511")]
    public async Task IndexerNullForgivingOperatorHandling()
    {
        await VerifyCustomCommitProviderAsync("""
            #nullable enable
 
            public class C
            {
                public int this[int i] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    C? c = null;
                    var i = c!.$$
                }
            }
            """, "this", """
            #nullable enable
 
            public class C
            {
                public int this[int i] => i;
            }
 
            public class Program
            {
                public static void Main()
                {
                    C? c = null;
                    var i = c![$$]
                }
            }
            """);
    }
}