BaseDiagnosticIncrementalAnalyzer.cs 17.0 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>
38
        /// <param name="reasons">The reason(s) this analysis was triggered.</param>
J
John Hamby 已提交
39 40
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
41
        public abstract Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken);
J
John Hamby 已提交
42 43
        /// <summary>
        /// Analyze a single project such that diagnostics for the entire project become available.
44
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> for each
J
John Hamby 已提交
45 46
        /// unique group of diagnostics, where a group is identified by analysis classification (project), project, and analyzer.
        /// </summary>
J
John Hamby 已提交
47
        /// <param name="project">The project to analyze.</param>
48
        /// <param name="semanticsChanged">Indicates a change to the declarative semantics of the project.</param>
49
        /// <param name="reasons">The reason(s) this analysis was triggered.</param>
J
John Hamby 已提交
50 51
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
52
        public abstract Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken);
J
John Hamby 已提交
53 54
        /// <summary>
        /// Apply syntax tree actions (that have not already been applied) to a document.
55
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> for each
J
John Hamby 已提交
56 57
        /// unique group of diagnostics, where a group is identified by analysis classification (syntax), document, and analyzer.
        /// </summary>
J
John Hamby 已提交
58
        /// <param name="document">The document to analyze.</param>
59
        /// <param name="reasons">The reason(s) this analysis was triggered.</param>
J
John Hamby 已提交
60 61
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
62
        public abstract Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken);
J
John Hamby 已提交
63 64 65
        /// <summary>
        /// Respond to a document being opened for editing in the host.
        /// </summary>
J
John Hamby 已提交
66
        /// <param name="document">The opened document.</param>
J
John Hamby 已提交
67 68
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
69
        public abstract Task DocumentOpenAsync(Document document, CancellationToken cancellationToken);
J
John Hamby 已提交
70
        /// <summary>
71 72 73 74 75 76 77 78 79 80
        /// 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 已提交
81
        /// </summary>
J
John Hamby 已提交
82
        /// <param name="document">The document whose diagnostics are to be flushed.</param>
J
John Hamby 已提交
83 84
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
85
        public abstract Task DocumentResetAsync(Document document, CancellationToken cancellationToken);
J
John Hamby 已提交
86
        /// <summary>
87
        /// ???
J
John Hamby 已提交
88
        /// </summary>
89
        /// <param name="solution">The solution.</param>
J
John Hamby 已提交
90 91
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
92
        public abstract Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken);
J
John Hamby 已提交
93 94 95
        /// <summary>
        /// Flush diagnostics produced by a prior analysis of a document,
        /// and suppress future analysis of the document.
96
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> with an empty collection.
J
John Hamby 已提交
97 98
        /// </summary>
        /// <param name="documentId"></param>
99
        public abstract void RemoveDocument(DocumentId documentId);
J
John Hamby 已提交
100 101 102
        /// <summary>
        /// Flush diagnostics produced by a prior analysis of a project,
        /// and suppress future analysis of the project.
103
        /// Calls <see cref="DiagnosticAnalyzerService.RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs)"/> with an empty collection.
J
John Hamby 已提交
104 105
        /// </summary>
        /// <param name="projectId"></param>
106 107 108 109
        public abstract void RemoveProject(ProjectId projectId);
        #endregion

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

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

197 198 199 200 201 202 203 204 205
        #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.
        /// </summary>
H
Heejae Chang 已提交
206
        public abstract Task SynchronizeWithBuildAsync(Workspace workspace, ImmutableDictionary<ProjectId, ImmutableArray<DiagnosticData>> diagnostics);
207 208
        #endregion

209 210 211 212 213 214 215 216 217 218
        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);
        }
219

220 221
        public virtual bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
        {
222
            return false;
223 224 225 226 227
        }

        public virtual void LogAnalyzerCountSummary()
        {
        }
228 229

        // internal for testing purposes.
M
mavasani 已提交
230
        internal Action<Exception, DiagnosticAnalyzer, Diagnostic> GetOnAnalyzerException(ProjectId projectId)
231
        {
232
            return Owner.GetOnAnalyzerException(projectId, DiagnosticLogAggregator);
233
        }
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257

        protected static ReportDiagnostic GetEffectiveSeverity(DiagnosticDescriptor descriptor, CompilationOptions options)
        {
            return options == null
                ? MapSeverityToReport(descriptor.DefaultSeverity)
                : descriptor.GetEffectiveSeverity(options);
        }

        protected static ReportDiagnostic MapSeverityToReport(DiagnosticSeverity severity)
        {
            switch (severity)
            {
                case DiagnosticSeverity.Hidden:
                    return ReportDiagnostic.Hidden;
                case DiagnosticSeverity.Info:
                    return ReportDiagnostic.Info;
                case DiagnosticSeverity.Warning:
                    return ReportDiagnostic.Warn;
                case DiagnosticSeverity.Error:
                    return ReportDiagnostic.Error;
                default:
                    throw ExceptionUtilities.Unreachable;
            }
        }
258 259
    }
}