async Task <Solution> PerformAction(Document document, SemanticModel model, SyntaxNode root, TypeDeclarationSyntax enclosingTypeDeclaration, INamedTypeSymbol declaringTypeSymbol, MethodDeclarationSyntax methodDeclaration, IMethodSymbol methodSymbol, CancellationToken cancellationToken) { // Collect all invocations of changed method var methodReferencesVisitor = new MethodReferencesVisitor(document.Project.Solution, methodSymbol, methodDeclaration, cancellationToken); await methodReferencesVisitor.Collect(); // Collect all references to type members and "this" expressions inside of changed method var memberReferencesVisitor = new MemberReferencesVisitor(model, declaringTypeSymbol.GetMembers().Where(m => m != methodSymbol), cancellationToken); memberReferencesVisitor.Collect(methodDeclaration.Body); Solution solution = document.Project.Solution; List <SyntaxNode> trackedNodesInMainDoc = new List <SyntaxNode>(); trackedNodesInMainDoc.Add(methodDeclaration); var methodReferencesInMainDocument = methodReferencesVisitor.NodesToChange.FirstOrDefault(n => n.Document.Id == document.Id); if (methodReferencesInMainDocument != null) { trackedNodesInMainDoc.AddRange(methodReferencesInMainDocument.References.Select(r => r.InvocationExpression)); } trackedNodesInMainDoc.AddRange(memberReferencesVisitor.NodesToChange); var newMainRoot = root.TrackNodes(trackedNodesInMainDoc); foreach (var invocationsInDocument in methodReferencesVisitor.NodesToChange) { SyntaxNode thisDocRoot = null; var thisDocumentId = invocationsInDocument.Document.Id; if (document.Id == thisDocumentId) { // We are in same document as changed method declaration, reuse new root from outside thisDocRoot = newMainRoot; } else { thisDocRoot = await invocationsInDocument.Document.GetSyntaxRootAsync(); if (thisDocRoot == null) { continue; } thisDocRoot = thisDocRoot.TrackNodes(invocationsInDocument.References.Select(r => r.InvocationExpression)); } foreach (var referencingInvocation in invocationsInDocument.References) { // Change this method invocation to invocation of a static method with instance parameter var thisInvocation = thisDocRoot.GetCurrentNode(referencingInvocation.InvocationExpression); ExpressionSyntax invocationExpressionPart = null; SimpleNameSyntax methodName = null; var memberAccessExpr = thisInvocation.Expression as MemberAccessExpressionSyntax; if (memberAccessExpr != null) { invocationExpressionPart = memberAccessExpr.Expression; methodName = memberAccessExpr.Name; } if (invocationExpressionPart == null) { var identifier = thisInvocation.Expression as IdentifierNameSyntax; if (identifier != null) { // If changed method references itself, use "instance" as additional parameter! In other methods of affected class, use "this"! if (referencingInvocation.IsInChangedMethod) { invocationExpressionPart = SyntaxFactory.IdentifierName("instance").WithLeadingTrivia(identifier.GetLeadingTrivia()); } else { invocationExpressionPart = SyntaxFactory.ThisExpression().WithLeadingTrivia(identifier.GetLeadingTrivia()); } methodName = identifier; } } if (invocationExpressionPart == null) { continue; } List <ArgumentSyntax> invocationArguments = new List <ArgumentSyntax>(); invocationArguments.Add(SyntaxFactory.Argument(invocationExpressionPart.WithoutLeadingTrivia())); invocationArguments.AddRange(referencingInvocation.InvocationExpression.ArgumentList.Arguments); thisDocRoot = thisDocRoot.ReplaceNode( thisInvocation, SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(enclosingTypeDeclaration.Identifier.WithoutTrivia()).WithLeadingTrivia(invocationExpressionPart.GetLeadingTrivia()), methodName.WithoutLeadingTrivia() ), SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(invocationArguments)).WithAdditionalAnnotations(Formatter.Annotation) )); } if (document.Id == thisDocumentId) { // Write new root back to outside newMainRoot = thisDocRoot; } else { // Another document, replace it with modified version in solution solution = solution.WithDocumentSyntaxRoot(thisDocumentId, thisDocRoot); } } foreach (var changedNode in memberReferencesVisitor.NodesToChange) { var trackedNode = newMainRoot.GetCurrentNode(changedNode); var thisExpression = trackedNode as ThisExpressionSyntax; if (thisExpression != null) { // Replace "this" with instance parameter name newMainRoot = newMainRoot.ReplaceNode( thisExpression, SyntaxFactory.IdentifierName("instance").WithLeadingTrivia(thisExpression.GetLeadingTrivia()) ); } var memberIdentifier = trackedNode as IdentifierNameSyntax; if (memberIdentifier != null) { newMainRoot = newMainRoot.ReplaceNode( memberIdentifier, SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("instance").WithLeadingTrivia(memberIdentifier.GetLeadingTrivia()), memberIdentifier.WithoutLeadingTrivia()) ); } } List <ParameterSyntax> parameters = new List <ParameterSyntax>(); parameters.Add(SyntaxFactory.Parameter( SyntaxFactory.List <AttributeListSyntax>(), SyntaxFactory.TokenList(), SyntaxFactory.ParseTypeName(enclosingTypeDeclaration.Identifier.ValueText), SyntaxFactory.Identifier("instance"), null) .WithAdditionalAnnotations(Formatter.Annotation)); parameters.AddRange(methodDeclaration.ParameterList.Parameters); var staticModifierLeadingTrivia = methodDeclaration.Modifiers.Any() ? SyntaxFactory.TriviaList() : methodDeclaration.GetLeadingTrivia(); var methodDeclarationLeadingTrivia = methodDeclaration.Modifiers.Any() ? methodDeclaration.GetLeadingTrivia() : SyntaxFactory.TriviaList(); var trackedMethodDeclaration = newMainRoot.GetCurrentNode(methodDeclaration); newMainRoot = newMainRoot.ReplaceNode((SyntaxNode)trackedMethodDeclaration, trackedMethodDeclaration .WithLeadingTrivia(methodDeclarationLeadingTrivia) .WithModifiers(trackedMethodDeclaration.Modifiers.Add(SyntaxFactory.Token(SyntaxKind.StaticKeyword).WithLeadingTrivia(staticModifierLeadingTrivia).WithTrailingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.Whitespace(" "))))) .WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters)).WithTrailingTrivia(trackedMethodDeclaration.ParameterList.GetTrailingTrivia()))); return(solution.WithDocumentSyntaxRoot(document.Id, newMainRoot)); }