未验证 提交 49e35a55 编写于 作者: A Allison Chou 提交者: GitHub

Merge pull request #43365 from allisonchou/PatternsFeature

Add keyword completion for new C# 9 patterns
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations
{
public class AndKeywordRecommenderTests : KeywordRecommenderTests
{
private const string InitializeObjectE = @"var e = new object();
";
#if !CODE_STYLE
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterConstant()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterMultipleConstants()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 or 2 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterType()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is int $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterRelationalOperator()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is >= 0 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterGenericType()
{
await VerifyKeywordAsync(
@"class C<T>
{
void M()
{
var e = new object();
if (e is T $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterArrayType()
{
await VerifyKeywordAsync(
@"class C
{
void M()
{
var e = new object();
if (e is int[] $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterListType()
{
await VerifyKeywordAsync(
@"using System.Collections.Generic;
class C
{
void M()
{
var e = new object();
if (e is List<int> $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterListType_FullyQualified()
{
await VerifyKeywordAsync(
@"class C
{
void M()
{
var e = new object();
if (e is System.Collections.Generic.List<int> $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern()
{
await VerifyKeywordAsync(
@"class C
{
public int P { get; }
void M(C test)
{
if (test is { P: 1 $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterOpenParen()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
if (C2 is { P: (1 $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterOpenParen_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: (N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterMultipleOpenParens()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
if (C2 is { P: (((1 $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterMultipleOpenParens_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: (((N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterParenPair()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
if (C2 is { P: (1) $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterParenPair_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: (N.C.P + 1) $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterMultipleParenPairs()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
if (C2 is { P: (((1))) $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterMultipleParenPairs_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: (((N.C.P))) $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterQualifiedName()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
var e = new object();
if (e is C2.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterQualifiedName2()
{
await VerifyKeywordAsync(
@"
namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
if (e is N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_Complex()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
var result = e switch
{
N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case 1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
(1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterOpenParen_Complex()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
var result = e switch
{
(N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterMultipleOpenParens()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
(((1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterMultipleOpenParens_Complex()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
var result = e switch
{
(((N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case (1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterMultipleOpenParens()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case (((1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterMultipleOpenParens_MemberAccessExpression()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
switch (e)
{
case (((N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterMultipleOpenParens_MemberAccessExpression2()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
void M()
{
var e = new object();
switch (e)
{
case (((N.C $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterIsKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterNotKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is not $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterVarKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is var $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterAndKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 and $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterOrKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 or $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterOpenParen()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterOpenBracket()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is { $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAtBeginningOfSwitchExpression()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
$$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAtBeginningOfSwitchStatement()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterTypeAndOpenParen()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is int ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterTypeAndCloseParen()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is int)$$"));
}
#endif
}
}
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations
{
public class NotKeywordRecommenderTests : KeywordRecommenderTests
{
private const string InitializeObjectE = @"object e = new object();
";
#if !CODE_STYLE
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterIsKeyword()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterNotKeyword()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is not $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterNotKeywordAndOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is not ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterAndKeyword_IntExpression()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 and $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterAndKeyword_StrExpression()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is ""str"" and $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterAndKeyword_RelationalExpression()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is <= 1 and $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterMultipleOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is ((($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInMiddleofCompletePattern()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is $$ 1 or 2)"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInMiddleOfCompleteQualifiedPattern()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
if (e is $$ N.C.P or 2) { }
}
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInMiddleOfCompleteQualifiedPattern_List()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
if (e is $$ System.Collections.Generic.List<int> or 2) { }
}
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInMiddleofCompletePattern_MultipleParens()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is ((($$ 1 or 2))))"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
$$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInMiddleOfSwitchExpression()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
1 => 2,
$$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInMiddleOfSwitchStatement()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case 1:
case $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInMiddleOfSwitchExpression_AfterOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
1 => 2,
($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInMiddleOfSwitchExpression_ComplexCase()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
1 and ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterOpenParen_CompleteStatement()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case ($$ 1)"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern()
{
await VerifyKeywordAsync(
@"class C
{
public int P { get; }
void M(C test)
{
if (test is { P: $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterOpenParen()
{
await VerifyKeywordAsync(
@"class C
{
public int P { get; }
void M(C test)
{
if (test is { P: ($$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterOpenParen_Complex()
{
await VerifyKeywordAsync(
@"class C
{
public int P { get; }
void M(C test)
{
if (test is { P: (1 or $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterConstant()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterMultipleConstants()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 or 2 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterType()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is int $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterRelationalOperator()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is >= 0 $$"));
}
#endif
}
}
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations
{
public class OrKeywordRecommenderTests : KeywordRecommenderTests
{
private const string InitializeObjectE = @"var e = new object();
";
#if !CODE_STYLE
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterConstant()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterMultipleConstants()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 or 2 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterType()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is int $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterRelationalOperator()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"if (e is >= 0 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterGenericType()
{
await VerifyKeywordAsync(
@"class C<T>
{
void M()
{
var e = new object();
if (e is T $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterArrayType()
{
await VerifyKeywordAsync(
@"class C
{
void M()
{
var e = new object();
if (e is int[] $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterListType()
{
await VerifyKeywordAsync(
@"using System.Collections.Generic;
class C
{
void M()
{
var e = new object();
if (e is List<int> $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterListType_FullyQualified()
{
await VerifyKeywordAsync(
@"class C
{
void M()
{
var e = new object();
if (e is System.Collections.Generic.List<int> $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern()
{
await VerifyKeywordAsync(
@"class C
{
public int P { get; }
void M(C test)
{
if (test is { P: 1 $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterOpenParen()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
if (C2 is { P: (1 $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterOpenParen_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: (N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterMultipleOpenParens()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
if (C2 is { P: (((1 $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterMultipleOpenParens_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: (((N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterMultipleOpenParens_MemberAccessExpression()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
switch (e)
{
case (((N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterMultipleOpenParens_MemberAccessExpression2()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
void M()
{
var e = new object();
switch (e)
{
case (((N.C $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterParenPair()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
if (C2 is { P: (1) $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterParenPair_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: (N.C.P + 1) $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterMultipleParenPairs()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
if (C2 is { P: (((1))) $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInsideSubpattern_AfterMultipleParenPairs_ComplexConstant()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
int Prop { get; }
void M(C test)
{
if (test is { Prop: (((N.C.P))) $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterQualifiedName()
{
await VerifyKeywordAsync(
@"class C
{
int P { get; }
void M()
{
var C2 = new C();
var e = new object();
if (e is C2.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterQualifiedName2()
{
await VerifyKeywordAsync(
@"
namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
if (e is N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_Complex()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
var result = e switch
{
N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case 1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
(1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterOpenParen_Complex()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
var result = e switch
{
(N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterMultipleOpenParens()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
(((1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchExpression_AfterMultipleOpenParens_Complex()
{
await VerifyKeywordAsync(
@"namespace N
{
class C
{
const int P = 1;
void M()
{
var e = new object();
var result = e switch
{
(((N.C.P $$");
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterOpenParen()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case (1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAtBeginningOfSwitchStatement_AfterMultipleOpenParens()
{
await VerifyKeywordAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case (((1 $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterIsKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterNotKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is not $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterVarKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is var $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterAndKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 and $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterOrKeyword()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is 1 or $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterOpenParen()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterOpenBracket()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is { $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAtBeginningOfSwitchExpression()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"var result = e switch
{
$$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAtBeginningOfSwitchStatement()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"switch (e)
{
case $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterTypeAndOpenParen()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is int ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestMissingAfterTypeAndCloseParen()
{
await VerifyAbsenceAsync(AddInsideMethod(InitializeObjectE +
@"if (e is int)$$"));
}
#endif
}
}
......@@ -37,6 +37,7 @@ private static ImmutableArray<IKeywordRecommender<CSharpSyntaxContext>> GetKeywo
new AbstractKeywordRecommender(),
new AddKeywordRecommender(),
new AliasKeywordRecommender(),
new AndKeywordRecommender(),
new AnnotationsKeywordRecommender(),
new AscendingKeywordRecommender(),
new AsKeywordRecommender(),
......@@ -109,6 +110,7 @@ private static ImmutableArray<IKeywordRecommender<CSharpSyntaxContext>> GetKeywo
new NamespaceKeywordRecommender(),
new NewKeywordRecommender(),
new NintKeywordRecommender(),
new NotKeywordRecommender(),
new NotNullKeywordRecommender(),
new NuintKeywordRecommender(),
new NullableKeywordRecommender(),
......@@ -117,6 +119,7 @@ private static ImmutableArray<IKeywordRecommender<CSharpSyntaxContext>> GetKeywo
new OnKeywordRecommender(),
new OperatorKeywordRecommender(),
new OrderByKeywordRecommender(),
new OrKeywordRecommender(),
new OutKeywordRecommender(),
new OverrideKeywordRecommender(),
new ParamKeywordRecommender(),
......
......@@ -20,7 +20,7 @@ private static bool IsValidContext(CSharpSyntaxContext context)
if (context.IsStatementContext ||
context.IsGlobalStatementContext ||
context.IsPossibleTupleContext ||
context.IsPatternContext ||
context.IsAtStartOfPattern ||
(context.IsTypeContext && !context.IsEnumBaseListContext))
{
return true;
......
// 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.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders
{
internal class AndKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
{
public AndKeywordRecommender()
: base(SyntaxKind.AndKeyword)
{
}
protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
{
return context.IsAtEndOfPattern;
}
}
}
// 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.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders
{
internal class NotKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
{
public NotKeywordRecommender()
: base(SyntaxKind.NotKeyword)
{
}
protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
{
return context.IsAtStartOfPattern;
}
}
}
// 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.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders
{
internal class OrKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
{
public OrKeywordRecommender()
: base(SyntaxKind.OrKeyword)
{
}
protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
{
return context.IsAtEndOfPattern;
}
}
}
......@@ -22,7 +22,7 @@ private static bool IsValidContext(CSharpSyntaxContext context)
if (context.IsStatementContext ||
context.IsGlobalStatementContext ||
context.IsPossibleTupleContext ||
context.IsPatternContext)
context.IsAtStartOfPattern)
{
return true;
}
......
......@@ -102,7 +102,8 @@ internal sealed class CSharpSyntaxContext : SyntaxContext
bool isCatchFilterContext,
bool isDestructorTypeContext,
bool isPossibleTupleContext,
bool isPatternContext,
bool isStartPatternContext,
bool isAfterPatternContext,
bool isRightSideOfNumericType,
bool isInArgumentList,
CancellationToken cancellationToken)
......@@ -112,7 +113,7 @@ internal sealed class CSharpSyntaxContext : SyntaxContext
isRightOfDotOrArrowOrColonColon, isStatementContext, isAnyExpressionContext,
isAttributeNameContext, isEnumTypeMemberAccessContext, isNameOfContext,
isInQuery, isInImportsDirective, IsWithinAsyncMethod(), isPossibleTupleContext,
isPatternContext, isRightSideOfNumericType, isInArgumentList,
isStartPatternContext, isAfterPatternContext, isRightSideOfNumericType, isInArgumentList,
cancellationToken)
{
this.ContainingTypeDeclaration = containingTypeDeclaration;
......@@ -260,7 +261,8 @@ private static CSharpSyntaxContext CreateContextWorker(Workspace workspace, Sema
isCatchFilterContext: syntaxTree.IsCatchFilterContext(position, leftToken),
isDestructorTypeContext: isDestructorTypeContext,
isPossibleTupleContext: syntaxTree.IsPossibleTupleContext(leftToken, position),
isPatternContext: syntaxTree.IsPatternContext(leftToken, position),
isStartPatternContext: syntaxTree.IsAtStartOfPattern(leftToken, position),
isAfterPatternContext: syntaxTree.IsAtEndOfPattern(leftToken, position),
isRightSideOfNumericType: isRightSideOfNumericType,
isInArgumentList: isArgumentListToken,
cancellationToken: cancellationToken);
......
......@@ -1253,10 +1253,28 @@ public static bool IsPossibleTupleContext(this SyntaxTree syntaxTree, SyntaxToke
return false;
}
public static bool IsPatternContext(this SyntaxTree syntaxTree, SyntaxToken leftToken, int position)
public static bool IsAtStartOfPattern(this SyntaxTree syntaxTree, SyntaxToken leftToken, int position)
{
leftToken = leftToken.GetPreviousTokenIfTouchingWord(position);
if (leftToken.IsKind(SyntaxKind.OpenParenToken))
{
if (leftToken.Parent.IsKind(SyntaxKind.ParenthesizedExpression, out ParenthesizedExpressionSyntax parenthesizedExpression))
{
// If we're dealing with an expression surrounded by one or more sets of open parentheses, we need to
// walk up the parens in order to see if we're actually at the start of a valid pattern or not.
return IsAtStartOfPattern(syntaxTree, parenthesizedExpression.GetFirstToken().GetPreviousToken(), parenthesizedExpression.SpanStart);
}
#if !CODE_STYLE
// e is ((($$ 1 or 2)))
if (leftToken.Parent.IsKind(SyntaxKind.ParenthesizedPattern))
{
return true;
}
#endif
}
// case $$
// is $$
if (leftToken.IsKind(SyntaxKind.CaseKeyword, SyntaxKind.IsKeyword))
......@@ -1286,7 +1304,115 @@ public static bool IsPatternContext(this SyntaxTree syntaxTree, SyntaxToken left
return true;
}
#if !CODE_STYLE
// e is 1 and $$
// e is 1 or $$
if (leftToken.IsKind(SyntaxKind.AndKeyword) || leftToken.IsKind(SyntaxKind.OrKeyword))
{
return leftToken.Parent is BinaryPatternSyntax;
}
// e is not $$
if (leftToken.IsKind(SyntaxKind.NotKeyword) && leftToken.Parent.IsKind(SyntaxKind.NotPattern))
{
return true;
}
#endif
return false;
}
public static bool IsAtEndOfPattern(this SyntaxTree syntaxTree, SyntaxToken leftToken, int position)
{
#if CODE_STYLE
return false;
#else
var originalLeftToken = leftToken;
leftToken = leftToken.GetPreviousTokenIfTouchingWord(position);
var patternSyntax = leftToken.GetAncestor<PatternSyntax>();
if (patternSyntax != null)
{
var lastTokenInPattern = patternSyntax.GetLastToken();
// This check should cover the majority of cases, e.g.:
// e is 1 $$
// e is >= 0 $$
// e is { P: (1 $$
// e is { P: (1) $$
if (leftToken == lastTokenInPattern)
{
// Patterns such as 'e is not $$', 'e is 1 or $$', and 'e is ($$' should be invalid here
// as they are incomplete patterns.
return !(leftToken.IsKind(SyntaxKind.OrKeyword) ||
leftToken.IsKind(SyntaxKind.AndKeyword) ||
leftToken.IsKind(SyntaxKind.NotKeyword) ||
leftToken.IsKind(SyntaxKind.OpenParenToken));
}
// We want to make sure that IsAtEndOfPattern returns true even when the user is in the middle of typing a keyword
// after a pattern.
// For example, with the keyword 'and', we want to make sure that 'e is int an$$' is still recognized as valid.
if (lastTokenInPattern.Parent is SingleVariableDesignationSyntax variableDesignationSyntax &&
originalLeftToken.Parent == variableDesignationSyntax)
{
return patternSyntax is DeclarationPatternSyntax || patternSyntax is RecursivePatternSyntax;
}
}
// e is C.P $$
// e is int $$
if (leftToken.IsLastTokenOfNode<TypeSyntax>(out var typeSyntax))
{
// If typeSyntax is part of a qualified name, we want to get the fully-qualified name so that we can
// later accurately perform the check comparing the right side of the BinaryExpressionSyntax to
// the typeSyntax.
while (typeSyntax.Parent is TypeSyntax parentTypeSyntax)
{
typeSyntax = parentTypeSyntax;
}
if (typeSyntax.Parent is BinaryExpressionSyntax binaryExpressionSyntax &&
binaryExpressionSyntax.OperatorToken.IsKind(SyntaxKind.IsKeyword) &&
binaryExpressionSyntax.Right == typeSyntax && !typeSyntax.IsVar)
{
return true;
}
}
// We need to include a special case for switch statement cases, as some are not currently parsed as patterns, e.g. case (1 $$
if (IsAtEndOfSwitchStatementPattern(leftToken))
{
return true;
}
return false;
static bool IsAtEndOfSwitchStatementPattern(SyntaxToken leftToken)
{
var node = leftToken.Parent;
// Walking up the tree for expressions such as 'case (((N.C.P $$'
while (node.IsParentKind(SyntaxKind.SimpleMemberAccessExpression))
{
node = node.Parent;
}
// Getting rid of the extra parentheses to deal with cases such as 'case (((1 $$'
while (node.IsParentKind(SyntaxKind.ParenthesizedExpression))
{
node = node.Parent;
}
// case (1 $$
if (node.IsParentKind(SyntaxKind.CaseSwitchLabel) && node.Parent.IsParentKind(SyntaxKind.SwitchSection))
{
return true;
}
return false;
}
#endif
}
private static SyntaxToken FindTokenOnLeftOfNode(SyntaxNode node)
......@@ -1850,7 +1976,7 @@ public static bool IsDefiniteCastTypeContext(this SyntaxTree syntaxTree, int pos
public static bool IsConstantExpressionContext(this SyntaxTree syntaxTree, int position,
SyntaxToken tokenOnLeftOfPosition)
{
if (IsPatternContext(syntaxTree, tokenOnLeftOfPosition, position))
if (IsAtStartOfPattern(syntaxTree, tokenOnLeftOfPosition, position))
{
return true;
}
......
......@@ -35,7 +35,8 @@ internal abstract class SyntaxContext
bool isInImportsDirective,
bool isWithinAsyncMethod,
bool isPossibleTupleContext,
bool isPatternContext,
bool isAtStartOfPattern,
bool isAtEndOfPattern,
bool isRightSideOfNumericType,
bool isOnArgumentListBracketOrComma,
CancellationToken cancellationToken)
......@@ -60,7 +61,8 @@ internal abstract class SyntaxContext
this.IsInImportsDirective = isInImportsDirective;
this.IsWithinAsyncMethod = isWithinAsyncMethod;
this.IsPossibleTupleContext = isPossibleTupleContext;
this.IsPatternContext = isPatternContext;
this.IsAtStartOfPattern = isAtStartOfPattern;
this.IsAtEndOfPattern = isAtEndOfPattern;
this.InferredTypes = ComputeInferredTypes(workspace, semanticModel, position, cancellationToken);
this.IsRightSideOfNumericType = isRightSideOfNumericType;
this.IsOnArgumentListBracketOrComma = isOnArgumentListBracketOrComma;
......@@ -92,7 +94,8 @@ internal abstract class SyntaxContext
public bool IsInImportsDirective { get; }
public bool IsWithinAsyncMethod { get; }
public bool IsPossibleTupleContext { get; }
public bool IsPatternContext { get; }
public bool IsAtStartOfPattern { get; }
public bool IsAtEndOfPattern { get; }
public bool IsRightSideOfNumericType { get; }
public bool IsOnArgumentListBracketOrComma { get; }
......
......@@ -100,7 +100,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
isInImportsDirective:=isInImportsDirective,
isWithinAsyncMethod:=IsWithinAsyncMethod(targetToken, cancellationToken),
isPossibleTupleContext:=isPossibleTupleContext,
isPatternContext:=False,
isAtStartOfPattern:=False,
isAtEndOfPattern:=False,
isRightSideOfNumericType:=False,
isOnArgumentListBracketOrComma:=isInArgumentList,
cancellationToken:=cancellationToken)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册