提交 f42de27b 编写于 作者: P Paul Chen 提交者: Jason Malinowski

Add ref, qualified generic name detection, and tuple handling for SpeculativeT completion. (#37323)

Fixes #37224
Fixes #37268
Fixes #37361
上级 d2296ee0
......@@ -162,6 +162,588 @@ class C
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
public async Task InRef0()
{
var markup = @"
using System;
class C
{
ref $$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
public async Task InRef1()
{
var markup = @"
using System;
class C
{
ref T$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
public async Task InRefGeneric0()
{
var markup = @"
using System;
class C
{
ref Func<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
public async Task InRefGeneric1()
{
var markup = @"
using System;
class C
{
ref Func<$$>
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
public async Task InRefGeneric2()
{
var markup = @"
using System;
class C
{
ref Func<T$$>
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
public async Task InRefGeneric3()
{
var markup = @"
using System;
class C
{
ref Func<int, $$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
public async Task InRefReadonlyGeneric()
{
var markup = @"
using System;
class C
{
ref readonly Func<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37268, "https://github.com/dotnet/roslyn/issues/37268")]
public async Task InQualifiedGeneric0()
{
var markup = @"
using System;
class C
{
System.Func<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37268, "https://github.com/dotnet/roslyn/issues/37268")]
public async Task InQualifiedGeneric1()
{
var markup = @"
using System;
class C
{
System.Collections.Generic.List<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
[WorkItem(37268, "https://github.com/dotnet/roslyn/issues/37268")]
public async Task InRefAndQualifiedGeneric0()
{
var markup = @"
using System;
class C
{
ref System.Func<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
[WorkItem(37268, "https://github.com/dotnet/roslyn/issues/37268")]
public async Task InRefAndQualifiedGeneric1()
{
var markup = @"
using System;
class C
{
internal ref System.Func<int,$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
[WorkItem(37268, "https://github.com/dotnet/roslyn/issues/37268")]
public async Task InRefAndQualifiedNestedGeneric0()
{
var markup = @"
using System;
class C
{
partial ref System.Func<Func<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
[WorkItem(37268, "https://github.com/dotnet/roslyn/issues/37268")]
public async Task InRefAndQualifiedNestedGeneric1()
{
var markup = @"
using System;
class C
{
private ref Func<System.Func<int,$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
[WorkItem(37268, "https://github.com/dotnet/roslyn/issues/37268")]
public async Task InRefAndQualifiedNestedGeneric2()
{
var markup = @"
using System;
class C
{
public ref Func<int, System.Func<int,$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37224, "https://github.com/dotnet/roslyn/issues/37224")]
[WorkItem(37268, "https://github.com/dotnet/roslyn/issues/37268")]
public async Task InRefAndQualifiedNestedGeneric3()
{
var markup = @"
using System;
class C
{
private protected ref Func<int, System.Func<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InTuple0()
{
var markup = @"
using System;
class C
{
protected ($$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task TupleInMethod0()
{
var markup = @"
using System;
class C
{
void M()
{
($$
}
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task TupleInMethod1()
{
var markup = @"
using System;
class C
{
void M()
{
var a = 0;
($$
}
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task TupleInMethod2()
{
var markup = @"
using System;
class C
{
void M()
{
($$)
}
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task TupleInMethod3()
{
var markup = @"
using System;
class C
{
void M()
{
var a = 0;
(T$$)
a = 1;
}
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InTupleNot0()
{
var markup = @"
using System;
class C
{
protected sealed (int $$
}";
await VerifyItemIsAbsentAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InTuple1()
{
var markup = @"
using System;
class C
{
sealed (int, $$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InTupleNot1()
{
var markup = @"
using System;
class C
{
virtual (int x, C $$
}";
await VerifyItemIsAbsentAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InTupleGeneric0()
{
var markup = @"
using System;
class C
{
(Func<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InTupleGeneric1()
{
var markup = @"
using System;
class C
{
(int, Func<$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InTupleGeneric2()
{
var markup = @"
using System;
class C
{
(int, Func<int, $$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InGenericTuple0()
{
var markup = @"
using System;
class C
{
Func<($$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InGenericTuple1()
{
var markup = @"
using System;
class C
{
Func<int, ($$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InGenericTuple1Not()
{
var markup = @"
using System;
class C
{
Func<int, (T $$
}";
await VerifyItemIsAbsentAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InGenericTuple2()
{
var markup = @"
using System;
class C
{
Func<(int, $$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InGenericTuple2Not()
{
var markup = @"
using System;
class C
{
Func<(C c, int $$
}";
await VerifyItemIsAbsentAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InGenericTuple3()
{
var markup = @"
using System;
class C
{
Func<int, (int,$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InGenericTuple3Not()
{
var markup = @"
using System;
class C
{
Func<C, (int, C $$
}";
await VerifyItemIsAbsentAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InRefTupleQualifiedNestedGeneric0()
{
var markup = @"
using System;
class C
{
ref (Func<System.Func<int,$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InRefTupleQualifiedNestedGeneric1()
{
var markup = @"
using System;
class C
{
ref (C c, Func<System.Func<int,$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InRefTupleQualifiedNestedGeneric2()
{
var markup = @"
using System;
class C
{
ref (C c, Func<int, System.Func<(int,T$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InRefTupleQualifiedNestedGeneric3()
{
var markup = @"
using System;
class C
{
ref (C c, System.Func<Func<int,(T$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InRefTupleQualifiedNestedGeneric4()
{
var markup = @"
using System;
class C
{
ref (System.Func<(int,C), (Func<int,T$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InRefTupleQualifiedNestedGeneric5()
{
var markup = @"
using System;
class C
{
ref readonly (System.Func<(int, (C, (Func<int,T$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(37361, "https://github.com/dotnet/roslyn/issues/37361")]
public async Task InRefTupleQualifiedNestedGeneric6()
{
var markup = @"
using System;
class C
{
ref readonly (System.Collections.Generic.List<(int, (C, (Func<int,T$$
}";
await VerifyItemExistsAsync(markup, "T");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InNestedGeneric1()
{
......@@ -286,7 +868,7 @@ class Program
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task NotAfterAsync()
public async Task OkAfterAsync()
{
var markup = @"
using System.Threading.Tasks;
......@@ -295,7 +877,7 @@ class Program
async $$
}";
await VerifyItemIsAbsentAsync(markup, "T");
await VerifyItemExistsAsync(markup, "T");
}
[WorkItem(968256, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/968256")]
......@@ -384,7 +966,7 @@ public void M()
async $$
}
}";
await VerifyItemIsAbsentAsync(markup, "T");
await VerifyItemExistsAsync(markup, "T");
}
}
}
......@@ -56,46 +56,94 @@ private async Task<bool> ShouldShowSpeculativeTCompletionItemAsync(Document docu
return false;
}
// If we're in a generic type argument context, use the start of the generic type name
// as the position for the rest of the context checks.
var testPosition = position;
var leftToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
// We could be in the middle of a ref/generic/tuple type, instead of a simple T case.
// If we managed to walk out and get a different SpanStart, we treat it as a simple $$T case.
var semanticModel = await document.GetSemanticModelForNodeAsync(leftToken.Parent, cancellationToken).ConfigureAwait(false);
if (syntaxTree.IsGenericTypeArgumentContext(position, leftToken, cancellationToken, semanticModel))
var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
var semanticModel = await document.GetSemanticModelForNodeAsync(token.Parent, cancellationToken).ConfigureAwait(false);
var spanStart = position;
while (true)
{
var oldSpanStart = spanStart;
spanStart = WalkOutOfGenericType(syntaxTree, spanStart, semanticModel, cancellationToken);
spanStart = WalkOutOfTupleType(syntaxTree, spanStart, cancellationToken);
spanStart = WalkOutOfRefType(syntaxTree, spanStart, cancellationToken);
if (spanStart == oldSpanStart)
{
break;
}
}
return IsStartOfSpeculativeTContext(syntaxTree, spanStart, cancellationToken);
}
private static bool IsStartOfSpeculativeTContext(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
{
var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
return syntaxTree.IsMemberDeclarationContext(position, contextOpt: null, SyntaxKindSet.AllMemberModifiers, SyntaxKindSet.ClassInterfaceStructTypeDeclarations, canBePartial: true, cancellationToken) ||
syntaxTree.IsStatementContext(position, token, cancellationToken) ||
syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
syntaxTree.IsGlobalStatementContext(position, cancellationToken) ||
syntaxTree.IsDelegateReturnTypeContext(position, token, cancellationToken);
}
private static int WalkOutOfGenericType(SyntaxTree syntaxTree, int position, SemanticModel semanticModel, CancellationToken cancellationToken)
{
var spanStart = position;
var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
if (syntaxTree.IsGenericTypeArgumentContext(position, token, cancellationToken, semanticModel))
{
// Walk out until we find the start of the partial written generic
while (syntaxTree.IsInPartiallyWrittenGeneric(testPosition, cancellationToken, out var nameToken))
if (syntaxTree.IsInPartiallyWrittenGeneric(spanStart, cancellationToken, out var nameToken))
{
testPosition = nameToken.SpanStart;
spanStart = nameToken.SpanStart;
}
// If the user types Goo<T, automatic brace completion will insert the close brace
// and the generic won't be "partially written".
if (testPosition == position)
if (spanStart == position)
{
spanStart = token.GetAncestor<GenericNameSyntax>()?.SpanStart ?? spanStart;
}
var tokenLeftOfGenericName = syntaxTree.FindTokenOnLeftOfPosition(spanStart, cancellationToken);
if (tokenLeftOfGenericName.IsKind(SyntaxKind.DotToken) && tokenLeftOfGenericName.Parent.IsKind(SyntaxKind.QualifiedName))
{
var typeArgumentList = leftToken.GetAncestor<TypeArgumentListSyntax>();
if (typeArgumentList != null)
{
if (typeArgumentList.LessThanToken != default && typeArgumentList.GreaterThanToken != default)
{
testPosition = typeArgumentList.LessThanToken.SpanStart;
}
}
spanStart = tokenLeftOfGenericName.Parent.SpanStart;
}
}
if ((!leftToken.GetPreviousTokenIfTouchingWord(position).IsKindOrHasMatchingText(SyntaxKind.AsyncKeyword) &&
syntaxTree.IsMemberDeclarationContext(testPosition, contextOpt: null, validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) ||
syntaxTree.IsStatementContext(testPosition, leftToken, cancellationToken) ||
syntaxTree.IsGlobalMemberDeclarationContext(testPosition, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
syntaxTree.IsGlobalStatementContext(testPosition, cancellationToken) ||
syntaxTree.IsDelegateReturnTypeContext(testPosition, syntaxTree.FindTokenOnLeftOfPosition(testPosition, cancellationToken), cancellationToken))
return spanStart;
}
private static int WalkOutOfRefType(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
{
var prevToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken)
.GetPreviousTokenIfTouchingWord(position);
if (prevToken.IsKind(SyntaxKind.RefKeyword, SyntaxKind.ReadOnlyKeyword) && prevToken.Parent.IsKind(SyntaxKind.RefType))
{
return prevToken.SpanStart;
}
return position;
}
private static int WalkOutOfTupleType(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
{
var prevToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken)
.GetPreviousTokenIfTouchingWord(position);
if (prevToken.IsPossibleTupleOpenParenOrComma())
{
return true;
return prevToken.Parent.SpanStart;
}
return false;
return position;
}
}
}
......@@ -1304,7 +1304,7 @@ private static SyntaxToken FindTokenOnLeftOfNode(SyntaxNode node)
}
public static bool IsPossibleTupleOpenParenOrComma(SyntaxToken possibleCommaOrParen)
public static bool IsPossibleTupleOpenParenOrComma(this SyntaxToken possibleCommaOrParen)
{
if (!possibleCommaOrParen.IsKind(SyntaxKind.OpenParenToken, SyntaxKind.CommaToken))
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册