diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index afd61c1a95ced47d2fac58ed1c9b3ced8cea6b64..c392c7220f8c0a2a89776457bf00170e48cacc93 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -10104,11 +10104,77 @@ void M(SomeCollection c) } }"; - await VerifyItemExistsAsync(markup, "Substring"); + await VerifyItemIsAbsentAsync(markup, "Substring"); await VerifyItemExistsAsync(markup, "A"); await VerifyItemExistsAsync(markup, "B"); } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [WorkItem(1056325, "https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1056325")] + public async Task CompletionForLambdaWithOverloads2() + { + var markup = @" +using System; + +class C +{ + void M(Action a) { } + void M(string s) { } + + void Test() + { + M(p => p.$$); + } +}"; + + await VerifyItemIsAbsentAsync(markup, "Substring"); + await VerifyItemExistsAsync(markup, "GetTypeCode"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [WorkItem(1056325, "https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1056325")] + public async Task CompletionForLambdaWithOverloads3() + { + var markup = @" +using System; + +class C +{ + void M(Action a) { } + void M(Action a) { } + + void Test() + { + M((int p) => p.$$); + } +}"; + + await VerifyItemIsAbsentAsync(markup, "Substring"); + await VerifyItemExistsAsync(markup, "GetTypeCode"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [WorkItem(1056325, "https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1056325")] + public async Task CompletionForLambdaWithOverloads4() + { + var markup = @" +using System; + +class C +{ + void M(Action a) { } + void M(Action a) { } + + void Test() + { + M(p => p.$$); + } +}"; + + await VerifyItemExistsAsync(markup, "Substring"); + await VerifyItemExistsAsync(markup, "GetTypeCode"); + } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] [WorkItem(40216, "https://github.com/dotnet/roslyn/issues/40216")] public async Task CompletionForLambdaPassedAsNamedArgumentAtDifferentPositionFromCorrespondingParameter1() diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb index 76bd5a19e84e9be47acf33ca9293ff1415103b7a..4e91fcecd6f6e5c0a70aec80be9095fa03e3b393 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb @@ -8110,11 +8110,89 @@ Namespace VBTest End Namespace ]]>.Value - Await VerifyItemExistsAsync(source, "Substring") + Await VerifyItemIsAbsentAsync(source, "Substring") Await VerifyItemExistsAsync(source, "A") Await VerifyItemExistsAsync(source, "B") End Function + + + Public Async Function CompletionForLambdaWithOverloads2() As Task + Dim source = + .Value + + Await VerifyItemIsAbsentAsync(source, "Substring") + Await VerifyItemExistsAsync(source, "GetTypeCode") + End Function + + + + Public Async Function CompletionForLambdaWithOverloads3() As Task + Dim source = + .Value + + Await VerifyItemIsAbsentAsync(source, "Substring") + Await VerifyItemExistsAsync(source, "GetTypeCode") + End Function + + + + Public Async Function CompletionForLambdaWithOverloads4() As Task + Dim source = + .Value + + Await VerifyItemExistsAsync(source, "Substring") + Await VerifyItemExistsAsync(source, "GetTypeCode") + End Function + Public Async Function CompletionForLambdaPassedAsNamedArgumentAtDifferentPositionFromCorrespondingParameter1() As Task diff --git a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs index 819f7b0a0aa315d421f1fdcb817c842aeef6e2fe..a59ceed2e4da70cba5c09252b3026baee37d470f 100644 --- a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs +++ b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs @@ -2,8 +2,10 @@ // 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; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; @@ -38,6 +40,27 @@ public override ImmutableArray GetSymbols() : GetSymbolsForCurrentContext(); } + public override bool TryGetExplicitTypeOfLambdaParameter(SyntaxNode lambdaSyntax, int ordinalInLambda, [NotNullWhen(true)] out ITypeSymbol explicitLambdaParameterType) + { + if (lambdaSyntax.IsKind(SyntaxKind.ParenthesizedLambdaExpression, out var parenthesizedLambdaSyntax)) + { + var parameters = parenthesizedLambdaSyntax.ParameterList.Parameters; + if (parameters.Count > ordinalInLambda) + { + var parameter = parameters[ordinalInLambda]; + if (parameter.Type != null) + { + explicitLambdaParameterType = _context.SemanticModel.GetTypeInfo(parameter.Type, _cancellationToken).Type; + return explicitLambdaParameterType != null; + } + } + } + + // Non-parenthesized lambdas cannot explicitly specify the type of the single parameter + explicitLambdaParameterType = null; + return false; + } + private ImmutableArray GetSymbolsForCurrentContext() { if (_context.IsGlobalStatementContext) diff --git a/src/Workspaces/Core/Portable/Recommendations/AbstractRecommendationServiceRunner.cs b/src/Workspaces/Core/Portable/Recommendations/AbstractRecommendationServiceRunner.cs index 8df74b20e711242c1b31f45e90bb28f5579da10d..4e83060d4ee5ad8fc9ac55b5d5a2fe2633c5da52 100644 --- a/src/Workspaces/Core/Portable/Recommendations/AbstractRecommendationServiceRunner.cs +++ b/src/Workspaces/Core/Portable/Recommendations/AbstractRecommendationServiceRunner.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -36,6 +37,8 @@ internal abstract class AbstractRecommendationServiceRunner public abstract ImmutableArray GetSymbols(); + public abstract bool TryGetExplicitTypeOfLambdaParameter(SyntaxNode lambdaSyntax, int ordinalInLambda, [NotNullWhen(returnValue: true)] out ITypeSymbol explicitLambdaParameterType); + // This code is to help give intellisense in the following case: // query.Include(a => a.SomeProperty).ThenInclude(a => a. // where there are more than one overloads of ThenInclude accepting different types of parameters. @@ -74,13 +77,22 @@ protected ImmutableArray GetSymbols(IParameterSymbol parameter, int pos var ordinalInInvocation = arguments.IndexOf(lambdaSyntax.Parent); var expressionOfInvocationExpression = syntaxFactsService.GetExpressionOfInvocationExpression(invocationExpression); - // Get all members potentially matching the invocation expression. - // We filter them out based on ordinality later. - var candidateSymbols = _context.SemanticModel.GetMemberGroup(expressionOfInvocationExpression, _cancellationToken); + var parameterTypeSymbols = ImmutableArray.Empty; - // parameter.Ordinal is the ordinal within (a,b,c) => b. - // For candidate symbols of (a,b,c) => b., get types of all possible b. - var parameterTypeSymbols = GetTypeSymbols(candidateSymbols, argumentName, ordinalInInvocation, ordinalInLambda: parameter.Ordinal); + if (TryGetExplicitTypeOfLambdaParameter(lambdaSyntax, parameter.Ordinal, out var explicitLambdaParameterType)) + { + parameterTypeSymbols = ImmutableArray.Create(explicitLambdaParameterType); + } + else + { + // Get all members potentially matching the invocation expression. + // We filter them out based on ordinality later. + var candidateSymbols = _context.SemanticModel.GetMemberGroup(expressionOfInvocationExpression, _cancellationToken); + + // parameter.Ordinal is the ordinal within (a,b,c) => b. + // For candidate symbols of (a,b,c) => b., get types of all possible b. + parameterTypeSymbols = GetTypeSymbols(candidateSymbols, argumentName, ordinalInInvocation, ordinalInLambda: parameter.Ordinal); + } // For each type of b., return all suitable members. return parameterTypeSymbols @@ -141,10 +153,8 @@ private ImmutableArray GetTypeSymbols(ImmutableArray candi continue; } - type = parameters[ordinalInLambda].Type; + builder.Add(parameters[ordinalInLambda].Type); } - - builder.Add(type); } } diff --git a/src/Workspaces/VisualBasic/Portable/Recommendations/VisualBasicRecommendationServiceRunner.vb b/src/Workspaces/VisualBasic/Portable/Recommendations/VisualBasicRecommendationServiceRunner.vb index cf3d48f4761b69ece23bd113286a482fff099c66..fb49a6b63984e12344fe8f3282c224f4931fe21e 100644 --- a/src/Workspaces/VisualBasic/Portable/Recommendations/VisualBasicRecommendationServiceRunner.vb +++ b/src/Workspaces/VisualBasic/Portable/Recommendations/VisualBasicRecommendationServiceRunner.vb @@ -3,7 +3,9 @@ ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable +Imports System.Diagnostics.CodeAnalysis Imports System.Threading +Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Recommendations Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery Imports Microsoft.CodeAnalysis.VisualBasic.Symbols @@ -54,6 +56,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Recommendations Return ImmutableArray(Of ISymbol).Empty End Function + Public Overrides Function TryGetExplicitTypeOfLambdaParameter(lambdaSyntax As SyntaxNode, ordinalInLambda As Integer, ByRef explicitLambdaParameterType As ITypeSymbol) As Boolean + Dim lambdaExpressionSyntax = DirectCast(lambdaSyntax, LambdaExpressionSyntax) + Dim parameters = lambdaExpressionSyntax.SubOrFunctionHeader.ParameterList.Parameters + If parameters.Count > ordinalInLambda Then + Dim parameterSyntax = parameters(ordinalInLambda) + If parameterSyntax.AsClause IsNot Nothing Then + explicitLambdaParameterType = _context.SemanticModel.GetTypeInfo(parameterSyntax.AsClause.Type, _cancellationToken).Type + Return explicitLambdaParameterType IsNot Nothing + End If + End If + + Return False + End Function + Private Function IsWritableFieldOrLocal(symbol As ISymbol) As Boolean If symbol.Kind() = SymbolKind.Field Then Dim field = DirectCast(symbol, IFieldSymbol)