File: Completion\CompletionProviders\PropertySubPatternCompletionProviderTests.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.
 
#nullable disable
 
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 PropertySubpatternCompletionProviderTests : AbstractCSharpCompletionProviderTests
{
    internal override Type GetCompletionProviderType()
        => typeof(PropertySubpatternCompletionProvider);
 
    [Fact]
    public async Task PropertiesInRecursivePattern()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this is Program { $$ }
                }
            }
            """;
        // VerifyItemExistsAsync also tests with the item typed.
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_WithPositional()
    {
        var markup =
            """
            public class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this is Program (1, 2) { $$ }
                }
                public void Deconstruct(out int x, out int y) => throw null;
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_WithPositional_UsingStaticType()
    {
        var markup =
            """
            public class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this is (1, 2) { $$ }
                }
                public void Deconstruct(out int x, out int y) => throw null;
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_WithEscapedKeyword()
    {
        var markup =
            """
            class Program
            {
                public int @new { get; set; }
                public int @struct { get; set; }
 
                void M()
                {
                    _ = this is Program { $$ }
                }
            }
            """;
        await VerifyItemExistsAsync(markup, "@new", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "@struct", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_WriteOnlyProperties()
    {
        var markup =
            """
            class Program
            {
                public int P1 { set => throw null; }
 
                void M()
                {
                    _ = this is Program { $$ }
                }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_WithDerivedType()
    {
        var markup =
            """
            class Program
            {
                void M()
                {
                    _ = this is Derived { $$ }
                }
            }
            class Derived
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_WithOtherType()
    {
        var markup =
            """
            class Program
            {
                void M(Other other)
                {
                    _ = other is Other { $$ }
                }
            }
            class Other
            {
                public int P1 { get; set; }
                public int F2;
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "F2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_WithDerivedType_WithInaccessibleMembers()
    {
        var markup =
            """
            class Program
            {
                void M()
                {
                    _ = this is Derived { $$ }
                }
            }
            class Derived : Program
            {
                private int P1 { get; set; }
                private int P2 { get; set; }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_WithDerivedType_WithPrivateMember()
    {
        var markup =
            """
            class Program
            {
                private int P1 { get; set; }
                void M()
                {
                    _ = this is Derived { $$ }
                }
            }
            class Derived : Program
            {
                private int P2 { get; set; }
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_UseStaticTypeFromIs()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this is { $$ }
                }
            }
            """;
        // VerifyItemExistsAsync also tests with the item typed.
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_InSwitchStatement()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    switch (this)
                    {
                        case Program { $$ }
                    }
                }
            }
            """;
        // VerifyItemExistsAsync also tests with the item typed.
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_UseStaticTypeFromSwitchStatement()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    switch (this)
                    {
                        case { $$ }
                    }
                }
            }
            """;
        // VerifyItemExistsAsync also tests with the item typed.
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_InSwitchExpression()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this switch { { $$ } }
                }
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_NestedInProperty()
    {
        var markup =
            """
            public class Nested
            {
                public int P3 { get; set; }
                public int P4 { get; set; }
            }
            class Program
            {
                public int P1 { get; set; }
                public Nested P2 { get; set; }
 
                void M()
                {
                     _ = this is Program { P2: { $$ } }
                }
            }
            """;
        await VerifyItemIsAbsentAsync(markup, "P1");
        await VerifyItemIsAbsentAsync(markup, "P2");
        await VerifyItemExistsAsync(markup, "P3", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P4", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_NestedInField()
    {
        var markup =
            """
            public class Nested
            {
                public int P3 { get; set; }
                public int P4 { get; set; }
            }
            class Program
            {
                public int P1 { get; set; }
                public Nested F2;
 
                void M()
                {
                     _ = this is Program { F2: { $$ } }
                }
            }
            """;
        await VerifyItemIsAbsentAsync(markup, "P1");
        await VerifyItemIsAbsentAsync(markup, "F2");
        await VerifyItemExistsAsync(markup, "P3", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P4", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_Nested_WithFields()
    {
        var markup =
            """
            public class Nested
            {
                public int F3;
                public int F4;
            }
            class Program
            {
                public int P1 { get; set; }
                public Nested P2 { get; set; }
 
                void M()
                {
                     _ = this is Program { P2: { $$ } }
                }
            }
            """;
        await VerifyItemIsAbsentAsync(markup, "P1");
        await VerifyItemIsAbsentAsync(markup, "P2");
        await VerifyItemExistsAsync(markup, "F3", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "F4", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_Nested_WithMissingProperty()
    {
        var markup =
            """
            public class Nested
            {
                public int P3 { get; set; }
                public int P4 { get; set; }
            }
            class Program
            {
                public int P1 { get; set; }
                public Nested P2 { get; set; }
 
                void M()
                {
                     _ = this is Program { : { $$ } }
                }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_NoType()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = missing is { $$ }
                }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_MissingAfterColon()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this is { P1: $$ }
                }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_SecondProperty()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this is Program { P2: 1, $$ }
                }
            }
            """;
        // VerifyItemExistsAsync also tests with the item typed.
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemIsAbsentAsync(markup, "P2");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_PositionalInFirstProperty()
    {
        var markup =
            """
            class Program
            {
                public D P1 { get; set; }
 
                void M()
                {
                    _ = this is Program { P1: ($$ }
                }
            }
            class D
            {
                public int P2 { get; set; }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_PositionalInFirstProperty_AfterComma()
    {
        var markup =
            """
            class Program
            {
                public D P1 { get; set; }
 
                void M()
                {
                    _ = this is Program { P1: (1, $$ }
                }
            }
            class D
            {
                public int P2 { get; set; }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_PositionalInFirstProperty_AfterCommaAndBeforeParen()
    {
        var markup =
            """
            class Program
            {
                public D P1 { get; set; }
 
                void M()
                {
                    _ = this is Program { P1: (1, $$) }
                }
            }
            class D
            {
                public int P2 { get; set; }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_InPositional_Incomplete()
    {
        var markup =
            """
            public class Program
            {
                public int P1 { get; set; }
 
                void M()
                {
                    _ = this is ({ $$ }) // Can deconstruct into a parenthesized property pattern
                }
 
                public void Deconstruct(out Program x, out Program y) => throw null;
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_InPositional_Incomplete_WithoutClosingBrace()
    {
        var markup =
            """
            public class Program
            {
                public int P1 { get; set; }
 
                void M()
                {
                    _ = this is ({ $$   // Can deconstruct into a parenthesized property pattern
                }
 
                public void Deconstruct(out Program x, out Program y) => throw null;
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_InPositional_Incomplete_WithTwoTypes()
    {
        var markup =
            """
            public class Program
            {
                void M()
                {
                    _ = this is ({ $$ }) // no deconstruction into 1 element
                }
 
                public void Deconstruct(out D x, out Program y) => throw null;
            }
            public class D
            {
                public int P2 { get; set; }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_InPositional_Complete_BeforeComma()
    {
        var markup =
            """
            public class Program
            {
                public int P1 { get; set; }
 
                void M()
                {
                    _ = this is ({ $$ }, )
                }
 
                public void Deconstruct(out Program x, out Program y) => throw null;
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_InPositional_Complete_AfterComma()
    {
        var markup =
            """
            public class Program
            {
                public int P1 { get; set; }
 
                void M()
                {
                    _ = this is ( , { $$ })
                }
 
                public void Deconstruct(out Program x, out Program y) => throw null;
            }
            """;
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_NoPropertyLeft()
    {
        var markup =
            """
            class Program
            {
                public int P1 { get; set; }
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this is Program { P2: 1, P1: 2, $$ }
                }
            }
            """;
        await VerifyNoItemsExistAsync(markup);
    }
 
    [Fact]
    public async Task PropertiesInRecursivePattern_NotForEditorUnbrowsable()
    {
        var markup =
            """
            class Program
            {
                [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
                public int P1 { get; set; }
 
                public int P2 { get; set; }
 
                void M()
                {
                    _ = this is Program { $$ }
                }
            }
            """;
        // Ignore browsability limiting attributes if the symbol is declared in source.
        await VerifyItemExistsAsync(markup, "P1", displayTextSuffix: "");
        await VerifyItemExistsAsync(markup, "P2", displayTextSuffix: "");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33250")]
    public async Task StaticProperties_NotSuggested()
    {
        var markup =
            """
            class Program
            {
                void M()
                {
                    _ = "" is { $$ }
                }
            }
            """;
        await VerifyItemIsAbsentAsync(markup, "Empty");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33250")]
    public async Task StaticFields_NotSuggested()
    {
        var markup =
            """
            class Program
            {
                static int x = 42;
 
                void M()
                {
                    _ = this is { $$ }
                }
            }
            """;
        await VerifyItemIsAbsentAsync(markup, "x");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33250")]
    public async Task ConstFields_NotSuggested()
    {
        var markup =
            """
            class Program
            {
                void M()
                {
                    _ = 5 is { $$ }
                }
            }
            """;
        await VerifyItemIsAbsentAsync(markup, "MaxValue");
    }
}