示例#1
0
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var document          = context.Document;
            var cancellationToken = context.CancellationToken;
            var compilation       = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

            var knownTypes = new KnownTypes(compilation);

            var diagnostic = context.Diagnostics.First();
            var token      = diagnostic.Location.FindToken(cancellationToken);
            var node       = token.GetAncestor(IsAsyncSupportingFunctionSyntax);

            if (node == null)
            {
                return;
            }

            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var methodSymbol = GetMethodSymbol(node, semanticModel, cancellationToken);

            if (methodSymbol == null)
            {
                return;
            }

            if (ShouldOfferFix(methodSymbol.ReturnType, knownTypes))
            {
                context.RegisterCodeFix(new MyCodeAction(GetDocumentUpdater(context)), context.Diagnostics);
            }
        }
示例#2
0
 private static SyntaxNode WrapExpressionWithTaskFromResult(
     SyntaxGenerator generator,
     SyntaxNode expression,
     ITypeSymbol returnType,
     KnownTypes knownTypes
     )
 {
     if (returnType.OriginalDefinition.Equals(knownTypes._taskOfTType))
     {
         var taskTypeExpression = TypeExpressionForStaticMemberAccess(
             generator,
             knownTypes._taskType
             );
         var taskFromResult = generator.MemberAccessExpression(
             taskTypeExpression,
             nameof(Task.FromResult)
             );
         return(generator
                .InvocationExpression(taskFromResult, expression.WithoutTrivia())
                .WithTriviaFrom(expression));
     }
     else
     {
         return(generator.ObjectCreationExpression(returnType, expression));
     }
 }
示例#3
0
        private static SyntaxNode GetReturnTaskCompletedTaskStatement(
            SyntaxGenerator generator,
            ITypeSymbol returnType,
            KnownTypes knownTypes
            )
        {
            SyntaxNode invocation;

            if (returnType.OriginalDefinition.Equals(knownTypes._taskType))
            {
                var taskTypeExpression = TypeExpressionForStaticMemberAccess(
                    generator,
                    knownTypes._taskType
                    );
                invocation = generator.MemberAccessExpression(
                    taskTypeExpression,
                    nameof(Task.CompletedTask)
                    );
            }
            else
            {
                invocation = generator.ObjectCreationExpression(knownTypes._valueTaskType);
            }

            var statement = generator.ReturnStatement(invocation);

            return(statement);
        }
示例#4
0
        protected sealed override async Task FixAllAsync(
            Document document,
            ImmutableArray <Diagnostic> diagnostics,
            SyntaxEditor editor,
            CancellationToken cancellationToken
            )
        {
            var generator     = editor.Generator;
            var semanticModel = await document
                                .GetRequiredSemanticModelAsync(cancellationToken)
                                .ConfigureAwait(false);

            var compilation = semanticModel.Compilation;
            var knownTypes  = new KnownTypes(compilation);

            // For fix all we need to do nested locals or lambdas first, so order the diagnostics by location descending
            foreach (
                var diagnostic in diagnostics.OrderByDescending(d => d.Location.SourceSpan.Start)
                )
            {
                var token = diagnostic.Location.FindToken(cancellationToken);
                var node  = token.GetAncestor(IsAsyncSupportingFunctionSyntax);
                if (node == null)
                {
                    Debug.Fail("We should always be able to find the node from the diagnostic.");
                    continue;
                }

                var methodSymbol = GetMethodSymbol(node, semanticModel, cancellationToken);
                if (methodSymbol == null)
                {
                    Debug.Fail(
                        "We should always be able to find the method symbol for the diagnostic."
                        );
                    continue;
                }

                // We might need to perform control flow analysis as part of the fix, so we need to do it on the original node
                // so do it up front. Nothing in the fixer changes the reachability of the end of the method so this is safe
                var controlFlow = GetControlFlowAnalysis(generator, semanticModel, node);
                // If control flow couldn't be computed then its probably an empty block, which means we need to add a return anyway
                var needsReturnStatementAdded =
                    controlFlow == null || controlFlow.EndPointIsReachable;

                editor.ReplaceNode(
                    node,
                    (updatedNode, generator) =>
                    RemoveAsyncModifier(
                        generator,
                        updatedNode,
                        methodSymbol.ReturnType,
                        knownTypes,
                        needsReturnStatementAdded
                        )
                    );
            }
        }
