BaseDiagnosticIncrementalAnalyzer.cs 16.5 KB
Newer Older
1 2
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

3
using System;
4 5 6 7
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
8
using Microsoft.CodeAnalysis.Diagnostics.Log;
9 10 11
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Text;
12
using Roslyn.Utilities;
13 14 15 16 17

namespace Microsoft.CodeAnalysis.Diagnostics
{
    internal abstract class BaseDiagnosticIncrementalAnalyzer : IIncrementalAnalyzer
    {
18
        protected BaseDiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, Workspace workspace, HostAnalyzerManager hostAnalyzerManager, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource)
19
        {
20 21
            Contract.ThrowIfNull(owner);

22
            this.Owner = owner;
23
            this.Workspace = workspace;
24
            this.HostAnalyzerManager = hostAnalyzerManager;
M
Manish Vasani 已提交
25
            this.HostDiagnosticUpdateSource = hostDiagnosticUpdateSource;
26
            this.DiagnosticLogAggregator = new DiagnosticLogAggregator(owner);
27 28
        }

29
        #region IIncrementalAnalyzer
J
John Hamby 已提交
30 31 32
        /// <summary>
        /// Analyze a single document such that local diagnostics for that document become available,
        /// prioritizing analyzing this document over analyzing the rest of the project.
33
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> for each
J
John Hamby 已提交
34 35
        /// unique group of diagnostics, where a group is identified by analysis classification (syntax/semantics), document, and analyzer.
        /// </summary>
J
John Hamby 已提交
36 37
        /// <param name="document">The document to analyze.</param>
        /// <param name="bodyOpt">If present, indicates a portion (e.g. a method body) of the document to analyze.</param>
J
John Hamby 已提交
38 39
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
40
        public abstract Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken);
J
John Hamby 已提交
41 42
        /// <summary>
        /// Analyze a single project such that diagnostics for the entire project become available.
43
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> for each
J
John Hamby 已提交
44 45
        /// unique group of diagnostics, where a group is identified by analysis classification (project), project, and analyzer.
        /// </summary>
J
John Hamby 已提交
46
        /// <param name="project">The project to analyze.</param>
47
        /// <param name="semanticsChanged">Indicates a change to the declarative semantics of the project.</param>
J
John Hamby 已提交
48 49
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
50
        public abstract Task AnalyzeProjectAsync(Project project, bool semanticsChanged, CancellationToken cancellationToken);
J
John Hamby 已提交
51 52
        /// <summary>
        /// Apply syntax tree actions (that have not already been applied) to a document.
53
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> for each
J
John Hamby 已提交
54 55
        /// unique group of diagnostics, where a group is identified by analysis classification (syntax), document, and analyzer.
        /// </summary>
J
John Hamby 已提交
56
        /// <param name="document">The document to analyze.</param>
J
John Hamby 已提交
57 58
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
59
        public abstract Task AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken);
J
John Hamby 已提交
60 61 62
        /// <summary>
        /// Respond to a document being opened for editing in the host.
        /// </summary>
J
John Hamby 已提交
63
        /// <param name="document">The opened document.</param>
J
John Hamby 已提交
64 65
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
66
        public abstract Task DocumentOpenAsync(Document document, CancellationToken cancellationToken);
J
John Hamby 已提交
67
        /// <summary>
68 69 70 71 72 73 74 75 76 77
        /// Respond to a document being closed in the host.
        /// </summary>
        /// <param name="document">The closed document.</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public abstract Task DocumentCloseAsync(Document document, CancellationToken cancellationToken);
        /// <summary>
        /// Flush cached diagnostics produced by a prior analysis of a document. 
        /// This will be called in a case where we need to re-analyze a document without textual, syntactic or semantic change of a solution.
        /// For example, engine or analyzer's option changes.
J
John Hamby 已提交
78
        /// </summary>
J
John Hamby 已提交
79
        /// <param name="document">The document whose diagnostics are to be flushed.</param>
J
John Hamby 已提交
80 81
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
82
        public abstract Task DocumentResetAsync(Document document, CancellationToken cancellationToken);
J
John Hamby 已提交
83
        /// <summary>
84
        /// ???
J
John Hamby 已提交
85
        /// </summary>
86
        /// <param name="solution">The solution.</param>
J
John Hamby 已提交
87 88
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
89
        public abstract Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken);
J
John Hamby 已提交
90 91 92
        /// <summary>
        /// Flush diagnostics produced by a prior analysis of a document,
        /// and suppress future analysis of the document.
93
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> with an empty collection.
J
John Hamby 已提交
94 95
        /// </summary>
        /// <param name="documentId"></param>
96
        public abstract void RemoveDocument(DocumentId documentId);
J
John Hamby 已提交
97 98 99
        /// <summary>
        /// Flush diagnostics produced by a prior analysis of a project,
        /// and suppress future analysis of the project.
