From 873f63effa3ab0eb29678c98a8e4208670eaa80b Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 4 Nov 2021 18:17:16 +1100 Subject: [PATCH 1/3] Don't crash on local functions in top level statements --- ...UseExpressionBodyForLocalFunctionHelper.cs | 11 +++ .../Helpers/UseExpressionBodyHelper.cs | 2 + .../Helpers/UseExpressionBodyHelper`1.cs | 8 +++ .../UseExpressionBodyCodeFixProvider.cs | 3 +- ...ssionBodyForLocalFunctionsAnalyzerTests.cs | 67 +++++++++++++++++++ 5 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs index 5dd65fd98daaf..2441123ee081b 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs @@ -5,10 +5,13 @@ #nullable disable using System.Collections.Immutable; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { @@ -45,6 +48,14 @@ protected override LocalFunctionStatementSyntax WithExpressionBody(LocalFunction protected override LocalFunctionStatementSyntax WithBody(LocalFunctionStatementSyntax statement, BlockSyntax body) => statement.WithBody(body); + protected override LocalFunctionStatementSyntax GetDeclaration(Location declarationLocation, CancellationToken cancellationToken) + => declarationLocation.FindNode(cancellationToken) switch + { + LocalFunctionStatementSyntax localFunc => localFunc, + GlobalStatementSyntax { Statement: LocalFunctionStatementSyntax localFunc } => localFunc, + _ => throw ExceptionUtilities.Unreachable + }; + protected override bool CreateReturnStatementForExpression( SemanticModel semanticModel, LocalFunctionStatementSyntax statement) { diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs index 9326e2018065b..c83e9a05409df 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Threading; #if CODE_STYLE using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; @@ -30,6 +31,7 @@ internal abstract class UseExpressionBodyHelper public abstract bool CanOfferUseExpressionBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); public abstract (bool canOffer, bool fixesError) CanOfferUseBlockBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); public abstract SyntaxNode Update(SemanticModel semanticModel, SyntaxNode declaration, bool useExpressionBody); + public abstract SyntaxNode GetDeclarationNode(Location declarationLocation, CancellationToken cancellationToken); public abstract Location GetDiagnosticLocation(SyntaxNode declaration); diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs index 805e7d14807ab..8a3367b12a8cf 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs @@ -7,11 +7,13 @@ using System; using System.Collections.Immutable; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; #if CODE_STYLE using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; @@ -87,6 +89,12 @@ public override Location GetDiagnosticLocation(SyntaxNode declaration) protected virtual Location GetDiagnosticLocation(TDeclaration declaration) => GetBody(declaration).Statements[0].GetLocation(); + public sealed override SyntaxNode GetDeclarationNode(Location declarationLocation, CancellationToken cancellationToken) + => GetDeclaration(declarationLocation, cancellationToken); + + protected virtual TDeclaration GetDeclaration(Location declarationLocation, CancellationToken cancellationToken) + => (TDeclaration)declarationLocation.FindNode(cancellationToken); + public bool CanOfferUseExpressionBody( OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) { diff --git a/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs index 10f7e8738ac58..0f4a5e8dd8f27 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -90,7 +89,7 @@ private static void AddEdits( { var declarationLocation = diagnostic.AdditionalLocations[0]; var helper = _helpers.Single(h => h.DiagnosticId == diagnostic.Id); - var declaration = declarationLocation.FindNode(cancellationToken); + var declaration = helper.GetDeclarationNode(declarationLocation, cancellationToken); var useExpressionBody = diagnostic.Properties.ContainsKey(nameof(UseExpressionBody)); var updatedDeclaration = helper.Update(semanticModel, declaration, useExpressionBody) diff --git a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForLocalFunctionsAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForLocalFunctionsAnalyzerTests.cs index 2ae402fdc5b2e..f0667646dd8fb 100644 --- a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForLocalFunctionsAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForLocalFunctionsAnalyzerTests.cs @@ -4,11 +4,13 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody @@ -829,5 +831,70 @@ void Bar() }"; await TestWithUseExpressionBody(code, fixedCode); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + [WorkItem(57570, "https://github.com/dotnet/roslyn/issues/57570")] + public async Task TestUseExpressionBodyTopLevelStatment() + { + await new VerifyCS.Test + { + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + Sources = + { + @" +{|IDE0061:int Bar(int x) +{ + return x; +}|} +" + }, + }, + FixedState = + { + Sources = + { + @" +int Bar(int x) => x; +" + }, + }, + LanguageVersion = LanguageVersion.CSharp9, + Options = { { CSharpCodeStyleOptions.PreferExpressionBodiedLocalFunctions, ExpressionBodyPreference.WhenPossible } }, + }.RunAsync(); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + [WorkItem(57570, "https://github.com/dotnet/roslyn/issues/57570")] + public async Task TestUseBlockBodyTopLevelStatment() + { + await new VerifyCS.Test + { + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + Sources = + { + @" +{|IDE0061:int Bar(int x) => x;|} +" + }, + }, + FixedState = + { + Sources = + { + @" +int Bar(int x) +{ + return x; +}" + }, + }, + LanguageVersion = LanguageVersion.CSharp9, + Options = { { CSharpCodeStyleOptions.PreferExpressionBodiedLocalFunctions, ExpressionBodyPreference.Never } }, + }.RunAsync(); + } } } From e5a6a2e7d0d36f6bfce6b9d43c2e1c4f39cbd402 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 5 Nov 2021 07:42:48 +1100 Subject: [PATCH 2/3] Simplify --- .../Helpers/UseExpressionBodyForLocalFunctionHelper.cs | 8 -------- .../UseExpressionBody/Helpers/UseExpressionBodyHelper.cs | 1 - .../Helpers/UseExpressionBodyHelper`1.cs | 6 ------ .../UseExpressionBody/UseExpressionBodyCodeFixProvider.cs | 2 +- 4 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs index 2441123ee081b..0c1dc67eeefaf 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs @@ -48,14 +48,6 @@ protected override LocalFunctionStatementSyntax WithExpressionBody(LocalFunction protected override LocalFunctionStatementSyntax WithBody(LocalFunctionStatementSyntax statement, BlockSyntax body) => statement.WithBody(body); - protected override LocalFunctionStatementSyntax GetDeclaration(Location declarationLocation, CancellationToken cancellationToken) - => declarationLocation.FindNode(cancellationToken) switch - { - LocalFunctionStatementSyntax localFunc => localFunc, - GlobalStatementSyntax { Statement: LocalFunctionStatementSyntax localFunc } => localFunc, - _ => throw ExceptionUtilities.Unreachable - }; - protected override bool CreateReturnStatementForExpression( SemanticModel semanticModel, LocalFunctionStatementSyntax statement) { diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs index c83e9a05409df..458b4a2029311 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs @@ -31,7 +31,6 @@ internal abstract class UseExpressionBodyHelper public abstract bool CanOfferUseExpressionBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); public abstract (bool canOffer, bool fixesError) CanOfferUseBlockBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); public abstract SyntaxNode Update(SemanticModel semanticModel, SyntaxNode declaration, bool useExpressionBody); - public abstract SyntaxNode GetDeclarationNode(Location declarationLocation, CancellationToken cancellationToken); public abstract Location GetDiagnosticLocation(SyntaxNode declaration); diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs index 8a3367b12a8cf..d2805baa57cc3 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs @@ -89,12 +89,6 @@ public override Location GetDiagnosticLocation(SyntaxNode declaration) protected virtual Location GetDiagnosticLocation(TDeclaration declaration) => GetBody(declaration).Statements[0].GetLocation(); - public sealed override SyntaxNode GetDeclarationNode(Location declarationLocation, CancellationToken cancellationToken) - => GetDeclaration(declarationLocation, cancellationToken); - - protected virtual TDeclaration GetDeclaration(Location declarationLocation, CancellationToken cancellationToken) - => (TDeclaration)declarationLocation.FindNode(cancellationToken); - public bool CanOfferUseExpressionBody( OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) { diff --git a/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs index 0f4a5e8dd8f27..eee0034fba488 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs @@ -89,7 +89,7 @@ private static void AddEdits( { var declarationLocation = diagnostic.AdditionalLocations[0]; var helper = _helpers.Single(h => h.DiagnosticId == diagnostic.Id); - var declaration = helper.GetDeclarationNode(declarationLocation, cancellationToken); + var declaration = declarationLocation.FindNode(getInnermostNodeForTie: true, cancellationToken); var useExpressionBody = diagnostic.Properties.ContainsKey(nameof(UseExpressionBody)); var updatedDeclaration = helper.Update(semanticModel, declaration, useExpressionBody) From 42df398960c40b9d86ee5df5fea325f64ca3cc9f Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 5 Nov 2021 07:43:55 +1100 Subject: [PATCH 3/3] Remove usings --- .../Helpers/UseExpressionBodyForLocalFunctionHelper.cs | 3 --- .../UseExpressionBody/Helpers/UseExpressionBodyHelper.cs | 1 - .../UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs index 0c1dc67eeefaf..5dd65fd98daaf 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs @@ -5,13 +5,10 @@ #nullable disable using System.Collections.Immutable; -using System.Threading; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs index 458b4a2029311..9326e2018065b 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Threading; #if CODE_STYLE using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs index d2805baa57cc3..805e7d14807ab 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs @@ -7,13 +7,11 @@ using System; using System.Collections.Immutable; using System.Linq; -using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; #if CODE_STYLE using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions;