private async Task <DiagnosticAnalysisResultMap <string, DiagnosticAnalysisResultBuilder> > AnalyzeAsync( BidirectionalMap <string, DiagnosticAnalyzer> analyzerMap, ImmutableArray <DiagnosticAnalyzer> analyzers, OptionSet options, bool reportSuppressedDiagnostics, bool logAnalyzerExecutionTime, CancellationToken cancellationToken) { // flag that controls concurrency var useConcurrent = true; // get original compilation var compilation = await _project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); // fork compilation with concurrent build. this is okay since WithAnalyzers will fork compilation // anyway to attach event queue. this should make compiling compilation concurrent and make things // faster compilation = compilation.WithOptions(compilation.Options.WithConcurrentBuild(useConcurrent)); // We need this to fork soluton, otherwise, option is cached at document. // all this can go away once we do this - https://github.com/dotnet/roslyn/issues/19284 var temporaryWorksapce = new TemporaryWorkspace(_project.Solution); // TODO: can we support analyzerExceptionFilter in remote host? // right now, host doesn't support watson, we might try to use new NonFatal watson API? var analyzerOptions = new CompilationWithAnalyzersOptions( options: new WorkspaceAnalyzerOptions(_project.AnalyzerOptions, MergeOptions(_project.Solution.Options, options), temporaryWorksapce.CurrentSolution), onAnalyzerException: OnAnalyzerException, analyzerExceptionFilter: null, concurrentAnalysis: useConcurrent, logAnalyzerExecutionTime: logAnalyzerExecutionTime, reportSuppressedDiagnostics: reportSuppressedDiagnostics); var analyzerDriver = compilation.WithAnalyzers(analyzers, analyzerOptions); // PERF: Run all analyzers at once using the new GetAnalysisResultAsync API. var analysisResult = await analyzerDriver.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); // REVIEW: the design of current analyzer engine is that, information/states in CompilationWithAnalyzer (more specifically AnalyzerManager singleton) // will live forever until analyzer references (analyzers), which is given to CompilationWithAnalyzer, go away. // that is not suitable for OOP since OOP will create new workspace // for each analysis but share all resources including analyzer references. // until, we address this issue, OOP will clear state every time analysis is done. // // * NOTE * this only works for now since we don't run analysis on multiple threads. // // best way to fix this is doing this - https://github.com/dotnet/roslyn/issues/2830 // host should control lifetime of all information related to analyzer reference explicitly CompilationWithAnalyzers.ClearAnalyzerState(analyzers); var builderMap = analysisResult.ToResultBuilderMap(_project, VersionStamp.Default, compilation, analysisResult.Analyzers, cancellationToken); return(DiagnosticAnalysisResultMap.Create( builderMap.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), analysisResult.AnalyzerTelemetryInfo.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), _exceptions.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value.ToImmutableArray()))); }
private async Task <DiagnosticAnalysisResultMap <string, DiagnosticAnalysisResultBuilder> > AnalyzeAsync( BidirectionalMap <string, DiagnosticAnalyzer> analyzerMap, ImmutableArray <DiagnosticAnalyzer> analyzers, OptionSet options, bool reportSuppressedDiagnostics, bool logAnalyzerExecutionTime, CancellationToken cancellationToken) { // flag that controls concurrency var useConcurrent = true; // get original compilation var compilation = await _project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); // fork compilation with concurrent build. this is okay since WithAnalyzers will fork compilation // anyway to attach event queue. this should make compiling compilation concurrent and make things // faster compilation = compilation.WithOptions(compilation.Options.WithConcurrentBuild(useConcurrent)); // We need this to fork soluton, otherwise, option is cached at document. // all this can go away once we do this - https://github.com/dotnet/roslyn/issues/19284 using (var temporaryWorksapce = new TemporaryWorkspace(_project.Solution)) { // TODO: can we support analyzerExceptionFilter in remote host? // right now, host doesn't support watson, we might try to use new NonFatal watson API? var analyzerOptions = new CompilationWithAnalyzersOptions( options: new WorkspaceAnalyzerOptions(_project.AnalyzerOptions, MergeOptions(_project.Solution.Options, options), temporaryWorksapce.CurrentSolution), onAnalyzerException: OnAnalyzerException, analyzerExceptionFilter: null, concurrentAnalysis: useConcurrent, logAnalyzerExecutionTime: logAnalyzerExecutionTime, reportSuppressedDiagnostics: reportSuppressedDiagnostics); var analyzerDriver = compilation.WithAnalyzers(analyzers, analyzerOptions); // PERF: Run all analyzers at once using the new GetAnalysisResultAsync API. var analysisResult = await analyzerDriver.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); // record performance if tracker is available if (_performanceTracker != null) { // +1 to include project itself _performanceTracker.AddSnapshot(analysisResult.AnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(), _project.DocumentIds.Count + 1); } var builderMap = analysisResult.ToResultBuilderMap(_project, VersionStamp.Default, compilation, analysisResult.Analyzers, cancellationToken); return(DiagnosticAnalysisResultMap.Create( builderMap.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), analysisResult.AnalyzerTelemetryInfo.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), _exceptions.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value.ToImmutableArray()))); } }