private async Task <ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> > ComputeDiagnosticsAsync( CompilationWithAnalyzers?compilationWithAnalyzers, Project project, ImmutableArray <DiagnosticAnalyzer> ideAnalyzers, bool forcedAnalysis, CancellationToken cancellationToken) { try { var result = ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> .Empty; // can be null if given project doesn't support compilation. if (compilationWithAnalyzers?.Analyzers.Length > 0) { // calculate regular diagnostic analyzers diagnostics var resultMap = await _diagnosticAnalyzerRunner.AnalyzeProjectAsync(project, compilationWithAnalyzers, forcedAnalysis, logPerformanceInfo : true, getTelemetryInfo : true, cancellationToken).ConfigureAwait(false); result = resultMap.AnalysisResult; // record telemetry data UpdateAnalyzerTelemetryData(resultMap.TelemetryInfo); } // check whether there is IDE specific project diagnostic analyzer return(await MergeProjectDiagnosticAnalyzerDiagnosticsAsync(project, ideAnalyzers, compilationWithAnalyzers?.Compilation, result, cancellationToken).ConfigureAwait(false)); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private async Task <ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> > ComputeDiagnosticsAsync( CompilationWithAnalyzers?compilationWithAnalyzers, Project project, IEnumerable <StateSet> stateSets, bool forcedAnalysis, ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> existing, CancellationToken cancellationToken) { try { // PERF: check whether we can reduce number of analyzers we need to run. // this can happen since caller could have created the driver with different set of analyzers that are different // than what we used to create the cache. var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer || a is DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty(); if (compilationWithAnalyzers != null && TryReduceAnalyzersToRun(compilationWithAnalyzers, project, version, existing, out var analyzersToRun)) { // it looks like we can reduce the set. create new CompilationWithAnalyzer. // if we reduced to 0, we just pass in null for analyzer drvier. it could be reduced to 0 // since we might have up to date results for analyzers from compiler but not for // workspace analyzers. var compilationWithReducedAnalyzers = (analyzersToRun.Length == 0) ? null : await AnalyzerHelper.CreateCompilationWithAnalyzersAsync(project, analyzersToRun, compilationWithAnalyzers.AnalysisOptions.ReportSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); var result = await ComputeDiagnosticsAsync(compilationWithReducedAnalyzers, project, ideAnalyzers, forcedAnalysis, cancellationToken).ConfigureAwait(false); return(MergeExistingDiagnostics(version, existing, result)); } // we couldn't reduce the set. return(await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, ideAnalyzers, forcedAnalysis, cancellationToken).ConfigureAwait(false)); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private LatestDiagnosticsForSpanGetter( DiagnosticIncrementalAnalyzer owner, CompilationWithAnalyzers?compilationWithAnalyzers, Document document, IEnumerable <StateSet> stateSets, Func <string, bool>?shouldIncludeDiagnostic, bool includeCompilerDiagnostics, TextSpan?range, bool blockForData, Func <string, IDisposable?>?addOperationScope, bool includeSuppressedDiagnostics, CodeActionRequestPriority priority) { _owner = owner; _compilationWithAnalyzers = compilationWithAnalyzers; _document = document; _stateSets = stateSets; _shouldIncludeDiagnostic = shouldIncludeDiagnostic; _includeCompilerDiagnostics = includeCompilerDiagnostics; _range = range; _blockForData = blockForData; _addOperationScope = addOperationScope; _includeSuppressedDiagnostics = includeSuppressedDiagnostics; _priority = priority; }
private async Task ReportAnalyzerPerformanceAsync(Document document, CompilationWithAnalyzers?compilation, CancellationToken cancellationToken) { try { if (compilation == null) { return; } var client = await RemoteHostClient.TryGetClientAsync(document.Project.Solution.Workspace, cancellationToken).ConfigureAwait(false); if (client == null) { // no remote support return; } cancellationToken.ThrowIfCancellationRequested(); using var pooledObject = SharedPools.Default <Dictionary <DiagnosticAnalyzer, AnalyzerTelemetryInfo> >().GetPooledObject(); var containsData = false; foreach (var analyzer in compilation.Analyzers) { var telemetryInfo = await compilation.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken).ConfigureAwait(false); if (!containsData && telemetryInfo.ExecutionTime.Ticks > 0) { // this is unfortunate tweak due to how GetAnalyzerTelemetryInfoAsync works when analyzers are asked // one by one rather than in bulk. containsData = true; } pooledObject.Object.Add(analyzer, telemetryInfo); } if (!containsData) { // looks like there is no new data from driver. skip reporting. return; } _ = await client.TryRunRemoteAsync( WellKnownServiceHubService.CodeAnalysis, nameof(IRemoteDiagnosticAnalyzerService.ReportAnalyzerPerformance), solution : null, new object[] { pooledObject.Object.ToAnalyzerPerformanceInfo(DiagnosticAnalyzerInfoCache), /* unit count */ 1 }, callbackTarget : null, cancellationToken).ConfigureAwait(false); } catch (Exception ex) when(FatalError.ReportWithoutCrashUnlessCanceled(ex)) { // this is fire and forget method } }
private void AssertAnalyzers(CompilationWithAnalyzers?compilation, IEnumerable <StateSet> stateSets) { if (compilation == null) { // this can happen if project doesn't support compilation or no stateSets are given. return; } // make sure analyzers are same. Contract.ThrowIfFalse(compilation.Analyzers.SetEquals(stateSets.Select(s => s.Analyzer).Where(a => !a.IsWorkspaceDiagnosticAnalyzer()))); }
public DocumentAnalysisExecutor( DocumentAnalysisScope analysisScope, CompilationWithAnalyzers?compilationWithAnalyzers, DiagnosticAnalyzerInfoCache analyzerInfoCache) { AnalysisScope = analysisScope; _compilationWithAnalyzers = compilationWithAnalyzers; _analyzerInfoCache = analyzerInfoCache; var compilationBasedAnalyzers = compilationWithAnalyzers?.Analyzers.ToImmutableHashSet(); _compilationBasedAnalyzersInAnalysisScope = compilationBasedAnalyzers != null ? analysisScope.Analyzers.WhereAsArray(compilationBasedAnalyzers.Contains) : ImmutableArray <DiagnosticAnalyzer> .Empty; }
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 DocumentAnalysisExecutor( DocumentAnalysisScope analysisScope, CompilationWithAnalyzers?compilationWithAnalyzers, InProcOrRemoteHostAnalyzerRunner diagnosticAnalyzerRunner, bool logPerformanceInfo) { AnalysisScope = analysisScope; _compilationWithAnalyzers = compilationWithAnalyzers; _diagnosticAnalyzerRunner = diagnosticAnalyzerRunner; _logPerformanceInfo = logPerformanceInfo; var compilationBasedAnalyzers = compilationWithAnalyzers?.Analyzers.ToImmutableHashSet(); _compilationBasedAnalyzersInAnalysisScope = compilationBasedAnalyzers != null ? analysisScope.Analyzers.WhereAsArray(compilationBasedAnalyzers.Contains) : ImmutableArray <DiagnosticAnalyzer> .Empty; }
private static bool CompilationHasOpenFileOnlyAnalyzers(CompilationWithAnalyzers?compilationWithAnalyzers, OptionSet options) { if (compilationWithAnalyzers == null) { return(false); } foreach (var analyzer in compilationWithAnalyzers.Analyzers) { if (analyzer.IsOpenFileOnly(options)) { return(true); } } return(false); }
private static bool CompilationHasOpenFileOnlyAnalyzers(CompilationWithAnalyzers?compilation, Workspace workspace) { if (compilation == null) { return(false); } foreach (var analyzer in compilation.Analyzers) { if (analyzer.IsOpenFileOnly(workspace)) { return(true); } } return(false); }
private async Task <ProjectAnalysisData> GetProjectAnalysisDataAsync( CompilationWithAnalyzers?compilationWithAnalyzers, Project project, IEnumerable <StateSet> stateSets, bool forceAnalyzerRun, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, project, stateSets, cancellationToken)) { try { // PERF: We need to flip this to false when we do actual diffing. var avoidLoadingData = true; var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var existingData = await ProjectAnalysisData.CreateAsync(PersistentStorageService, project, stateSets, avoidLoadingData, cancellationToken).ConfigureAwait(false); // We can't return here if we have open file only analyzers since saved data for open file only analyzer // is incomplete -- it only contains info on open files rather than whole project. if (existingData.Version == version && !CompilationHasOpenFileOnlyAnalyzers(compilationWithAnalyzers, project.Solution.Options)) { return(existingData); } // PERF: Check whether we want to analyze this project or not. if (!FullAnalysisEnabled(project, forceAnalyzerRun)) { Logger.Log(FunctionId.Diagnostics_ProjectDiagnostic, p => $"FSA off ({p.FilePath ?? p.Name})", project); return(new ProjectAnalysisData(project.Id, VersionStamp.Default, existingData.Result, ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> .Empty)); } var result = await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, stateSets, forceAnalyzerRun, existingData.Result, cancellationToken).ConfigureAwait(false); // If project is not loaded successfully, get rid of any semantic errors from compiler analyzer. // Note: In the past when project was not loaded successfully we did not run any analyzers on the project. // Now we run analyzers but filter out some information. So on such projects, there will be some perf degradation. result = await RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync(result, project, cancellationToken).ConfigureAwait(false); return(new ProjectAnalysisData(project.Id, version, existingData.Result, result)); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } }
private async Task <DocumentAnalysisData> GetDocumentAnalysisDataAsync( CompilationWithAnalyzers?compilation, Document document, StateSet stateSet, AnalysisKind kind, CancellationToken cancellationToken) { // get log title and functionId GetLogFunctionIdAndTitle(kind, out var functionId, out var title); using (Logger.LogBlock(functionId, GetDocumentLogMessage, title, document, stateSet.Analyzer, cancellationToken)) { try { var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); var state = stateSet.GetOrCreateActiveFileState(document.Id); var existingData = state.GetAnalysisData(kind); if (existingData.Version == version) { return(existingData); } // perf optimization. check whether analyzer is suppressed and avoid getting diagnostics if suppressed. // REVIEW: IsAnalyzerSuppressed call seems can be quite expensive in certain condition. is there any other way to do this? if (AnalyzerService.IsAnalyzerSuppressed(stateSet.Analyzer, document.Project)) { return(new DocumentAnalysisData(version, existingData.Items, ImmutableArray <DiagnosticData> .Empty)); } var nullFilterSpan = (TextSpan?)null; var diagnostics = await ComputeDiagnosticsAsync(compilation, document, stateSet.Analyzer, kind, nullFilterSpan, cancellationToken).ConfigureAwait(false); // this is no-op in product. only run in test environment Logger.Log(functionId, (t, d, a, ds) => $"{GetDocumentLogMessage(t, d, a)}, {string.Join(Environment.NewLine, ds)}", title, document, stateSet.Analyzer, diagnostics); // we only care about local diagnostics return(new DocumentAnalysisData(version, existingData.Items, diagnostics.ToImmutableArrayOrEmpty())); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } }
private LatestDiagnosticsForSpanGetter( DiagnosticIncrementalAnalyzer owner, CompilationWithAnalyzers?compilationWithAnalyzers, Document document, IEnumerable <StateSet> stateSets, string?diagnosticId, TextSpan range, bool blockForData, Func <string, IDisposable?>?addOperationScope, bool includeSuppressedDiagnostics) { _owner = owner; _compilationWithAnalyzers = compilationWithAnalyzers; _document = document; _stateSets = stateSets; _diagnosticId = diagnosticId; _range = range; _blockForData = blockForData; _addOperationScope = addOperationScope; _includeSuppressedDiagnostics = includeSuppressedDiagnostics; }
private LatestDiagnosticsForSpanGetter( DiagnosticIncrementalAnalyzer owner, CompilationWithAnalyzers?compilation, Document document, IEnumerable <StateSet> stateSets, string?diagnosticId, TextSpan range, bool blockForData, bool includeSuppressedDiagnostics) { _owner = owner; _project = document.Project; _document = document; _stateSets = stateSets; _diagnosticId = diagnosticId; _compilation = compilation; _range = range; _blockForData = blockForData; _includeSuppressedDiagnostics = includeSuppressedDiagnostics; }
private Task <IEnumerable <DiagnosticData> > ComputeDiagnosticsAsync( CompilationWithAnalyzers?compilation, Document document, DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan?spanOpt, CancellationToken cancellationToken) { return(AnalyzerService.ComputeDiagnosticsAsync(compilation, document, analyzer, kind, spanOpt, DiagnosticLogAggregator, cancellationToken)); }
public ProjectAndCompilationWithAnalyzers(Project project, CompilationWithAnalyzers?compilationWithAnalyzers) { Project = project; CompilationWithAnalyzers = compilationWithAnalyzers; }