From aa16ff811a6f4cc01ec2140dabc70f05b5c9b85b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 1 Mar 2020 21:51:07 -0800 Subject: [PATCH] Adjust cases where we offer ?.Invoke --- ...nvokeDelegateWithConditionalAccessTests.cs | 130 ++++++++++++++++++ ...keDelegateWithConditionalAccessAnalyzer.cs | 19 ++- 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests.cs index 43e6b1b71e5..5bc1e26e558 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests.cs @@ -757,6 +757,136 @@ int Goo() return v(); } } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)] + [WorkItem(13226, "https://github.com/dotnet/roslyn/issues/13226")] + public async Task TestWithLambdaInitializer() + { + await TestInRegularAndScript1Async( +@" +using System; + +class C +{ + void Goo() + { + Action v = () => {}; + [||]if (v != null) + { + v(); + } + } +}", + +@" +using System; + +class C +{ + void Goo() + { + Action v = () => {}; + v?.Invoke(); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)] + [WorkItem(13226, "https://github.com/dotnet/roslyn/issues/13226")] + public async Task TestWithLambdaInitializer2() + { + await TestInRegularAndScript1Async( +@" +using System; + +class C +{ + void Goo() + { + Action v = (() => {}); + [||]if (v != null) + { + v(); + } + } +}", + +@" +using System; + +class C +{ + void Goo() + { + Action v = (() => {}); + v?.Invoke(); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)] + [WorkItem(13226, "https://github.com/dotnet/roslyn/issues/13226")] + public async Task TestForWithAnonymousMethod() + { + await TestInRegularAndScript1Async( +@" +using System; + +class C +{ + void Goo() + { + Action v = delegate {}; + [||]if (v != null) + { + v(); + } + } +}", +@" +using System; + +class C +{ + void Goo() + { + Action v = delegate {}; + v?.Invoke(); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)] + [WorkItem(13226, "https://github.com/dotnet/roslyn/issues/13226")] + public async Task TestWithMethodReference() + { + await TestInRegularAndScript1Async( +@" +using System; + +class C +{ + void Goo() + { + Action v = Console.WriteLine; + [||]if (v != null) + { + v(); + } + } +}", +@" +using System; + +class C +{ + void Goo() + { + Action v = Console.WriteLine; + v?.Invoke(); + } }"); } } diff --git a/src/Features/CSharp/Portable/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs b/src/Features/CSharp/Portable/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs index 0981fa2de16..2f809c0dc75 100644 --- a/src/Features/CSharp/Portable/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs +++ b/src/Features/CSharp/Portable/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs @@ -2,6 +2,7 @@ // 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.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; @@ -9,6 +10,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.InvokeDelegateWithConditionalAccess @@ -252,7 +254,7 @@ private void SyntaxNodeAction(SyntaxNodeAnalysisContext syntaxContext) } var declarator = variableDeclaration.Variables[0]; - if (declarator.Initializer == null) + if (declarator.Initializer?.Value == null) { return false; } @@ -265,6 +267,21 @@ private void SyntaxNodeAction(SyntaxNodeAnalysisContext syntaxContext) // Syntactically this looks good. Now make sure that the local is a delegate type. var semanticModel = syntaxContext.SemanticModel; + + // The initializer can't be inlined if it's an actual lambda/method reference. + // These cannot be invoked with `?.` (only delegate *values* can be). + var initializer = declarator.Initializer.Value.WalkDownParentheses(); + if (initializer.IsAnyLambdaOrAnonymousMethod()) + { + return false; + } + + var initializerSymbol = semanticModel.GetSymbolInfo(initializer, cancellationToken).GetAnySymbol(); + if (initializerSymbol is IMethodSymbol) + { + return false; + } + var localSymbol = (ILocalSymbol)semanticModel.GetDeclaredSymbol(declarator, cancellationToken); // Ok, we made a local just to check it for null and invoke it. Looks like something -- GitLab