100
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> with an empty collection.
J
John Hamby 已提交
101 102
        /// </summary>
        /// <param name="projectId"></param>
103 104 105 106
        public abstract void RemoveProject(ProjectId projectId);
        #endregion

        #region delegating methods from diagnostic analyzer service to each implementation of the engine
J
John Hamby 已提交
107 108 109 110
        /// <summary>
        /// Get previously-computed (and potentially stale) diagnostics associated with a particular combination of
        /// analysis classification (syntax/semantics/project), document/project, and analyzer.
        /// </summary>
J
John Hamby 已提交
111
        /// <param name="solution">The solution.</param>
112
        /// <param name="id">Matched against values supplied in a <see cref="DiagnosticsUpdatedArgs"/> to <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/>.</param>
113
        /// <param name="includeSuppressedDiagnostics">Flag indicating whether diagnostics with source suppressions (pragma/SuppressMessageAttribute) should be included.</param>
J
John Hamby 已提交
114 115
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
116
        public abstract Task<ImmutableArray<DiagnosticData>> GetSpecificCachedDiagnosticsAsync(Solution solution, object id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken));
J
John Hamby 已提交
117 118 119
        /// <summary>
        /// Get previously-computed (and potentially stale) diagnostics associated with a particular document, project, or solution.
        /// </summary>
J
John Hamby 已提交
120 121 122
        /// <param name="solution">The solution. If projectId and documentId are both null, returned diagnostics are for the entire solution.</param>
        /// <param name="projectId">If projectId is non null and documentId is null, returned diagnostics are for that project only.</param>
        /// <param name="documentId">If documentId is non null, returned diagnostics are for that document only.</param>
123
        /// <param name="includeSuppressedDiagnostics">Flag indicating whether diagnostics with source suppressions (pragma/SuppressMessageAttribute) should be included.</param>
J
John Hamby 已提交
124 125
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
126
        public abstract Task<ImmutableArray<DiagnosticData>> GetCachedDiagnosticsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken));
J
John Hamby 已提交
127 128 129 130
        /// <summary>
        /// Get diagnostics associated with a particular combination of
        /// analysis classification (syntax/semantics/project), document/project, and analyzer.
        /// </summary>
J
John Hamby 已提交
131
        /// <param name="solution">The solution.</param>
132
        /// <param name="id">Matched against values supplied in a <see cref="DiagnosticsUpdatedArgs"/> to <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/>.</param>
133
        /// <param name="includeSuppressedDiagnostics">Flag indicating whether diagnostics with source suppressions (pragma/SuppressMessageAttribute) should be included.</param>
J
John Hamby 已提交
134 135
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
136
        public abstract Task<ImmutableArray<DiagnosticData>> GetSpecificDiagnosticsAsync(Solution solution, object id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken));
J
John Hamby 已提交
137 138 139
        /// <summary>
        /// Get diagnostics associated with a particular document, project, or solution.
        /// </summary>
J
John Hamby 已提交
140 141 142
        /// <param name="solution">The solution. If projectId and documentId are both null, returned diagnostics are for the entire solution.</param>
        /// <param name="projectId">If projectId is non null and documentId is null, returned diagnostics are for that project only.</param>
        /// <param name="documentId">If documentId is non null, returned diagnostics are for that document only.</param>
143
        /// <param name="includeSuppressedDiagnostics">Flag indicating whether diagnostics with source suppressions (pragma/SuppressMessageAttribute) should be included.</param>
J
John Hamby 已提交
144 145
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
146
        public abstract Task<ImmutableArray<DiagnosticData>> GetDiagnosticsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken));
J
John Hamby 已提交
147 148 149
        /// <summary>
        /// Get diagnostics matching one of a set of diagnostic IDs associated with a particular document, project, or solution.
        /// </summary>
J
John Hamby 已提交
150 151 152 153
        /// <param name="solution">The solution. If projectId and documentId are both null, returned diagnostics are for the entire solution.</param>
        /// <param name="projectId">If projectId is non null and documentId is null, returned diagnostics are for that project only.</param>
        /// <param name="documentId">If documentId is non null, returned diagnostics are for that document only.</param>
        /// <param name="diagnosticIds">The diagnostic IDs to match.</param>
154
        /// <param name="includeSuppressedDiagnostics">Flag indicating whether diagnostics with source suppressions (pragma/SuppressMessageAttribute) should be included.</param>
J
John Hamby 已提交
155 156
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
157
        public abstract Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId = null, DocumentId documentId = null, ImmutableHashSet<string> diagnosticIds = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken));
J
John Hamby 已提交
158 159 160
        /// <summary>
        /// Get diagnostics matching one of a set of diagnostic IDs that are not associated with a particular document.
        /// </summary>
