private static async Task <DocumentAnalyzerPerformance> TestDocumentPerformanceAsync(ImmutableDictionary <string, ImmutableArray <DiagnosticAnalyzer> > analyzers, Project project, DocumentId documentId, Options analyzerOptionsInternal, CancellationToken cancellationToken) { if (!analyzers.TryGetValue(project.Language, out var languageAnalyzers)) { languageAnalyzers = ImmutableArray <DiagnosticAnalyzer> .Empty; } Compilation compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var stopwatch = PerformanceTracker.StartNew(); for (int i = 0; i < analyzerOptionsInternal.TestDocumentIterations; i++) { var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution); CompilationWithAnalyzers compilationWithAnalyzers = compilation.WithAnalyzers(languageAnalyzers, new CompilationWithAnalyzersOptions(workspaceAnalyzerOptions, null, analyzerOptionsInternal.RunConcurrent, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: analyzerOptionsInternal.ReportSuppressedDiagnostics)); SyntaxTree tree = await project.GetDocument(documentId).GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); await compilationWithAnalyzers.GetAnalyzerSyntaxDiagnosticsAsync(tree, cancellationToken).ConfigureAwait(false); await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(compilation.GetSemanticModel(tree), null, cancellationToken).ConfigureAwait(false); } return(new DocumentAnalyzerPerformance(analyzerOptionsInternal.TestDocumentIterations / stopwatch.Elapsed.TotalSeconds, stopwatch.AllocatedBytes / Math.Max(1, analyzerOptionsInternal.TestDocumentIterations))); }
private async Task <IEnumerable <Diagnostic> > ComputeDiagnosticAnalyzerDiagnosticsAsync( CompilationWithAnalyzers analyzerDriverOpt, Document document, DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan?spanOpt, CancellationToken cancellationToken) { // quick optimization to reduce allocations. if (analyzerDriverOpt == null || !_owner.SupportAnalysisKind(analyzer, document.Project.Language, kind)) { return(ImmutableArray <Diagnostic> .Empty); } // REVIEW: more unnecessary allocations just to get diagnostics per analyzer var oneAnalyzers = ImmutableArray.Create(analyzer); switch (kind) { case AnalysisKind.Syntax: var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var diagnostics = await analyzerDriverOpt.GetAnalyzerSyntaxDiagnosticsAsync(tree, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(diagnostics.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, analyzerDriverOpt.Compilation).Count()); return(diagnostics.ToImmutableArrayOrEmpty()); case AnalysisKind.Semantic: var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); diagnostics = await analyzerDriverOpt.GetAnalyzerSemanticDiagnosticsAsync(model, spanOpt, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(diagnostics.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, analyzerDriverOpt.Compilation).Count()); return(diagnostics.ToImmutableArrayOrEmpty()); default: return(Contract.FailWithReturn <ImmutableArray <Diagnostic> >("shouldn't reach here")); } }
private static async Task <DocumentAnalyzerPerformance> TestDocumentPerformanceAsync(ImmutableDictionary <string, ImmutableArray <DiagnosticAnalyzer> > analyzers, Project project, DocumentId documentId, Options analyzerOptionsInternal, CancellationToken cancellationToken) { // update the project compilation options var modifiedSpecificDiagnosticOptions = project.CompilationOptions.SpecificDiagnosticOptions .Add("AD0001", ReportDiagnostic.Error) .Add("AD0002", ReportDiagnostic.Error); // Report exceptions during the analysis process as errors var modifiedCompilationOptions = project.CompilationOptions.WithSpecificDiagnosticOptions(modifiedSpecificDiagnosticOptions); var processedProject = project.WithCompilationOptions(modifiedCompilationOptions); if (!analyzers.TryGetValue(project.Language, out var languageAnalyzers)) { languageAnalyzers = ImmutableArray <DiagnosticAnalyzer> .Empty; } var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < analyzerOptionsInternal.TestDocumentIterations; i++) { Compilation compilation = await processedProject.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution.Options, project.Solution); CompilationWithAnalyzers compilationWithAnalyzers = compilation.WithAnalyzers(languageAnalyzers, new CompilationWithAnalyzersOptions(workspaceAnalyzerOptions, null, analyzerOptionsInternal.RunConcurrent, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: analyzerOptionsInternal.ReportSuppressedDiagnostics)); SyntaxTree tree = await project.GetDocument(documentId).GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); await compilationWithAnalyzers.GetAnalyzerSyntaxDiagnosticsAsync(tree, cancellationToken).ConfigureAwait(false); await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(compilation.GetSemanticModel(tree), null, cancellationToken).ConfigureAwait(false); } return(new DocumentAnalyzerPerformance(analyzerOptionsInternal.TestDocumentIterations / stopwatch.Elapsed.TotalSeconds)); }
public static async Task <ImmutableDictionary <DiagnosticAnalyzer, AnalysisResult> > AnalyzeAsync(this CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) { var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); // Run all analyzers at once. // REVIEW: why there are 2 different cancellation token? one that I can give to constructor and one I can give in to each method? // REVIEW: we drop all those allocations for the diagnostics returned. can we avoid this? await analyzerDriver.GetAnalyzerDiagnosticsAsync(cancellationToken).ConfigureAwait(false); // this is wierd, but now we iterate through each analyzer for each tree to get cached result. // REVIEW: no better way to do this? var noSpanFilter = default(TextSpan?); var analyzers = analyzerDriver.Analyzers; var compilation = analyzerDriver.Compilation; var builder = ImmutableDictionary.CreateBuilder <DiagnosticAnalyzer, AnalysisResult>(); foreach (var analyzer in analyzers) { var result = new Builder(project, version); // REVIEW: more unnecessary allocations just to get diagnostics per analyzer var oneAnalyzers = ImmutableArray.Create(analyzer); foreach (var tree in compilation.SyntaxTrees) { var model = compilation.GetSemanticModel(tree); var syntax = await analyzerDriver.GetAnalyzerSyntaxDiagnosticsAsync(tree, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(syntax.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(syntax, analyzerDriver.Compilation).Count()); result.AddSyntaxDiagnostics(tree, syntax); var semantic = await analyzerDriver.GetAnalyzerSemanticDiagnosticsAsync(model, noSpanFilter, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(semantic.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(semantic, analyzerDriver.Compilation).Count()); result.AddSemanticDiagnostics(tree, semantic); } var rest = await analyzerDriver.GetAnalyzerCompilationDiagnosticsAsync(oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(rest.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(rest, analyzerDriver.Compilation).Count()); result.AddCompilationDiagnostics(rest); builder.Add(analyzer, result.ToResult()); } return(builder.ToImmutable()); }
private static async Task <DocumentAnalyzerPerformance> TestDocumentPerformanceAsync(ImmutableArray <DiagnosticAnalyzer> analyzers, Project project, DocumentId documentId, int iterations, bool force, CancellationToken cancellationToken) { var supportedDiagnosticsSpecificOptions = new Dictionary <string, ReportDiagnostic>(); if (force) { foreach (var analyzer in analyzers) { foreach (var diagnostic in analyzer.SupportedDiagnostics) { // make sure the analyzers we are testing are enabled supportedDiagnosticsSpecificOptions[diagnostic.Id] = ReportDiagnostic.Default; } } } // Report exceptions during the analysis process as errors supportedDiagnosticsSpecificOptions.Add("AD0001", ReportDiagnostic.Error); // update the project compilation options var modifiedSpecificDiagnosticOptions = supportedDiagnosticsSpecificOptions.ToImmutableDictionary().SetItems(project.CompilationOptions.SpecificDiagnosticOptions); var modifiedCompilationOptions = project.CompilationOptions.WithSpecificDiagnosticOptions(modifiedSpecificDiagnosticOptions); var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { var processedProject = project.WithCompilationOptions(modifiedCompilationOptions); Compilation compilation = await processedProject.GetCompilationAsync(cancellationToken).ConfigureAwait(false); CompilationWithAnalyzers compilationWithAnalyzers = compilation.WithAnalyzers(analyzers, new CompilationWithAnalyzersOptions(processedProject.AnalyzerOptions, null, true, false)); SyntaxTree tree = await project.GetDocument(documentId).GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); await compilationWithAnalyzers.GetAnalyzerSyntaxDiagnosticsAsync(tree, cancellationToken).ConfigureAwait(false); await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(compilation.GetSemanticModel(tree), null, cancellationToken).ConfigureAwait(false); } return(new DocumentAnalyzerPerformance(iterations / stopwatch.Elapsed.TotalSeconds)); }
private async Task<IEnumerable<Diagnostic>> ComputeDiagnosticAnalyzerDiagnosticsAsync( CompilationWithAnalyzers analyzerDriverOpt, Document document, DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan? spanOpt, CancellationToken cancellationToken) { // quick optimization to reduce allocations. if (analyzerDriverOpt == null || !_owner.SupportAnalysisKind(analyzer, document.Project.Language, kind)) { return ImmutableArray<Diagnostic>.Empty; } // REVIEW: more unnecessary allocations just to get diagnostics per analyzer var oneAnalyzers = ImmutableArray.Create(analyzer); switch (kind) { case AnalysisKind.Syntax: var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var diagnostics = await analyzerDriverOpt.GetAnalyzerSyntaxDiagnosticsAsync(tree, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(diagnostics.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, analyzerDriverOpt.Compilation).Count()); return diagnostics.ToImmutableArrayOrEmpty(); case AnalysisKind.Semantic: var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); diagnostics = await analyzerDriverOpt.GetAnalyzerSemanticDiagnosticsAsync(model, spanOpt, oneAnalyzers, cancellationToken).ConfigureAwait(false); Contract.Requires(diagnostics.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, analyzerDriverOpt.Compilation).Count()); return diagnostics.ToImmutableArrayOrEmpty(); default: return Contract.FailWithReturn<ImmutableArray<Diagnostic>>("shouldn't reach here"); } }
private static async Task <(ImmutableArray <Diagnostic> reportedDiagnostics, ImmutableArray <string> unhandledIds)> GetReportedDiagnosticsForIdsAsync( ImmutableHashSet <string> idsToAnalyze, SyntaxNode root, SemanticModel semanticModel, CompilationWithAnalyzers compilationWithAnalyzers, Func <DiagnosticAnalyzer, ImmutableArray <DiagnosticDescriptor> > getSupportedDiagnostics, Func <DiagnosticAnalyzer, bool> getIsCompilationEndAnalyzer, PooledHashSet <string> compilerDiagnosticIds, CancellationToken cancellationToken) { using var _1 = ArrayBuilder <DiagnosticAnalyzer> .GetInstance(out var analyzersBuilder); using var _2 = ArrayBuilder <string> .GetInstance(out var unhandledIds); // First, we compute the relevant analyzers whose reported diagnostics need to be computed. var addedCompilerAnalyzer = false; var hasNonCompilerAnalyzers = idsToAnalyze.Count > compilerDiagnosticIds.Count; foreach (var analyzer in compilationWithAnalyzers.Analyzers) { if (!addedCompilerAnalyzer && analyzer.IsCompilerAnalyzer()) { addedCompilerAnalyzer = true; analyzersBuilder.Add(analyzer); if (!hasNonCompilerAnalyzers) { break; } continue; } if (hasNonCompilerAnalyzers) { Debug.Assert(!analyzer.IsCompilerAnalyzer()); bool?lazyIsUnhandledAnalyzer = null; foreach (var descriptor in getSupportedDiagnostics(analyzer)) { if (!idsToAnalyze.Contains(descriptor.Id)) { continue; } lazyIsUnhandledAnalyzer ??= getIsCompilationEndAnalyzer(analyzer) || analyzer is IPragmaSuppressionsAnalyzer; if (lazyIsUnhandledAnalyzer.Value) { unhandledIds.Add(descriptor.Id); } } if (lazyIsUnhandledAnalyzer.HasValue && !lazyIsUnhandledAnalyzer.Value) { analyzersBuilder.Add(analyzer); } } } // Then, we execute these analyzers on the current file to fetch these diagnostics. // Note that if an analyzer has already executed, then this will be just a cache access // as computed analyzer diagnostics are cached on CompilationWithAnalyzers instance. using var _3 = ArrayBuilder <Diagnostic> .GetInstance(out var reportedDiagnostics); if (!addedCompilerAnalyzer && compilerDiagnosticIds.Count > 0) { // Special case when compiler analyzer could not be found. Debug.Assert(semanticModel.Compilation.Options.ReportSuppressedDiagnostics); reportedDiagnostics.AddRange(root.GetDiagnostics()); reportedDiagnostics.AddRange(semanticModel.GetDiagnostics(cancellationToken: cancellationToken)); cancellationToken.ThrowIfCancellationRequested(); } if (analyzersBuilder.Count > 0) { var analyzers = analyzersBuilder.ToImmutable(); var syntaxDiagnostics = await compilationWithAnalyzers.GetAnalyzerSyntaxDiagnosticsAsync(semanticModel.SyntaxTree, analyzers, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); reportedDiagnostics.AddRange(syntaxDiagnostics); var semanticDiagnostics = await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(semanticModel, filterSpan : null, analyzers, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); reportedDiagnostics.AddRange(semanticDiagnostics); } return(reportedDiagnostics.ToImmutable(), unhandledIds.ToImmutable()); }