示例#5
0
        private SyntaxNode RemoveAsyncModifier(
            SyntaxGenerator generator,
            SyntaxNode node,
            ITypeSymbol returnType,
            KnownTypes knownTypes,
            bool needsReturnStatementAdded
            )
        {
            node = RemoveAsyncModifier(generator, node);

            var expression = generator.GetExpression(node);

            if (expression is TExpressionSyntax expressionBody)
            {
                if (IsTaskType(returnType, knownTypes))
                {
                    // We need to add a `return Task.CompletedTask;` so we have to convert to a block body
                    var blockBodiedNode = ConvertToBlockBody(node, expressionBody);

                    // Expression bodied members can't have return statements so if we can't convert to a block
                    // body then we've done all we can
                    if (blockBodiedNode != null)
                    {
                        node = AddReturnStatement(generator, blockBodiedNode);
                    }
                }
                else
                {
                    // For Task<T> returning expression bodied methods we can just wrap the whole expression
                    var newExpressionBody = WrapExpressionWithTaskFromResult(
                        generator,
                        expressionBody,
                        returnType,
                        knownTypes
                        );
                    node = generator.WithExpression(node, newExpressionBody);
                }
            }
            else
            {
                if (IsTaskType(returnType, knownTypes))
                {
                    // If the end of the method isn't reachable, or there were no statements to analyze, then we
                    // need to add an explicit return
                    if (needsReturnStatementAdded)
                    {
                        node = AddReturnStatement(generator, node);
                    }
                }
            }

            node = ChangeReturnStatements(generator, node, returnType, knownTypes);

            return(node);
        }
示例#6
0
        private SyntaxNode ChangeReturnStatements(
            SyntaxGenerator generator,
            SyntaxNode node,
            ITypeSymbol returnType,
            KnownTypes knownTypes
            )
        {
            var editor = new SyntaxEditor(node, generator);

            // Look for all return statements, but if we find a new node that can have the async modifier we stop
            // because that will have its own diagnostic and fix, if applicable
            var returns = node.DescendantNodes(
                n => n == node || !IsAsyncSupportingFunctionSyntax(n)
                )
                          .OfType <TReturnStatementSyntax>();

            foreach (var returnSyntax in returns)
            {
                var returnExpression = generator.SyntaxFacts.GetExpressionOfReturnStatement(
                    returnSyntax
                    );
                if (returnExpression is null)
                {
                    // Convert return; into return Task.CompletedTask;
                    var returnTaskCompletedTask = GetReturnTaskCompletedTaskStatement(
                        generator,
                        returnType,
                        knownTypes
                        );
                    editor.ReplaceNode(returnSyntax, returnTaskCompletedTask);
                }
                else
                {
                    // Convert return <expr>; into return Task.FromResult(<expr>);
                    var newExpression = WrapExpressionWithTaskFromResult(
                        generator,
                        returnExpression,
                        returnType,
                        knownTypes
                        );
                    editor.ReplaceNode(returnExpression, newExpression);
                }
            }

            return(editor.GetChangedRoot());
        }
示例#7
0
 private static bool IsTaskType(ITypeSymbol returnType, KnownTypes knownTypes)
 => returnType.OriginalDefinition.Equals(knownTypes._taskType) ||
 returnType.OriginalDefinition.Equals(knownTypes._valueTaskType);
示例#8
0
 private static bool ShouldOfferFix(ITypeSymbol returnType, KnownTypes knownTypes)
 => IsTaskType(returnType, knownTypes) ||
 returnType.OriginalDefinition.Equals(knownTypes._taskOfTType) ||
 returnType.OriginalDefinition.Equals(knownTypes._valueTaskOfTTypeOpt);