private void RemoveInMemoryCache(DiagnosticAnalysisResult lastResult) { // remove old cache foreach (var documentId in lastResult.DocumentIdsOrEmpty) { RemoveInMemoryCacheEntries(documentId); } }
public static DiagnosticAnalysisResultMap <DiagnosticAnalyzer, DiagnosticAnalysisResult> Deserialize( ObjectReader reader, IDictionary <string, DiagnosticAnalyzer> analyzerMap, Project project, VersionStamp version, CancellationToken cancellationToken) { var diagnosticDataSerializer = new DiagnosticDataSerializer(VersionStamp.Default, VersionStamp.Default); var analysisMap = ImmutableDictionary.CreateBuilder <DiagnosticAnalyzer, DiagnosticAnalysisResult>(); var analysisCount = reader.ReadInt32(); for (var i = 0; i < analysisCount; i++) { var analyzer = analyzerMap[reader.ReadString()]; var syntaxLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); var semanticLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); var nonLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); var others = diagnosticDataSerializer.ReadFrom(reader, project, cancellationToken); var analysisResult = DiagnosticAnalysisResult.CreateFromSerialization( project, version, syntaxLocalMap, semanticLocalMap, nonLocalMap, GetOrDefault(others), documentIds: null); analysisMap.Add(analyzer, analysisResult); } var telemetryMap = ImmutableDictionary.CreateBuilder <DiagnosticAnalyzer, AnalyzerTelemetryInfo>(); var telemetryCount = reader.ReadInt32(); for (var i = 0; i < telemetryCount; i++) { var analyzer = analyzerMap[reader.ReadString()]; var telemetryInfo = Deserialize(reader, cancellationToken); telemetryMap.Add(analyzer, telemetryInfo); } var exceptionMap = ImmutableDictionary.CreateBuilder <DiagnosticAnalyzer, ImmutableArray <DiagnosticData> >(); var exceptionCount = reader.ReadInt32(); for (var i = 0; i < exceptionCount; i++) { var analyzer = analyzerMap[reader.ReadString()]; var exceptions = diagnosticDataSerializer.ReadFrom(reader, project, cancellationToken); exceptionMap.Add(analyzer, GetOrDefault(exceptions)); } return(DiagnosticAnalysisResultMap.Create(analysisMap.ToImmutable(), telemetryMap.ToImmutable(), exceptionMap.ToImmutable())); }
private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> map, DiagnosticAnalyzer analyzer, ProjectId projectId, VersionStamp version) { if (map.TryGetValue(analyzer, out var result)) { return(result); } return(DiagnosticAnalysisResult.CreateEmpty(projectId, version)); }
public DiagnosticAnalysisResult ToResult() { return(DiagnosticAnalysisResult.Create(_project, _version, _syntaxLocals?.ToImmutable() ?? ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty, _semanticLocals?.ToImmutable() ?? ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty, _nonLocals?.ToImmutable() ?? ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty, _others.NullToEmpty(), _documentIds)); }
private async Task <ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> > MergeProjectDiagnosticAnalyzerDiagnosticsAsync( Project project, IEnumerable <StateSet> stateSets, Compilation compilationOpt, ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> result, CancellationToken cancellationToken) { try { // check whether there is IDE specific project diagnostic analyzer var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer || a is DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty(); if (ideAnalyzers.Length <= 0) { return(result); } // create result map var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); foreach (var analyzer in ideAnalyzers) { var builder = new DiagnosticAnalysisResultBuilder(project, version); if (analyzer is DocumentDiagnosticAnalyzer documentAnalyzer) { foreach (var document in project.Documents) { if (document.SupportsSyntaxTree) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); builder.AddSyntaxDiagnostics(tree, await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, AnalysisKind.Syntax, compilationOpt, cancellationToken).ConfigureAwait(false)); builder.AddSemanticDiagnostics(tree, await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, AnalysisKind.Semantic, compilationOpt, cancellationToken).ConfigureAwait(false)); } else { builder.AddExternalSyntaxDiagnostics(document.Id, await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, AnalysisKind.Syntax, compilationOpt, cancellationToken).ConfigureAwait(false)); builder.AddExternalSemanticDiagnostics(document.Id, await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, AnalysisKind.Semantic, compilationOpt, cancellationToken).ConfigureAwait(false)); } } } if (analyzer is ProjectDiagnosticAnalyzer projectAnalyzer) { builder.AddCompilationDiagnostics(await ComputeProjectDiagnosticAnalyzerDiagnosticsAsync(project, projectAnalyzer, compilationOpt, cancellationToken).ConfigureAwait(false)); } // merge the result to existing one. // there can be existing one from compiler driver with empty set. overwrite it with // ide one. result = result.SetItem(analyzer, DiagnosticAnalysisResult.CreateFromBuilder(builder)); } return(result); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private void RaiseDocumentDiagnosticsIfNeeded( Document document, StateSet stateSet, AnalysisKind kind, DiagnosticAnalysisResult oldResult, DiagnosticAnalysisResult newResult, Action <DiagnosticsUpdatedArgs> raiseEvents) { var oldItems = GetResult(oldResult, kind, document.Id); var newItems = GetResult(newResult, kind, document.Id); RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, raiseEvents); }
private async Task <ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> > MergeProjectDiagnosticAnalyzerDiagnosticsAsync( Project project, ImmutableArray <DiagnosticAnalyzer> ideAnalyzers, Compilation?compilation, ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> result, CancellationToken cancellationToken) { try { // create result map var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); foreach (var analyzer in ideAnalyzers) { var builder = new DiagnosticAnalysisResultBuilder(project, version); if (analyzer is DocumentDiagnosticAnalyzer documentAnalyzer) { foreach (var document in project.Documents) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (tree != null) { builder.AddSyntaxDiagnostics(tree, await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, AnalysisKind.Syntax, compilation, cancellationToken).ConfigureAwait(false)); builder.AddSemanticDiagnostics(tree, await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, AnalysisKind.Semantic, compilation, cancellationToken).ConfigureAwait(false)); } else { builder.AddExternalSyntaxDiagnostics(document.Id, await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, AnalysisKind.Syntax, compilation, cancellationToken).ConfigureAwait(false)); builder.AddExternalSemanticDiagnostics(document.Id, await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(document, documentAnalyzer, AnalysisKind.Semantic, compilation, cancellationToken).ConfigureAwait(false)); } } } if (analyzer is ProjectDiagnosticAnalyzer projectAnalyzer) { builder.AddCompilationDiagnostics(await ComputeProjectDiagnosticAnalyzerDiagnosticsAsync(project, projectAnalyzer, compilation, cancellationToken).ConfigureAwait(false)); } // merge the result to existing one. // there can be existing one from compiler driver with empty set. overwrite it with // ide one. result = result.SetItem(analyzer, DiagnosticAnalysisResult.CreateFromBuilder(builder)); } return(result); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private ImmutableArray <DiagnosticData> GetDiagnostics(DiagnosticAnalysisResult result) { // PERF: don't allocation anything if not needed if (result.IsAggregatedForm || result.IsEmpty) { return(ImmutableArray <DiagnosticData> .Empty); } return(result.SyntaxLocals.Values.SelectMany(v => v).Concat( result.SemanticLocals.Values.SelectMany(v => v)).Concat( result.NonLocals.Values.SelectMany(v => v)).Concat( result.Others).ToImmutableArray()); }
private static ImmutableArray <DiagnosticData> GetResult(DiagnosticAnalysisResult result, AnalysisKind kind, DocumentId id) { if (result.IsEmpty || !result.DocumentIds.Contains(id) || result.IsAggregatedForm) { return(ImmutableArray <DiagnosticData> .Empty); } return(kind switch { AnalysisKind.Syntax => result.GetResultOrEmpty(result.SyntaxLocals, id), AnalysisKind.Semantic => result.GetResultOrEmpty(result.SemanticLocals, id), AnalysisKind.NonLocal => result.GetResultOrEmpty(result.NonLocals, id), _ => Contract.FailWithReturn <ImmutableArray <DiagnosticData> >("shouldn't reach here"), });
public static DiagnosticAnalysisResultMap<DiagnosticAnalyzer, DiagnosticAnalysisResult> Deserialize( ObjectReader reader, IDictionary<string, DiagnosticAnalyzer> analyzerMap, Project project, VersionStamp version, CancellationToken cancellationToken) { var diagnosticDataSerializer = new DiagnosticDataSerializer(VersionStamp.Default, VersionStamp.Default); var analysisMap = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, DiagnosticAnalysisResult>(); var analysisCount = reader.ReadInt32(); for (var i = 0; i < analysisCount; i++) { var analyzer = analyzerMap[reader.ReadString()]; var syntaxLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); var semanticLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); var nonLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); var others = diagnosticDataSerializer.ReadFrom(reader, project, cancellationToken); var analysisResult = new DiagnosticAnalysisResult( project.Id, version, syntaxLocalMap, semanticLocalMap, nonLocalMap, others, documentIds: null, fromBuild: false); analysisMap.Add(analyzer, analysisResult); } var telemetryMap = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, AnalyzerTelemetryInfo>(); var telemetryCount = reader.ReadInt32(); for (var i = 0; i < telemetryCount; i++) { var analyzer = analyzerMap[reader.ReadString()]; var telemetryInfo = Deserialize(reader, cancellationToken); telemetryMap.Add(analyzer, telemetryInfo); } var exceptionMap = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<DiagnosticData>>(); var exceptionCount = reader.ReadInt32(); for (var i = 0; i < exceptionCount; i++) { var analyzer = analyzerMap[reader.ReadString()]; var exceptions = diagnosticDataSerializer.ReadFrom(reader, project, cancellationToken); exceptionMap.Add(analyzer, exceptions); } return DiagnosticAnalysisResultMap.Create(analysisMap.ToImmutable(), telemetryMap.ToImmutable(), exceptionMap.ToImmutable()); }
private static async Task <(DiagnosticAnalysisResult loadDiagnostics, ImmutableHashSet <Document>?failedDocuments)> GetDocumentLoadFailuresAsync( Project project, VersionStamp version, CancellationToken cancellationToken ) { ImmutableHashSet <Document> .Builder?failedDocuments = null; ImmutableDictionary < DocumentId, ImmutableArray <DiagnosticData> > .Builder?lazyLoadDiagnostics = null; foreach (var document in project.Documents) { var loadDiagnostic = await document.State .GetLoadDiagnosticAsync(cancellationToken) .ConfigureAwait(false); if (loadDiagnostic != null) { lazyLoadDiagnostics ??= ImmutableDictionary.CreateBuilder < DocumentId, ImmutableArray <DiagnosticData> >(); lazyLoadDiagnostics.Add( document.Id, ImmutableArray.Create(DiagnosticData.Create(loadDiagnostic, document)) ); failedDocuments ??= ImmutableHashSet.CreateBuilder <Document>(); failedDocuments.Add(document); } } var result = DiagnosticAnalysisResult.Create( project, version, syntaxLocalMap: lazyLoadDiagnostics?.ToImmutable() ?? ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty, semanticLocalMap: ImmutableDictionary < DocumentId, ImmutableArray <DiagnosticData> > .Empty, nonLocalMap: ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty, others: ImmutableArray <DiagnosticData> .Empty, documentIds: null ); return(result, failedDocuments?.ToImmutable()); }
private async Task <DiagnosticAnalysisResult> LoadInitialProjectAnalysisDataAsync(IPersistentStorageService persistentService, Project project, CancellationToken cancellationToken) { // loading data can be cancelled any time. var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version); var builder = new Builder(project, version); if (!await TryDeserializeProjectDiagnosticsAsync(persistentService, serializer, project, builder, cancellationToken).ConfigureAwait(false)) { return(DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default)); } return(builder.ToResult()); }
private async Task <DiagnosticAnalysisResult> LoadInitialProjectAnalysisDataAsync(Project project, CancellationToken cancellationToken) { // loading data can be canceled any time. var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var serializerVersion = version; var builder = new Builder(project, version); if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) { return(DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default)); } return(builder.ToResult()); }
public async Task MergeAsync(ActiveFileState state, Document document) { Contract.ThrowIfFalse(state.DocumentId == document.Id); // merge active file state to project state var lastResult = _lastResult; var syntax = state.GetAnalysisData(AnalysisKind.Syntax); var semantic = state.GetAnalysisData(AnalysisKind.Semantic); AnalyzerABTestLogger.LogDocumentDiagnostics(document, _owner.StateName, syntax.Items, semantic.Items); var project = document.Project; // if project didn't successfully loaded, then it is same as FSA off var fullAnalysis = ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(project) && await project.HasSuccessfullyLoadedAsync(CancellationToken.None).ConfigureAwait(false); // keep from build flag if full analysis is off var fromBuild = fullAnalysis ? false : lastResult.FromBuild; var openFileOnlyAnalyzer = _owner.Analyzer.IsOpenFileOnly(document.Project.Solution.Workspace); // if it is allowed to keep project state, check versions and if they are same, bail out. // if full solution analysis is off or we are asked to reset document state, we always merge. if (fullAnalysis && !openFileOnlyAnalyzer && syntax.Version != VersionStamp.Default && syntax.Version == semantic.Version && syntax.Version == lastResult.Version) { // all data is in sync already. return; } // we have mixed versions or full analysis is off, set it to default so that it can be re-calculated next time so data can be in sync. var version = VersionStamp.Default; // serialization can't be cancelled. var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version); // save active file diagnostics back to project state await SerializeAsync(serializer, document, document.Id, _owner.SyntaxStateName, syntax.Items).ConfigureAwait(false); await SerializeAsync(serializer, document, document.Id, _owner.SemanticStateName, semantic.Items).ConfigureAwait(false); // save last aggregated form of analysis result _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId, fromBuild); }
public async ValueTask MergeAsync(ActiveFileState state, TextDocument document, IGlobalOptionService globalOptions) { Contract.ThrowIfFalse(state.DocumentId == document.Id); // merge active file state to project state var lastResult = _lastResult; var syntax = state.GetAnalysisData(AnalysisKind.Syntax); var semantic = state.GetAnalysisData(AnalysisKind.Semantic); var project = document.Project; // if project didn't successfully loaded, then it is same as FSA off var fullAnalysis = globalOptions.GetBackgroundAnalysisScope(project.Language) == BackgroundAnalysisScope.FullSolution && await project.HasSuccessfullyLoadedAsync(CancellationToken.None).ConfigureAwait(false); // keep from build flag if full analysis is off var fromBuild = fullAnalysis ? false : lastResult.FromBuild; var languageServices = document.Project.LanguageServices; var simplifierOptions = (languageServices.GetService <ISimplifierOptionsStorage>() != null) ? globalOptions.GetSimplifierOptions(languageServices) : null; var openFileOnlyAnalyzer = _owner.Analyzer.IsOpenFileOnly(simplifierOptions); // if it is allowed to keep project state, check versions and if they are same, bail out. // if full solution analysis is off or we are asked to reset document state, we always merge. if (fullAnalysis && !openFileOnlyAnalyzer && syntax.Version != VersionStamp.Default && syntax.Version == semantic.Version && syntax.Version == lastResult.Version) { // all data is in sync already. return; } // we have mixed versions or full analysis is off, set it to default so that it can be re-calculated next time so data can be in sync. var version = VersionStamp.Default; // serialization can't be canceled. var serializerVersion = version; // save active file diagnostics back to project state await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, syntax.Items).ConfigureAwait(false); await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SemanticStateName, semantic.Items).ConfigureAwait(false); // save last aggregated form of analysis result _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId, fromBuild); }
> CreateAnalysisResults( Project project, ImmutableArray <StateSet> stateSets, ImmutableArray <DiagnosticData> diagnostics ) { using var poolObject = SharedPools.Default <HashSet <string> >().GetPooledObject(); var lookup = diagnostics.ToLookup(d => d.Id); var builder = ImmutableDictionary.CreateBuilder < DiagnosticAnalyzer, DiagnosticAnalysisResult >(); using var _ = PooledHashSet <DocumentId> .GetInstance( out var existingDocumentsInStateSet ); foreach (var stateSet in stateSets) { var descriptors = DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors( stateSet.Analyzer ); var liveDiagnostics = ConvertToLiveDiagnostics( lookup, descriptors, poolObject.Object ); // Ensure that all documents with diagnostics in the previous state set are added to the result. existingDocumentsInStateSet.Clear(); stateSet.CollectDocumentsWithDiagnostics(project.Id, existingDocumentsInStateSet); builder.Add( stateSet.Analyzer, DiagnosticAnalysisResult.CreateFromBuild( project, liveDiagnostics, existingDocumentsInStateSet ) ); } return(builder.ToImmutable()); }
private async Task <DiagnosticAnalysisResult> LoadInitialAnalysisDataAsync(Document document, CancellationToken cancellationToken) { // loading data can be cancelled any time. var project = document.Project; var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version); var builder = new Builder(project, version); if (!await TryDeserializeDocumentAsync(serializer, document, builder, cancellationToken).ConfigureAwait(false)) { return(DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default)); } return(builder.ToResult()); }
public static DiagnosticAnalysisResultMap <DiagnosticAnalyzer, DiagnosticAnalysisResult> ReadDiagnosticAnalysisResults( ObjectReader reader, IDictionary <string, DiagnosticAnalyzer> analyzerMap, Project project, VersionStamp version, CancellationToken cancellationToken) { var diagnosticDataSerializer = new DiagnosticDataSerializer(VersionStamp.Default, VersionStamp.Default); var analysisMap = ImmutableDictionary.CreateBuilder <DiagnosticAnalyzer, DiagnosticAnalysisResult>(); var analysisCount = reader.ReadInt32(); for (var i = 0; i < analysisCount; i++) { var analyzer = analyzerMap[reader.ReadString()]; var syntaxLocalMap = ReadDiagnosticDataMap(reader, diagnosticDataSerializer, project, cancellationToken); var semanticLocalMap = ReadDiagnosticDataMap(reader, diagnosticDataSerializer, project, cancellationToken); var nonLocalMap = ReadDiagnosticDataMap(reader, diagnosticDataSerializer, project, cancellationToken); var others = diagnosticDataSerializer.ReadDiagnosticData(reader, project, document: null, cancellationToken); var analysisResult = DiagnosticAnalysisResult.Create( project, version, syntaxLocalMap, semanticLocalMap, nonLocalMap, others.NullToEmpty(), documentIds: null); analysisMap.Add(analyzer, analysisResult); } var telemetryMap = ImmutableDictionary.CreateBuilder <DiagnosticAnalyzer, AnalyzerTelemetryInfo>(); var telemetryCount = reader.ReadInt32(); for (var i = 0; i < telemetryCount; i++) { var analyzer = analyzerMap[reader.ReadString()]; var telemetryInfo = ReadTelemetry(reader, cancellationToken); telemetryMap.Add(analyzer, telemetryInfo); } return(DiagnosticAnalysisResultMap.Create(analysisMap.ToImmutable(), telemetryMap.ToImmutable())); }
public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAnalysisResult result) { Contract.ThrowIfTrue(result.IsAggregatedForm); Contract.ThrowIfNull(result.DocumentIds); RemoveInMemoryCache(_lastResult); // save last aggregated form of analysis result _lastResult = result.ToAggregatedForm(); // serialization can't be canceled. var serializerVersion = result.Version; foreach (var documentId in result.DocumentIds) { var document = project.GetTextDocument(documentId); // If we couldn't find a normal document, and all features are enabled for source generated // documents, attempt to locate a matching source generated document in the project. if (document is null && project.Solution.Workspace.Services.GetService <IWorkspaceConfigurationService>()?.Options.EnableOpeningSourceGeneratedFiles == true) { document = await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); } if (document == null) { // it can happen with build synchronization since, in build case, // we don't have actual snapshot (we have no idea what sources out of proc build has picked up) // so we might be out of sync. // example of such cases will be changing anything about solution while building is going on. // it can be user explicit actions such as unloading project, deleting a file, but also it can be // something project system or roslyn workspace does such as populating workspace right after // solution is loaded. continue; } await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)).ConfigureAwait(false); await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)).ConfigureAwait(false); await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)).ConfigureAwait(false); } await AddToInMemoryStorageAsync(serializerVersion, project, document : null, result.ProjectId, _owner.NonLocalStateName, result.GetOtherDiagnostics()).ConfigureAwait(false); }
private void RaiseDocumentDiagnosticsIfNeeded( Document document, StateSet stateSet, AnalysisKind kind, DiagnosticAnalysisResult oldResult, DiagnosticAnalysisResult newResult, Action <DiagnosticsUpdatedArgs> raiseEvents) { // if our old result is from build and we don't have actual data, don't try micro-optimize and always refresh diagnostics. // most of time, we don't actually load or hold the old data in memory from persistent storage due to perf reasons. // // we need this special behavior for errors from build since unlike live errors, we don't know whether errors // from build is for syntax, semantic or others. due to that, we blindly mark them as semantic errors (most common type of errors from build) // // that can sometime cause issues. for example, if the error turns out to be syntax error (live) then we at the end fail to de-dup. // but since this optimization saves us a lot of refresh between live errors analysis we want to disable this only in this condition. var forceUpdate = oldResult.FromBuild && oldResult.IsAggregatedForm; var oldItems = oldResult.GetDocumentDiagnostics(document.Id, kind); var newItems = newResult.GetDocumentDiagnostics(document.Id, kind); RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, raiseEvents, forceUpdate); }
private async Task <ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> > FilterOutCompilerSemanticErrorsIfNeccessaryAsync( Project project, ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> result, CancellationToken cancellationToken) { // see whether solution is loaded successfully var projectLoadedSuccessfully = await project.HasSuccessfullyLoadedAsync(cancellationToken).ConfigureAwait(false); if (projectLoadedSuccessfully) { return(result); } var compilerAnalyzer = _owner.HostAnalyzerManager.GetCompilerDiagnosticAnalyzer(project.Language); if (compilerAnalyzer == null) { // this language doesn't support compiler analyzer return(result); } if (!result.TryGetValue(compilerAnalyzer, out var analysisResult)) { // no result from compiler analyzer return(result); } Logger.Log(FunctionId.Diagnostics_ProjectDiagnostic, p => $"Failed to Load Successfully ({p.FilePath ?? p.Name})", project); // get rid of any result except syntax from compiler analyzer result var newCompilerAnalysisResult = new DiagnosticAnalysisResult( analysisResult.ProjectId, analysisResult.Version, analysisResult.SyntaxLocals, semanticLocals: ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty, nonLocals: ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty, others: ImmutableArray <DiagnosticData> .Empty, documentIds: null, fromBuild: false); // return new result return(result.SetItem(compilerAnalyzer, newCompilerAnalysisResult)); }
public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAnalysisResult result) { Contract.ThrowIfTrue(result.IsAggregatedForm); Contract.ThrowIfNull(result.DocumentIds); RemoveInMemoryCache(_lastResult); // save last aggregated form of analysis result _lastResult = result.ToAggregatedForm(); // serialization can't be canceled. var serializerVersion = result.Version; foreach (var documentId in result.DocumentIds) { var document = project.GetTextDocument(documentId); // If we couldn't find a normal document, and all features are enabled for source generated // documents, attempt to locate a matching source generated document in the project. if (document is null && project.Solution.Workspace.Services.GetService <ISyntaxTreeConfigurationService>() is { EnableOpeningSourceGeneratedFilesInWorkspace: true })
private async Task <DiagnosticAnalysisResultMap <DiagnosticAnalyzer, DiagnosticAnalysisResult> > AnalyzeInProcAsync( CompilationWithAnalyzers compilation, Project project, RemoteHostClient?client, CancellationToken cancellationToken) { Debug.Assert(compilation.Analyzers.Length != 0); var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); // PERF: Run all analyzers at once using the new GetAnalysisResultAsync API. var analysisResult = await compilation.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); // if remote host is there, report performance data var asyncToken = _asyncOperationListener.BeginAsyncOperation(nameof(AnalyzeInProcAsync)); var _ = FireAndForgetReportAnalyzerPerformanceAsync(project, client, analysisResult, cancellationToken).CompletesAsyncOperation(asyncToken); // get compiler result builder map var builderMap = analysisResult.ToResultBuilderMap(project, version, compilation.Compilation, compilation.Analyzers, cancellationToken); return(DiagnosticAnalysisResultMap.Create( builderMap.ToImmutableDictionary(kv => kv.Key, kv => DiagnosticAnalysisResult.CreateFromBuilder(kv.Value)), analysisResult.AnalyzerTelemetryInfo)); }
private ImmutableDictionary <DiagnosticAnalyzer, DiagnosticAnalysisResult> CreateAnalysisResults( Project project, ImmutableArray <StateSet> stateSets, ProjectAnalysisData oldAnalysisData, ImmutableArray <DiagnosticData> diagnostics) { using var poolObject = SharedPools.Default <HashSet <string> >().GetPooledObject(); var lookup = diagnostics.ToLookup(d => d.Id); var builder = ImmutableDictionary.CreateBuilder <DiagnosticAnalyzer, DiagnosticAnalysisResult>(); foreach (var stateSet in stateSets) { var descriptors = HostAnalyzerManager.GetDiagnosticDescriptors(stateSet.Analyzer); var liveDiagnostics = MergeDiagnostics(ConvertToLiveDiagnostics(lookup, descriptors, poolObject.Object), GetDiagnostics(oldAnalysisData.GetResult(stateSet.Analyzer))); var result = DiagnosticAnalysisResult.CreateFromBuild(project, liveDiagnostics); builder.Add(stateSet.Analyzer, result); } return(builder.ToImmutable()); }
public async Task MergeAsync(ActiveFileState state, Document document) { Contract.ThrowIfFalse(state.DocumentId == document.Id); // merge active file state to project state var lastResult = _lastResult; var syntax = state.GetAnalysisData(AnalysisKind.Syntax); var semantic = state.GetAnalysisData(AnalysisKind.Semantic); var project = document.Project; var fullAnalysis = ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(project); // keep from build flag if full analysis is off var fromBuild = fullAnalysis ? false : lastResult.FromBuild; // if it is allowed to keep project state, check versions and if they are same, bail out // if full solution analysis is off or we are asked to reset document state, we always merge. if (fullAnalysis && syntax.Version != VersionStamp.Default && syntax.Version == semantic.Version && syntax.Version == lastResult.Version) { // all data is in sync already. return; } // we have mixed versions or full analysis is off, set it to default so that it can be re-calculated next time so data can be in sync. var version = VersionStamp.Default; // serialization can't be cancelled. var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version); // save active file diagnostics back to project state await SerializeAsync(serializer, document, document.Id, _owner.SyntaxStateName, syntax.Items).ConfigureAwait(false); await SerializeAsync(serializer, document, document.Id, _owner.SemanticStateName, semantic.Items).ConfigureAwait(false); // save last aggregated form of analysis result _lastResult = new DiagnosticAnalysisResult(_lastResult.ProjectId, version, _lastResult.DocumentIdsOrEmpty.Add(state.DocumentId), isEmpty: false, fromBuild: fromBuild); }
private static ImmutableArray <DiagnosticData> GetResult(DiagnosticAnalysisResult result, AnalysisKind kind, DocumentId id) { if (result.IsEmpty || !result.DocumentIds.Contains(id) || result.IsAggregatedForm) { return(ImmutableArray <DiagnosticData> .Empty); } switch (kind) { case AnalysisKind.Syntax: return(result.GetResultOrEmpty(result.SyntaxLocals, id)); case AnalysisKind.Semantic: return(result.GetResultOrEmpty(result.SemanticLocals, id)); case AnalysisKind.NonLocal: return(result.GetResultOrEmpty(result.NonLocals, id)); default: return(Contract.FailWithReturn <ImmutableArray <DiagnosticData> >("shouldn't reach here")); } }
public async Task <DiagnosticAnalysisResult> GetAnalysisDataAsync(IPersistentStorageService persistentService, TextDocument document, bool avoidLoadingData, CancellationToken cancellationToken) { // make a copy of last result. var lastResult = _lastResult; Contract.ThrowIfFalse(lastResult.ProjectId == document.Project.Id); if (lastResult.IsDefault) { return(await LoadInitialAnalysisDataAsync(persistentService, document, cancellationToken).ConfigureAwait(false)); } var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); if (avoidLoadingData && lastResult.Version != version) { return(lastResult); } // if given document doesnt have any diagnostics, return empty. if (IsEmpty(lastResult, document.Id)) { return(DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version)); } // loading data can be cancelled any time. var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version); var builder = new Builder(document.Project, lastResult.Version); if (!await TryDeserializeDocumentDiagnosticsAsync(persistentService, serializer, document, builder, cancellationToken).ConfigureAwait(false)) { Debug.Assert(lastResult.Version == VersionStamp.Default); // this can happen if we merged back active file diagnostics back to project state but // project state didn't have diagnostics for the file yet. (since project state was staled) } return(builder.ToResult()); }
public static void LogProjectDiagnostics(Project project, DiagnosticAnalysisResult result) { if (!s_reportErrors || !s_reported.TryAdd(project.Id, null)) { // doesn't meet the bar to report the issue. return; } // logs count of errors for this project. this won't log anything if FSA off since // we don't collect any diagnostics for a project if FSA is off. var map = new Dictionary <string, int>(); foreach (var documentId in result.DocumentIdsOrEmpty) { CountErrors(map, result.GetResultOrEmpty(result.SyntaxLocals, documentId)); CountErrors(map, result.GetResultOrEmpty(result.SemanticLocals, documentId)); CountErrors(map, result.GetResultOrEmpty(result.NonLocals, documentId)); } CountErrors(map, result.Others); LogErrors(project, "ProjectDignostics", project.Id.Id, map); }
public async Task <DiagnosticAnalysisResult> GetProjectAnalysisDataAsync(IPersistentStorageService persistentService, Project project, bool avoidLoadingData, CancellationToken cancellationToken) { // make a copy of last result. var lastResult = _lastResult; Contract.ThrowIfFalse(lastResult.ProjectId == project.Id); if (lastResult.IsDefault) { return(await LoadInitialProjectAnalysisDataAsync(persistentService, project, cancellationToken).ConfigureAwait(false)); } var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); if (avoidLoadingData && lastResult.Version != version) { return(lastResult); } // if given document doesnt have any diagnostics, return empty. if (lastResult.IsEmpty) { return(DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version)); } // loading data can be cancelled any time. var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version); var builder = new Builder(project, lastResult.Version); if (!await TryDeserializeProjectDiagnosticsAsync(persistentService, serializer, project, builder, cancellationToken).ConfigureAwait(false)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. } return(builder.ToResult()); }
private async Task <DiagnosticAnalysisResultMap <DiagnosticAnalyzer, DiagnosticAnalysisResult> > AnalyzeInProcAsync( DocumentAnalysisScope?documentAnalysisScope, Project project, CompilationWithAnalyzers compilationWithAnalyzers, RemoteHostClient?client, bool logPerformanceInfo, bool getTelemetryInfo, CancellationToken cancellationToken) { var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var(analysisResult, additionalPragmaSuppressionDiagnostics) = await compilationWithAnalyzers.GetAnalysisResultAsync( documentAnalysisScope, project, AnalyzerInfoCache, cancellationToken).ConfigureAwait(false); if (logPerformanceInfo) { // if remote host is there, report performance data var asyncToken = _asyncOperationListener.BeginAsyncOperation(nameof(AnalyzeInProcAsync)); var _ = FireAndForgetReportAnalyzerPerformanceAsync(documentAnalysisScope, project, client, analysisResult, cancellationToken).CompletesAsyncOperation(asyncToken); } var analyzers = documentAnalysisScope?.Analyzers ?? compilationWithAnalyzers.Analyzers; var skippedAnalyzersInfo = project.GetSkippedAnalyzersInfo(AnalyzerInfoCache); // get compiler result builder map var builderMap = await analysisResult.ToResultBuilderMapAsync( additionalPragmaSuppressionDiagnostics, documentAnalysisScope, project, version, compilationWithAnalyzers.Compilation, analyzers, skippedAnalyzersInfo, compilationWithAnalyzers.AnalysisOptions.ReportSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); var result = builderMap.ToImmutableDictionary(kv => kv.Key, kv => DiagnosticAnalysisResult.CreateFromBuilder(kv.Value)); var telemetry = getTelemetryInfo ? analysisResult.AnalyzerTelemetryInfo : ImmutableDictionary <DiagnosticAnalyzer, AnalyzerTelemetryInfo> .Empty; return(DiagnosticAnalysisResultMap.Create(result, telemetry)); }
public async Task SaveAsync(Project project, DiagnosticAnalysisResult result) { Contract.ThrowIfTrue(result.IsAggregatedForm); RemoveInMemoryCache(_lastResult); // save last aggregated form of analysis result _lastResult = result.ToAggregatedForm(); // serialization can't be cancelled. var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, result.Version); foreach (var documentId in result.DocumentIds) { var document = project.GetDocument(documentId); if (document == null) { // it can happen with build synchronization since, in build case, // we don't have actual snapshot (we have no idea what sources out of proc build has picked up) // so we might be out of sync. // example of such cases will be changing anything about solution while building is going on. // it can be user explict actions such as unloading project, deleting a file, but also it can be // something project system or roslyn workspace does such as populating workspace right after // solution is loaded. continue; } await SerializeAsync(serializer, document, document.Id, _owner.SyntaxStateName, GetResult(result, AnalysisKind.Syntax, document.Id)).ConfigureAwait(false); await SerializeAsync(serializer, document, document.Id, _owner.SemanticStateName, GetResult(result, AnalysisKind.Semantic, document.Id)).ConfigureAwait(false); await SerializeAsync(serializer, document, document.Id, _owner.NonLocalStateName, GetResult(result, AnalysisKind.NonLocal, document.Id)).ConfigureAwait(false); } await SerializeAsync(serializer, project, result.ProjectId, _owner.NonLocalStateName, result.Others).ConfigureAwait(false); AnalyzerABTestLogger.LogProjectDiagnostics(project, _owner.StateName, result); }
private ImmutableDictionary<DiagnosticAnalyzer, DiagnosticAnalysisResult> CreateAnalysisResults( Project project, ImmutableArray<StateSet> stateSets, ProjectAnalysisData oldAnalysisData, ImmutableArray<DiagnosticData> diagnostics) { using (var poolObject = SharedPools.Default<HashSet<string>>().GetPooledObject()) { // we can't distinguish locals and non locals from build diagnostics nor determine right snapshot version for the build. // so we put everything in as semantic local with default version. this lets us to replace those to live diagnostics when needed easily. var version = VersionStamp.Default; var lookup = diagnostics.ToLookup(d => d.Id); var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, DiagnosticAnalysisResult>(); foreach (var stateSet in stateSets) { var descriptors = HostAnalyzerManager.GetDiagnosticDescriptors(stateSet.Analyzer); var liveDiagnostics = MergeDiagnostics(ConvertToLiveDiagnostics(lookup, descriptors, poolObject.Object), GetDiagnostics(oldAnalysisData.GetResult(stateSet.Analyzer))); var group = liveDiagnostics.GroupBy(d => d.DocumentId); var result = new DiagnosticAnalysisResult( project.Id, version, documentIds: group.Where(g => g.Key != null).Select(g => g.Key).ToImmutableHashSet(), syntaxLocals: ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>>.Empty, semanticLocals: group.Where(g => g.Key != null).ToImmutableDictionary(g => g.Key, g => g.ToImmutableArray()), nonLocals: ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>>.Empty, others: group.Where(g => g.Key == null).SelectMany(g => g).ToImmutableArrayOrEmpty(), fromBuild: true); builder.Add(stateSet.Analyzer, result); } return builder.ToImmutable(); } }
private static ImmutableArray<DiagnosticData> GetResult(DiagnosticAnalysisResult result, AnalysisKind kind, DocumentId id) { if (result.IsEmpty || !result.DocumentIds.Contains(id) || result.IsAggregatedForm) { return ImmutableArray<DiagnosticData>.Empty; } switch (kind) { case AnalysisKind.Syntax: return result.GetResultOrEmpty(result.SyntaxLocals, id); case AnalysisKind.Semantic: return result.GetResultOrEmpty(result.SemanticLocals, id); case AnalysisKind.NonLocal: return result.GetResultOrEmpty(result.NonLocals, id); default: return Contract.FailWithReturn<ImmutableArray<DiagnosticData>>("shouldn't reach here"); } }
private ImmutableArray<DiagnosticData> GetDiagnostics(DiagnosticAnalysisResult result) { // PERF: don't allocation anything if not needed if (result.IsAggregatedForm || result.IsEmpty) { return ImmutableArray<DiagnosticData>.Empty; } return result.SyntaxLocals.Values.SelectMany(v => v).Concat( result.SemanticLocals.Values.SelectMany(v => v)).Concat( result.NonLocals.Values.SelectMany(v => v)).Concat( result.Others).ToImmutableArray(); }
private void RaiseProjectDiagnosticsCreated(Project project, StateSet stateSet, DiagnosticAnalysisResult oldAnalysisResult, DiagnosticAnalysisResult newAnalysisResult, Action<DiagnosticsUpdatedArgs> raiseEvents) { foreach (var documentId in newAnalysisResult.DocumentIds) { var document = project.GetDocument(documentId); if (document == null) { // it can happen with build synchronization since, in build case, // we don't have actual snapshot (we have no idea what sources out of proc build has picked up) // so we might be out of sync. // example of such cases will be changing anything about solution while building is going on. // it can be user explict actions such as unloading project, deleting a file, but also it can be // something project system or roslyn workspace does such as populating workspace right after // solution is loaded. continue; } RaiseDocumentDiagnosticsIfNeeded(document, stateSet, AnalysisKind.NonLocal, oldAnalysisResult, newAnalysisResult, raiseEvents); // we don't raise events for active file. it will be taken cared by active file analysis if (stateSet.IsActiveFile(documentId)) { continue; } RaiseDocumentDiagnosticsIfNeeded(document, stateSet, AnalysisKind.Syntax, oldAnalysisResult, newAnalysisResult, raiseEvents); RaiseDocumentDiagnosticsIfNeeded(document, stateSet, AnalysisKind.Semantic, oldAnalysisResult, newAnalysisResult, raiseEvents); } RaiseDiagnosticsCreated(project, stateSet, newAnalysisResult.Others, raiseEvents); }
private void RaiseDocumentDiagnosticsIfNeeded( Document document, StateSet stateSet, AnalysisKind kind, DiagnosticAnalysisResult oldResult, DiagnosticAnalysisResult newResult, Action<DiagnosticsUpdatedArgs> raiseEvents) { var oldItems = GetResult(oldResult, kind, document.Id); var newItems = GetResult(newResult, kind, document.Id); RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, raiseEvents); }