Exemplo n.º 1
0
        private static async Task <Solution> PullMembersIntoClassAsync(
            Document document,
            PullMembersUpOptions result,
            Solution solution,
            CancellationToken cancellationToken)
        {
            var solutionEditor        = new SolutionEditor(solution);
            var codeGenerationService = document.Project.LanguageServices.GetRequiredService <ICodeGenerationService>();
            var destinationSyntaxNode = await codeGenerationService.FindMostRelevantNameSpaceOrTypeDeclarationAsync(
                solution, result.Destination, options : null, cancellationToken).ConfigureAwait(false);

            var symbolToDeclarations = await InitializeSymbolToDeclarationsMapAsync(result, solution, solutionEditor, destinationSyntaxNode, cancellationToken).ConfigureAwait(false);

            // Add members to destination
            var pullUpMembersSymbols = result.MemberAnalysisResults.SelectAsArray(
                memberResult =>
            {
                if (memberResult.MakeMemberDeclarationAbstract && !memberResult.Member.IsKind(SymbolKind.Field))
                {
                    // Change the member to abstract if user choose to make them abstract
                    return(MakeAbstractVersion(memberResult.Member));
                }
                else
                {
                    return(memberResult.Member);
                }
            });
            var options        = new CodeGenerationOptions(reuseSyntax: true, generateMethodBodies: false);
            var newDestination = codeGenerationService.AddMembers(destinationSyntaxNode, pullUpMembersSymbols, options: options);

            // Remove some original members since we are pulling members into class.
            // Note: If the user chooses to make the member abstract, then the original member will be changed to an override,
            // and it will pull an abstract declaration up to the destination.
            // But if the member is abstract itself, it will still be removed.
            foreach (var analysisResult in result.MemberAnalysisResults)
            {
                foreach (var syntax in symbolToDeclarations[analysisResult.Member])
                {
                    var originalMemberEditor = await solutionEditor.GetDocumentEditorAsync(
                        solution.GetDocumentId(syntax.SyntaxTree),
                        cancellationToken).ConfigureAwait(false);

                    if (!analysisResult.MakeMemberDeclarationAbstract || analysisResult.Member.IsAbstract)
                    {
                        originalMemberEditor.RemoveNode(originalMemberEditor.Generator.GetDeclaration(syntax));
                    }
                    else
                    {
                        var declarationSyntax = originalMemberEditor.Generator.GetDeclaration(syntax);
                        originalMemberEditor.ReplaceNode(declarationSyntax, (node, generator) => generator.WithModifiers(node, DeclarationModifiers.Override));
                    }
                }
            }

            // Change the destination to abstract class if needed.
            var destinationEditor = await solutionEditor.GetDocumentEditorAsync(
                solution.GetDocumentId(destinationSyntaxNode.SyntaxTree),
                cancellationToken).ConfigureAwait(false);

            if (!result.Destination.IsAbstract &&
                result.MemberAnalysisResults.Any(analysis => analysis.Member.IsAbstract || analysis.MakeMemberDeclarationAbstract))
            {
                var modifiers = DeclarationModifiers.From(result.Destination).WithIsAbstract(true);
                newDestination = destinationEditor.Generator.WithModifiers(newDestination, modifiers);
            }

            destinationEditor.ReplaceNode(destinationSyntaxNode, (syntaxNode, generator) => newDestination);
            return(solutionEditor.GetChangedSolution());
        }
        private async Task <Solution> InlineMethodAsync(Document document,
                                                        TInvocationSyntax calleeInvocationNode,
                                                        IMethodSymbol calleeMethodSymbol,
                                                        TMethodDeclarationSyntax calleeMethodNode,
                                                        ISymbol callerSymbol,
                                                        SyntaxNode callerNode,
                                                        TExpressionSyntax rawInlineExpression,
                                                        IInvocationOperation invocationOperation,
                                                        bool removeCalleeDeclarationNode,
                                                        CancellationToken cancellationToken)
        {
            // Find the statement contains the invocation. This should happen when Callee is invoked in a block
            // example:
            // void Caller()
            // {
            //     Action a = () =>
            //     {
            //         var x = Callee();
            //     }
            // } (Local declaration x is the containing node)
            // Note: Stop the searching when it hits lambda or local function, because for this case below don't
            // treat the declaration of a is the containing node
            // void Caller()
            // {
            //     Action a = () => Callee();
            // }
            // it could be null if the caller is invoked as arrow function
            var statementContainsInvocation = calleeInvocationNode.GetAncestors()
                                              .TakeWhile(node => !_syntaxFacts.IsAnonymousFunction(node) && !_syntaxFacts.IsLocalFunction(node))
                                              .FirstOrDefault(node => node is TStatementSyntax) as TStatementSyntax;

            var methodParametersInfo = await GetMethodParametersInfoAsync(
                document,
                calleeInvocationNode,
                calleeMethodNode,
                statementContainsInvocation,
                rawInlineExpression,
                invocationOperation, cancellationToken).ConfigureAwait(false);

            var inlineContext = await GetInlineMethodContextAsync(
                document,
                calleeMethodNode,
                calleeInvocationNode,
                calleeMethodSymbol,
                rawInlineExpression,
                methodParametersInfo,
                cancellationToken).ConfigureAwait(false);

            var solution       = document.Project.Solution;
            var solutionEditor = new SolutionEditor(solution);

            if (removeCalleeDeclarationNode)
            {
                var calleeDocumentId = solution.GetDocumentId(calleeMethodNode.SyntaxTree);
                if (calleeDocumentId != null)
                {
                    var calleeDocumentEditor = await solutionEditor.GetDocumentEditorAsync(calleeDocumentId, cancellationToken).ConfigureAwait(false);

                    calleeDocumentEditor.RemoveNode(calleeMethodNode);
                }
            }

            var newCallerMethodNode = await GetChangedCallerAsync(
                document,
                calleeInvocationNode,
                calleeMethodSymbol,
                callerSymbol,
                callerNode,
                statementContainsInvocation,
                rawInlineExpression,
                methodParametersInfo, inlineContext, cancellationToken).ConfigureAwait(false);

            var callerDocumentEditor = await solutionEditor.GetDocumentEditorAsync(document.Id, cancellationToken).ConfigureAwait(false);

            callerDocumentEditor.ReplaceNode(callerNode, newCallerMethodNode);
            return(solutionEditor.GetChangedSolution());
        }
 protected abstract Task UpdateReferencesAsync(Project project, SolutionEditor solutionEditor, ISymbol implMember, INamedTypeSymbol containingType, CancellationToken cancellationToken);
