diff --git a/src/EditorFeatures/VisualBasicTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.vb b/src/EditorFeatures/VisualBasicTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.vb index 698951fb82005e0338551cacc1f2ec7daf0308e8..6d10791b33bbc12306487687e74e032db9616cd6 100644 --- a/src/EditorFeatures/VisualBasicTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.vb @@ -1,11 +1,8 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis.CodeRefactorings -Imports Microsoft.CodeAnalysis.VisualBasic.MoveDeclarationNearReference Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings -Imports Roslyn.Test.Utilities -Imports Xunit +Imports Microsoft.CodeAnalysis.VisualBasic.MoveDeclarationNearReference Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.MoveDeclarationNearReference Public Class MoveDeclarationNearReferenceTests diff --git a/src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceCodeRefactoringProvider.cs index 806964afce6f46f1f0460cfcd490c84d11b3fb47..4c0bd6a0d614e44bbb690d0f50f6bbd7c0fdfc65 100644 --- a/src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceCodeRefactoringProvider.cs @@ -38,21 +38,6 @@ protected override bool IsValidVariableDeclarator(VariableDeclaratorSyntax varia protected override SyntaxToken GetIdentifierOfVariableDeclarator(VariableDeclaratorSyntax variableDeclarator) => variableDeclarator.Identifier; - protected override LocalDeclarationStatementSyntax CreateMergedDeclarationStatement( - LocalDeclarationStatementSyntax localDeclaration, StatementSyntax statementSyntax) - { - var assignExpression = (AssignmentExpressionSyntax)((ExpressionStatementSyntax)statementSyntax).Expression; - var declaration = localDeclaration.Declaration; - var declarator = declaration.Variables[0]; - - return localDeclaration.ReplaceNode( - declarator, - declarator.WithInitializer( - SyntaxFactory.EqualsValueClause( - assignExpression.OperatorToken, - assignExpression.Right))); - } - protected override async Task TypesAreCompatibleAsync( Document document, ILocalSymbol localSymbol, LocalDeclarationStatementSyntax declarationStatement, diff --git a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs index c46cc028b8ef46b3713a6c6a5a7f50b33f0a2056..a646517856be20b21d21ab8d662bbc1209ebc36e 100644 --- a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs @@ -140,7 +140,7 @@ private static async Task MoveDeclarationToFirstReferenceAsync(Document document Document document, State state, SyntaxEditor editor, SyntaxAnnotation warningAnnotation) { // Replace the first reference with a new declaration. - var declarationStatement = CreateMergedDeclarationStatement(state.DeclarationStatement, state.FirstStatementAffectedInInnermostBlock); + var declarationStatement = CreateMergedDeclarationStatement(document, state); declarationStatement = warningAnnotation == null ? declarationStatement : declarationStatement.WithAdditionalAnnotations(warningAnnotation); @@ -186,8 +186,6 @@ private bool CrossesMeaningfulBlock(State state) protected abstract bool IsValidVariableDeclarator(TVariableDeclaratorSyntax variableDeclarator); protected abstract SyntaxToken GetIdentifierOfVariableDeclarator(TVariableDeclaratorSyntax variableDeclarator); - protected abstract TLocalDeclarationStatementSyntax CreateMergedDeclarationStatement( - TLocalDeclarationStatementSyntax localDeclaration, TStatementSyntax statementSyntax); protected abstract Task TypesAreCompatibleAsync(Document document, ILocalSymbol localSymbol, TLocalDeclarationStatementSyntax declarationStatement, SyntaxNode right, CancellationToken cancellationToken); private async Task CanMergeDeclarationAndAssignmentAsync( @@ -221,6 +219,23 @@ private bool CrossesMeaningfulBlock(State state) return false; } + private TLocalDeclarationStatementSyntax CreateMergedDeclarationStatement( + Document document, State state) + { + var generator = SyntaxGenerator.GetGenerator(document); + + var syntaxFacts = document.GetLanguageService(); + syntaxFacts.GetPartsOfAssignmentStatement( + state.FirstStatementAffectedInInnermostBlock, + out var left, out var operatorToken, out var right); + + return state.DeclarationStatement.ReplaceNode( + state.VariableDeclarator, + generator.WithInitializer( + state.VariableDeclarator, + generator.EqualsValueClause(operatorToken, right))); + } + private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) diff --git a/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs b/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs index 08740adfe0bcd5902f9745fc321e95e0028441f3..8f069179c09ba8cf030141f481718cceb916be60 100644 --- a/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs +++ b/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs @@ -5,6 +5,7 @@ using System.Threading; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.UseCollectionInitializer diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index c65b1d8513fdf78dc53cbdf9bd4236fe14952fb8..ea5dc9271e3494190323b4a66bdccc8c41979021 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -3935,6 +3935,12 @@ public override SyntaxNode LocalDeclarationStatement(SyntaxNode type, string nam VariableDeclaration(type, name, initializer)); } + internal override SyntaxNode WithInitializer(SyntaxNode variableDeclarator, SyntaxNode initializer) + => ((VariableDeclaratorSyntax)variableDeclarator).WithInitializer((EqualsValueClauseSyntax)initializer); + + internal override SyntaxNode EqualsValueClause(SyntaxToken operatorToken, SyntaxNode value) + => SyntaxFactory.EqualsValueClause(operatorToken, (ExpressionSyntax)value); + private static VariableDeclarationSyntax VariableDeclaration(SyntaxNode type, string name, SyntaxNode expression = null) { return SyntaxFactory.VariableDeclaration( diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTokenExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTokenExtensions.cs index 5db32839e542b98f3b030cc54828a5eb550bba13..8d058a9ea9f18a18e4a9731e133b2d45df06b250 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTokenExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTokenExtensions.cs @@ -1,13 +1,12 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs index a4b93d207d8c342db72b8b8231fbe39911e5b5e7..3acb0647fad1bcf1be456285e29bd0e89222a1bc 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs @@ -1450,10 +1450,12 @@ public bool IsSimpleAssignmentStatement(SyntaxNode statement) ((ExpressionStatementSyntax)statement).Expression.IsKind(SyntaxKind.SimpleAssignmentExpression); } - public void GetPartsOfAssignmentStatement(SyntaxNode statement, out SyntaxNode left, out SyntaxNode right) + public void GetPartsOfAssignmentStatement( + SyntaxNode statement, out SyntaxNode left, out SyntaxToken operatorToken, out SyntaxNode right) { var assignment = (AssignmentExpressionSyntax)((ExpressionStatementSyntax)statement).Expression; left = assignment.Left; + operatorToken = assignment.OperatorToken; right = assignment.Right; } diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index a62ab0a246a88e312dff44267f8f0f398e528ab8..c6328e5bcc96a755c15920f7be7b2684159c192c 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -1381,6 +1381,9 @@ protected static SyntaxList RemoveRange(SyntaxList list, in /// public abstract SyntaxNode LocalDeclarationStatement(SyntaxNode type, string identifier, SyntaxNode initializer = null, bool isConst = false); + internal abstract SyntaxNode WithInitializer(SyntaxNode variableDeclarator, SyntaxNode initializer); + internal abstract SyntaxNode EqualsValueClause(SyntaxToken operatorToken, SyntaxNode value); + /// /// Creates a statement that declares a single local variable. /// diff --git a/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs index 63723beb45e25f43307fee40299ac285c878f735..4f26a05e36931291753e77016b768b121be9f53b 100644 --- a/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs @@ -93,7 +93,7 @@ internal interface ISyntaxFactsService : ILanguageService bool IsLeftSideOfAssignment(SyntaxNode node); bool IsSimpleAssignmentStatement(SyntaxNode statement); - void GetPartsOfAssignmentStatement(SyntaxNode statement, out SyntaxNode left, out SyntaxNode right); + void GetPartsOfAssignmentStatement(SyntaxNode statement, out SyntaxNode left, out SyntaxToken operatorToken, out SyntaxNode right); // Left side of any assignment (for example *= or += ) bool IsLeftSideOfAnyAssignment(SyntaxNode node); diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISyntaxFactsServiceExtensions.cs b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsServiceExtensions.cs similarity index 80% rename from src/Workspaces/Core/Portable/Shared/Extensions/ISyntaxFactsServiceExtensions.cs rename to src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsServiceExtensions.cs index 58401aa1469f0fac11bb525c550b2f3b41b2e591..1e64f14ec2c8f5b8a917514f800cd67d00a7a1a6 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISyntaxFactsServiceExtensions.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsServiceExtensions.cs @@ -4,7 +4,7 @@ using System.Linq; using Microsoft.CodeAnalysis.LanguageServices; -namespace Microsoft.CodeAnalysis.Shared.Extensions +namespace Microsoft.CodeAnalysis.LanguageServices { internal static class ISyntaxFactsServiceExtensions { @@ -31,5 +31,12 @@ public static bool IsRegularOrDocumentationComment(this ISyntaxFactsService synt var leadingBlankLines = syntaxFacts.GetLeadingBlankLines(node); return node.GetLeadingTrivia().Skip(leadingBlankLines.Length).ToImmutableArray(); } + + public static void GetPartsOfAssignmentStatement( + this ISyntaxFactsService syntaxFacts, SyntaxNode statement, + out SyntaxNode left, out SyntaxNode right) + { + syntaxFacts.GetPartsOfAssignmentStatement(statement, out left, out _, out right); + } } } diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index caab39cc1d650f84996260c7ab6c9ee75cdf2795..fcbbc2b890900fbb18cbefffbb75d5ad78f18f89 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -915,7 +915,7 @@ - + diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index fc6b5da79e2112439cd1f620d23f6db4a675efe7..078955def959895e17d64260da21f2dd5eb82ae6 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -438,6 +438,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration SyntaxFactory.SingletonSeparatedList(VariableDeclarator(type, identifier, initializer))) End Function + Friend Overrides Function WithInitializer(variableDeclarator As SyntaxNode, initializer As SyntaxNode) As SyntaxNode + Return DirectCast(variableDeclarator, VariableDeclaratorSyntax).WithInitializer(DirectCast(initializer, EqualsValueSyntax)) + End Function + + Friend Overrides Function EqualsValueClause(operatorToken As SyntaxToken, value As SyntaxNode) As SyntaxNode + Return SyntaxFactory.EqualsValue(operatorToken, DirectCast(value, ExpressionSyntax)) + End Function + Private Function VariableDeclarator(type As SyntaxNode, name As String, Optional expression As SyntaxNode = Nothing) As VariableDeclaratorSyntax Return SyntaxFactory.VariableDeclarator( SyntaxFactory.SingletonSeparatedList(name.ToModifiedIdentifier), diff --git a/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxTokenExtensions.vb b/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxTokenExtensions.vb index 826124d034fe15868ca40be19dbe42e5f9219682..ca187e2ce8a6e27403bdfb40dc8e991957a3d7f7 100644 --- a/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxTokenExtensions.vb +++ b/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxTokenExtensions.vb @@ -3,6 +3,7 @@ Imports System.Runtime.CompilerServices Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.LanguageServices Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions diff --git a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb index bd92e9df94574094b404b10d1af8916c05380b9f..279870073698ba8c8fda04e76fb5d532927e6685 100644 --- a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb +++ b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb @@ -1352,9 +1352,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return statement.IsKind(SyntaxKind.SimpleAssignmentStatement) End Function - Public Sub GetPartsOfAssignmentStatement(statement As SyntaxNode, ByRef left As SyntaxNode, ByRef right As SyntaxNode) Implements ISyntaxFactsService.GetPartsOfAssignmentStatement + Public Sub GetPartsOfAssignmentStatement(statement As SyntaxNode, ByRef left As SyntaxNode, ByRef operatorToken As SyntaxToken, ByRef right As SyntaxNode) Implements ISyntaxFactsService.GetPartsOfAssignmentStatement Dim assignment = DirectCast(statement, AssignmentStatementSyntax) left = assignment.Left + operatorToken = assignment.OperatorToken right = assignment.Right End Sub