Welcome to mirror list, hosted at ThFree Co, Russian Federation.

CSharpAddAsyncCodeFixProvider.cs « Async « MonoDevelop.CSharp.CodeFixes « CSharpBinding « addins « src « main - github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 11e5cf2d72deeae31f320f9f4e688e5899cf2c3c (plain)
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.CodeActions;
using ICSharpCode.NRefactory6.CSharp.Refactoring;
using MonoDevelop.CSharp.CodeFixes;
using ICSharpCode.NRefactory6.CSharp;
using Microsoft.CodeAnalysis.CSharp;
using MonoDevelop.Core;

namespace MonoDevelop.CSharp.CodeFixes
{
	internal abstract partial class AbstractAddAsyncAwaitCodeFixProvider : AbstractAsyncCodeFix
	{
		protected abstract string GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken);
		protected abstract Task<SyntaxNode> GetNewRoot(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken);

		protected override async Task<CodeAction> GetCodeFix(SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
		{
			var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

			var newRoot = await this.GetNewRoot(root, node, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false);
			if (newRoot != null)
			{
				return new DocumentChangeAction(node.Span, DiagnosticSeverity.Error,
					this.GetDescription(diagnostic, node, semanticModel, cancellationToken),
					token => Task.FromResult(document.WithSyntaxRoot(newRoot)));
			}

			return null;
		}

		protected bool TryGetTypes(
			SyntaxNode expression,
			SemanticModel semanticModel,
			out INamedTypeSymbol source,
			out INamedTypeSymbol destination)
		{
			source = null;
			destination = null;

			var info = semanticModel.GetSymbolInfo(expression);
			var methodSymbol = info.Symbol as IMethodSymbol;
			if (methodSymbol == null)
			{
				return false;
			}

			var compilation = semanticModel.Compilation;
			var taskType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task");
			if (taskType == null)
			{
				return false;
			}

			var returnType = methodSymbol.ReturnType as INamedTypeSymbol;
			if (returnType == null)
			{
				return false;
			}

			source = taskType;
			destination = returnType;
			return true;
		}

	}

	[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddAsync), Shared]
	internal class CSharpAddAsyncCodeFixProvider : AbstractAddAsyncCodeFixProvider
	{
		/// <summary>
		/// The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
		/// </summary>
		private const string CS4032 = "CS4032";

		/// <summary>
		/// The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
		/// </summary>
		private const string CS4033 = "CS4033";

		/// <summary>
		/// The 'await' operator can only be used within an async lambda expression. Consider marking this method with the 'async' modifier.
		/// </summary>
		private const string CS4034 = "CS4034";

		public override ImmutableArray<string> FixableDiagnosticIds
		{
			get { return ImmutableArray.Create(CS4032, CS4033, CS4034); }
		}

		protected override string GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
		{
			return GettextCatalog.GetString ("Make async");
		}

		protected override async Task<SyntaxNode> GetNewRoot(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken)
		{
			var methodNode = GetContainingMember(oldNode);
			if (methodNode == null)
			{
				return null;
			}

			var newMethodNode = await ConvertToAsync(methodNode, semanticModel, document, cancellationToken).ConfigureAwait(false);
			if (newMethodNode != null)
			{
				return root.ReplaceNode(methodNode, newMethodNode);
			}

			return null;
		}

		private static SyntaxNode GetContainingMember(SyntaxNode oldNode)
		{
			var parenthesizedLambda = oldNode
				.Ancestors()
				.FirstOrDefault(n =>
				                n.IsKind(SyntaxKind.ParenthesizedLambdaExpression));

			if (parenthesizedLambda != null)
			{
				return parenthesizedLambda;
			}

			var simpleLambda = oldNode
				.Ancestors()
				.FirstOrDefault(n =>
				                n.IsKind(SyntaxKind.SimpleLambdaExpression));

			if (simpleLambda != null)
			{
				return simpleLambda;
			}

			return oldNode
				.Ancestors()
				.FirstOrDefault(n =>
				                n.IsKind(SyntaxKind.MethodDeclaration));
		}

		private async Task<SyntaxNode> ConvertToAsync(SyntaxNode node, SemanticModel semanticModel, Document document, CancellationToken cancellationToken)
		{
			var methodNode = node as MethodDeclarationSyntax;
			if (methodNode != null)
			{
				return await ConvertMethodToAsync(document, semanticModel, methodNode, cancellationToken).ConfigureAwait(false);
			}

			var parenthesizedLambda = node as ParenthesizedLambdaExpressionSyntax;
			if (parenthesizedLambda != null)
			{
				return ConvertParenthesizedLambdaToAsync(parenthesizedLambda);
			}

			var simpleLambda = node as SimpleLambdaExpressionSyntax;
			if (simpleLambda != null)
			{
				return ConvertSimpleLambdaToAsync(simpleLambda);
			}

			return null;
		}

		private static SyntaxNode ConvertParenthesizedLambdaToAsync(ParenthesizedLambdaExpressionSyntax parenthesizedLambda)
		{
			return SyntaxFactory.ParenthesizedLambdaExpression(
				SyntaxFactory.Token(SyntaxKind.AsyncKeyword),
				parenthesizedLambda.ParameterList,
				parenthesizedLambda.ArrowToken,
				parenthesizedLambda.Body)
				                .WithAdditionalAnnotations(Formatter.Annotation);
		}

		private static SyntaxNode ConvertSimpleLambdaToAsync(SimpleLambdaExpressionSyntax simpleLambda)
		{
			return SyntaxFactory.SimpleLambdaExpression(
				SyntaxFactory.Token(SyntaxKind.AsyncKeyword),
				simpleLambda.Parameter,
				simpleLambda.ArrowToken,
				simpleLambda.Body)
				                .WithAdditionalAnnotations(Formatter.Annotation);
		}

		protected override SyntaxNode AddAsyncKeyword(SyntaxNode node)
		{
			var methodNode = node as MethodDeclarationSyntax;
			if (methodNode == null)
			{
				return null;
			}

			return methodNode
				.AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword))
				.WithAdditionalAnnotations(Formatter.Annotation);
		}

		protected override SyntaxNode AddAsyncKeywordAndTaskReturnType(SyntaxNode node, ITypeSymbol existingReturnType, INamedTypeSymbol taskTypeSymbol)
		{
			var methodNode = node as MethodDeclarationSyntax;
			if (methodNode == null)
			{
				return null;
			}

			if (taskTypeSymbol == null)
			{
				return null;
			}

			var returnType = taskTypeSymbol.Construct(existingReturnType).GenerateTypeSyntax();
			return AddAsyncKeyword(methodNode.WithReturnType(returnType));
		}

		protected override bool DoesConversionExist(Compilation compilation, ITypeSymbol source, ITypeSymbol destination)
		{
			return compilation.ClassifyConversion(source, destination).Exists;
		}
	}
}