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);
        }
Example #2
0
        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));
        }
Example #3
0
        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());
        }
Example #4
0
        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);
        }
Example #5
0
        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));
            }
        }
Example #6
0
        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());
        }
Example #7
0
        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);
        }