/// <summary> /// Return list of <see cref="StateSet"/> to be used for full solution analysis. /// </summary> private ImmutableArray <StateSet> GetStateSetsForFullSolutionAnalysis(ImmutableArray <StateSet> stateSets, Project project) { // If full analysis is off, remove state that is created from build. // this will make sure diagnostics from build (converted from build to live) will never be cleared // until next build. _ = GlobalOptions.IsFullSolutionAnalysisEnabled(project.Language, out var compilerFullSolutionAnalysisEnabled, out var analyzersFullSolutionAnalysisEnabled); if (!compilerFullSolutionAnalysisEnabled) { // Full solution analysis is not enabled for compiler diagnostics, // so we remove the compiler analyzer state sets that are from build. // We do so by retaining only those state sets that are // either not for compiler analyzer or those which are for compiler // analyzer, but not from build. stateSets = stateSets.WhereAsArray(s => !s.Analyzer.IsCompilerAnalyzer() || !s.FromBuild(project.Id)); } if (!analyzersFullSolutionAnalysisEnabled) { // Full solution analysis is not enabled for analyzer diagnostics, // so we remove the analyzer state sets that are from build. // We do so by retaining only those state sets that are // either for the special compiler/workspace analyzers or those which are for // other analyzers, but not from build. stateSets = stateSets.WhereAsArray(s => s.Analyzer.IsCompilerAnalyzer() || s.Analyzer.IsWorkspaceDiagnosticAnalyzer() || !s.FromBuild(project.Id)); } // Include only analyzers we want to run for full solution analysis. // Analyzers not included here will never be saved because result is unknown. return(stateSets.WhereAsArray(s => IsCandidateForFullSolutionAnalysis(s.Analyzer, project))); }
private void RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(TextDocument document, IEnumerable <StateSet> stateSets, bool documentHadDiagnostics) { // If there was no diagnostic reported for this document, nothing to clean up // This is done for Perf to reduce raising events unnecessarily. if (!documentHadDiagnostics) { return; } // If full solution analysis is enabled for both compiler diagnostics and analyzers, // we don't need to clear diagnostics for individual documents on document close/reset. // This is done for Perf to reduce raising events unnecessarily. var _ = GlobalOptions.IsFullSolutionAnalysisEnabled(document.Project.Language, out var compilerFullAnalysisEnabled, out var analyzersFullAnalysisEnabled); if (compilerFullAnalysisEnabled && analyzersFullAnalysisEnabled) { return; } var removeDiagnosticsOnDocumentClose = GlobalOptions.GetOption(SolutionCrawlerOptionsStorage.RemoveDocumentDiagnosticsOnDocumentClose, document.Project.Language); if (!removeDiagnosticsOnDocumentClose) { return; } RaiseDiagnosticsRemovedForDocument(document.Id, stateSets); }
private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, CancellationToken cancellationToken) { try { var stateSets = GetStateSetsForFullSolutionAnalysis(_stateManager.GetOrUpdateStateSets(project), project); // get driver only with active analyzers. var ideOptions = AnalyzerService.GlobalOptions.GetIdeAnalyzerOptions(project); // PERF: get analyzers that are not suppressed and marked as open file only // this is perf optimization. we cache these result since we know the result. (no diagnostics) var activeAnalyzers = stateSets .Select(s => s.Analyzer) .Where(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, project, GlobalOptions) && !a.IsOpenFileOnly(ideOptions.CleanupOptions?.SimplifierOptions)); CompilationWithAnalyzers?compilationWithAnalyzers = null; if (forceAnalyzerRun || GlobalOptions.IsFullSolutionAnalysisEnabled(project.Language)) { compilationWithAnalyzers = await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, ideOptions, activeAnalyzers, includeSuppressedDiagnostics : true, cancellationToken).ConfigureAwait(false); } var result = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, ideOptions, stateSets, forceAnalyzerRun, cancellationToken).ConfigureAwait(false); if (result.OldResult == null) { RaiseProjectDiagnosticsIfNeeded(project, stateSets, result.Result); return; } // no cancellation after this point. // any analyzer that doesn't have result will be treated as returned empty set // which means we will remove those from error list foreach (var stateSet in stateSets) { var state = stateSet.GetOrCreateProjectState(project.Id); await state.SaveToInMemoryStorageAsync(project, result.GetResult(stateSet.Analyzer)).ConfigureAwait(false); } RaiseProjectDiagnosticsIfNeeded(project, stateSets, result.OldResult, result.Result); } catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { throw ExceptionUtilities.Unreachable; } }
public async ValueTask SynchronizeWithBuildAsync( ImmutableDictionary <ProjectId, ImmutableArray <DiagnosticData> > buildDiagnostics, TaskQueue postBuildAndErrorListRefreshTaskQueue, bool onBuildCompleted, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.DiagnosticIncrementalAnalyzer_SynchronizeWithBuildAsync, LogSynchronizeWithBuild, buildDiagnostics, cancellationToken)) { DebugVerifyDiagnosticLocations(buildDiagnostics); var solution = Workspace.CurrentSolution; foreach (var(projectId, diagnostics) in buildDiagnostics) { cancellationToken.ThrowIfCancellationRequested(); var project = solution.GetProject(projectId); if (project == null) { continue; } var stateSets = _stateManager.CreateBuildOnlyProjectStateSet(project); var newResult = CreateAnalysisResults(project, stateSets, diagnostics); // PERF: Save the diagnostics into in-memory cache on the main thread. // Saving them into persistent storage is expensive, so we invoke that operation on a separate task queue // to ensure faster error list refresh. foreach (var stateSet in stateSets) { cancellationToken.ThrowIfCancellationRequested(); var state = stateSet.GetOrCreateProjectState(project.Id); var result = GetResultOrEmpty(newResult, stateSet.Analyzer, project.Id, VersionStamp.Default); await state.SaveToInMemoryStorageAsync(project, result).ConfigureAwait(false); } // Raise diagnostic updated events after the new diagnostics have been stored into the in-memory cache. if (diagnostics.IsEmpty) { ClearAllDiagnostics(stateSets, projectId); } else { RaiseProjectDiagnosticsIfNeeded(project, stateSets, newResult); } } // Refresh live diagnostics after solution build completes. if (onBuildCompleted) { // Enqueue re-analysis of active document with high-priority right away. if (_documentTrackingService.GetActiveDocument(solution) is { } activeDocument) { AnalyzerService.Reanalyze(Workspace, documentIds: ImmutableArray.Create(activeDocument.Id), highPriority: true); } // Enqueue remaining re-analysis with normal priority on a separate task queue // that will execute at the end of all the post build and error list refresh tasks. _ = postBuildAndErrorListRefreshTaskQueue.ScheduleTask(nameof(SynchronizeWithBuildAsync), () => { // Enqueue re-analysis of open documents. AnalyzerService.Reanalyze(Workspace, documentIds: Workspace.GetOpenDocumentIds()); // Enqueue re-analysis of projects, if required. foreach (var projectsByLanguage in solution.Projects.GroupBy(p => p.Language)) { if (GlobalOptions.IsFullSolutionAnalysisEnabled(projectsByLanguage.Key)) { AnalyzerService.Reanalyze(Workspace, projectsByLanguage.Select(p => p.Id)); } } }, cancellationToken); } } }