PageRenderTime 28ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Diagnostics/Roslyn/Core/ApiDesign/DeclarePublicAPIFix.cs

https://gitlab.com/sharadag/TestProject2
C# | 258 lines | 207 code | 50 blank | 1 comment | 12 complexity | f151d92a0f2e4e1b559da757ab3e9a10 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.Generic;
  4. using System.Collections.Immutable;
  5. using System.Composition;
  6. using System.Linq;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using Microsoft.CodeAnalysis;
  10. using Microsoft.CodeAnalysis.CodeActions;
  11. using Microsoft.CodeAnalysis.CodeFixes;
  12. using Microsoft.CodeAnalysis.Text;
  13. namespace Roslyn.Diagnostics.Analyzers.ApiDesign
  14. {
  15. [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = "DeclarePublicAPIFix"), Shared]
  16. public class DeclarePublicAPIFix : CodeFixProvider
  17. {
  18. public sealed override ImmutableArray<string> FixableDiagnosticIds
  19. {
  20. get { return ImmutableArray.Create(RoslynDiagnosticIds.DeclarePublicApiRuleId); }
  21. }
  22. public sealed override FixAllProvider GetFixAllProvider()
  23. {
  24. return new PublicSurfaceAreaFixAllProvider();
  25. }
  26. public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
  27. {
  28. var project = context.Document.Project;
  29. TextDocument publicSurfaceAreaDocument = GetPublicSurfaceAreaDocument(project);
  30. if (publicSurfaceAreaDocument == null)
  31. {
  32. return;
  33. }
  34. var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
  35. var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
  36. foreach (var diagnostic in context.Diagnostics)
  37. {
  38. string minimalSymbolName = diagnostic.Properties[DeclarePublicAPIAnalyzer.MinimalNamePropertyBagKey];
  39. string publicSurfaceAreaSymbolName = diagnostic.Properties[DeclarePublicAPIAnalyzer.PublicApiNamePropertyBagKey];
  40. context.RegisterCodeFix(
  41. new AdditionalDocumentChangeAction(
  42. $"Add {minimalSymbolName} to public API",
  43. c => GetFix(publicSurfaceAreaDocument, publicSurfaceAreaSymbolName, c)),
  44. diagnostic);
  45. }
  46. }
  47. private static TextDocument GetPublicSurfaceAreaDocument(Project project)
  48. {
  49. return project.AdditionalDocuments.FirstOrDefault(doc => doc.Name.Equals(DeclarePublicAPIAnalyzer.PublicApiFileName, StringComparison.OrdinalIgnoreCase));
  50. }
  51. private async Task<Solution> GetFix(TextDocument publicSurfaceAreaDocument, string newSymbolName, CancellationToken cancellationToken)
  52. {
  53. var sourceText = await publicSurfaceAreaDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
  54. var newSourceText = AddSymbolNamesToSourceText(sourceText, new[] { newSymbolName });
  55. return publicSurfaceAreaDocument.Project.Solution.WithAdditionalDocumentText(publicSurfaceAreaDocument.Id, newSourceText);
  56. }
  57. private static SourceText AddSymbolNamesToSourceText(SourceText sourceText, IEnumerable<string> newSymbolNames)
  58. {
  59. HashSet<string> lines = GetLinesFromSourceText(sourceText);
  60. foreach (var name in newSymbolNames)
  61. {
  62. lines.Add(name);
  63. }
  64. var sortedLines = lines.OrderBy(s => s, StringComparer.Ordinal);
  65. var newSourceText = sourceText.Replace(new TextSpan(0, sourceText.Length), string.Join(Environment.NewLine, sortedLines));
  66. return newSourceText;
  67. }
  68. private static HashSet<string> GetLinesFromSourceText(SourceText sourceText)
  69. {
  70. var lines = new HashSet<string>();
  71. foreach (var textLine in sourceText.Lines)
  72. {
  73. var text = textLine.ToString();
  74. if (!string.IsNullOrWhiteSpace(text))
  75. {
  76. lines.Add(text);
  77. }
  78. }
  79. return lines;
  80. }
  81. private static ISymbol FindDeclaration(SyntaxNode root, Location location, SemanticModel semanticModel, CancellationToken cancellationToken)
  82. {
  83. var node = root.FindNode(location.SourceSpan);
  84. ISymbol symbol = null;
  85. while (node != null)
  86. {
  87. symbol = semanticModel.GetDeclaredSymbol(node, cancellationToken);
  88. if (symbol != null)
  89. {
  90. break;
  91. }
  92. node = node.Parent;
  93. }
  94. return symbol;
  95. }
  96. private class AdditionalDocumentChangeAction : CodeAction
  97. {
  98. private readonly Func<CancellationToken, Task<Solution>> _createChangedAdditionalDocument;
  99. public AdditionalDocumentChangeAction(string title, Func<CancellationToken, Task<Solution>> createChangedAdditionalDocument)
  100. {
  101. this.Title = title;
  102. _createChangedAdditionalDocument = createChangedAdditionalDocument;
  103. }
  104. public override string Title { get; }
  105. protected override Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
  106. {
  107. return _createChangedAdditionalDocument(cancellationToken);
  108. }
  109. }
  110. private class FixAllAdditionalDocumentChangeAction : CodeAction
  111. {
  112. private readonly List<KeyValuePair<Project, ImmutableArray<Diagnostic>>> _diagnosticsToFix;
  113. private readonly Solution _solution;
  114. public FixAllAdditionalDocumentChangeAction(string title, Solution solution, List<KeyValuePair<Project, ImmutableArray<Diagnostic>>> diagnosticsToFix)
  115. {
  116. this.Title = title;
  117. _solution = solution;
  118. _diagnosticsToFix = diagnosticsToFix;
  119. }
  120. public override string Title { get; }
  121. protected override async Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
  122. {
  123. var updatedPublicSurfaceAreaText = new List<KeyValuePair<DocumentId, SourceText>>();
  124. foreach (var pair in _diagnosticsToFix)
  125. {
  126. var project = pair.Key;
  127. var diagnostics = pair.Value;
  128. var publicSurfaceAreaAdditionalDocument = GetPublicSurfaceAreaDocument(project);
  129. if (publicSurfaceAreaAdditionalDocument == null)
  130. {
  131. continue;
  132. }
  133. var sourceText = await publicSurfaceAreaAdditionalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
  134. var groupedDiagnostics =
  135. diagnostics
  136. .Where(d => d.Location.IsInSource)
  137. .GroupBy(d => d.Location.SourceTree);
  138. var newSymbolNames = new List<string>();
  139. foreach (var grouping in groupedDiagnostics)
  140. {
  141. var document = project.GetDocument(grouping.Key);
  142. if (document == null)
  143. {
  144. continue;
  145. }
  146. var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
  147. var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
  148. foreach (var diagnostic in grouping)
  149. {
  150. string publicSurfaceAreaSymbolName = diagnostic.Properties[DeclarePublicAPIAnalyzer.PublicApiNamePropertyBagKey];
  151. newSymbolNames.Add(publicSurfaceAreaSymbolName);
  152. }
  153. }
  154. var newSourceText = AddSymbolNamesToSourceText(sourceText, newSymbolNames);
  155. updatedPublicSurfaceAreaText.Add(new KeyValuePair<DocumentId, SourceText>(publicSurfaceAreaAdditionalDocument.Id, newSourceText));
  156. }
  157. var newSolution = _solution;
  158. foreach (var pair in updatedPublicSurfaceAreaText)
  159. {
  160. newSolution = newSolution.WithAdditionalDocumentText(pair.Key, pair.Value);
  161. }
  162. return newSolution;
  163. }
  164. }
  165. private class PublicSurfaceAreaFixAllProvider : FixAllProvider
  166. {
  167. public override async Task<CodeAction> GetFixAsync(FixAllContext fixAllContext)
  168. {
  169. var diagnosticsToFix = new List<KeyValuePair<Project, ImmutableArray<Diagnostic>>>();
  170. string titleFormat = "Add all items in {0} {1} to the public API";
  171. string title = null;
  172. switch (fixAllContext.Scope)
  173. {
  174. case FixAllScope.Document:
  175. {
  176. var diagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(fixAllContext.Document).ConfigureAwait(false);
  177. diagnosticsToFix.Add(new KeyValuePair<Project, ImmutableArray<Diagnostic>>(fixAllContext.Project, diagnostics));
  178. title = string.Format(titleFormat, "document", fixAllContext.Document.Name);
  179. break;
  180. }
  181. case FixAllScope.Project:
  182. {
  183. var project = fixAllContext.Project;
  184. ImmutableArray<Diagnostic> diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false);
  185. diagnosticsToFix.Add(new KeyValuePair<Project, ImmutableArray<Diagnostic>>(fixAllContext.Project, diagnostics));
  186. title = string.Format(titleFormat, "project", fixAllContext.Project.Name);
  187. break;
  188. }
  189. case FixAllScope.Solution:
  190. {
  191. foreach (var project in fixAllContext.Solution.Projects)
  192. {
  193. ImmutableArray<Diagnostic> diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false);
  194. diagnosticsToFix.Add(new KeyValuePair<Project, ImmutableArray<Diagnostic>>(project, diagnostics));
  195. }
  196. title = "Add all items in the solution to the public API";
  197. break;
  198. }
  199. case FixAllScope.Custom:
  200. return null;
  201. default:
  202. break;
  203. }
  204. return new FixAllAdditionalDocumentChangeAction(title, fixAllContext.Solution, diagnosticsToFix);
  205. }
  206. }
  207. }
  208. }