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

CSharpAddAwaitCodeFixProvider.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: 9bcb3033d1a31d972ef75a136282fff9e1b67b18 (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
// 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.Linq;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.CodeActions;
using ICSharpCode.NRefactory6.CSharp;
using MonoDevelop.CSharp.CodeFixes;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using MonoDevelop.Core;

namespace MonoDevelop.CSharp.CodeFixes
{
	internal abstract partial class AbstractAddAsyncCodeFixProvider : AbstractAddAsyncAwaitCodeFixProvider
	{
		protected const string SystemThreadingTasksTask = "System.Threading.Tasks.Task";
		protected const string SystemThreadingTasksTaskT = "System.Threading.Tasks.Task`1";
		protected abstract SyntaxNode AddAsyncKeyword(SyntaxNode methodNode);
		protected abstract SyntaxNode AddAsyncKeywordAndTaskReturnType(SyntaxNode methodNode, ITypeSymbol existingReturnType, INamedTypeSymbol taskTypeSymbol);
		protected abstract bool DoesConversionExist(Compilation compilation, ITypeSymbol source, ITypeSymbol destination);

		protected async Task<SyntaxNode> ConvertMethodToAsync(Document document, SemanticModel semanticModel, SyntaxNode methodNode, CancellationToken cancellationToken)
		{
			var methodSymbol = semanticModel.GetDeclaredSymbol(methodNode, cancellationToken) as IMethodSymbol;

			if (methodSymbol.ReturnsVoid)
			{
				return AddAsyncKeyword(methodNode);
			}

			var returnType = methodSymbol.ReturnType;
			var compilation = semanticModel.Compilation;

			var taskSymbol = compilation.GetTypeByMetadataName(SystemThreadingTasksTask);
			var genericTaskSymbol = compilation.GetTypeByMetadataName(SystemThreadingTasksTaskT);
			if (taskSymbol == null)
			{
				return null;
			}

			if (returnType is IErrorTypeSymbol)
			{
				// The return type of the method will not bind.  This could happen for a lot of reasons.
				// The type may not actually exist or the user could just be missing a using/import statement.
				// We're going to try and see if there are any known types that have the same name as 
				// our return type, and then check if those are convertible to Task.  If they are then
				// we assume the user just has a missing using.  If they are not, we wrap the return
				// type in a generic Task.
				var typeName = returnType.Name;

				var results = await SymbolFinder.FindDeclarationsAsync(
					document.Project, typeName, ignoreCase: false, filter: SymbolFilter.Type, cancellationToken: cancellationToken).ConfigureAwait(false);

				if (results.OfType<ITypeSymbol>().Any(s => DoesConversionExist(compilation, s, taskSymbol)))
				{
					return AddAsyncKeyword(methodNode);
				}

				return AddAsyncKeywordAndTaskReturnType(methodNode, returnType, genericTaskSymbol);
			}

			if (DoesConversionExist(compilation, returnType, taskSymbol))
			{
				return AddAsyncKeyword(methodNode);
			}

			return AddAsyncKeywordAndTaskReturnType(methodNode, returnType, genericTaskSymbol);
		}
	}


	[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddAwait), Shared]
	internal class CSharpAddAwaitCodeFixProvider : AbstractAddAsyncAwaitCodeFixProvider
	{
		/// <summary>
		/// Since this is an async method, the return expression must be of type 'blah' rather than 'baz'
		/// </summary>
		private const string CS4014 = "CS4014";

		/// <summary>
		/// Because this call is not awaited, execution of the current method continues before the call is completed.
		/// </summary>
		private const string CS4016 = "CS4016";

		public override ImmutableArray<string> FixableDiagnosticIds
		{
			get { return ImmutableArray.Create(CS4014, CS4016); }
		}

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

		protected override Task<SyntaxNode> GetNewRoot(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken)
		{
			var expression = oldNode as ExpressionSyntax;

			switch (diagnostic.Id)
			{
				case CS4014:
					if (expression == null)
					{
						return Task.FromResult<SyntaxNode>(null);
					}

				return Task.FromResult(root.ReplaceNode(oldNode, ConvertToAwaitExpression(expression)));
				case CS4016:
					if (expression == null)
					{
					return Task.FromResult (default (SyntaxNode));
					}

					if (!IsCorrectReturnType(expression, semanticModel))
					{
					return Task.FromResult (default (SyntaxNode));
					}

				return Task.FromResult(root.ReplaceNode(oldNode, ConvertToAwaitExpression(expression)));
				default:
				return Task.FromResult (default (SyntaxNode));
			}
		}

		private bool IsCorrectReturnType(ExpressionSyntax expression, SemanticModel semanticModel)
		{
			INamedTypeSymbol taskType = null;
			INamedTypeSymbol returnType = null;
			return TryGetTypes(expression, semanticModel, out taskType, out returnType) &&
				semanticModel.Compilation.ClassifyConversion(taskType, returnType).Exists;
		}

		private static ExpressionSyntax ConvertToAwaitExpression(ExpressionSyntax expression)
		{
			return SyntaxFactory.AwaitExpression(expression)
				                .WithAdditionalAnnotations(Formatter.Annotation);
		}
	}
}