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