public override async Task RegisterCodeFixesAsync(CodeFixContext context) { Diagnostic?diagnostic = context.Diagnostics.First(); SyntaxNode?root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (TryFindNodeAtSource(diagnostic, root, out _, out _)) { context.RegisterCodeFix( CodeAction.Create( Strings.VSTHRD002_CodeFix_Await_Title, async ct => { Document?document = context.Document; if (TryFindNodeAtSource(diagnostic, root, out ExpressionSyntax? node, out Func <ExpressionSyntax, CancellationToken, ExpressionSyntax>?transform)) { (document, node, _) = await FixUtils.UpdateDocumentAsync( document, node, n => SyntaxFactory.AwaitExpression(transform(n, ct)), ct).ConfigureAwait(false); MethodDeclarationSyntax?method = node.FirstAncestorOrSelf <MethodDeclarationSyntax>(); if (method is object) { (document, method) = await FixUtils.MakeMethodAsync(method, document, ct).ConfigureAwait(false); } } return(document.Project.Solution); }, "only action"), diagnostic); } }
public override Task RegisterCodeFixesAsync(CodeFixContext context) { var diagnostic = context.Diagnostics.First(); context.RegisterCodeFix( CodeAction.Create( Strings.VSTHRD107_CodeFix_Title, async ct => { var document = context.Document; var root = await document.GetSyntaxRootAsync(ct).ConfigureAwait(false); var method = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <MethodDeclarationSyntax>(); (document, method, _) = await FixUtils.UpdateDocumentAsync( document, method, m => { root = m.SyntaxTree.GetRoot(ct); var usingStatement = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <UsingStatementSyntax>(); var awaitExpression = SyntaxFactory.AwaitExpression( SyntaxFactory.ParenthesizedExpression(usingStatement.Expression)); var modifiedUsingStatement = usingStatement.WithExpression(awaitExpression) .WithAdditionalAnnotations(Simplifier.Annotation); return(m.ReplaceNode(usingStatement, modifiedUsingStatement)); }, ct).ConfigureAwait(false); (document, method) = await method.MakeMethodAsync(document, ct).ConfigureAwait(false); return(document.Project.Solution); }, "only action"), diagnostic); return(Task.FromResult <object>(null)); }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { foreach (var diagnostic in context.Diagnostics) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); ExpressionSyntax?syntaxNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) as ExpressionSyntax; if (syntaxNode is null) { continue; } var container = Utils.GetContainingFunction(syntaxNode); if (container.BlockOrExpression == null) { return; } if (!container.IsAsync) { if (!(container.Function is MethodDeclarationSyntax || container.Function is AnonymousFunctionExpressionSyntax)) { // We don't support converting whatever this is into an async method. return; } } var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); var enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken); if (enclosingSymbol == null) { return; } var options = await CommonFixes.ReadMethodsAsync(context, CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread, context.CancellationToken); int positionForLookup = diagnostic.Location.SourceSpan.Start; ISymbol cancellationTokenSymbol = Utils.FindCancellationToken(semanticModel, positionForLookup, context.CancellationToken).FirstOrDefault(); foreach (var option in options) { // We're looking for methods that either require no parameters, // or (if we have one to give) that have just one parameter that is a CancellationToken. var proposedMethod = Utils.FindMethodGroup(semanticModel, option) .FirstOrDefault(m => !m.Parameters.Any(p => !p.HasExplicitDefaultValue) || (cancellationTokenSymbol != null && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0]))); if (proposedMethod == null) { // We can't find it, so don't offer to use it. continue; } if (proposedMethod.IsStatic) { OfferFix(option.ToString()); } else { foreach (var candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken)) { if (candidate.Item1) { OfferFix($"{candidate.Item2.Name}.{proposedMethod.Name}"); } else { OfferFix($"{candidate.Item2.ContainingNamespace}.{candidate.Item2.ContainingType.Name}.{candidate.Item2.Name}.{proposedMethod.Name}"); } } } void OfferFix(string fullyQualifiedMethod) { context.RegisterCodeFix(CodeAction.Create($"Use 'await {fullyQualifiedMethod}'", ct => Fix(fullyQualifiedMethod, proposedMethod, ct), fullyQualifiedMethod), context.Diagnostics); } } async Task <Solution> Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, CancellationToken cancellationToken) { var assertionStatementToRemove = syntaxNode !.FirstAncestorOrSelf <StatementSyntax>(); int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.'); IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1)); ExpressionSyntax invokedMethod = Utils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName) .WithAdditionalAnnotations(Simplifier.Annotation); var invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); var cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter); if (cancellationTokenParameter != null && cancellationTokenSymbol != null) { var arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Name)); if (methodSymbol.Parameters.IndexOf(cancellationTokenParameter) > 0) { arg = arg.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(cancellationTokenParameter.Name))); } invocationExpression = invocationExpression.AddArgumentListArguments(arg); } ExpressionSyntax awaitExpression = SyntaxFactory.AwaitExpression(invocationExpression); var addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression) .WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation); var methodAnnotation = new SyntaxAnnotation(); CSharpSyntaxNode methodSyntax = container.Function.ReplaceNode(assertionStatementToRemove, addedStatement) .WithAdditionalAnnotations(methodAnnotation); Document newDocument = context.Document.WithSyntaxRoot(root.ReplaceNode(container.Function, methodSyntax)); var newSyntaxRoot = await newDocument.GetSyntaxRootAsync(cancellationToken); methodSyntax = (CSharpSyntaxNode)newSyntaxRoot.GetAnnotatedNodes(methodAnnotation).Single(); if (!container.IsAsync) { switch (methodSyntax) { case AnonymousFunctionExpressionSyntax anonFunc: semanticModel = await newDocument.GetSemanticModelAsync(cancellationToken); methodSyntax = FixUtils.MakeMethodAsync(anonFunc, semanticModel, cancellationToken); newDocument = newDocument.WithSyntaxRoot(newSyntaxRoot.ReplaceNode(anonFunc, methodSyntax)); break; case MethodDeclarationSyntax methodDecl: (newDocument, methodSyntax) = await FixUtils.MakeMethodAsync(methodDecl, newDocument, cancellationToken); break; } } return(newDocument.Project.Solution); } } }