private IEnumerable <Diagnostic> GetDiagnostics(DiagnosticAnalyzer workspaceAnalyzerOpt, Document document, TextSpan span, Project project, bool getDocumentDiagnostics, bool getProjectDiagnostics) { var documentDiagnostics = SpecializedCollections.EmptyEnumerable <Diagnostic>(); var projectDiagnostics = SpecializedCollections.EmptyEnumerable <Diagnostic>(); if (getDocumentDiagnostics) { var dxs = _diagnosticAnalyzerService.GetDiagnosticsAsync(project.Solution, project.Id, document.Id, _includeSuppressedDiagnostics).WaitAndGetResult(CancellationToken.None); documentDiagnostics = DiagnosticData.ToDiagnosticsAsync(project, dxs.Where(d => d.HasTextSpan && d.TextSpan.IntersectsWith(span)), CancellationToken.None).WaitAndGetResult(CancellationToken.None); } if (getProjectDiagnostics) { var dxs = _diagnosticAnalyzerService.GetDiagnosticsAsync(project.Solution, project.Id, includeSuppressedDiagnostics: _includeSuppressedDiagnostics).WaitAndGetResult(CancellationToken.None); projectDiagnostics = DiagnosticData.ToDiagnosticsAsync(project, dxs.Where(d => !d.HasTextSpan), CancellationToken.None).WaitAndGetResult(CancellationToken.None); } var exceptionDiagnostics = DiagnosticData.ToDiagnosticsAsync(project, _exceptionDiagnosticsSource.TestOnly_GetReportedDiagnostics(), CancellationToken.None).WaitAndGetResult(CancellationToken.None); var allDiagnostics = documentDiagnostics.Concat(projectDiagnostics).Concat(exceptionDiagnostics); if (!_includeSuppressedDiagnostics) { Assert.True(!allDiagnostics.Any(d => d.IsSuppressed)); } return(allDiagnostics); }
private async Task <IEnumerable <Diagnostic> > GetDocumentDiagnosticsAsync(Document document, ImmutableHashSet <string> diagnosticIds, CancellationToken cancellationToken) { Contract.ThrowIfNull(document); var solution = document.Project.Solution; var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(solution, null, document.Id, diagnosticIds, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return(await DiagnosticData.ToDiagnosticsAsync(document.Project, diagnostics, cancellationToken).ConfigureAwait(false)); }
private async Task <ImmutableDictionary <Project, ImmutableArray <Diagnostic> > > GetProjectDiagnosticsToFixAsync(IEnumerable <DiagnosticData> diagnosticsToFix, Func <Project, bool> shouldFixInProject, CancellationToken cancellationToken) { Func <DiagnosticData, bool> isProjectDiagnostic = d => d.DataLocation == null && d.ProjectId != null; var builder = ImmutableDictionary.CreateBuilder <ProjectId, List <DiagnosticData> >(); foreach (var diagnosticData in diagnosticsToFix.Where(isProjectDiagnostic)) { List <DiagnosticData> diagnosticsPerProject; if (!builder.TryGetValue(diagnosticData.ProjectId, out diagnosticsPerProject)) { diagnosticsPerProject = new List <DiagnosticData>(); builder[diagnosticData.ProjectId] = diagnosticsPerProject; } diagnosticsPerProject.Add(diagnosticData); } if (builder.Count == 0) { return(ImmutableDictionary <Project, ImmutableArray <Diagnostic> > .Empty); } var finalBuilder = ImmutableDictionary.CreateBuilder <Project, ImmutableArray <Diagnostic> >(); var latestDiagnosticsToFix = new HashSet <DiagnosticData>(); foreach (var kvp in builder) { var projectId = kvp.Key; var project = _workspace.CurrentSolution.GetProject(projectId); if (project == null || !shouldFixInProject(project)) { continue; } var diagnostics = kvp.Value; var uniqueDiagnosticIds = diagnostics.Select(d => d.Id).ToImmutableHashSet(); var latestDiagnosticsFromDiagnosticService = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, diagnosticIds: uniqueDiagnosticIds, includeSuppressedDiagnostics: true, cancellationToken: cancellationToken) .ConfigureAwait(false)); latestDiagnosticsToFix.Clear(); latestDiagnosticsToFix.AddRange(latestDiagnosticsFromDiagnosticService.Where(isProjectDiagnostic)); // Filter out stale diagnostics in error list. var projectDiagnosticsToFix = diagnostics.Where(d => latestDiagnosticsFromDiagnosticService.Contains(d) || SuppressionHelpers.IsSynthesizedExternalSourceDiagnostic(d)); if (projectDiagnosticsToFix.IsEmpty()) { continue; } var projectDiagnostics = await DiagnosticData.ToDiagnosticsAsync(project, projectDiagnosticsToFix, cancellationToken).ConfigureAwait(false); finalBuilder.Add(project, projectDiagnostics.ToImmutableArray()); } return(finalBuilder.ToImmutableDictionary()); }
private async Task <List <CodeFixCollection> > AppendSuppressionsAsync( Document document, TextSpan span, IEnumerable <DiagnosticData> diagnosticDataCollection, List <CodeFixCollection> result, CancellationToken cancellationToken) { Lazy <ISuppressionFixProvider> lazySuppressionProvider; if (!_suppressionProvidersMap.TryGetValue(document.Project.Language, out lazySuppressionProvider) || lazySuppressionProvider.Value == null) { return(result); } var diagnostics = await DiagnosticData.ToDiagnosticsAsync(document.Project, diagnosticDataCollection, cancellationToken).ConfigureAwait(false); Func <Diagnostic, bool> hasFix = (d) => lazySuppressionProvider.Value.CanBeSuppressed(d); Func <ImmutableArray <Diagnostic>, Task <IEnumerable <CodeFix> > > getFixes = (dxs) => lazySuppressionProvider.Value.GetSuppressionsAsync(document, span, dxs, cancellationToken); await AppendFixesOrSuppressionsAsync(document, span, diagnostics, result, lazySuppressionProvider.Value, hasFix, getFixes, cancellationToken).ConfigureAwait(false); return(result); }
private async Task <IEnumerable <Diagnostic> > GetProjectDiagnosticsAsync(Project project, bool includeAllDocumentDiagnostics, ImmutableHashSet <string> diagnosticIds, CancellationToken cancellationToken) { Contract.ThrowIfNull(project); if (includeAllDocumentDiagnostics) { // Get all diagnostics for the entire project, including document diagnostics. var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, diagnosticIds : diagnosticIds, cancellationToken : cancellationToken).ConfigureAwait(false); var documentIdsToTreeMap = await GetDocumentIdsToTreeMapAsync(project, cancellationToken).ConfigureAwait(false); return(await DiagnosticData.ToDiagnosticsAsync(project, diagnostics, documentIdsToTreeMap, cancellationToken).ConfigureAwait(false)); } else { // Get all no-location diagnostics for the project, doesn't include document diagnostics. var diagnostics = await _diagnosticService.GetProjectDiagnosticsForIdsAsync(project.Solution, project.Id, diagnosticIds, cancellationToken : cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId == null)); return(await DiagnosticData.ToDiagnosticsAsync(project, diagnostics, cancellationToken).ConfigureAwait(false)); } }
private async Task <ImmutableDictionary <Document, ImmutableArray <Diagnostic> > > GetDocumentDiagnosticsToFixAsync(IEnumerable <DiagnosticData> diagnosticsToFix, Func <Project, bool> shouldFixInProject, bool filterStaleDiagnostics, CancellationToken cancellationToken) { Func <DiagnosticData, bool> isDocumentDiagnostic = d => d.DataLocation != null && d.HasTextSpan; var builder = ImmutableDictionary.CreateBuilder <DocumentId, List <DiagnosticData> >(); foreach (var diagnosticData in diagnosticsToFix.Where(isDocumentDiagnostic)) { List <DiagnosticData> diagnosticsPerDocument; if (!builder.TryGetValue(diagnosticData.DocumentId, out diagnosticsPerDocument)) { diagnosticsPerDocument = new List <DiagnosticData>(); builder[diagnosticData.DocumentId] = diagnosticsPerDocument; } diagnosticsPerDocument.Add(diagnosticData); } if (builder.Count == 0) { return(ImmutableDictionary <Document, ImmutableArray <Diagnostic> > .Empty); } var finalBuilder = ImmutableDictionary.CreateBuilder <Document, ImmutableArray <Diagnostic> >(); var latestDocumentDiagnosticsMapOpt = filterStaleDiagnostics ? new Dictionary <DocumentId, ImmutableHashSet <DiagnosticData> >() : null; foreach (var group in builder.GroupBy(kvp => kvp.Key.ProjectId)) { var projectId = group.Key; var project = _workspace.CurrentSolution.GetProject(projectId); if (project == null || !shouldFixInProject(project)) { continue; } if (filterStaleDiagnostics) { var uniqueDiagnosticIds = group.SelectMany(kvp => kvp.Value.Select(d => d.Id)).ToImmutableHashSet(); var latestProjectDiagnostics = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, diagnosticIds: uniqueDiagnosticIds, includeSuppressedDiagnostics: true, cancellationToken: cancellationToken) .ConfigureAwait(false)).Where(isDocumentDiagnostic); latestDocumentDiagnosticsMapOpt.Clear(); foreach (var kvp in latestProjectDiagnostics.Where(d => d.DocumentId != null).GroupBy(d => d.DocumentId)) { latestDocumentDiagnosticsMapOpt.Add(kvp.Key, kvp.ToImmutableHashSet()); } } foreach (var documentDiagnostics in group) { var document = project.GetDocument(documentDiagnostics.Key); if (document == null) { continue; } IEnumerable <DiagnosticData> documentDiagnosticsToFix; if (filterStaleDiagnostics) { ImmutableHashSet <DiagnosticData> latestDocumentDiagnostics; if (!latestDocumentDiagnosticsMapOpt.TryGetValue(document.Id, out latestDocumentDiagnostics)) { // Ignore stale diagnostics in error list. latestDocumentDiagnostics = ImmutableHashSet <DiagnosticData> .Empty; } // Filter out stale diagnostics in error list. documentDiagnosticsToFix = documentDiagnostics.Value.Where(d => latestDocumentDiagnostics.Contains(d) || SuppressionHelpers.IsSynthesizedExternalSourceDiagnostic(d)); } else { documentDiagnosticsToFix = documentDiagnostics.Value; } if (documentDiagnosticsToFix.Any()) { var diagnostics = await DiagnosticData.ToDiagnosticsAsync(project, documentDiagnosticsToFix, cancellationToken).ConfigureAwait(false); finalBuilder.Add(document, diagnostics.ToImmutableArray()); } } } return(finalBuilder.ToImmutableDictionary()); }
private async Task <List <CodeFixCollection> > AppendFixesAsync( Document document, TextSpan span, IEnumerable <DiagnosticData> diagnosticDataCollection, List <CodeFixCollection> result, CancellationToken cancellationToken) { Lazy <ImmutableDictionary <DiagnosticId, ImmutableArray <CodeFixProvider> > > fixerMap; bool hasAnySharedFixer = _workspaceFixersMap.TryGetValue(document.Project.Language, out fixerMap); var projectFixersMap = GetProjectFixers(document.Project); var hasAnyProjectFixer = projectFixersMap.Any(); if (!hasAnySharedFixer && !hasAnyProjectFixer) { return(result); } ImmutableArray <CodeFixProvider> workspaceFixers; List <CodeFixProvider> projectFixers; var allFixers = new List <CodeFixProvider>(); foreach (var diagnosticId in diagnosticDataCollection.Select(d => d.Id).Distinct()) { cancellationToken.ThrowIfCancellationRequested(); if (hasAnySharedFixer && fixerMap.Value.TryGetValue(diagnosticId, out workspaceFixers)) { allFixers.AddRange(workspaceFixers); } if (hasAnyProjectFixer && projectFixersMap.TryGetValue(diagnosticId, out projectFixers)) { allFixers.AddRange(projectFixers); } } var diagnostics = await DiagnosticData.ToDiagnosticsAsync(document.Project, diagnosticDataCollection, cancellationToken).ConfigureAwait(false); var extensionManager = document.Project.Solution.Workspace.Services.GetService <IExtensionManager>(); foreach (var fixer in allFixers.Distinct()) { cancellationToken.ThrowIfCancellationRequested(); Func <Diagnostic, bool> hasFix = (d) => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id); Func <ImmutableArray <Diagnostic>, Task <IEnumerable <CodeFix> > > getFixes = async(dxs) => { var fixes = new List <CodeFix>(); var context = new CodeFixContext(document, span, dxs, // TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? (a, d) => { // Serialize access for thread safety - we don't know what thread the fix provider will call this delegate from. lock (fixes) { fixes.Add(new CodeFix(a, d)); } }, verifyArguments: false, cancellationToken: cancellationToken); var task = fixer.RegisterCodeFixesAsync(context) ?? SpecializedTasks.EmptyTask; await task.ConfigureAwait(false); return(fixes); }; await AppendFixesOrSuppressionsAsync(document, span, diagnostics, result, fixer, hasFix, getFixes, cancellationToken).ConfigureAwait(false); } return(result); }