From 67a0fc5cfe9dd793cc6e504513ed6805678c1739 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 13 Mar 2021 19:26:18 +0100 Subject: [PATCH] Add analyzer ConvertExpressionBodyToBlockBody (RCS1016i) --- src/Analyzers/Analyzers.xml | 16 + ...BodyToExpressionBodyOrViceVersaAnalyzer.cs | 85 +- ...erOptionDiagnosticDescriptors.Generated.cs | 12 + ...erOptionDiagnosticIdentifiers.Generated.cs | 1 + .../CSharp/AnalyzerOptions.Generated.cs | 1 + .../AnalyzerOptionsAnalyzer.Generated.cs | 2 +- .../CSharp/DiagnosticDescriptors.Generated.cs | 12 + ...16ConvertExpressionBodyToBlockBodyTests.cs | 837 ++++++++++++++++++ ...ertExpressionBodyToBlockBodyRefactoring.cs | 12 +- 9 files changed, 972 insertions(+), 6 deletions(-) create mode 100644 src/Tests/Analyzers.Tests/RCS1016ConvertExpressionBodyToBlockBodyTests.cs diff --git a/src/Analyzers/Analyzers.xml b/src/Analyzers/Analyzers.xml index c057e3d718..10c843037d 100644 --- a/src/Analyzers/Analyzers.xml +++ b/src/Analyzers/Analyzers.xml @@ -379,6 +379,22 @@ foreach (var item in items) // [|Id|] + diff --git a/src/Analyzers/CSharp/Analysis/ConvertBlockBodyToExpressionBodyOrViceVersaAnalyzer.cs b/src/Analyzers/CSharp/Analysis/ConvertBlockBodyToExpressionBodyOrViceVersaAnalyzer.cs index 83fdd98d4f..d613fbec8b 100644 --- a/src/Analyzers/CSharp/Analysis/ConvertBlockBodyToExpressionBodyOrViceVersaAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/ConvertBlockBodyToExpressionBodyOrViceVersaAnalyzer.cs @@ -66,6 +66,9 @@ private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) if (!analysis.Success) return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && methodDeclaration.SyntaxTree.IsMultiLineSpan(methodDeclaration.HeaderSpan())) { @@ -80,6 +83,12 @@ private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) if (expressionBody?.ContainsDirectives == false) { + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && methodDeclaration.SyntaxTree.IsMultiLineSpan(methodDeclaration.HeaderSpan())) { @@ -107,6 +116,12 @@ private static void AnalyzePropertyDeclaration(SyntaxNodeAnalysisContext context if (expressionBody?.ContainsDirectives == false) { + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && propertyDeclaration.SyntaxTree.IsMultiLineSpan(propertyDeclaration.HeaderSpan())) { @@ -133,6 +148,12 @@ private static void AnalyzeIndexerDeclaration(SyntaxNodeAnalysisContext context) if (expressionBody?.ContainsDirectives == false) { + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && indexerDeclaration.SyntaxTree.IsMultiLineSpan(indexerDeclaration.HeaderSpan())) { @@ -166,6 +187,9 @@ private static void AnalyzeOperatorDeclaration(SyntaxNodeAnalysisContext context if (!analysis.Success) return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && operatorDeclaration.SyntaxTree.IsMultiLineSpan(operatorDeclaration.HeaderSpan())) { @@ -180,6 +204,12 @@ private static void AnalyzeOperatorDeclaration(SyntaxNodeAnalysisContext context if (expressionBody?.ContainsDirectives == false) { + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && operatorDeclaration.SyntaxTree.IsMultiLineSpan(operatorDeclaration.HeaderSpan())) { @@ -214,6 +244,9 @@ private static void AnalyzeConversionOperatorDeclaration(SyntaxNodeAnalysisConte if (!analysis.Success) return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && operatorDeclaration.SyntaxTree.IsMultiLineSpan(operatorDeclaration.HeaderSpan())) { @@ -228,6 +261,12 @@ private static void AnalyzeConversionOperatorDeclaration(SyntaxNodeAnalysisConte if (expressionBody?.ContainsDirectives == false) { + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && operatorDeclaration.SyntaxTree.IsMultiLineSpan(operatorDeclaration.HeaderSpan())) { @@ -262,6 +301,9 @@ private static void AnalyzeConstructorDeclaration(SyntaxNodeAnalysisContext cont if (!analysis.Success) return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && constructorDeclaration.SyntaxTree.IsMultiLineSpan(constructorDeclaration.HeaderSpan())) { @@ -276,6 +318,12 @@ private static void AnalyzeConstructorDeclaration(SyntaxNodeAnalysisContext cont if (expressionBody?.ContainsDirectives == false) { + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && constructorDeclaration.SyntaxTree.IsMultiLineSpan(constructorDeclaration.HeaderSpan())) { @@ -310,6 +358,9 @@ private static void AnalyzeDestructorDeclaration(SyntaxNodeAnalysisContext conte if (!analysis.Success) return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && destructorDeclaration.SyntaxTree.IsMultiLineSpan(destructorDeclaration.HeaderSpan())) { @@ -324,6 +375,12 @@ private static void AnalyzeDestructorDeclaration(SyntaxNodeAnalysisContext conte if (expressionBody?.ContainsDirectives == false) { + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && destructorDeclaration.SyntaxTree.IsMultiLineSpan(destructorDeclaration.HeaderSpan())) { @@ -358,6 +415,9 @@ private static void AnalyzeLocalFunctionStatement(SyntaxNodeAnalysisContext cont if (!analysis.Success) return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && localFunction.SyntaxTree.IsMultiLineSpan(localFunction.HeaderSpan())) { @@ -372,6 +432,12 @@ private static void AnalyzeLocalFunctionStatement(SyntaxNodeAnalysisContext cont if (expressionBody?.ContainsDirectives == false) { + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine.IsEnabled(context) && localFunction.SyntaxTree.IsMultiLineSpan(localFunction.HeaderSpan())) { @@ -405,11 +471,19 @@ private static void AnalyzeAccessorDeclaration(SyntaxNodeAnalysisContext context { ArrowExpressionClauseSyntax expressionBody = accessor.ExpressionBody; - if (expressionBody?.ContainsDirectives == false - && AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenExpressionIsMultiLine.IsEnabled(context) - && expressionBody.Expression?.IsMultiLine() == true) + if (expressionBody?.ContainsDirectives == false) { - DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBodyWhenExpressionIsMultiLine, expressionBody); + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBody, expressionBody); + return; + } + + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenExpressionIsMultiLine.IsEnabled(context) + && expressionBody.Expression?.IsMultiLine() == true) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.ReportOnly.ConvertExpressionBodyToBlockBodyWhenExpressionIsMultiLine, expressionBody); + } } } } @@ -434,6 +508,9 @@ private static void AnalyzeAccessorDeclarationBlock( if (expression == null) return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBody.IsEnabled(context)) + return; + if (AnalyzerOptions.ConvertExpressionBodyToBlockBodyWhenExpressionIsMultiLine.IsEnabled(context) && expression.IsMultiLine()) { diff --git a/src/Analyzers/CSharp/AnalyzerOptionDiagnosticDescriptors.Generated.cs b/src/Analyzers/CSharp/AnalyzerOptionDiagnosticDescriptors.Generated.cs index f6e280e84a..5a63c34069 100644 --- a/src/Analyzers/CSharp/AnalyzerOptionDiagnosticDescriptors.Generated.cs +++ b/src/Analyzers/CSharp/AnalyzerOptionDiagnosticDescriptors.Generated.cs @@ -57,6 +57,18 @@ public static partial class AnalyzerOptionDiagnosticDescriptors helpLinkUri: AnalyzerOptionDiagnosticIdentifiers.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine, customTags: Array.Empty()); + /// RCS1016i + public static readonly DiagnosticDescriptor ConvertExpressionBodyToBlockBody = DiagnosticDescriptorFactory.Default.Create( + id: AnalyzerOptionDiagnosticIdentifiers.ConvertExpressionBodyToBlockBody, + title: "Convert expression-body to block body.", + messageFormat: "Convert expression-body to block body.", + category: DiagnosticCategories.AnalyzerOption, + defaultSeverity: DiagnosticSeverity.Hidden, + isEnabledByDefault: false, + description: null, + helpLinkUri: AnalyzerOptionDiagnosticIdentifiers.ConvertExpressionBodyToBlockBody, + customTags: Array.Empty()); + /// RCS1018i public static readonly DiagnosticDescriptor RemoveAccessibilityModifiers = DiagnosticDescriptorFactory.Default.Create( id: AnalyzerOptionDiagnosticIdentifiers.RemoveAccessibilityModifiers, diff --git a/src/Analyzers/CSharp/AnalyzerOptionDiagnosticIdentifiers.Generated.cs b/src/Analyzers/CSharp/AnalyzerOptionDiagnosticIdentifiers.Generated.cs index 3636fc33be..3f3288fe2a 100644 --- a/src/Analyzers/CSharp/AnalyzerOptionDiagnosticIdentifiers.Generated.cs +++ b/src/Analyzers/CSharp/AnalyzerOptionDiagnosticIdentifiers.Generated.cs @@ -12,6 +12,7 @@ public static partial class AnalyzerOptionDiagnosticIdentifiers public const string UseImplicitlyTypedArray = "RCS1014i"; public const string ConvertExpressionBodyToBlockBodyWhenExpressionIsMultiLine = "RCS1016a"; public const string ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine = "RCS1016b"; + public const string ConvertExpressionBodyToBlockBody = "RCS1016i"; public const string RemoveAccessibilityModifiers = "RCS1018i"; public const string RemoveEmptyLineBetweenClosingBraceAndSwitchSection = "RCS1036a"; public const string DoNotRenamePrivateStaticReadOnlyFieldToCamelCaseWithUnderscore = "RCS1045a"; diff --git a/src/Analyzers/CSharp/AnalyzerOptions.Generated.cs b/src/Analyzers/CSharp/AnalyzerOptions.Generated.cs index 794f5f31c4..d543db0626 100644 --- a/src/Analyzers/CSharp/AnalyzerOptions.Generated.cs +++ b/src/Analyzers/CSharp/AnalyzerOptions.Generated.cs @@ -17,6 +17,7 @@ public static partial class AnalyzerOptions internal static readonly AnalyzerOptionDescriptor UseIsNullPatternInsteadOfInequalityOperator = new AnalyzerOptionDescriptor(AnalyzerOptionDiagnosticDescriptors.UseIsNullPatternInsteadOfInequalityOperator, DiagnosticDescriptors.UseIsNullPatternInsteadOfComparisonOrViceVersa, "roslynator.RCS1248.enable_inequality_operator"); internal static readonly AnalyzerOptionDescriptor ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine = new AnalyzerOptionDescriptor(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine, DiagnosticDescriptors.ConvertBlockBodyToExpressionBodyOrViceVersa, "roslynator.RCS1016.use_block_body_when_declaration_is_multiline"); internal static readonly AnalyzerOptionDescriptor UseImplicitlyTypedArray = new AnalyzerOptionDescriptor(AnalyzerOptionDiagnosticDescriptors.UseImplicitlyTypedArray, DiagnosticDescriptors.UseExplicitlyTypedArrayOrViceVersa, "roslynator.RCS1014.invert"); + internal static readonly AnalyzerOptionDescriptor ConvertExpressionBodyToBlockBody = new AnalyzerOptionDescriptor(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody, DiagnosticDescriptors.ConvertBlockBodyToExpressionBodyOrViceVersa, "roslynator.RCS1016.invert"); internal static readonly AnalyzerOptionDescriptor RemoveAccessibilityModifiers = new AnalyzerOptionDescriptor(AnalyzerOptionDiagnosticDescriptors.RemoveAccessibilityModifiers, DiagnosticDescriptors.AddAccessibilityModifiersOrViceVersa, "roslynator.RCS1018.invert"); internal static readonly AnalyzerOptionDescriptor RemoveArgumentListFromObjectCreation = new AnalyzerOptionDescriptor(AnalyzerOptionDiagnosticDescriptors.RemoveArgumentListFromObjectCreation, DiagnosticDescriptors.AddArgumentListToObjectCreationOrViceVersa, "roslynator.RCS1050.invert"); internal static readonly AnalyzerOptionDescriptor UseStringEmptyInsteadOfEmptyStringLiteral = new AnalyzerOptionDescriptor(AnalyzerOptionDiagnosticDescriptors.UseStringEmptyInsteadOfEmptyStringLiteral, DiagnosticDescriptors.UseEmptyStringLiteralInsteadOfStringEmptyOrViceVersa, "roslynator.RCS1078.invert"); diff --git a/src/Analyzers/CSharp/AnalyzerOptionsAnalyzer.Generated.cs b/src/Analyzers/CSharp/AnalyzerOptionsAnalyzer.Generated.cs index eda0e9284b..cf701ae6c5 100644 --- a/src/Analyzers/CSharp/AnalyzerOptionsAnalyzer.Generated.cs +++ b/src/Analyzers/CSharp/AnalyzerOptionsAnalyzer.Generated.cs @@ -15,7 +15,7 @@ public override ImmutableArray SupportedDiagnostics { get { - return ImmutableArray.Create(AnalyzerOptionDiagnosticDescriptors.UseImplicitlyTypedArrayWhenTypeIsObvious, AnalyzerOptionDiagnosticDescriptors.UseImplicitlyTypedArray, AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBodyWhenExpressionIsMultiLine, AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine, AnalyzerOptionDiagnosticDescriptors.RemoveAccessibilityModifiers, AnalyzerOptionDiagnosticDescriptors.RemoveEmptyLineBetweenClosingBraceAndSwitchSection, AnalyzerOptionDiagnosticDescriptors.DoNotRenamePrivateStaticReadOnlyFieldToCamelCaseWithUnderscore, AnalyzerOptionDiagnosticDescriptors.RemoveArgumentListFromObjectCreation, AnalyzerOptionDiagnosticDescriptors.RemoveParenthesesFromConditionOfConditionalExpressionWhenExpressionIsSingleToken, AnalyzerOptionDiagnosticDescriptors.UseStringEmptyInsteadOfEmptyStringLiteral, AnalyzerOptionDiagnosticDescriptors.RemoveCallToConfigureAwait, AnalyzerOptionDiagnosticDescriptors.ConvertBitwiseOperationToHasFlagCall, AnalyzerOptionDiagnosticDescriptors.SimplifyConditionalExpressionWhenItIncludesNegationOfCondition, AnalyzerOptionDiagnosticDescriptors.ConvertMethodGroupToAnonymousFunction, AnalyzerOptionDiagnosticDescriptors.DoNotUseElementAccessWhenExpressionIsInvocation, AnalyzerOptionDiagnosticDescriptors.UseIsNullPatternInsteadOfInequalityOperator, AnalyzerOptionDiagnosticDescriptors.UseComparisonInsteadOfIsNullPattern); + return ImmutableArray.Create(AnalyzerOptionDiagnosticDescriptors.UseImplicitlyTypedArrayWhenTypeIsObvious, AnalyzerOptionDiagnosticDescriptors.UseImplicitlyTypedArray, AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBodyWhenExpressionIsMultiLine, AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBodyWhenDeclarationIsMultiLine, AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody, AnalyzerOptionDiagnosticDescriptors.RemoveAccessibilityModifiers, AnalyzerOptionDiagnosticDescriptors.RemoveEmptyLineBetweenClosingBraceAndSwitchSection, AnalyzerOptionDiagnosticDescriptors.DoNotRenamePrivateStaticReadOnlyFieldToCamelCaseWithUnderscore, AnalyzerOptionDiagnosticDescriptors.RemoveArgumentListFromObjectCreation, AnalyzerOptionDiagnosticDescriptors.RemoveParenthesesFromConditionOfConditionalExpressionWhenExpressionIsSingleToken, AnalyzerOptionDiagnosticDescriptors.UseStringEmptyInsteadOfEmptyStringLiteral, AnalyzerOptionDiagnosticDescriptors.RemoveCallToConfigureAwait, AnalyzerOptionDiagnosticDescriptors.ConvertBitwiseOperationToHasFlagCall, AnalyzerOptionDiagnosticDescriptors.SimplifyConditionalExpressionWhenItIncludesNegationOfCondition, AnalyzerOptionDiagnosticDescriptors.ConvertMethodGroupToAnonymousFunction, AnalyzerOptionDiagnosticDescriptors.DoNotUseElementAccessWhenExpressionIsInvocation, AnalyzerOptionDiagnosticDescriptors.UseIsNullPatternInsteadOfInequalityOperator, AnalyzerOptionDiagnosticDescriptors.UseComparisonInsteadOfIsNullPattern); } } diff --git a/src/Analyzers/CSharp/DiagnosticDescriptors.Generated.cs b/src/Analyzers/CSharp/DiagnosticDescriptors.Generated.cs index b8154a5c8a..42bf9fa1ef 100644 --- a/src/Analyzers/CSharp/DiagnosticDescriptors.Generated.cs +++ b/src/Analyzers/CSharp/DiagnosticDescriptors.Generated.cs @@ -2503,6 +2503,18 @@ public static partial class ReportOnly helpLinkUri: DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa, customTags: Array.Empty()); + /// RCS1016i + public static readonly DiagnosticDescriptor ConvertExpressionBodyToBlockBody = DiagnosticDescriptorFactory.Default.Create( + id: DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa, + title: "Convert block body to expression-body (or vice versa).", + messageFormat: "Convert expression-body to block body.", + category: DiagnosticCategories.Usage, + defaultSeverity: DiagnosticSeverity.Hidden, + isEnabledByDefault: false, + description: null, + helpLinkUri: DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa, + customTags: Array.Empty()); + /// RCS1018i public static readonly DiagnosticDescriptor RemoveAccessibilityModifiers = DiagnosticDescriptorFactory.Default.Create( id: DiagnosticIdentifiers.AddAccessibilityModifiersOrViceVersa, diff --git a/src/Tests/Analyzers.Tests/RCS1016ConvertExpressionBodyToBlockBodyTests.cs b/src/Tests/Analyzers.Tests/RCS1016ConvertExpressionBodyToBlockBodyTests.cs new file mode 100644 index 0000000000..abf644a28c --- /dev/null +++ b/src/Tests/Analyzers.Tests/RCS1016ConvertExpressionBodyToBlockBodyTests.cs @@ -0,0 +1,837 @@ +// Copyright (c) Josef Pihrt. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Roslynator.CSharp.CodeFixes; +using Roslynator.Testing.CSharp; +using Xunit; + +namespace Roslynator.CSharp.Analysis.Tests +{ + public class RCS1016ConvertExpressionBodyToBlockBodyTests : AbstractCSharpDiagnosticVerifier + { + public override DiagnosticDescriptor Descriptor { get; } = DiagnosticDescriptors.ConvertBlockBodyToExpressionBodyOrViceVersa; + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Constructor() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + public C() [|=> M()|]; + + void M() { } +} +", @" +class C +{ + public C() + { + M(); + } + + void M() { } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Destructor() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + ~C() [|=> M()|]; + + void M() { } +} +", @" +class C +{ + ~C() + { + M(); + } + + void M() { } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Method() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string M() [|=> null|]; +} +", @" +class C +{ + string M() + { + return null; + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Method_MultilineExpression() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string M(object x, object y) [|=> M( + x, + y)|]; +} +", @" +class C +{ + string M(object x, object y) + { + return M( + x, + y); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Method_MultilineExpression2() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string M(object x, object y, object z) [|=> M( + x, + y, + z)|]; +} +", @" +class C +{ + string M(object x, object y, object z) + { + return M( + x, + y, + z); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_VoidMethod() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M() [|=> M()|]; +} +", @" +class C +{ + void M() + { + M(); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_LocalFunction() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M() + { + string LF() [|=> null|]; + } +} +", @" +class C +{ + void M() + { + string LF() + { + return null; + } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_LocalFunction_MultilineExpression() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string M(object x, object y) + { + return null; + + string LF() [|=> M( + x, + y)|]; + } +} +", @" +class C +{ + string M(object x, object y) + { + return null; + + string LF() + { + return M( + x, + y); + } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_VoidLocalFunction() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M() + { + void LF() [|=> M()|]; + } +} +", @" +class C +{ + void M() + { + void LF() + { + M(); + } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_PropertyWithGetter() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string P [|=> null|]; +} +", @" +class C +{ + string P + { + get { return null; } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_PropertyWithGetter_MultilineExpression() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string P [|=> M( + null, + null)|]; + + string M(string x, string y) [|=> null|]; +} +", @" +class C +{ + string P + { + get + { + return M( + null, + null); + } + } + + string M(string x, string y) + { + return null; + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_PropertyWithGetterAndSetter() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string _f; + + public string P + { + get [|=> M( + null, + null)|]; + + set [|=> _f = value|]; + } + + string M(string x, string y) [|=> null|]; +} +", @" +class C +{ + string _f; + + public string P + { + get + { + return M( + null, + null); + } + + set + { + _f = value; + } + } + + string M(string x, string y) + { + return null; + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_PropertyWithGetterAndInitSetter() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string _f; + + public string P + { + get [|=> M( + null, + null)|]; + + init [|=> _f = value|]; + } + + string M(string x, string y) [|=> null|]; +} +", @" +class C +{ + string _f; + + public string P + { + get + { + return M( + null, + null); + } + + init + { + _f = value; + } + } + + string M(string x, string y) + { + return null; + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody).AddAllowedCompilerDiagnosticId("CS0518")); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_IndexerWithGetter() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string this[int index] [|=> null|]; +} +", @" +class C +{ + string this[int index] + { + get { return null; } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_IndexerWithGetterAndSetter() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string _f; + + string this[int index] + { + get [|=> _f|]; + set [|=> _f = value|]; + } +} +", @" +class C +{ + string _f; + + string this[int index] + { + get + { + return _f; + } + + set + { + _f = value; + } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Operator() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + public static C operator !(C value) [|=> value|]; +} +", @" +class C +{ + public static C operator !(C value) + { + return value; + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_ConversionOperator() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + public static explicit operator C(string value) [|=> new C()|]; +} +", @" +class C +{ + public static explicit operator C(string value) + { + return new C(); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Constructor_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + public C() [|=> throw new System.NotImplementedException()|]; + + void M() { } +} +", @" +class C +{ + public C() + { + throw new System.NotImplementedException(); + } + + void M() { } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Destructor_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + ~C() [|=> throw new System.NotImplementedException()|]; +} +", @" +class C +{ + ~C() + { + throw new System.NotImplementedException(); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Method_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string M() [|=> throw new System.NotImplementedException()|]; +} +", @" +class C +{ + string M() + { + throw new System.NotImplementedException(); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_VoidMethod_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M() [|=> throw new System.NotImplementedException()|]; +} +", @" +class C +{ + void M() + { + throw new System.NotImplementedException(); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_LocalFunction_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M() + { + string LF() [|=> throw new System.NotImplementedException()|]; + } +} +", @" +class C +{ + void M() + { + string LF() + { + throw new System.NotImplementedException(); + } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_VoidLocalFunction_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M() + { + void LF() [|=> throw new System.NotImplementedException()|]; + } +} +", @" +class C +{ + void M() + { + void LF() + { + throw new System.NotImplementedException(); + } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_PropertyWithGetter_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string P [|=> throw new System.NotImplementedException()|]; +} +", @" +class C +{ + string P + { + get { throw new System.NotImplementedException(); } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_PropertyWithGetterAndSetter_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string _f; + + public string P + { + get [|=> throw new System.NotImplementedException()|]; + set [|=> throw new System.NotImplementedException()|]; + } +} +", @" +class C +{ + string _f; + + public string P + { + get + { + throw new System.NotImplementedException(); + } + + set + { + throw new System.NotImplementedException(); + } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_IndexerWithGetter_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string this[int index] [|=> throw new System.NotImplementedException()|]; +} +", @" +class C +{ + string this[int index] + { + get { throw new System.NotImplementedException(); } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_IndexerWithGetterAndSetter_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + string _f; + + string this[int index] + { + get [|=> throw new System.NotImplementedException()|]; + set [|=> throw new System.NotImplementedException()|]; + } +} +", @" +class C +{ + string _f; + + string this[int index] + { + get + { + throw new System.NotImplementedException(); + } + + set + { + throw new System.NotImplementedException(); + } + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_Operator_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + public static C operator !(C value) [|=> throw new System.NotImplementedException()|]; +} +", @" +class C +{ + public static C operator !(C value) + { + throw new System.NotImplementedException(); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task Test_ConversionOperator_Throw() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + public static explicit operator C(string value) [|=> throw new System.NotImplementedException()|]; +} +", @" +class C +{ + public static explicit operator C(string value) + { + throw new System.NotImplementedException(); + } +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.ConvertBlockBodyToExpressionBodyOrViceVersa)] + public async Task TestNoDiagnostic_ExpressionBody() + { + await VerifyNoDiagnosticAsync(@" +class C +{ + public C() + { + VM(); + } + + ~C() + { + VM(); + } + + string M() + { + return null; + } + + void VM() + { + VM(); + } + + void M2() + { + string LF() + { + return null; + } + } + + void M3() + { + void LF() + { + VM(); + } + } + + string P + { + get { return null; } + } + + public string P2 + { + get + { + return _f; + } + + set + { + _f = value; + } + } + + string this[int index] + { + get { return null; } + } + + string this[int index, int index2] + { + get + { + return _f; + } + + set + { + _f = value; + } + } + + public static C operator !(C value) + { + return value; + } + + public static explicit operator C(string value) + { + return new C(); + } + + string _f; +} +", options: Options.EnableDiagnostic(AnalyzerOptionDiagnosticDescriptors.ConvertExpressionBodyToBlockBody)); + } + } +} diff --git a/src/Workspaces.Common/CSharp/Refactorings/ConvertExpressionBodyToBlockBodyRefactoring.cs b/src/Workspaces.Common/CSharp/Refactorings/ConvertExpressionBodyToBlockBodyRefactoring.cs index 47f490b575..ec77a91ea5 100644 --- a/src/Workspaces.Common/CSharp/Refactorings/ConvertExpressionBodyToBlockBodyRefactoring.cs +++ b/src/Workspaces.Common/CSharp/Refactorings/ConvertExpressionBodyToBlockBodyRefactoring.cs @@ -266,7 +266,17 @@ private static BlockSyntax CreateBlockWithExpressionStatement( declaration, expression, semicolon, - (e, s) => ExpressionStatement(e, s), + (e, s) => + { + if (e is ThrowExpressionSyntax throwExpression) + { + return ThrowStatement(Token(SyntaxKind.ThrowKeyword), throwExpression.Expression, s); + } + else + { + return ExpressionStatement(e, s); + } + }, increaseCount: increaseCount); }