forked from dotnet/roslyn
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDiagnosticIncrementalAnalyzer_GetDiagnostics.cs
157 lines (132 loc) · 8.2 KB
/
DiagnosticIncrementalAnalyzer_GetDiagnostics.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics;
internal partial class DiagnosticAnalyzerService
{
private partial class DiagnosticIncrementalAnalyzer
{
public Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet<string>? diagnosticIds, Func<DiagnosticAnalyzer, bool>? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken)
=> new DiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken);
public Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, ImmutableHashSet<string>? diagnosticIds, Func<DiagnosticAnalyzer, bool>? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken)
=> new DiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken);
private sealed class DiagnosticGetter(
DiagnosticIncrementalAnalyzer owner,
Solution solution,
ProjectId projectId,
DocumentId? documentId,
ImmutableHashSet<string>? diagnosticIds,
Func<DiagnosticAnalyzer, bool>? shouldIncludeAnalyzer,
bool includeLocalDocumentDiagnostics,
bool includeNonLocalDocumentDiagnostics)
{
private readonly DiagnosticIncrementalAnalyzer Owner = owner;
private readonly Solution Solution = solution;
private readonly ProjectId ProjectId = projectId;
private readonly DocumentId? DocumentId = documentId;
private readonly ImmutableHashSet<string>? _diagnosticIds = diagnosticIds;
private readonly Func<DiagnosticAnalyzer, bool>? _shouldIncludeAnalyzer = shouldIncludeAnalyzer;
private readonly bool IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics;
private readonly bool IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics;
private StateManager StateManager => Owner._stateManager;
private bool ShouldIncludeDiagnostic(DiagnosticData diagnostic)
=> _diagnosticIds == null || _diagnosticIds.Contains(diagnostic.Id);
public async Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(CancellationToken cancellationToken)
{
var project = Solution.GetProject(ProjectId);
if (project == null)
return [];
// return diagnostics specific to one project or document
var includeProjectNonLocalResult = DocumentId == null;
return await ProduceProjectDiagnosticsAsync(
project,
// Ensure we compute and return diagnostics for both the normal docs and the additional docs in this
// project if no specific document id was requested.
this.DocumentId != null ? [this.DocumentId] : [.. project.DocumentIds, .. project.AdditionalDocumentIds],
includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false);
}
private async Task<ImmutableArray<DiagnosticData>> ProduceProjectDiagnosticsAsync(
Project project, IReadOnlyList<DocumentId> documentIds,
bool includeProjectNonLocalResult, CancellationToken cancellationToken)
{
using var _ = ArrayBuilder<DiagnosticData>.GetInstance(out var builder);
await this.ProduceDiagnosticsAsync(
project, documentIds, includeProjectNonLocalResult, builder, cancellationToken).ConfigureAwait(false);
return builder.ToImmutableAndClear();
}
private void AddIncludedDiagnostics(ArrayBuilder<DiagnosticData> builder, ImmutableArray<DiagnosticData> diagnostics)
{
foreach (var diagnostic in diagnostics)
{
if (ShouldIncludeDiagnostic(diagnostic))
builder.Add(diagnostic);
}
}
public async Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsAsync(CancellationToken cancellationToken)
{
var project = Solution.GetProject(ProjectId);
if (project is null)
return [];
return await ProduceProjectDiagnosticsAsync(
project, documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false);
}
private async Task ProduceDiagnosticsAsync(
Project project,
IReadOnlyList<DocumentId> documentIds,
bool includeProjectNonLocalResult,
ArrayBuilder<DiagnosticData> builder,
CancellationToken cancellationToken)
{
// get analyzers that are not suppressed.
var stateSetsForProject = await StateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false);
var stateSets = stateSetsForProject.Where(s => ShouldIncludeStateSet(project, s)).ToImmutableArrayOrEmpty();
// unlike the suppressed (disabled) analyzer, we will include hidden diagnostic only analyzers here.
var compilation = await CreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false);
var result = await Owner.GetProjectAnalysisDataAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false);
foreach (var stateSet in stateSets)
{
var analysisResult = result.GetResult(stateSet.Analyzer);
foreach (var documentId in documentIds)
{
if (IncludeLocalDocumentDiagnostics)
{
AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax));
AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic));
}
if (IncludeNonLocalDocumentDiagnostics)
AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal));
}
if (includeProjectNonLocalResult)
{
// include project diagnostics if there is no target document
AddIncludedDiagnostics(builder, analysisResult.GetOtherDiagnostics());
}
}
}
private bool ShouldIncludeStateSet(Project project, StateSet stateSet)
{
if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(stateSet.Analyzer, project, Owner.GlobalOptions))
{
return false;
}
if (_shouldIncludeAnalyzer != null && !_shouldIncludeAnalyzer(stateSet.Analyzer))
{
return false;
}
if (_diagnosticIds != null && Owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(stateSet.Analyzer).All(d => !_diagnosticIds.Contains(d.Id)))
{
return false;
}
return true;
}
}
}
}