J
John Hamby 已提交
161 162 163
        /// <param name="solution">The solution. If projectId is null, returned diagnostics are for the entire solution.</param>
        /// <param name="projectId">If projectId is non null, returned diagnostics are for that project only.</param>
        /// <param name="diagnosticIds">The diagnostic IDs to match.</param>
164
        /// <param name="includeSuppressedDiagnostics">Flag indicating whether diagnostics with source suppressions (pragma/SuppressMessageAttribute) should be included.</param>
J
John Hamby 已提交
165 166
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
167
        public abstract Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId = null, ImmutableHashSet<string> diagnosticIds = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken));
J
John Hamby 已提交
168 169 170
        /// <summary>
        /// Add diagnostics local to a span to a list of diagnostics.
        /// </summary>
J
John Hamby 已提交
171 172 173
        /// <param name="document">The document containing the span.</param>
        /// <param name="range">The span for which to produce diagnostics.</param>
        /// <param name="diagnostics">The list of diagnostics to be augmented.</param>
174
        /// <param name="includeSuppressedDiagnostics">Flag indicating whether diagnostics with source suppressions (pragma/SuppressMessageAttribute) should be included.</param>
J
John Hamby 已提交
175 176
        /// <param name="cancellationToken"></param>
        /// <returns>True if the set of results is complete, false if getting a complete set requires running per-document actions.</returns>
177
        public abstract Task<bool> TryAppendDiagnosticsForSpanAsync(Document document, TextSpan range, List<DiagnosticData> diagnostics, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken));
J
John Hamby 已提交
178 179 180
        /// <summary>
        /// Get diagnostics local to a span.
        /// </summary>
J
John Hamby 已提交
181 182
        /// <param name="document">The document containing the span.</param>
        /// <param name="range">The span for which to produce diagnostics.</param>
183
        /// <param name="includeSuppressedDiagnostics">Flag indicating whether diagnostics with source suppressions (pragma/SuppressMessageAttribute) should be included.</param>
J
John Hamby 已提交
184 185
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
186
        public abstract Task<IEnumerable<DiagnosticData>> GetDiagnosticsForSpanAsync(Document document, TextSpan range, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default(CancellationToken));
187 188 189 190 191

        /// <summary>
        /// True if given project has any diagnostics
        /// </summary>
        public abstract bool ContainsDiagnostics(Workspace workspace, ProjectId projectId);
192 193
        #endregion

194 195 196 197 198 199 200 201 202
        #region build error synchronization
        /// <summary>
        /// Callback from build listener.
        /// 
        /// Given diagnostics are errors host got from explicit build.
        /// It is up to each incremental analyzer how they will merge this information with live diagnostic info.
        /// 
        /// this API doesn't have cancellationToken since it can't be cancelled.
        /// 
C
Charles Stoner 已提交
203
        /// given diagnostics are project wide diagnostics that doesn't contain a source location.
204
        /// </summary>
205
        public abstract Task SynchronizeWithBuildAsync(DiagnosticAnalyzerService.BatchUpdateToken token, Project project, ImmutableArray<DiagnosticData> diagnostics);
206 207 208 209 210 211 212 213 214 215 216

        /// <summary>
        /// Callback from build listener.
        /// 
        /// Given diagnostics are errors host got from explicit build.
        /// It is up to each incremental analyzer how they will merge this information with live diagnostic info
        /// 
        /// this API doesn't have cancellationToken since it can't be cancelled.
        /// 
        /// given diagnostics are ones that has a source location.
        /// </summary>
217
        public abstract Task SynchronizeWithBuildAsync(DiagnosticAnalyzerService.BatchUpdateToken token, Document document, ImmutableArray<DiagnosticData> diagnostics);
218 219
        #endregion

220 221 222 223 224 225 226 227 228 229
        internal DiagnosticAnalyzerService Owner { get; }
        internal Workspace Workspace { get; }
        internal AbstractHostDiagnosticUpdateSource HostDiagnosticUpdateSource { get; }
        internal HostAnalyzerManager HostAnalyzerManager { get; }
        internal DiagnosticLogAggregator DiagnosticLogAggregator { get; private set; }

        protected void ResetDiagnosticLogAggregator()
        {
            DiagnosticLogAggregator = new DiagnosticLogAggregator(Owner);
        }
230

231 232
        public virtual bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
        {
233
            return false;
234 235 236 237 238
        }

        public virtual void LogAnalyzerCountSummary()
        {
        }
239 240

        // internal for testing purposes.
M
mavasani 已提交
241
        internal Action<Exception, DiagnosticAnalyzer, Diagnostic> GetOnAnalyzerException(ProjectId projectId)
242
        {
243
            return Owner.GetOnAnalyzerException(projectId, DiagnosticLogAggregator);
244
        }
245 246
    }
}