Exemplo n.º 4
0
 // When converting to implicit, we don't need to update any references.
 protected override Task UpdateReferencesAsync(Project project, SolutionEditor solutionEditor, ISymbol implMember, INamedTypeSymbol containingType, CancellationToken cancellationToken)
 => Task.CompletedTask;
        static async Task <Solution> ApplyInvocationsAsync(
            Solution solution,
            [NotNull] IEnumerable <KeyValuePair <DocumentId, Invocation[]> > invocationsByDocument,
            CancellationToken cancellationToken)
        {
            // apply changes at invocation sites
            var slnEditor = new SolutionEditor(solution);

            var invocationsByDocumentArray = invocationsByDocument as KeyValuePair <DocumentId, Invocation[]>[] ??
                                             invocationsByDocument.ToArray();

            foreach (var pair in invocationsByDocumentArray)
            {
                var docEditor = await slnEditor.GetDocumentEditorAsync(pair.Key, cancellationToken);

                foreach (var i in pair.Value)
                {
                    docEditor.ReplaceNode(
                        i.ExpressionToReplace,
                        i.ConstantAccessSyntax.WithTriviaFrom(i.ExpressionToReplace));

                    if (i.NewMessageArgs.Any())
                    {
                        docEditor.InsertAfter(
                            i.ExpressionToReplace.FirstAncestorOrSelf <ArgumentSyntax>(),
                            i.NewMessageArgs.Select(a => SyntaxFactory.Argument(a).WithAdditionalAnnotations(Formatter.Annotation)));
                    }
                }

                AddUsingIfNeeded(docEditor);
            }

            solution = slnEditor.GetChangedSolution();

            // refresh model and find message set types where we need to add constants
            var constantsByTypeName = from pair in invocationsByDocumentArray
                                      from inv in pair.Value
                                      group inv.GetConstantDeclarationSyntax by inv.MessagesTypeName;

            foreach (var group in constantsByTypeName)
            {
                // find type
                bool ok = false;

                foreach (var project in solution.Projects)
                {
                    var compilation = await project.GetCompilationAsync(cancellationToken);

                    var messagesTypeSymbol = GetAllTypes(compilation).FirstOrDefault(t => t.Name == group.Key);

                    if (messagesTypeSymbol == null)
                    {
                        continue;
                    }

                    var messagesDefinition   = messagesTypeSymbol.OriginalDefinition;
                    var messagesDefSyntaxRef = messagesDefinition.DeclaringSyntaxReferences.First();
                    var messagesDefDocument  = solution.GetDocument(messagesDefSyntaxRef.SyntaxTree);

                    var messagesDefSyntaxRoot = await messagesDefDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                    var messagesDefSyntax = (ClassDeclarationSyntax)await messagesDefSyntaxRef.GetSyntaxAsync(cancellationToken).ConfigureAwait(false);

                    // find next unused message number
                    var nextCode = (from child in messagesDefSyntax.DescendantNodes().OfType <FieldDeclarationSyntax>()
                                    where child.Modifiers.Any(SyntaxKind.ConstKeyword)
                                    from v in child.Declaration.Variables
                                    let initializer = v.Initializer.Value as LiteralExpressionSyntax
                                                      where initializer != null && initializer.Kind() == SyntaxKind.NumericLiteralExpression
                                                      select(int) initializer.Token.Value)
                                   .Concat(Enumerable.Repeat(1, 1))
                                   .Max() + 1;

                    var newConstants = group.Select((getConstant, i) => getConstant(nextCode + i)).Cast <MemberDeclarationSyntax>().ToArray();

                    var newMessagesDefSyntax = messagesDefSyntax.AddMembers(newConstants);
                    var newSyntaxRoot        = messagesDefSyntaxRoot.ReplaceNode(messagesDefSyntax, newMessagesDefSyntax);

                    solution = solution.WithDocumentSyntaxRoot(messagesDefDocument.Id, newSyntaxRoot);
                    ok       = true;
                    break;
                }

                if (!ok)
                {
                    throw new InvalidOperationException("Can't find message set type " + group.Key);
                }
            }

            return(solution);
        }