Skip to content

Commit e57bab3

Browse files
committed
Use Roslyn ErrorLogger for SARIF format logs and log analyzer execution time
1 parent 29f2913 commit e57bab3

File tree

1 file changed

+52
-22
lines changed

1 file changed

+52
-22
lines changed

src/Microsoft.DotNet.CodeFormatting/FormattingEngineImplementation.cs

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
using System.Collections.Immutable;
88
using System.Composition;
99
using System.Diagnostics;
10+
using System.IO;
1011
using System.Linq;
12+
using System.Reflection;
1113
using System.Threading;
1214
using System.Threading.Tasks;
1315

@@ -40,6 +42,8 @@ internal sealed partial class FormattingEngineImplementation : IFormattingEngine
4042
private readonly Dictionary<string, bool> _ruleEnabledMap = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
4143
private readonly ImmutableDictionary<string, CodeFixProvider> _diagnosticIdToFixerMap;
4244
private CompilationWithAnalyzers _compilationWithAnalyzers;
45+
private object _outputLogger; // Microsoft.CodeAnalysis.ErrorLogger
46+
private Assembly _loggerAssembly;
4347

4448
public ImmutableArray<string> CopyrightHeader
4549
{
@@ -92,6 +96,27 @@ public ImmutableArray<DiagnosticDescriptor> AllSupportedDiagnostics
9296
.SelectMany(a => a.SupportedDiagnostics)
9397
.OrderBy(a => a.Id)
9498
.ToImmutableArray();
99+
100+
// Use the Roslyn ErrorLogger type to log diagnostics per analyzer to SARIF format
101+
public void LogDiagnostics(string filePath, ImmutableArray<Diagnostic> diagnostics)
102+
{
103+
if (_loggerAssembly == null)
104+
{
105+
_loggerAssembly = Assembly.Load(typeof(CommandLineParser).Assembly.FullName);
106+
}
107+
108+
_outputLogger = Activator.CreateInstance(
109+
_loggerAssembly.GetType("Microsoft.CodeAnalysis.ErrorLogger"),
110+
new object[] { new FileStream(filePath, FileMode.Append, FileAccess.Write), "CodeFormatter", "0.1", _loggerAssembly.GetName().Version });
111+
112+
var logDiagMethodInfo = _outputLogger.GetType().GetMethod("LogDiagnostic", BindingFlags.NonPublic | BindingFlags.Instance);
113+
foreach (var diagnostic in diagnostics)
114+
{
115+
logDiagMethodInfo.Invoke(_outputLogger, new object[] { diagnostic, System.Globalization.CultureInfo.DefaultThreadCurrentCulture });
116+
}
117+
118+
((IDisposable)_outputLogger).Dispose();
119+
}
95120

96121
public FormattingEngineImplementation(
97122
FormattingOptions options,
@@ -260,35 +285,39 @@ private async Task<int> getProjectLinesOfCodeCount(Project project)
260285
return totalLines.Sum();
261286
}
262287

263-
private string createAnalyzerResultText(DiagnosticAnalyzer analyzer, ImmutableArray<Diagnostic> diagnostics, int documentCount, int linesOfCodeInProject)
264-
{
265-
return String.Format("{0}\t{1}\t{2}\t{3}\n", analyzer.ToString(), documentCount, linesOfCodeInProject, diagnostics.Count());
266-
}
267-
268288
private async Task FormatWithAnalyzersCoreAsync(Workspace workspace, ProjectId projectId, IEnumerable<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
269289
{
270290
if (analyzers != null && analyzers.Count() != 0)
271291
{
272292
var project = workspace.CurrentSolution.GetProject(projectId);
273293
var diagnostics = await GetDiagnostics(project, analyzers, cancellationToken).ConfigureAwait(false);
274-
275-
var projectResultText = "";
276-
var ext = project.Language == "C#" ? ".csproj" : ".vbproj";
277-
var resultFile = project.FilePath.Substring(project.FilePath.LastIndexOf(System.IO.Path.DirectorySeparatorChar)).Replace(ext, "_CodeFormatterResults.txt");
278-
279-
var linesOfCodeInProject = -1;
280-
foreach (var analyzer in analyzers)
281-
{
282-
var diags = await _compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync(ImmutableArray.Create(analyzer), cancellationToken);
283-
linesOfCodeInProject = linesOfCodeInProject == -1 ? await getProjectLinesOfCodeCount(project) : linesOfCodeInProject;
284-
var analyzerResultText = createAnalyzerResultText(analyzer, diags, project.Documents.Count(), linesOfCodeInProject);
285-
FormatLogger.Write(analyzerResultText);
286-
projectResultText += analyzerResultText;
287-
}
288-
289-
if (LogOutputPath != null)
294+
// Ensure at least 1 analyzer supporting the current project's language ran
295+
if (_compilationWithAnalyzers != null)
290296
{
291-
System.IO.File.WriteAllText(System.IO.Path.Combine(LogOutputPath, resultFile), projectResultText);
297+
var ext = project.Language == "C#" ? ".csproj" : ".vbproj";
298+
var resultFile = project.FilePath.Substring(project.FilePath.LastIndexOf(Path.DirectorySeparatorChar)).Replace(ext, "_CodeFormatterResults.txt");
299+
300+
var linesOfCodeInProject = -1;
301+
foreach (var analyzer in analyzers)
302+
{
303+
var diags = await _compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync(ImmutableArray.Create(analyzer), cancellationToken);
304+
linesOfCodeInProject = linesOfCodeInProject == -1 ? await getProjectLinesOfCodeCount(project) : linesOfCodeInProject;
305+
var analyzerTelemetryInfo = await _compilationWithAnalyzers.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken);
306+
var analyzerResultText = String.Format("{0}\t{1}\t{2}\t{3}\t{4}\r\n",
307+
analyzer.ToString(),
308+
project.Documents.Count(),
309+
linesOfCodeInProject,
310+
diagnostics.Count(),
311+
analyzerTelemetryInfo.ExecutionTime);
312+
313+
FormatLogger.Write(analyzerResultText);
314+
315+
if (LogOutputPath != null)
316+
{
317+
LogDiagnostics((LogOutputPath + resultFile).Replace(".txt", ".json"), diags);
318+
File.AppendAllText(LogOutputPath + resultFile, analyzerResultText);
319+
}
320+
}
292321
}
293322

294323
if (ApplyFixes)
@@ -444,6 +473,7 @@ private async Task<ImmutableArray<Diagnostic>> GetDiagnostics(Project project, I
444473
}
445474
else
446475
{
476+
_compilationWithAnalyzers = null;
447477
return ImmutableArray<Diagnostic>.Empty;
448478
}
449479
}

0 commit comments

Comments
 (0)