From 9ed14adbd058e3a3b0ed86eecdb1c6fc1ae2350c Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sun, 13 Mar 2022 11:47:24 +0100 Subject: [PATCH] Put back refactoring RR0194 (fix #881) --- ChangeLog.md | 1 + docs/Configuration.md | 1 + .../RefactoringDescriptors.Generated.cs | 1 + ...ctoringIdentifiers.Deprecated.Generated.cs | 2 - .../RefactoringIdentifiers.Generated.cs | 1 + .../LocalDeclarationStatementRefactoring.cs | 3 + ...ocalDeclarationAndAssignmentRefactoring.cs | 104 ++++++++++++++++++ src/Refactorings/Refactorings.xml | 5 +- ...SplitLocalDeclarationAndAssignmentTests.cs | 36 ++++++ .../src/configurationFiles.generated.ts | 1 + 10 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 src/Refactorings/CSharp/Refactorings/SplitLocalDeclarationAndAssignmentRefactoring.cs create mode 100644 src/Tests/Refactorings.Tests/RR0194SplitLocalDeclarationAndAssignmentTests.cs diff --git a/ChangeLog.md b/ChangeLog.md index cdf5fe6fc1..6b9d38ebb1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,7 @@ * Call AddRange instead of Add (RCS1235) * Add code fix for CS8602, CS8604 * Fix code fix for CS0225 +* Put back refactoring SplitLocalDeclarationAndAssignment (RR0194) ([issue](https://github.com/JosefPihrt/Roslynator/issues/881)) ### 4.0.3 (2022-01-29) diff --git a/docs/Configuration.md b/docs/Configuration.md index a28b3f30cb..0e0c40ca9a 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1133,6 +1133,7 @@ roslynator_refactoring.sort_member_declarations.enabled = true roslynator_refactoring.split_attributes.enabled = true roslynator_refactoring.split_if.enabled = true roslynator_refactoring.split_if_else.enabled = true +roslynator_refactoring.split_local_declaration_and_assignment.enabled = true roslynator_refactoring.split_switch_labels.enabled = true roslynator_refactoring.split_variable_declaration.enabled = true roslynator_refactoring.swap_binary_operands.enabled = true diff --git a/src/Refactorings/CSharp/RefactoringDescriptors.Generated.cs b/src/Refactorings/CSharp/RefactoringDescriptors.Generated.cs index 85863cc3b6..e95028c33d 100644 --- a/src/Refactorings/CSharp/RefactoringDescriptors.Generated.cs +++ b/src/Refactorings/CSharp/RefactoringDescriptors.Generated.cs @@ -170,6 +170,7 @@ public static class RefactoringDescriptors public static RefactoringDescriptor SplitAttributes = new RefactoringDescriptor("RR0156", "roslynator_refactoring.split_attributes.enabled", isEnabledByDefault: true); public static RefactoringDescriptor SplitIf = new RefactoringDescriptor("RR0184", "roslynator_refactoring.split_if.enabled", isEnabledByDefault: true); public static RefactoringDescriptor SplitIfElse = new RefactoringDescriptor("RR0190", "roslynator_refactoring.split_if_else.enabled", isEnabledByDefault: true); + public static RefactoringDescriptor SplitLocalDeclarationAndAssignment = new RefactoringDescriptor("RR0194", "roslynator_refactoring.split_local_declaration_and_assignment.enabled", isEnabledByDefault: true); public static RefactoringDescriptor SplitSwitchLabels = new RefactoringDescriptor("RR0157", "roslynator_refactoring.split_switch_labels.enabled", isEnabledByDefault: true); public static RefactoringDescriptor SplitVariableDeclaration = new RefactoringDescriptor("RR0158", "roslynator_refactoring.split_variable_declaration.enabled", isEnabledByDefault: true); public static RefactoringDescriptor SwapBinaryOperands = new RefactoringDescriptor("RR0159", "roslynator_refactoring.swap_binary_operands.enabled", isEnabledByDefault: true); diff --git a/src/Refactorings/CSharp/RefactoringIdentifiers.Deprecated.Generated.cs b/src/Refactorings/CSharp/RefactoringIdentifiers.Deprecated.Generated.cs index 777475c8b7..29a4df267c 100644 --- a/src/Refactorings/CSharp/RefactoringIdentifiers.Deprecated.Generated.cs +++ b/src/Refactorings/CSharp/RefactoringIdentifiers.Deprecated.Generated.cs @@ -31,8 +31,6 @@ public static partial class RefactoringIdentifiers [Obsolete("", error: false)] public const string MergeInterpolationIntoInterpolatedString = Prefix + "0076"; [Obsolete("", error: false)] - public const string SplitDeclarationAndInitialization = Prefix + "0194"; - [Obsolete("", error: false)] public const string UseEmptyStringLiteralInsteadOfStringEmpty = Prefix + "0168"; [Obsolete("", error: false)] public const string WrapInElseClause = Prefix + "0173"; diff --git a/src/Refactorings/CSharp/RefactoringIdentifiers.Generated.cs b/src/Refactorings/CSharp/RefactoringIdentifiers.Generated.cs index 1a3beafd0a..e7e6e3b654 100644 --- a/src/Refactorings/CSharp/RefactoringIdentifiers.Generated.cs +++ b/src/Refactorings/CSharp/RefactoringIdentifiers.Generated.cs @@ -172,6 +172,7 @@ public static partial class RefactoringIdentifiers public const string SplitAttributes = Prefix + "0156"; public const string SplitIf = Prefix + "0184"; public const string SplitIfElse = Prefix + "0190"; + public const string SplitLocalDeclarationAndAssignment = Prefix + "0194"; public const string SplitSwitchLabels = Prefix + "0157"; public const string SplitVariableDeclaration = Prefix + "0158"; public const string SwapBinaryOperands = Prefix + "0159"; diff --git a/src/Refactorings/CSharp/Refactorings/LocalDeclarationStatementRefactoring.cs b/src/Refactorings/CSharp/Refactorings/LocalDeclarationStatementRefactoring.cs index e43b0238a2..4e409d96c1 100644 --- a/src/Refactorings/CSharp/Refactorings/LocalDeclarationStatementRefactoring.cs +++ b/src/Refactorings/CSharp/Refactorings/LocalDeclarationStatementRefactoring.cs @@ -21,6 +21,9 @@ public static async Task ComputeRefactoringsAsync(RefactoringContext context, Lo if (context.IsRefactoringEnabled(RefactoringDescriptors.RemoveInstantiationOfLocalVariable)) await RemoveInstantiationOfLocalVariableRefactoring.ComputeRefactoringAsync(context, localDeclaration).ConfigureAwait(false); + + if (context.IsRefactoringEnabled(RefactoringDescriptors.SplitLocalDeclarationAndAssignment)) + await SplitLocalDeclarationAndAssignmentRefactoring.ComputeRefactoringAsync(context, localDeclaration).ConfigureAwait(false); } } } diff --git a/src/Refactorings/CSharp/Refactorings/SplitLocalDeclarationAndAssignmentRefactoring.cs b/src/Refactorings/CSharp/Refactorings/SplitLocalDeclarationAndAssignmentRefactoring.cs new file mode 100644 index 0000000000..7297a0acb8 --- /dev/null +++ b/src/Refactorings/CSharp/Refactorings/SplitLocalDeclarationAndAssignmentRefactoring.cs @@ -0,0 +1,104 @@ +// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslynator.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static Roslynator.CSharp.CSharpFactory; + +namespace Roslynator.CSharp.Refactorings +{ + internal static class SplitLocalDeclarationAndAssignmentRefactoring + { + public static async Task ComputeRefactoringAsync( + RefactoringContext context, + LocalDeclarationStatementSyntax localDeclaration) + { + StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo(localDeclaration); + + if (!statementsInfo.Success) + return; + + SingleLocalDeclarationStatementInfo localInfo = SyntaxInfo.SingleLocalDeclarationStatementInfo(localDeclaration); + + if (!localInfo.Success) + return; + + if (!context.Span.IsEmpty + && context.Span.Start == localInfo.EqualsToken.SpanStart) + { + return; + } + + ExpressionSyntax value = localInfo.Value; + + if (value == null) + return; + + SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); + + TypeSyntax type = localInfo.Type; + + if (type.IsVar) + { + ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(value, context.CancellationToken); + + if (typeSymbol?.SupportsExplicitDeclaration() != true) + return; + + type = typeSymbol.ToMinimalTypeSyntax(semanticModel, type.SpanStart); + } + else + { + ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(type, context.CancellationToken); + + if (typeSymbol?.IsErrorType() != false) + return; + } + + context.RegisterRefactoring( + "Split declaration and assignment", + ct => RefactorAsync(context.Document, localInfo, type, statementsInfo, ct), + RefactoringDescriptors.SplitLocalDeclarationAndAssignment); + } + + private static Task RefactorAsync( + Document document, + in SingleLocalDeclarationStatementInfo localInfo, + TypeSyntax type, + in StatementListInfo statementsInfo, + CancellationToken cancellationToken) + { + LocalDeclarationStatementSyntax localStatement = localInfo.Statement; + + int index = statementsInfo.IndexOf(localStatement); + + VariableDeclaratorSyntax declarator = localInfo.Declarator; + + VariableDeclaratorSyntax newDeclarator = declarator.WithInitializer(null); + + VariableDeclarationSyntax newDeclaration = localInfo.Declaration.ReplaceNode(declarator, newDeclarator); + + if (type != null) + newDeclaration = newDeclaration.WithType(type.WithTriviaFrom(newDeclaration.Type)); + + LocalDeclarationStatementSyntax newLocalStatement = localStatement + .WithDeclaration(newDeclaration) + .WithSemicolonToken(localStatement.SemicolonToken.WithNavigationAnnotation()) + .WithTrailingTrivia(NewLine()) + .WithFormatterAnnotation(); + + ExpressionStatementSyntax assignmentStatement = SimpleAssignmentStatement(IdentifierName(localInfo.Identifier), localInfo.Initializer.Value) + .WithTrailingTrivia(localStatement.GetTrailingTrivia()) + .WithFormatterAnnotation(); + + StatementListInfo newStatementsInfo = statementsInfo + .Insert(index + 1, assignmentStatement) + .ReplaceAt(index, newLocalStatement); + + return document.ReplaceStatementsAsync(statementsInfo, newStatementsInfo, cancellationToken); + } + } +} diff --git a/src/Refactorings/Refactorings.xml b/src/Refactorings/Refactorings.xml index c8b42764f2..b3163b50eb 100644 --- a/src/Refactorings/Refactorings.xml +++ b/src/Refactorings/Refactorings.xml @@ -1892,8 +1892,9 @@ public class Foo - - + + split_local_declaration_and_assignment + local variable declaration equals token diff --git a/src/Tests/Refactorings.Tests/RR0194SplitLocalDeclarationAndAssignmentTests.cs b/src/Tests/Refactorings.Tests/RR0194SplitLocalDeclarationAndAssignmentTests.cs new file mode 100644 index 0000000000..c47dc792c1 --- /dev/null +++ b/src/Tests/Refactorings.Tests/RR0194SplitLocalDeclarationAndAssignmentTests.cs @@ -0,0 +1,36 @@ +// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Roslynator.Testing.CSharp; +using Xunit; + +namespace Roslynator.CSharp.Refactorings.Tests +{ + public class RR0194SplitLocalDeclarationAndAssignmentTests : AbstractCSharpRefactoringVerifier + { + public override string RefactoringId { get; } = RefactoringIdentifiers.SplitLocalDeclarationAndAssignment; + + [Fact, Trait(Traits.Refactoring, RefactoringIdentifiers.SplitLocalDeclarationAndAssignment)] + public async Task TestRefactoring() + { + await VerifyRefactoringAsync(@" +class C +{ + void M() + { + string s [||]= new string(' ', 1); + } +} +", @" +class C +{ + void M() + { + string s; + s = new string(' ', 1); + } +} +", equivalenceKey: EquivalenceKey.Create(RefactoringId)); + } + } +} diff --git a/src/VisualStudioCode/package/src/configurationFiles.generated.ts b/src/VisualStudioCode/package/src/configurationFiles.generated.ts index 526da409dd..9b185615ee 100644 --- a/src/VisualStudioCode/package/src/configurationFiles.generated.ts +++ b/src/VisualStudioCode/package/src/configurationFiles.generated.ts @@ -1105,6 +1105,7 @@ roslynator_analyzers.enabled_by_default = true|false #roslynator_refactoring.split_attributes.enabled = true #roslynator_refactoring.split_if.enabled = true #roslynator_refactoring.split_if_else.enabled = true +#roslynator_refactoring.split_local_declaration_and_assignment.enabled = true #roslynator_refactoring.split_switch_labels.enabled = true #roslynator_refactoring.split_variable_declaration.enabled = true #roslynator_refactoring.swap_binary_operands.enabled = true