未验证 提交 d32f2d4b 编写于 作者: M Manish Vasani 提交者: GitHub

Merge pull request #28599 from CyrusNajmabadi/extensionMethodSuggestionMode

Put completion in 'suggestion' mode when typing a lambda to a method that could be an extension or instance method.
......@@ -843,6 +843,262 @@ void A()
await VerifyBuilderAsync(markup);
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithExtensionAndInstanceMethod1()
{
var markup = @"
using System;
public sealed class Goo
{
public void Bar()
{
}
}
public static class GooExtensions
{
public static void Bar(this Goo goo, Action<int> action)
{
}
}
public static class Repro
{
public static void ReproMethod(Goo goo)
{
goo.Bar(a$$
}
}
";
await VerifyBuilderAsync(markup);
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithExtensionAndInstanceMethod2()
{
var markup = @"
using System;
public sealed class Goo
{
public void Bar()
{
}
}
public static class GooExtensions
{
public static void Bar(this Goo goo, Action<int> action)
{
}
}
public static class Repro
{
public static void ReproMethod(Goo goo)
{
goo.Bar(a$$)
}
}
";
await VerifyBuilderAsync(markup);
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithExtensionAndInstanceMethod3()
{
var markup = @"
using System;
public sealed class Goo
{
public void Bar()
{
}
}
public static class GooExtensions
{
public static void Bar(this Goo goo, Action<int> action)
{
}
}
public static class Repro
{
public static void ReproMethod(Goo goo)
{
goo.Bar(($$
}
}
";
await VerifyBuilderAsync(markup);
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithExtensionAndInstanceMethod4()
{
var markup = @"
using System;
public sealed class Goo
{
public void Bar()
{
}
}
public static class GooExtensions
{
public static void Bar(this Goo goo, Action<int> action)
{
}
}
public static class Repro
{
public static void ReproMethod(Goo goo)
{
goo.Bar(($$)
}
}
";
await VerifyBuilderAsync(markup);
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithExtensionAndInstanceMethod5()
{
var markup = @"
using System;
public sealed class Goo
{
public void Bar()
{
}
}
public static class GooExtensions
{
public static void Bar(this Goo goo, Action<int> action)
{
}
}
public static class Repro
{
public static void ReproMethod(Goo goo)
{
goo.Bar(($$))
}
}
";
await VerifyBuilderAsync(markup);
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithExtensionAndInstanceMethod6()
{
var markup = @"
using System;
public sealed class Goo
{
public void Bar()
{
}
}
public static class GooExtensions
{
public static void Bar(this Goo goo, Action<int> action)
{
}
}
public static class Repro
{
public static void ReproMethod(Goo goo)
{
goo.Bar((a, $$
}
}
";
await VerifyBuilderAsync(markup);
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithExtensionAndInstanceMethod7()
{
var markup = @"
using System;
public sealed class Goo
{
public void Bar()
{
}
}
public static class GooExtensions
{
public static void Bar(this Goo goo, Action<int> action)
{
}
}
public static class Repro
{
public static void ReproMethod(Goo goo)
{
goo.Bar(async (a$$
}
}
";
await VerifyBuilderAsync(markup);
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task WithNonDelegateExtensionAndInstanceMethod1()
{
var markup = @"
using System;
public sealed class Goo
{
public void Bar()
{
}
}
public static class GooExtensions
{
public static void Bar(this Goo goo, int val)
{
}
}
public static class Repro
{
public static void ReproMethod(Goo goo)
{
goo.Bar(a$$
}
}
";
await VerifyNotBuilderAsync(markup);
}
private async Task VerifyNotBuilderAsync(string markup)
{
await VerifyWorkerAsync(markup, isBuilder: false);
......
......@@ -115,5 +115,26 @@ public async Task TestNotWithinNumericLiteral()
await VerifyAbsenceAsync(AddInsideMethod(
@"var x = .$$0;"));
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterAsync()
{
await VerifyAbsenceAsync(
@"
using System;
class C
{
void Goo()
{
Bar(async $$
}
void Bar(Func<int, string> f)
{
}
}");
}
}
}
......@@ -167,5 +167,26 @@ public async Task TestNotWithinNumericLiteral()
await VerifyAbsenceAsync(AddInsideMethod(
@"var x = .$$0;"));
}
[WorkItem(28586, "https://github.com/dotnet/roslyn/issues/28586")]
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterAsync()
{
await VerifyAbsenceAsync(
@"
using System;
class C
{
void Goo()
{
Bar(async $$
}
void Bar(Func<int, string> f)
{
}
}");
}
}
}
......@@ -223,7 +223,7 @@ private static CSharpSyntaxContext CreateContextWorker(Workspace workspace, Sema
syntaxTree.IsLabelContext(position, cancellationToken),
syntaxTree.IsTypeArgumentOfConstraintClause(position, cancellationToken),
syntaxTree.IsRightOfDotOrArrowOrColonColon(position, cancellationToken),
syntaxTree.IsIsOrAsContext(position, leftToken, cancellationToken),
syntaxTree.IsIsOrAsContext(semanticModel, position, leftToken, cancellationToken),
syntaxTree.IsObjectCreationTypeContext(position, leftToken, cancellationToken),
syntaxTree.IsDefiniteCastTypeContext(position, leftToken, cancellationToken),
syntaxTree.IsGenericTypeArgumentContext(position, leftToken, cancellationToken),
......
......@@ -2546,7 +2546,10 @@ public static bool IsNameOfContext(this SyntaxTree syntaxTree, int position, Sem
return false;
}
public static bool IsIsOrAsContext(this SyntaxTree syntaxTree, int position, SyntaxToken tokenOnLeftOfPosition, CancellationToken cancellationToken)
public static bool IsIsOrAsContext(
this SyntaxTree syntaxTree, SemanticModel semanticModel,
int position, SyntaxToken tokenOnLeftOfPosition,
CancellationToken cancellationToken)
{
// cases:
// expr |
......@@ -2617,6 +2620,20 @@ public static bool IsIsOrAsContext(this SyntaxTree syntaxTree, int position, Syn
}
}
if (token.Text == SyntaxFacts.GetText(SyntaxKind.AsyncKeyword))
{
// async $$
//
// 'async' will look like a normal identifier. But we don't want to follow it
// with 'is' or 'as' if it's actually the start of a lambda.
var delegateType = CSharpTypeInferenceService.Instance.InferDelegateType(
semanticModel, token.SpanStart, cancellationToken);
if (delegateType != null)
{
return false;
}
}
// Now, make sure the name was actually in a location valid for
// an expression. If so, then we know we can follow it.
if (syntaxTree.IsExpressionContext(nameExpr.SpanStart, syntaxTree.FindTokenOnLeftOfPosition(nameExpr.SpanStart, cancellationToken), attributes: false, cancellationToken: cancellationToken))
......
......@@ -446,19 +446,18 @@ private IEnumerable<TypeInferenceInfo> InferTypeInObjectCreationExpression(Objec
// extension method then it will need one more argument in order for us to
// call it.
var info = SemanticModel.GetSymbolInfo(invocation, CancellationToken);
IEnumerable<IMethodSymbol> methods = null;
var methods = info.GetBestOrAllSymbols().OfType<IMethodSymbol>();
// Overload resolution (see DevDiv 611477) in certain extension method cases
// can result in GetSymbolInfo returning nothing. In this case, get the
// method group info, which is what signature help already does.
if (info.Symbol == null && info.CandidateReason == CandidateReason.None)
if (info.Symbol == null)
{
methods = SemanticModel.GetMemberGroup(invocation.Expression, CancellationToken)
.OfType<IMethodSymbol>();
}
else
{
methods = info.GetBestOrAllSymbols().OfType<IMethodSymbol>();
var memberGroupMethods =
SemanticModel.GetMemberGroup(invocation.Expression, CancellationToken)
.OfType<IMethodSymbol>();
methods = methods.Concat(memberGroupMethods).Distinct();
}
return InferTypeInArgument(index, methods, argumentOpt);
......
......@@ -15,6 +15,8 @@ namespace Microsoft.CodeAnalysis.CSharp
[ExportLanguageService(typeof(ITypeInferenceService), LanguageNames.CSharp), Shared]
internal partial class CSharpTypeInferenceService : AbstractTypeInferenceService<ExpressionSyntax>
{
public static readonly CSharpTypeInferenceService Instance = new CSharpTypeInferenceService();
protected override AbstractTypeInferrer CreateTypeInferrer(SemanticModel semanticModel, CancellationToken cancellationToken)
{
return new TypeInferrer(semanticModel, cancellationToken);
......
......@@ -29,6 +29,21 @@ public static ImmutableArray<TypeInferenceInfo> GetTypeInferenceInfo(this ITypeI
CancellationToken cancellationToken)
{
var types = typeInferenceService.InferTypes(semanticModel, expression, cancellationToken);
return GetFirstDelegateType(semanticModel, types);
}
public static INamedTypeSymbol InferDelegateType(
this ITypeInferenceService typeInferenceService,
SemanticModel semanticModel,
int position,
CancellationToken cancellationToken)
{
var types = typeInferenceService.InferTypes(semanticModel, position, cancellationToken);
return GetFirstDelegateType(semanticModel, types);
}
private static INamedTypeSymbol GetFirstDelegateType(SemanticModel semanticModel, ImmutableArray<ITypeSymbol> types)
{
var delegateTypes = types.Select(t => t.GetDelegateType(semanticModel.Compilation));
return delegateTypes.WhereNotNull().FirstOrDefault();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册