PageRenderTime 59ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.DiagnosticProvider.cs

https://gitlab.com/sharadag/Roslyn
C# | 212 lines | 164 code | 31 blank | 17 comment | 15 complexity | 31275720534452cb4c51131695c130a6 MD5 | raw file
  1. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using System.Collections.Immutable;
  6. using System.Linq;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using Microsoft.CodeAnalysis.GeneratedCodeRecognition;
  10. using Microsoft.CodeAnalysis.Internal.Log;
  11. using Microsoft.CodeAnalysis.Shared.Utilities;
  12. using Roslyn.Utilities;
  13. namespace Microsoft.CodeAnalysis.CodeFixes
  14. {
  15. /// <summary>
  16. /// Context for "Fix all occurrences" code fixes provided by an <see cref="FixAllProvider"/>.
  17. /// </summary>
  18. public partial class FixAllContext
  19. {
  20. /// <summary>
  21. /// Diagnostic provider to fetch document/project diagnostics to fix in a <see cref="FixAllContext"/>.
  22. /// </summary>
  23. public abstract class DiagnosticProvider
  24. {
  25. internal virtual bool IsFixMultiple => false;
  26. /// <summary>
  27. /// Gets all the diagnostics to fix in the given document in a <see cref="FixAllContext"/>.
  28. /// </summary>
  29. public abstract Task<IEnumerable<Diagnostic>> GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken);
  30. /// <summary>
  31. /// Gets all the project-level diagnostics to fix, i.e. diagnostics with no source location, in the given project in a <see cref="FixAllContext"/>.
  32. /// </summary>
  33. public abstract Task<IEnumerable<Diagnostic>> GetProjectDiagnosticsAsync(Project project, CancellationToken cancellationToken);
  34. /// <summary>
  35. /// Gets all the diagnostics to fix in the given project in a <see cref="FixAllContext"/>.
  36. /// This includes both document-level diagnostics for all documents in the given project and project-level diagnostics, i.e. diagnostics with no source location, in the given project.
  37. /// </summary>
  38. public abstract Task<IEnumerable<Diagnostic>> GetAllDiagnosticsAsync(Project project, CancellationToken cancellationToken);
  39. internal virtual async Task<ImmutableDictionary<Document, ImmutableArray<Diagnostic>>> GetDocumentDiagnosticsToFixAsync(
  40. FixAllContext fixAllContext)
  41. {
  42. using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Diagnostics, fixAllContext.CancellationToken))
  43. {
  44. var allDiagnostics = ImmutableArray<Diagnostic>.Empty;
  45. var projectsToFix = ImmutableArray<Project>.Empty;
  46. var document = fixAllContext.Document;
  47. var project = fixAllContext.Project;
  48. var generatedCodeServices = project.Solution.Workspace.Services.GetService<IGeneratedCodeRecognitionService>();
  49. switch (fixAllContext.Scope)
  50. {
  51. case FixAllScope.Document:
  52. if (document != null && !generatedCodeServices.IsGeneratedCode(document))
  53. {
  54. var documentDiagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(document).ConfigureAwait(false);
  55. var kvp = SpecializedCollections.SingletonEnumerable(KeyValuePair.Create(document, documentDiagnostics));
  56. return ImmutableDictionary.CreateRange(kvp);
  57. }
  58. break;
  59. case FixAllScope.Project:
  60. projectsToFix = ImmutableArray.Create(project);
  61. allDiagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false);
  62. break;
  63. case FixAllScope.Solution:
  64. projectsToFix = project.Solution.Projects
  65. .Where(p => p.Language == project.Language)
  66. .ToImmutableArray();
  67. var progressTracker = fixAllContext.ProgressTracker;
  68. progressTracker.AddItems(projectsToFix.Length);
  69. var diagnostics = new ConcurrentBag<Diagnostic>();
  70. var tasks = new Task[projectsToFix.Length];
  71. for (int i = 0; i < projectsToFix.Length; i++)
  72. {
  73. fixAllContext.CancellationToken.ThrowIfCancellationRequested();
  74. var projectToFix = projectsToFix[i];
  75. tasks[i] = Task.Run(async () =>
  76. {
  77. var projectDiagnostics = await fixAllContext.GetAllDiagnosticsAsync(projectToFix).ConfigureAwait(false);
  78. foreach (var diagnostic in projectDiagnostics)
  79. {
  80. fixAllContext.CancellationToken.ThrowIfCancellationRequested();
  81. diagnostics.Add(diagnostic);
  82. }
  83. progressTracker.ItemCompleted();
  84. }, fixAllContext.CancellationToken);
  85. }
  86. await Task.WhenAll(tasks).ConfigureAwait(false);
  87. allDiagnostics = allDiagnostics.AddRange(diagnostics);
  88. break;
  89. }
  90. if (allDiagnostics.IsEmpty)
  91. {
  92. return ImmutableDictionary<Document, ImmutableArray<Diagnostic>>.Empty;
  93. }
  94. return await GetDocumentDiagnosticsToFixAsync(allDiagnostics, projectsToFix, generatedCodeServices.IsGeneratedCode, fixAllContext.CancellationToken).ConfigureAwait(false);
  95. }
  96. }
  97. private async static Task<ImmutableDictionary<Document, ImmutableArray<Diagnostic>>> GetDocumentDiagnosticsToFixAsync(
  98. ImmutableArray<Diagnostic> diagnostics,
  99. ImmutableArray<Project> projects,
  100. Func<Document, bool> isGeneratedCode, CancellationToken cancellationToken)
  101. {
  102. var treeToDocumentMap = await GetTreeToDocumentMapAsync(projects, cancellationToken).ConfigureAwait(false);
  103. var builder = ImmutableDictionary.CreateBuilder<Document, ImmutableArray<Diagnostic>>();
  104. foreach (var documentAndDiagnostics in diagnostics.GroupBy(d => GetReportedDocument(d, treeToDocumentMap)))
  105. {
  106. cancellationToken.ThrowIfCancellationRequested();
  107. var document = documentAndDiagnostics.Key;
  108. if (!isGeneratedCode(document))
  109. {
  110. var diagnosticsForDocument = documentAndDiagnostics.ToImmutableArray();
  111. builder.Add(document, diagnosticsForDocument);
  112. }
  113. }
  114. return builder.ToImmutable();
  115. }
  116. private static async Task<ImmutableDictionary<SyntaxTree, Document>> GetTreeToDocumentMapAsync(ImmutableArray<Project> projects, CancellationToken cancellationToken)
  117. {
  118. var builder = ImmutableDictionary.CreateBuilder<SyntaxTree, Document>();
  119. foreach (var project in projects)
  120. {
  121. cancellationToken.ThrowIfCancellationRequested();
  122. foreach (var document in project.Documents)
  123. {
  124. cancellationToken.ThrowIfCancellationRequested();
  125. var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
  126. builder.Add(tree, document);
  127. }
  128. }
  129. return builder.ToImmutable();
  130. }
  131. private static Document GetReportedDocument(Diagnostic diagnostic, ImmutableDictionary<SyntaxTree, Document> treeToDocumentsMap)
  132. {
  133. var tree = diagnostic.Location.SourceTree;
  134. if (tree != null)
  135. {
  136. Document document;
  137. if (treeToDocumentsMap.TryGetValue(tree, out document))
  138. {
  139. return document;
  140. }
  141. }
  142. return null;
  143. }
  144. internal virtual async Task<ImmutableDictionary<Project, ImmutableArray<Diagnostic>>> GetProjectDiagnosticsToFixAsync(
  145. FixAllContext fixAllContext)
  146. {
  147. using (Logger.LogBlock(FunctionId.CodeFixes_FixAllOccurrencesComputation_Diagnostics, fixAllContext.CancellationToken))
  148. {
  149. var project = fixAllContext.Project;
  150. if (project != null)
  151. {
  152. switch (fixAllContext.Scope)
  153. {
  154. case FixAllScope.Project:
  155. var diagnostics = await fixAllContext.GetProjectDiagnosticsAsync(project).ConfigureAwait(false);
  156. var kvp = SpecializedCollections.SingletonEnumerable(KeyValuePair.Create(project, diagnostics));
  157. return ImmutableDictionary.CreateRange(kvp);
  158. case FixAllScope.Solution:
  159. var projectsAndDiagnostics = ImmutableDictionary.CreateBuilder<Project, ImmutableArray<Diagnostic>>();
  160. var tasks = project.Solution.Projects.Select(async p => new
  161. {
  162. Project = p,
  163. Diagnostics = await fixAllContext.GetProjectDiagnosticsAsync(p).ConfigureAwait(false)
  164. }).ToArray();
  165. await Task.WhenAll(tasks).ConfigureAwait(false);
  166. foreach (var task in tasks)
  167. {
  168. if (task.Result.Diagnostics.Any())
  169. {
  170. projectsAndDiagnostics[task.Result.Project] = task.Result.Diagnostics;
  171. }
  172. }
  173. return projectsAndDiagnostics.ToImmutable();
  174. }
  175. }
  176. return ImmutableDictionary<Project, ImmutableArray<Diagnostic>>.Empty;
  177. }
  178. }
  179. }
  180. }
  181. }