Exemple #1
0
        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));
        }