protected override async Task GetChangedSolutionAsync()

in src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD103UseAsyncOptionCodeFix.cs [112:205]


            protected override async Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
            {
                Document? document = this.document;
                SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                // Find the synchronously blocking call member,
                // and bookmark it so we can find it again after some mutations have taken place.
                var syncAccessBookmark = new SyntaxAnnotation();
                SimpleNameSyntax syncMethodName = (SimpleNameSyntax)root.FindNode(this.diagnostic.Location.SourceSpan);
                if (syncMethodName is null)
                {
                    MemberAccessExpressionSyntax? syncMemberAccess = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MemberAccessExpressionSyntax>();
                    syncMethodName = syncMemberAccess.Name;
                }

                // When we give the Document a modified SyntaxRoot, yet another is created. So we first assign it to the Document,
                // then we query for the SyntaxRoot from the Document.
                document = document.WithSyntaxRoot(
                    root.ReplaceNode(syncMethodName, syncMethodName.WithAdditionalAnnotations(syncAccessBookmark)));
                root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
                syncMethodName = (SimpleNameSyntax)root.GetAnnotatedNodes(syncAccessBookmark).Single();

                // We'll need the semantic model later. But because we've annotated a node, that changes the SyntaxRoot
                // and that renders the default semantic model broken (even though we've already updated the document's SyntaxRoot?!).
                // So after acquiring the semantic model, update it with the new method body.
                SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
                AnonymousFunctionExpressionSyntax? originalAnonymousMethodContainerIfApplicable = syncMethodName.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>();
                MethodDeclarationSyntax? originalMethodDeclaration = syncMethodName.FirstAncestorOrSelf<MethodDeclarationSyntax>();

                ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(this.diagnostic.Location.SourceSpan.Start, cancellationToken);
                var hasReturnValue = ((enclosingSymbol as IMethodSymbol)?.ReturnType as INamedTypeSymbol)?.IsGenericType ?? false;

                // Ensure that the method or anonymous delegate is using the async keyword.
                MethodDeclarationSyntax updatedMethod;
                if (originalAnonymousMethodContainerIfApplicable is object)
                {
                    updatedMethod = originalMethodDeclaration.ReplaceNode(
                        originalAnonymousMethodContainerIfApplicable,
                        originalAnonymousMethodContainerIfApplicable.MakeMethodAsync(hasReturnValue, semanticModel, cancellationToken));
                }
                else
                {
                    (document, updatedMethod) = await originalMethodDeclaration.MakeMethodAsync(document, cancellationToken).ConfigureAwait(false);
                    semanticModel = null; // out-dated
                }

                if (updatedMethod != originalMethodDeclaration)
                {
                    // Re-discover our synchronously blocking member.
                    syncMethodName = (SimpleNameSyntax)updatedMethod.GetAnnotatedNodes(syncAccessBookmark).Single();
                }

                ExpressionSyntax? syncExpression = GetSynchronousExpression(syncMethodName);

                ExpressionSyntax awaitExpression;
                if (!string.IsNullOrEmpty(this.AlternativeAsyncMethod))
                {
                    // Replace the member being called and await the invocation expression.
                    // While doing so, move leading trivia to the surrounding await expression.
                    SimpleNameSyntax? asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName]));
                    awaitExpression = SyntaxFactory.AwaitExpression(
                        syncExpression.ReplaceNode(syncMethodName, asyncMethodName).WithoutLeadingTrivia())
                        .WithLeadingTrivia(syncExpression.GetLeadingTrivia());
                }
                else
                {
                    // Remove the member being accessed that causes a synchronous block and simply await the object.
                    MemberAccessExpressionSyntax? syncMemberAccess = syncMethodName.FirstAncestorOrSelf<MemberAccessExpressionSyntax>();
                    ExpressionSyntax? syncMemberStrippedExpression = syncMemberAccess.Expression;

                    // Special case a common pattern of calling task.GetAwaiter().GetResult() and remove both method calls.
                    var expressionMethodCall = (syncMemberStrippedExpression as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax;
                    if (expressionMethodCall?.Name.Identifier.Text == nameof(Task.GetAwaiter))
                    {
                        syncMemberStrippedExpression = expressionMethodCall.Expression;
                    }

                    awaitExpression = SyntaxFactory.AwaitExpression(syncMemberStrippedExpression.WithoutLeadingTrivia())
                        .WithLeadingTrivia(syncMemberStrippedExpression.GetLeadingTrivia());
                }

                if (!(syncExpression.Parent is ExpressionStatementSyntax))
                {
                    awaitExpression = SyntaxFactory.ParenthesizedExpression(awaitExpression)
                        .WithAdditionalAnnotations(Simplifier.Annotation);
                }

                updatedMethod = updatedMethod
                    .ReplaceNode(syncExpression, awaitExpression);

                SyntaxNode? newRoot = root.ReplaceNode(originalMethodDeclaration, updatedMethod);
                Document? newDocument = document.WithSyntaxRoot(newRoot);
                return newDocument.Project.Solution;
            }