forked from microsoft/vs-threading
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVSTHRD100AsyncVoidMethodCodeFix.cs
More file actions
91 lines (81 loc) · 4.03 KB
/
Copy pathVSTHRD100AsyncVoidMethodCodeFix.cs
File metadata and controls
91 lines (81 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.VisualStudio.Threading;
/// <summary>
/// Provides a code action to fix the Async Void Method by changing the return type to Task.
/// </summary>
/// <remarks>
/// [Background] Async void methods have different error-handling semantics.
/// When an exception is thrown out of an async Task or async <see cref="Task{T}"/> method/lambda,
/// that exception is captured and placed on the Task object. With async void methods,
/// there is no Task object, so any exceptions thrown out of an async void method will
/// be raised directly on the SynchronizationContext that was active when the async
/// void method started, and it would crash the process.
/// Refer to Stephen's article https://msdn.microsoft.com/en-us/magazine/jj991977.aspx for more info.
///
/// i.e.
/// <![CDATA[
/// async void MyMethod() /* This code action will change 'void' to 'Task'. */
/// {
/// }
/// ]]>
/// </remarks>
[ExportCodeFixProvider(LanguageNames.CSharp)]
public class VSTHRD100AsyncVoidMethodCodeFix : CodeFixProvider
{
private static readonly ImmutableArray<string> ReusableFixableDiagnosticIds = ImmutableArray.Create(
VSTHRD100AsyncVoidMethodAnalyzer.Descriptor.Id);
/// <inheritdoc />
public override ImmutableArray<string> FixableDiagnosticIds => ReusableFixableDiagnosticIds;
/// <inheritdoc />
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
Diagnostic? diagnostic = context.Diagnostics.First();
context.RegisterCodeFix(new VoidToTaskCodeAction(context.Document, diagnostic), diagnostic);
return Task.FromResult<object?>(null);
}
/// <inheritdoc />
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
private class VoidToTaskCodeAction : CodeAction
{
private Document document;
private Diagnostic diagnostic;
internal VoidToTaskCodeAction(Document document, Diagnostic diagnostic)
{
this.document = document;
this.diagnostic = diagnostic;
}
/// <inheritdoc />
public override string Title => Strings.VSTHRD100_CodeFix_Title;
/// <inheritdoc />
public override string? EquivalenceKey => null;
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
{
SyntaxNode? root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
MethodDeclarationSyntax? methodDeclaration = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MethodDeclarationSyntax>();
TypeSyntax? taskType = SyntaxFactory.ParseTypeName(typeof(Task).FullName)
.WithAdditionalAnnotations(Simplifier.Annotation)
.WithTrailingTrivia(methodDeclaration.ReturnType.GetTrailingTrivia());
MethodDeclarationSyntax? newMethodDeclaration = methodDeclaration.WithReturnType(taskType);
SyntaxNode? newRoot = root.ReplaceNode(methodDeclaration, newMethodDeclaration);
Document? newDocument = this.document.WithSyntaxRoot(newRoot);
return newDocument;
}
}
}
}