Skip to content

Commit 26e2202

Browse files
committed
Cleanup, perf fixes
1 parent 7bd2d43 commit 26e2202

File tree

4 files changed

+121
-49
lines changed

4 files changed

+121
-49
lines changed

src/CodeFormatter/CodeFormatter.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<HintPath>..\packages\CommandLineParser.2.0.273-beta\lib\net45\CommandLine.dll</HintPath>
2222
<Private>True</Private>
2323
</Reference>
24+
<Reference Include="Microsoft.Build" />
2425
<Reference Include="Microsoft.CodeAnalysis, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
2526
<HintPath>..\packages\Microsoft.CodeAnalysis.Common.1.2.0-beta1-20160226-01\lib\net45\Microsoft.CodeAnalysis.dll</HintPath>
2627
<Private>True</Private>

src/CodeFormatter/Program.cs

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ private static int RunCommand(CommandLineOptions options, bool applyCodeFixes) {
109109

110110
Console.CancelKeyPress += delegate { cts.Cancel(); };
111111

112+
var stopwatch = new Stopwatch();
113+
stopwatch.Start();
114+
112115
try
113116
{
114117
RunAsync(options, ct).Wait(ct);
@@ -128,6 +131,11 @@ private static int RunCommand(CommandLineOptions options, bool applyCodeFixes) {
128131

129132
return FAILED;
130133
}
134+
finally
135+
{
136+
stopwatch.Stop();
137+
Console.WriteLine("Total time: {0}", stopwatch.Elapsed);
138+
}
131139
}
132140

133141
private static IAnalyzerAssemblyLoader _analyzerAssemblyLoader;
@@ -210,7 +218,26 @@ private static async Task<int> RunAsync(CommandLineOptions options, Cancellation
210218

211219
foreach (var item in options.Targets)
212220
{
213-
await RunItemAsync(engine, item, options.Language, options.UseAnalyzers, cancellationToken);
221+
// target was a text file with a list of project files to run against
222+
if (StringComparer.OrdinalIgnoreCase.Equals(Path.GetExtension(item), ".txt"))
223+
{
224+
var targets = File.ReadAllLines(item);
225+
foreach (var target in targets)
226+
{
227+
try
228+
{
229+
await RunItemAsync(engine, target, options.Language, options.UseAnalyzers, cancellationToken);
230+
}
231+
catch (Exception e)
232+
{
233+
Console.WriteLine("Exception: {0} with project {1}", e.Message, target);
234+
}
235+
}
236+
}
237+
else
238+
{
239+
await RunItemAsync(engine, item, options.Language, options.UseAnalyzers, cancellationToken);
240+
}
214241
}
215242

216243
return SUCCEEDED;
@@ -225,32 +252,40 @@ private static async Task RunItemAsync(
225252
{
226253
Console.WriteLine(Path.GetFileName(item));
227254
string extension = Path.GetExtension(item);
228-
if (StringComparer.OrdinalIgnoreCase.Equals(extension, ".rsp"))
255+
try
229256
{
230-
using (var workspace = ResponseFileWorkspace.Create())
257+
if (StringComparer.OrdinalIgnoreCase.Equals(extension, ".rsp"))
231258
{
232-
Project project = workspace.OpenCommandLineProject(item, language);
233-
await engine.FormatProjectAsync(project, useAnalyzers, cancellationToken);
259+
using (var workspace = ResponseFileWorkspace.Create())
260+
{
261+
Project project = workspace.OpenCommandLineProject(item, language);
262+
await engine.FormatProjectAsync(project, useAnalyzers, cancellationToken);
263+
}
234264
}
235-
}
236-
else if (StringComparer.OrdinalIgnoreCase.Equals(extension, ".sln"))
237-
{
238-
using (var workspace = MSBuildWorkspace.Create())
265+
else if (StringComparer.OrdinalIgnoreCase.Equals(extension, ".sln"))
239266
{
240-
workspace.LoadMetadataForReferencedProjects = true;
241-
var solution = await workspace.OpenSolutionAsync(item, cancellationToken);
242-
await engine.FormatSolutionAsync(solution, useAnalyzers, cancellationToken);
267+
using (var workspace = MSBuildWorkspace.Create())
268+
{
269+
workspace.LoadMetadataForReferencedProjects = true;
270+
var solution = await workspace.OpenSolutionAsync(item, cancellationToken);
271+
await engine.FormatSolutionAsync(solution, useAnalyzers, cancellationToken);
272+
}
243273
}
244-
}
245-
else
246-
{
247-
using (var workspace = MSBuildWorkspace.Create())
274+
else
248275
{
249-
workspace.LoadMetadataForReferencedProjects = true;
250-
var project = await workspace.OpenProjectAsync(item, cancellationToken);
251-
await engine.FormatProjectAsync(project, useAnalyzers, cancellationToken);
276+
using (var workspace = MSBuildWorkspace.Create())
277+
{
278+
workspace.LoadMetadataForReferencedProjects = true;
279+
var project = await workspace.OpenProjectAsync(item, cancellationToken);
280+
await engine.FormatProjectAsync(project, useAnalyzers, cancellationToken);
281+
}
252282
}
253283
}
284+
catch (Microsoft.Build.Exceptions.InvalidProjectFileException)
285+
{
286+
// Can occur if for example a Mono based project with unknown targets files is supplied
287+
Console.WriteLine("Invalid project file in target {0}", item);
288+
}
254289
}
255290

256291
private static bool SetRuleMap(IFormattingEngine engine, ImmutableDictionary<string, bool> ruleMap)

src/Microsoft.DotNet.CodeFormatting/FormattingEngine.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,6 @@ private static ConventionBuilder GetConventions()
6868
.Export<ILocalSemanticFormattingRule>();
6969
conventions.ForTypesDerivedFrom<IGlobalSemanticFormattingRule>()
7070
.Export<IGlobalSemanticFormattingRule>();
71-
conventions.ForTypesDerivedFrom<DiagnosticAnalyzer>()
72-
.Export<DiagnosticAnalyzer>();
7371
// New per-analyzer options mechanism, deriving
7472
// from VS Workspaces functionality
7573
conventions.ForTypesDerivedFrom<IOptionsProvider>()

src/Microsoft.DotNet.CodeFormatting/FormattingEngineImplementation.cs

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public ImmutableArray<DiagnosticDescriptor> AllSupportedDiagnostics
9696
.SelectMany(a => a.SupportedDiagnostics)
9797
.OrderBy(a => a.Id)
9898
.ToImmutableArray();
99-
99+
100100
// Use the Roslyn ErrorLogger type to log diagnostics per analyzer to SARIF format
101101
public void LogDiagnostics(string filePath, ImmutableArray<Diagnostic> diagnostics)
102102
{
@@ -241,6 +241,7 @@ public async Task FormatProjectWithAnalyzersAsync(Project project, CancellationT
241241
await FormatProjectWithSyntaxAnalyzersAsync(workspace, project.Id, cancellationToken);
242242
await FormatProjectWithLocalAnalyzersAsync(workspace, project.Id, cancellationToken);
243243
await FormatProjectWithGlobalAnalyzersAsync(workspace, project.Id, cancellationToken);
244+
await FormatProjectWithUnspecifiedAnalyzersAsync(workspace, project.Id, cancellationToken);
244245

245246
watch.Stop();
246247
FormatLogger.WriteLine("Total time for formatting {0} - {1}", project.Name, watch.Elapsed);
@@ -260,12 +261,7 @@ private async Task FormatProjectWithLocalAnalyzersAsync(Workspace workspace, Pro
260261

261262
private async Task FormatProjectWithGlobalAnalyzersAsync(Workspace workspace, ProjectId projectId, CancellationToken cancellationToken)
262263
{
263-
// Remaining analyzers are either RuleType.GlobalSemantic, in which case we need to run them one by one lest they conflict
264-
// with each other, or else they are of an unknown type in which case we should conservatively treat them as if they
265-
// may conflict with one another.
266-
var analyzers = _analyzers.Where(a => {
267-
return a.SupportedDiagnostics.All(d => !(d.CustomTags.Contains(RuleType.Syntactic) || d.CustomTags.Contains(RuleType.LocalSemantic)));
268-
});
264+
var analyzers = _analyzers.Where(a => a.SupportedDiagnostics.All(d => d.CustomTags.Contains(RuleType.GlobalSemantic)));
269265

270266
// Since global analyzers can potentially conflict with each other, run them one by one.
271267
foreach (var analyzer in analyzers)
@@ -274,10 +270,22 @@ private async Task FormatProjectWithGlobalAnalyzersAsync(Workspace workspace, Pr
274270
}
275271
}
276272

277-
private async Task<int> getProjectLinesOfCodeCount(Project project)
273+
private async Task FormatProjectWithUnspecifiedAnalyzersAsync(Workspace workspace, ProjectId projectId, CancellationToken cancellationToken)
278274
{
279-
var allLines = project.Documents.Select(async doc =>
275+
var analyzers = _analyzers.Where(a => a.SupportedDiagnostics.All(d => {
276+
return !(d.CustomTags.Contains(RuleType.Syntactic) || d.CustomTags.Contains(RuleType.LocalSemantic) || d.CustomTags.Contains(RuleType.GlobalSemantic));
277+
}));
278+
279+
// Treat analyzers with unknown rule types as if they were global in case they might conflict with each other
280+
foreach (var analyzer in analyzers)
280281
{
282+
await FormatWithAnalyzersCoreAsync(workspace, projectId, new[] { analyzer }, cancellationToken);
283+
}
284+
}
285+
286+
private async Task<int> getProjectLinesOfCodeCount(Project project)
287+
{
288+
var allLines = project.Documents.Select(async doc => {
281289
var text = await doc.GetTextAsync();
282290
return text.Lines.Count;
283291
});
@@ -294,28 +302,35 @@ private async Task FormatWithAnalyzersCoreAsync(Workspace workspace, ProjectId p
294302
// Ensure at least 1 analyzer supporting the current project's language ran
295303
if (_compilationWithAnalyzers != null)
296304
{
297-
var ext = project.Language == "C#" ? ".csproj" : ".vbproj";
298-
var resultFile = project.FilePath.Substring(project.FilePath.LastIndexOf(Path.DirectorySeparatorChar)).Replace(ext, "_CodeFormatterResults.txt");
305+
var extension = StringComparer.OrdinalIgnoreCase.Equals(project.Language, "C#") ? ".csproj" : ".vbproj";
306+
var resultFile = project.FilePath.Substring(project.FilePath.LastIndexOf(Path.DirectorySeparatorChar)).Replace(extension, "_CodeFormatterResults.txt");
299307

300308
var linesOfCodeInProject = -1;
301309
foreach (var analyzer in analyzers)
302310
{
303311
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)
312+
if (Verbose || LogOutputPath != null)
316313
{
317-
LogDiagnostics((LogOutputPath + resultFile).Replace(".txt", ".json"), diags);
318-
File.AppendAllText(LogOutputPath + resultFile, analyzerResultText);
314+
linesOfCodeInProject = linesOfCodeInProject == -1 ? await getProjectLinesOfCodeCount(project) : linesOfCodeInProject;
315+
var analyzerTelemetryInfo = await _compilationWithAnalyzers.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken);
316+
var analyzerResultText = string.Format("{0}\t{1}\t{2}\t{3}\t{4}\r\n",
317+
analyzer.ToString(),
318+
project.Documents.Count(),
319+
linesOfCodeInProject,
320+
diagnostics.Count(),
321+
analyzerTelemetryInfo.ExecutionTime);
322+
323+
if (Verbose)
324+
{
325+
FormatLogger.Write(analyzerResultText);
326+
}
327+
328+
if (LogOutputPath != null)
329+
{
330+
var resultPath = LogOutputPath + resultFile;
331+
LogDiagnostics(Path.ChangeExtension(resultPath, "json"), diags);
332+
File.AppendAllText(resultPath, analyzerResultText);
333+
}
319334
}
320335
}
321336
}
@@ -343,7 +358,7 @@ private async Task FormatWithAnalyzersCoreAsync(Workspace workspace, ProjectId p
343358
}
344359
}
345360
}
346-
361+
347362
public void ToggleRuleEnabled(IRuleMetadata ruleMetaData, bool enabled)
348363
{
349364
_ruleEnabledMap[ruleMetaData.Name] = enabled;
@@ -654,7 +669,30 @@ private async Task<Solution> RunGlobalSemanticPass(Solution solution, IReadOnlyL
654669

655670
public void AddAnalyzers(ImmutableArray<DiagnosticAnalyzer> analyzers)
656671
{
657-
_analyzers = _analyzers.Concat(analyzers);
672+
var toAdd = new List<DiagnosticAnalyzer>();
673+
foreach (var analyzer in analyzers)
674+
{
675+
IEqualityComparer<DiagnosticAnalyzer> comparer = new AnalyzerComparer();
676+
if (!_analyzers.Contains(analyzer, comparer))
677+
{
678+
toAdd.Add(analyzer);
679+
}
680+
}
681+
_analyzers = _analyzers.Concat(toAdd.ToArray());
682+
}
683+
}
684+
685+
686+
internal class AnalyzerComparer : IEqualityComparer<DiagnosticAnalyzer>
687+
{
688+
public bool Equals(DiagnosticAnalyzer x, DiagnosticAnalyzer y)
689+
{
690+
return x.ToString() == y.ToString();
691+
}
692+
693+
public int GetHashCode(DiagnosticAnalyzer obj)
694+
{
695+
return obj.GetHashCode();
658696
}
659697
}
660-
}
698+
}

0 commit comments

Comments
 (0)