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

using System;
4
using System.Collections.Concurrent;
5 6 7 8
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
V
Vladimir Reshetnikov 已提交
9
using System.Runtime.InteropServices;
10 11
using System.Threading;
using System.Threading.Tasks;
12
using Microsoft.CodeAnalysis.Diagnostics.Telemetry;
13
using Microsoft.CodeAnalysis.Operations;
T
Tomas Matousek 已提交
14
using Microsoft.CodeAnalysis.PooledObjects;
15
using Microsoft.CodeAnalysis.Text;
16
using Roslyn.Utilities;
17 18 19 20 21 22 23

namespace Microsoft.CodeAnalysis.Diagnostics
{
    /// <summary>
    /// Driver to execute diagnostic analyzers for a given compilation.
    /// It uses a <see cref="AsyncQueue{TElement}"/> of <see cref="CompilationEvent"/>s to drive its analysis.
    /// </summary>
24
    internal abstract partial class AnalyzerDriver : IDisposable
25 26 27 28
    {
        // Protect against vicious analyzers that provide large values for SymbolKind.
        private const int MaxSymbolKind = 100;

29 30 31
        // Cache delegates for static methods
        private static readonly Func<DiagnosticAnalyzer, bool> s_IsCompilerAnalyzerFunc = IsCompilerAnalyzer;

32
        protected readonly ImmutableArray<DiagnosticAnalyzer> analyzers;
M
Manish Vasani 已提交
33
        protected readonly AnalyzerManager analyzerManager;
34
        private readonly Func<SyntaxTree, CancellationToken, bool> _isGeneratedCode;
35

36 37
        // Lazy fields
        private CancellationTokenRegistration _queueRegistration;
M
Manish Vasani 已提交
38
        protected AnalyzerExecutor analyzerExecutor;
39
        protected CompilationData compilationData;
M
Manish Vasani 已提交
40
        internal AnalyzerActions analyzerActions;
41 42
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>> _symbolActionsByKind;
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<SemanticModelAnalyzerAction>> _semanticModelActionsMap;
43
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<SyntaxTreeAnalyzerAction>> _syntaxTreeActionsMap;
44 45 46 47
        // Compilation actions and compilation end actions have separate maps so that it is easy to
        // execute the compilation actions before the compilation end actions.
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>> _compilationActionsMap;
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>> _compilationEndActionsMap;
48

49 50 51 52 53 54 55
        /// <summary>
        /// Default analysis mode for generated code.
        /// </summary>
        /// <remarks>
        /// This mode should always guarantee that analyzer action callbacks are enabled for generated code, i.e. <see cref="GeneratedCodeAnalysisFlags.Analyze"/> is set.
        /// However, the default diagnostic reporting mode is liable to change in future.
        /// </remarks>
56
        internal const GeneratedCodeAnalysisFlags DefaultGeneratedCodeAnalysisFlags = GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics;
57

58 59 60 61 62
        /// <summary>
        /// Map from non-concurrent analyzers to the gate guarding callback into the analyzer. 
        /// </summary>
        private ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim> _analyzerGateMap = ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim>.Empty;

63 64 65 66 67
        /// <summary>
        /// Map from analyzers to their <see cref="GeneratedCodeAnalysisFlags"/> setting. 
        /// </summary>
        private ImmutableDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags> _generatedCodeAnalysisFlagsMap;

68 69 70 71 72
        /// <summary>
        /// True if all analyzers need to analyze and report diagnostics in generated code - we can assume all code to be non-generated code.
        /// </summary>
        private bool _treatAllCodeAsNonGeneratedCode;

73 74 75 76 77 78
        /// <summary>
        /// True if no analyzer needs generated code analysis - we can skip all analysis on a generated code symbol/tree.
        /// </summary>
        private bool _doNotAnalyzeGeneratedCode;

        /// <summary>
79
        /// Lazily populated dictionary indicating whether a source file is a generated code file or not - we populate it lazily to avoid realizing all syntax trees in the compilation upfront.
80
        /// </summary>
81
        private ConcurrentDictionary<SyntaxTree, bool> _lazyGeneratedCodeFilesMap;
82

83 84 85 86 87
        /// <summary>
        /// Lazily populated dictionary from tree to declared symbols with GeneratedCodeAttribute.
        /// </summary>
        private Dictionary<SyntaxTree, ImmutableHashSet<ISymbol>> _lazyGeneratedCodeSymbolsMap;

88 89 90
        /// <summary>
        /// Lazily populated dictionary indicating whether a source file has any hidden regions - we populate it lazily to avoid realizing all syntax trees in the compilation upfront.
        /// </summary>
91
        private ConcurrentDictionary<SyntaxTree, bool> _lazyTreesWithHiddenRegionsMap;
92

93 94 95 96 97
        /// <summary>
        /// Symbol for <see cref="System.CodeDom.Compiler.GeneratedCodeAttribute"/>.
        /// </summary>
        private INamedTypeSymbol _generatedCodeAttribute;

98 99 100 101 102 103 104 105 106 107 108
        /// <summary>
        /// Driver task which initializes all analyzers.
        /// This task is initialized and executed only once at start of analysis.
        /// </summary>
        private Task _initializeTask;

        /// <summary>
        /// Flag to indicate if the <see cref="_initializeTask"/> was successfully started.
        /// </summary>
        private bool _initializeSucceeded = false;

109 110 111 112 113
        /// <summary>
        /// Primary driver task which processes all <see cref="CompilationEventQueue"/> events, runs analyzer actions and signals completion of <see cref="DiagnosticQueue"/> at the end.
        /// </summary>
        private Task _primaryTask;

114 115 116 117 118
        /// <summary>
        /// Number of worker tasks processing compilation events and executing analyzer actions.
        /// </summary>
        private readonly int _workerCount = Environment.ProcessorCount;

119
        /// <summary>
120
        /// Events queue for analyzer execution.
121
        /// </summary>
122
        public AsyncQueue<CompilationEvent> CompilationEventQueue { get; private set; }
123 124

        /// <summary>
125
        /// <see cref="DiagnosticQueue"/> that is fed the diagnostics as they are computed.
126
        /// </summary>
127
        public DiagnosticQueue DiagnosticQueue { get; private set; }
128 129

        /// <summary>
130
        /// Create an analyzer driver.
131
        /// </summary>
132 133
        /// <param name="analyzers">The set of analyzers to include in the analysis</param>
        /// <param name="analyzerManager">AnalyzerManager to manage analyzers for analyzer host's lifetime.</param>
134 135
        /// <param name="isComment">Delegate to identify if the given trivia is a comment.</param>
        protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerManager analyzerManager, Func<SyntaxTrivia, bool> isComment)
136 137 138
        {
            this.analyzers = analyzers;
            this.analyzerManager = analyzerManager;
139
            _isGeneratedCode = (tree, ct) => GeneratedCodeUtilities.IsGeneratedCode(tree, isComment, ct);
140 141 142 143 144 145 146
        }

        /// <summary>
        /// Initializes the <see cref="analyzerActions"/> and related actions maps for the analyzer driver.
        /// It kicks off the <see cref="WhenInitializedTask"/> task for initialization.
        /// Note: This method must be invoked exactly once on the driver.
        /// </summary>
147
        private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagnosticQueue, CompilationData compilationData, CancellationToken cancellationToken)
148 149 150
        {
            try
            {
151
                Debug.Assert(_initializeTask == null);
152

M
Manish Vasani 已提交
153
                this.analyzerExecutor = analyzerExecutor;
154
                this.compilationData = compilationData;
155
                this.DiagnosticQueue = diagnosticQueue;
156 157

                // Compute the set of effective actions based on suppression, and running the initial analyzers
158
                _initializeTask = Task.Run(async () =>
M
Manish Vasani 已提交
159
                {
160 161 162 163 164 165
                    var unsuppressedAnalyzers = GetUnsuppressedAnalyzers(analyzers, analyzerManager, analyzerExecutor);
                    this.analyzerActions = await GetAnalyzerActionsAsync(unsuppressedAnalyzers, analyzerManager, analyzerExecutor).ConfigureAwait(false);
                    _analyzerGateMap = await GetAnalyzerGateMapAsync(unsuppressedAnalyzers, analyzerManager, analyzerExecutor).ConfigureAwait(false);

                    _generatedCodeAnalysisFlagsMap = await GetGeneratedCodeAnalysisFlagsAsync(unsuppressedAnalyzers, analyzerManager, analyzerExecutor).ConfigureAwait(false);
                    _doNotAnalyzeGeneratedCode = ShouldSkipAnalysisOnGeneratedCode(unsuppressedAnalyzers);
166
                    _treatAllCodeAsNonGeneratedCode = ShouldTreatAllCodeAsNonGeneratedCode(unsuppressedAnalyzers, _generatedCodeAnalysisFlagsMap);
167
                    _lazyGeneratedCodeFilesMap = _treatAllCodeAsNonGeneratedCode ? null : new ConcurrentDictionary<SyntaxTree, bool>();
168
                    _lazyGeneratedCodeSymbolsMap = _treatAllCodeAsNonGeneratedCode ? null : new Dictionary<SyntaxTree, ImmutableHashSet<ISymbol>>();
169
                    _lazyTreesWithHiddenRegionsMap = _treatAllCodeAsNonGeneratedCode ? null : new ConcurrentDictionary<SyntaxTree, bool>();
170
                    _generatedCodeAttribute = analyzerExecutor.Compilation?.GetTypeByMetadataName("System.CodeDom.Compiler.GeneratedCodeAttribute");
171

M
Manish Vasani 已提交
172 173
                    _symbolActionsByKind = MakeSymbolActionsByKind();
                    _semanticModelActionsMap = MakeSemanticModelActionsByAnalyzer();
174
                    _syntaxTreeActionsMap = MakeSyntaxTreeActionsByAnalyzer();
175 176
                    _compilationActionsMap = MakeCompilationActionsByAnalyzer(this.analyzerActions.CompilationActions);
                    _compilationEndActionsMap = MakeCompilationActionsByAnalyzer(this.analyzerActions.CompilationEndActions);
177
                }, cancellationToken);
178

179
                // create the primary driver task. 
180
                cancellationToken.ThrowIfCancellationRequested();
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206

                _initializeSucceeded = true;
            }
            finally
            {
                if (_initializeTask == null)
                {
                    // Set initializeTask to be a cancelled task.
                    var tcs = new TaskCompletionSource<int>();
                    tcs.SetCanceled();
                    _initializeTask = tcs.Task;

                    // Set primaryTask to be a cancelled task.
                    tcs = new TaskCompletionSource<int>();
                    tcs.SetCanceled();
                    _primaryTask = tcs.Task;

                    // Try to set the DiagnosticQueue to be complete.
                    this.DiagnosticQueue.TryComplete();
                }
            }
        }

        internal void Initialize(
           Compilation compilation,
           CompilationWithAnalyzersOptions analysisOptions,
207
           CompilationData compilationData,
208 209 210 211 212 213 214
           bool categorizeDiagnostics,
           CancellationToken cancellationToken)
        {
            Debug.Assert(_initializeTask == null);

            var diagnosticQueue = DiagnosticQueue.Create(categorizeDiagnostics);

215 216 217 218
            Action<Diagnostic> addNotCategorizedDiagnosticOpt = null;
            Action<Diagnostic, DiagnosticAnalyzer, bool> addCategorizedLocalDiagnosticOpt = null;
            Action<Diagnostic, DiagnosticAnalyzer> addCategorizedNonLocalDiagnosticOpt = null;
            if (categorizeDiagnostics)
219
            {
220 221
                addCategorizedLocalDiagnosticOpt = GetDiagnosticSink(diagnosticQueue.EnqueueLocal, compilation);
                addCategorizedNonLocalDiagnosticOpt = GetDiagnosticSink(diagnosticQueue.EnqueueNonLocal, compilation);
222 223 224
            }
            else
            {
225
                addNotCategorizedDiagnosticOpt = GetDiagnosticSink(diagnosticQueue.Enqueue, compilation);
226 227
            }

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
            // Wrap onAnalyzerException to pass in filtered diagnostic.
            Action<Exception, DiagnosticAnalyzer, Diagnostic> newOnAnalyzerException = (ex, analyzer, diagnostic) =>
            {
                var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation);
                if (filteredDiagnostic != null)
                {
                    if (analysisOptions.OnAnalyzerException != null)
                    {
                        analysisOptions.OnAnalyzerException(ex, analyzer, filteredDiagnostic);
                    }
                    else if (categorizeDiagnostics)
                    {
                        addCategorizedNonLocalDiagnosticOpt(filteredDiagnostic, analyzer);
                    }
                    else
                    {
                        addNotCategorizedDiagnosticOpt(filteredDiagnostic);
                    }
                }
            };

249 250 251 252 253 254 255
            if (analysisOptions.LogAnalyzerExecutionTime)
            {
                // If we are reporting detailed analyzer performance numbers, then do a dummy invocation of Compilation.GetTypeByMetadataName API upfront.
                // This API seems to cause a severe hit for the first analyzer invoking it and hence introduces lot of noise in the computed analyzer execution times.
                var unused = compilation.GetTypeByMetadataName("System.Object");
            }

256 257
            var analyzerExecutor = AnalyzerExecutor.Create(
                compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addNotCategorizedDiagnosticOpt, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter,
M
Manish Vasani 已提交
258
                IsCompilerAnalyzer, analyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedOrHiddenCodeLocation, GetAnalyzerGate,
259
                analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnosticOpt, addCategorizedNonLocalDiagnosticOpt, cancellationToken);
260

261
            Initialize(analyzerExecutor, diagnosticQueue, compilationData, cancellationToken);
262 263
        }

264 265 266 267 268 269 270 271 272 273 274 275 276 277
        private SemaphoreSlim GetAnalyzerGate(DiagnosticAnalyzer analyzer)
        {
            SemaphoreSlim gate;
            if (_analyzerGateMap.TryGetValue(analyzer, out gate))
            {
                // Non-concurrent analyzer, needs all the callbacks guarded by a gate.
                Debug.Assert(gate != null);
                return gate;
            }

            // Concurrent analyzer.
            return null;
        }

278 279 280 281 282 283 284 285 286 287 288 289 290
        private bool ShouldSkipAnalysisOnGeneratedCode(ImmutableArray<DiagnosticAnalyzer> analyzers)
        {
            foreach (var analyzer in analyzers)
            {
                if (!ShouldSkipAnalysisOnGeneratedCode(analyzer))
                {
                    return false;
                }
            }

            return true;
        }

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
        /// <summary>
        /// Returns true if all analyzers need to analyze and report diagnostics in generated code - we can assume all code to be non-generated code.
        /// </summary>
        private bool ShouldTreatAllCodeAsNonGeneratedCode(ImmutableArray<DiagnosticAnalyzer> analyzers, ImmutableDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags> generatedCodeAnalysisFlagsMap)
        {
            foreach (var analyzer in analyzers)
            {
                var flags = generatedCodeAnalysisFlagsMap[analyzer];
                var analyze = (flags & GeneratedCodeAnalysisFlags.Analyze) != 0;
                var report = (flags & GeneratedCodeAnalysisFlags.ReportDiagnostics) != 0;
                if (!analyze || !report)
                {
                    return false;
                }
            }

            return true;
        }

310 311
        private bool ShouldSkipAnalysisOnGeneratedCode(DiagnosticAnalyzer analyzer)
        {
312 313 314 315 316
            if (_treatAllCodeAsNonGeneratedCode)
            {
                return false;
            }

317 318 319 320 321 322
            var mode = _generatedCodeAnalysisFlagsMap[analyzer];
            return (mode & GeneratedCodeAnalysisFlags.Analyze) == 0;
        }

        private bool ShouldSuppressGeneratedCodeDiagnostic(Diagnostic diagnostic, DiagnosticAnalyzer analyzer, Compilation compilation, CancellationToken cancellationToken)
        {
323 324 325 326 327
            if (_treatAllCodeAsNonGeneratedCode)
            {
                return false;
            }

328 329 330 331 332
            var generatedCodeAnalysisFlags = _generatedCodeAnalysisFlagsMap[analyzer];
            var suppressInGeneratedCode = (generatedCodeAnalysisFlags & GeneratedCodeAnalysisFlags.ReportDiagnostics) == 0;
            return suppressInGeneratedCode && IsInGeneratedCode(diagnostic.Location, compilation, cancellationToken);
        }

333 334 335 336 337 338 339
        /// <summary>
        /// Attaches a pre-populated event queue to the driver and processes all events in the queue.
        /// </summary>
        /// <param name="eventQueue">Compilation events to analyze.</param>
        /// <param name="analysisScope">Scope of analysis.</param>
        /// <param name="analysisStateOpt">An optional object to track partial analysis state.</param>
        /// <param name="cancellationToken">Cancellation token to abort analysis.</param>
340
        /// <remarks>Driver must be initialized before invoking this method, i.e. <see cref="Initialize(AnalyzerExecutor, DiagnosticQueue, CompilationData, CancellationToken)"/> method must have been invoked and <see cref="WhenInitializedTask"/> must be non-null.</remarks>
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
        internal async Task AttachQueueAndProcessAllEventsAsync(AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
        {
            try
            {
                if (_initializeSucceeded)
                {
                    this.CompilationEventQueue = eventQueue;
                    _queueRegistration = default(CancellationTokenRegistration);

                    await ExecutePrimaryAnalysisTaskAsync(analysisScope, analysisStateOpt, usingPrePopulatedEventQueue: true, cancellationToken: cancellationToken).ConfigureAwait(false);

                    _primaryTask = Task.FromResult(true);
                }
            }
            finally
            {
                if (_primaryTask == null)
                {
                    // Set primaryTask to be a cancelled task.
                    var tcs = new TaskCompletionSource<int>();
                    tcs.SetCanceled();
                    _primaryTask = tcs.Task;
                }
            }
        }

        /// <summary>
        /// Attaches event queue to the driver and start processing all events pertaining to the given analysis scope.
        /// </summary>
        /// <param name="eventQueue">Compilation events to analyze.</param>
        /// <param name="analysisScope">Scope of analysis.</param>
        /// <param name="cancellationToken">Cancellation token to abort analysis.</param>
373
        /// <remarks>Driver must be initialized before invoking this method, i.e. <see cref="Initialize(AnalyzerExecutor, DiagnosticQueue, CompilationData, CancellationToken)"/> method must have been invoked and <see cref="WhenInitializedTask"/> must be non-null.</remarks>
374 375 376 377 378 379 380 381
        internal void AttachQueueAndStartProcessingEvents(AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken)
        {
            try
            {
                if (_initializeSucceeded)
                {
                    this.CompilationEventQueue = eventQueue;
                    _queueRegistration = cancellationToken.Register(() =>
382
                    {
383 384 385
                        this.CompilationEventQueue.TryComplete();
                        this.DiagnosticQueue.TryComplete();
                    });
386

387 388 389
                    _primaryTask = ExecutePrimaryAnalysisTaskAsync(analysisScope, analysisStateOpt: null, usingPrePopulatedEventQueue: false, cancellationToken: cancellationToken)
                        .ContinueWith(c => DiagnosticQueue.TryComplete(), cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
                }
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
            }
            finally
            {
                if (_primaryTask == null)
                {
                    // Set primaryTask to be a cancelled task.
                    var tcs = new TaskCompletionSource<int>();
                    tcs.SetCanceled();
                    _primaryTask = tcs.Task;

                    // Try to set the DiagnosticQueue to be complete.
                    this.DiagnosticQueue.TryComplete();
                }
            }
        }

406
        private async Task ExecutePrimaryAnalysisTaskAsync(AnalysisScope analysisScope, AnalysisState analysisStateOpt, bool usingPrePopulatedEventQueue, CancellationToken cancellationToken)
407
        {
408 409 410 411 412
            Debug.Assert(analysisScope != null);
            Debug.Assert(WhenInitializedTask != null);

            await WhenInitializedTask.ConfigureAwait(false);

413 414 415 416 417
            if (WhenInitializedTask.IsFaulted)
            {
                OnDriverException(WhenInitializedTask, this.analyzerExecutor, analysisScope.Analyzers);
            }
            else if (!WhenInitializedTask.IsCanceled)
418 419 420 421 422 423 424
            {
                this.analyzerExecutor = this.analyzerExecutor.WithCancellationToken(cancellationToken);

                await ProcessCompilationEventsAsync(analysisScope, analysisStateOpt, usingPrePopulatedEventQueue, cancellationToken).ConfigureAwait(false);
            }
        }

425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
        private static void OnDriverException(Task faultedTask, AnalyzerExecutor analyzerExecutor, ImmutableArray<DiagnosticAnalyzer> analyzers)
        {
            Debug.Assert(faultedTask.IsFaulted);

            var innerException = faultedTask.Exception?.InnerException;
            if (innerException == null || innerException is OperationCanceledException)
            {
                return;
            }

            var diagnostic = AnalyzerExecutor.CreateDriverExceptionDiagnostic(innerException);

            // Just pick the first analyzer from the scope for the onAnalyzerException callback.
            // The exception diagnostic's message and description will not include the analyzer, but explicitly state its a driver exception.
            var analyzer = analyzers[0];

            analyzerExecutor.OnAnalyzerException(innerException, analyzer, diagnostic);
        }

444
        private void ExecuteSyntaxTreeActions(AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
445 446 447 448 449 450 451 452
        {
            if (analysisScope.IsTreeAnalysis && !analysisScope.IsSyntaxOnlyTreeAnalysis)
            {
                // For partial analysis, only execute syntax tree actions if performing syntax analysis.
                return;
            }

            foreach (var tree in analysisScope.SyntaxTrees)
453
            {
454 455 456 457 458 459 460
                var isGeneratedCode = IsGeneratedCode(tree);
                if (isGeneratedCode && DoNotAnalyzeGeneratedCode)
                {
                    analysisStateOpt?.MarkSyntaxAnalysisComplete(tree, analysisScope.Analyzers);
                    continue;
                }

461
                foreach (var analyzer in analysisScope.Analyzers)
462
                {
463 464
                    cancellationToken.ThrowIfCancellationRequested();

465 466
                    ImmutableArray<SyntaxTreeAnalyzerAction> syntaxTreeActions;
                    if (_syntaxTreeActionsMap.TryGetValue(analyzer, out syntaxTreeActions))
467 468
                    {
                        // Execute actions for a given analyzer sequentially.
469
                        analyzerExecutor.TryExecuteSyntaxTreeActions(syntaxTreeActions, analyzer, tree, analysisScope, analysisStateOpt, isGeneratedCode);
470
                    }
471
                    else
472
                    {
473
                        analysisStateOpt?.MarkSyntaxAnalysisComplete(tree, analyzer);
474
                    }
475 476 477 478 479 480 481 482 483 484
                }
            }
        }

        /// <summary>
        /// Create an <see cref="AnalyzerDriver"/> and attach it to the given compilation. 
        /// </summary>
        /// <param name="compilation">The compilation to which the new driver should be attached.</param>
        /// <param name="analyzers">The set of analyzers to include in the analysis.</param>
        /// <param name="options">Options that are passed to analyzers.</param>
485
        /// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param>
M
Manish Vasani 已提交
486
        /// <param name="addExceptionDiagnostic">Delegate to add diagnostics generated for exceptions from third party analyzers.</param>
487
        /// <param name="reportAnalyzer">Report additional information related to analyzers, such as analyzer execution time.</param>
488 489 490 491 492 493 494
        /// <param name="newCompilation">The new compilation with the analyzer driver attached.</param>
        /// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param>
        /// <returns>A newly created analyzer driver</returns>
        /// <remarks>
        /// Note that since a compilation is immutable, the act of creating a driver and attaching it produces
        /// a new compilation. Any further actions on the compilation should use the new compilation.
        /// </remarks>
495
        public static AnalyzerDriver CreateAndAttachToCompilation(
496
            Compilation compilation,
497 498 499
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerOptions options,
            AnalyzerManager analyzerManager,
500 501
            Action<Diagnostic> addExceptionDiagnostic,
            bool reportAnalyzer,
502
            out Compilation newCompilation,
M
Manish Vasani 已提交
503
            CancellationToken cancellationToken)
504
        {
505
            Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException =
V
Vladimir Reshetnikov 已提交
506 507
                (ex, analyzer, diagnostic) => addExceptionDiagnostic?.Invoke(diagnostic);

508 509
            Func<Exception, bool> nullFilter = null;
            return CreateAndAttachToCompilation(compilation, analyzers, options, analyzerManager, onAnalyzerException, nullFilter, reportAnalyzer, out newCompilation, cancellationToken: cancellationToken);
510 511 512
        }

        // internal for testing purposes
513
        internal static AnalyzerDriver CreateAndAttachToCompilation(
M
Manish Vasani 已提交
514
            Compilation compilation,
515 516
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerOptions options,
M
Manish Vasani 已提交
517
            AnalyzerManager analyzerManager,
518
            Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException,
519
            Func<Exception, bool> analyzerExceptionFilter,
520
            bool reportAnalyzer,
521
            out Compilation newCompilation,
M
Manish Vasani 已提交
522
            CancellationToken cancellationToken)
523
        {
524
            AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, analyzerManager);
525
            newCompilation = compilation.WithEventQueue(new AsyncQueue<CompilationEvent>());
526

527
            var categorizeDiagnostics = false;
528
            var analysisOptions = new CompilationWithAnalyzersOptions(options, onAnalyzerException, analyzerExceptionFilter: analyzerExceptionFilter, concurrentAnalysis: true, logAnalyzerExecutionTime: reportAnalyzer, reportSuppressedDiagnostics: false);
529
            analyzerDriver.Initialize(newCompilation, analysisOptions, new CompilationData(newCompilation), categorizeDiagnostics, cancellationToken);
M
Manish Vasani 已提交
530

531 532
            var analysisScope = new AnalysisScope(newCompilation, analyzers, concurrentAnalysis: newCompilation.Options.ConcurrentBuild, categorizeDiagnostics: categorizeDiagnostics);
            analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue, analysisScope, cancellationToken: cancellationToken);
533 534 535 536 537 538 539 540
            return analyzerDriver;
        }

        /// <summary>
        /// Returns all diagnostics computed by the analyzers since the last time this was invoked.
        /// If <see cref="CompilationEventQueue"/> has been completed with all compilation events, then it waits for
        /// <see cref="WhenCompletedTask"/> task for the driver to finish processing all events and generate remaining analyzer diagnostics.
        /// </summary>
541
        public async Task<ImmutableArray<Diagnostic>> GetDiagnosticsAsync(Compilation compilation)
542 543 544 545 546
        {
            var allDiagnostics = DiagnosticBag.GetInstance();
            if (CompilationEventQueue.IsCompleted)
            {
                await this.WhenCompletedTask.ConfigureAwait(false);
547 548 549 550 551

                if (this.WhenCompletedTask.IsFaulted)
                {
                    OnDriverException(this.WhenCompletedTask, this.analyzerExecutor, this.analyzers);
                }
552 553
            }

554
            var suppressMessageState = compilationData.SuppressMessageAttributeState;
555
            var reportSuppressedDiagnostics = compilation.Options.ReportSuppressedDiagnostics;
556 557 558
            Diagnostic d;
            while (DiagnosticQueue.TryDequeue(out d))
            {
559
                d = suppressMessageState.ApplySourceSuppressions(d);
560
                if (reportSuppressedDiagnostics || !d.IsSuppressed)
561 562
                {
                    allDiagnostics.Add(d);
563
                }
564 565
            }

566 567
            return allDiagnostics.ToReadOnlyAndFree();
        }
568

569
        public ImmutableArray<Diagnostic> DequeueLocalDiagnostics(DiagnosticAnalyzer analyzer, bool syntax, Compilation compilation)
570
        {
571
            var diagnostics = syntax ? DiagnosticQueue.DequeueLocalSyntaxDiagnostics(analyzer) : DiagnosticQueue.DequeueLocalSemanticDiagnostics(analyzer);
572
            return FilterDiagnosticsSuppressedInSource(diagnostics, compilation, compilationData.SuppressMessageAttributeState);
573
        }
574

575
        public ImmutableArray<Diagnostic> DequeueNonLocalDiagnostics(DiagnosticAnalyzer analyzer, Compilation compilation)
576
        {
577
            var diagnostics = DiagnosticQueue.DequeueNonLocalDiagnostics(analyzer);
578
            return FilterDiagnosticsSuppressedInSource(diagnostics, compilation, compilationData.SuppressMessageAttributeState);
579 580
        }

581
        private ImmutableArray<Diagnostic> FilterDiagnosticsSuppressedInSource(ImmutableArray<Diagnostic> diagnostics, Compilation compilation, SuppressMessageAttributeState suppressMessageState)
582 583 584 585 586 587
        {
            if (diagnostics.IsEmpty)
            {
                return diagnostics;
            }

588
            var reportSuppressedDiagnostics = compilation.Options.ReportSuppressedDiagnostics;
589
            var builder = ImmutableArray.CreateBuilder<Diagnostic>();
590 591
            for (var i = 0; i < diagnostics.Length; i++)
            {
592 593 594 595 596
#if DEBUG
                // We should have ignored diagnostics with invalid locations and reported analyzer exception diagnostic for the same.
                DiagnosticAnalysisContextHelpers.VerifyDiagnosticLocationsInCompilation(diagnostics[i], compilation);
#endif

597
                var diagnostic = suppressMessageState.ApplySourceSuppressions(diagnostics[i]);
598
                if (!reportSuppressedDiagnostics && diagnostic.IsSuppressed)
599
                {
600 601
                    // Diagnostic suppressed in source.
                    continue;
602
                }
603 604

                builder.Add(diagnostic);
605 606
            }

607
            return builder.ToImmutable();
608 609
        }

610 611
        private bool IsInGeneratedCode(Location location, Compilation compilation, CancellationToken cancellationToken)
        {
612
            if (_treatAllCodeAsNonGeneratedCode || !location.IsInSource)
613 614 615 616
            {
                return false;
            }

617
            // Check if this is a generated code location.
618
            if (IsGeneratedOrHiddenCodeLocation(location.SourceTree, location.SourceSpan))
619 620 621 622
            {
                return true;
            }

623 624
            // Check if the file has generated code definitions (i.e. symbols with GeneratedCodeAttribute).
            if (_generatedCodeAttribute != null && _lazyGeneratedCodeSymbolsMap != null)
625
            {
626 627
                var generatedCodeSymbolsInTree = GetOrComputeGeneratedCodeSymbolsInTree(location.SourceTree, compilation, cancellationToken);
                if (generatedCodeSymbolsInTree.Count > 0)
628
                {
629 630 631 632
                    var model = compilation.GetSemanticModel(location.SourceTree);
                    for (var node = location.SourceTree.GetRoot(cancellationToken).FindNode(location.SourceSpan, getInnermostNodeForTie: true);
                        node != null;
                        node = node.Parent)
633
                    {
634 635 636 637
                        var declaredSymbols = model.GetDeclaredSymbolsForNode(node, cancellationToken);
                        Debug.Assert(declaredSymbols != null);

                        foreach (var symbol in declaredSymbols)
638
                        {
639 640 641 642
                            if (generatedCodeSymbolsInTree.Contains(symbol))
                            {
                                return true;
                            }
643 644 645 646 647 648 649 650
                        }
                    }
                }
            }

            return false;
        }

651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
        private ImmutableHashSet<ISymbol> GetOrComputeGeneratedCodeSymbolsInTree(SyntaxTree tree, Compilation compilation, CancellationToken cancellationToken)
        {
            Debug.Assert(_lazyGeneratedCodeSymbolsMap != null);

            ImmutableHashSet<ISymbol> generatedCodeSymbols;
            lock (_lazyGeneratedCodeSymbolsMap)
            {
                if (_lazyGeneratedCodeSymbolsMap.TryGetValue(tree, out generatedCodeSymbols))
                {
                    return generatedCodeSymbols;
                }
            }

            generatedCodeSymbols = ComputeGeneratedCodeSymbolsInTree(tree, compilation, cancellationToken);

            lock (_lazyGeneratedCodeSymbolsMap)
            {
                ImmutableHashSet<ISymbol> existingGeneratedCodeSymbols;
                if (!_lazyGeneratedCodeSymbolsMap.TryGetValue(tree, out existingGeneratedCodeSymbols))
                {
                    _lazyGeneratedCodeSymbolsMap.Add(tree, generatedCodeSymbols);
                }
                else
                {
                    Debug.Assert(existingGeneratedCodeSymbols.SetEquals(generatedCodeSymbols));
                }
            }

            return generatedCodeSymbols;
        }

        private ImmutableHashSet<ISymbol> ComputeGeneratedCodeSymbolsInTree(SyntaxTree tree, Compilation compilation, CancellationToken cancellationToken)
        {
            // PERF: Bail out early if file doesn't have "GeneratedCode" text.
            var text = tree.GetText(cancellationToken).ToString();
            if (!text.Contains("GeneratedCode"))
            {
                return ImmutableHashSet<ISymbol>.Empty;
            }

            var model = compilation.GetSemanticModel(tree);
            var root = tree.GetRoot(cancellationToken);
            var span = root.FullSpan;
            var builder = new List<DeclarationInfo>();
            model.ComputeDeclarationsInSpan(span, getSymbol: true, builder: builder, cancellationToken: cancellationToken);

            ImmutableHashSet<ISymbol>.Builder generatedSymbolsBuilderOpt = null;
            foreach (var declarationInfo in builder)
            {
                var symbol = declarationInfo.DeclaredSymbol;
                if (symbol != null &&
                    GeneratedCodeUtilities.IsGeneratedSymbolWithGeneratedCodeAttribute(symbol, _generatedCodeAttribute))
                {
                    generatedSymbolsBuilderOpt = generatedSymbolsBuilderOpt ?? ImmutableHashSet.CreateBuilder<ISymbol>();
                    generatedSymbolsBuilderOpt.Add(symbol);
                }
            }

            return generatedSymbolsBuilderOpt != null ? generatedSymbolsBuilderOpt.ToImmutable() : ImmutableHashSet<ISymbol>.Empty;
        }

712 713 714 715 716
        /// <summary>
        /// Return a task that completes when the driver is initialized.
        /// </summary>
        public Task WhenInitializedTask => _initializeTask;

717 718 719 720 721
        /// <summary>
        /// Return a task that completes when the driver is done producing diagnostics.
        /// </summary>
        public Task WhenCompletedTask => _primaryTask;

722
        internal ImmutableDictionary<DiagnosticAnalyzer, TimeSpan> AnalyzerExecutionTimes => analyzerExecutor.AnalyzerExecutionTimes;
723
        internal TimeSpan ResetAnalyzerExecutionTime(DiagnosticAnalyzer analyzer) => analyzerExecutor.ResetAnalyzerExecutionTime(analyzer);
724

725 726 727
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>> MakeSymbolActionsByKind()
        {
            var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<ImmutableArray<SymbolAnalyzerAction>>>();
M
Manish Vasani 已提交
728
            var actionsByAnalyzers = this.analyzerActions.SymbolActions.GroupBy(action => action.Analyzer);
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
            foreach (var analyzerAndActions in actionsByAnalyzers)
            {
                var actionsByKindBuilder = new List<ArrayBuilder<SymbolAnalyzerAction>>();
                foreach (var symbolAction in analyzerAndActions)
                {
                    var kinds = symbolAction.Kinds;
                    foreach (int kind in kinds.Distinct())
                    {
                        if (kind > MaxSymbolKind) continue; // protect against vicious analyzers
                        while (kind >= actionsByKindBuilder.Count)
                        {
                            actionsByKindBuilder.Add(ArrayBuilder<SymbolAnalyzerAction>.GetInstance());
                        }

                        actionsByKindBuilder[kind].Add(symbolAction);
                    }
                }

                var actionsByKind = actionsByKindBuilder.Select(a => a.ToImmutableAndFree()).ToImmutableArray();
                builder.Add(analyzerAndActions.Key, actionsByKind);
            }

            return builder.ToImmutable();
        }

754 755 756 757 758 759 760 761 762 763 764 765
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<SyntaxTreeAnalyzerAction>> MakeSyntaxTreeActionsByAnalyzer()
        {
            var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<SyntaxTreeAnalyzerAction>>();
            var actionsByAnalyzers = this.analyzerActions.SyntaxTreeActions.GroupBy(action => action.Analyzer);
            foreach (var analyzerAndActions in actionsByAnalyzers)
            {
                builder.Add(analyzerAndActions.Key, analyzerAndActions.ToImmutableArray());
            }

            return builder.ToImmutable();
        }

766 767 768
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<SemanticModelAnalyzerAction>> MakeSemanticModelActionsByAnalyzer()
        {
            var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<SemanticModelAnalyzerAction>>();
M
Manish Vasani 已提交
769
            var actionsByAnalyzers = this.analyzerActions.SemanticModelActions.GroupBy(action => action.Analyzer);
770 771 772 773 774 775 776 777
            foreach (var analyzerAndActions in actionsByAnalyzers)
            {
                builder.Add(analyzerAndActions.Key, analyzerAndActions.ToImmutableArray());
            }

            return builder.ToImmutable();
        }

778
        private static ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>> MakeCompilationActionsByAnalyzer(ImmutableArray<CompilationAnalyzerAction> compilationActions)
779
        {
780 781
            var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>>();
            var actionsByAnalyzers = compilationActions.GroupBy(action => action.Analyzer);
782 783 784 785 786 787 788 789
            foreach (var analyzerAndActions in actionsByAnalyzers)
            {
                builder.Add(analyzerAndActions.Key, analyzerAndActions.ToImmutableArray());
            }

            return builder.ToImmutable();
        }

790
        private async Task ProcessCompilationEventsAsync(AnalysisScope analysisScope, AnalysisState analysisStateOpt, bool prePopulatedEventQueue, CancellationToken cancellationToken)
791
        {
792
            try
793
            {
794
                CompilationCompletedEvent completedEvent = null;
795

796
                if (analysisScope.ConcurrentAnalysis)
797
                {
798 799
                    // Kick off worker tasks to process all compilation events (except the compilation end event) in parallel.
                    // Compilation end event must be processed after all other events.
800

801 802 803 804 805
                    var workerCount = prePopulatedEventQueue ? Math.Min(CompilationEventQueue.Count, _workerCount) : _workerCount;

                    var workerTasks = new Task<CompilationCompletedEvent>[workerCount];
                    for (int i = 0; i < workerCount; i++)
                    {
806 807
                        // Create separate worker tasks to process all compilation events - we do not want to process any events on the main thread.
                        workerTasks[i] = Task.Run(async () => await ProcessCompilationEventsCoreAsync(analysisScope, analysisStateOpt, prePopulatedEventQueue, cancellationToken).ConfigureAwait(false));
808
                    }
809

810
                    cancellationToken.ThrowIfCancellationRequested();
811

812
                    // Kick off tasks to execute syntax tree actions.
813
                    var syntaxTreeActionsTask = Task.Run(() => ExecuteSyntaxTreeActions(analysisScope, analysisStateOpt, cancellationToken));
814

815 816 817 818
                    // Wait for all worker threads to complete processing events.
                    await Task.WhenAll(workerTasks.Concat(syntaxTreeActionsTask)).ConfigureAwait(false);

                    for (int i = 0; i < workerCount; i++)
819
                    {
820 821 822 823 824
                        if (workerTasks[i].Status == TaskStatus.RanToCompletion && workerTasks[i].Result != null)
                        {
                            completedEvent = workerTasks[i].Result;
                            break;
                        }
825 826
                    }
                }
827 828 829
                else
                {
                    completedEvent = await ProcessCompilationEventsCoreAsync(analysisScope, analysisStateOpt, prePopulatedEventQueue, cancellationToken).ConfigureAwait(false);
830

831
                    ExecuteSyntaxTreeActions(analysisScope, analysisStateOpt, cancellationToken);
832
                }
833

834 835 836
                // Finally process the compilation completed event, if any.
                if (completedEvent != null)
                {
837
                    ProcessEvent(completedEvent, analysisScope, analysisStateOpt, cancellationToken);
838 839
                }
            }
840
            catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
841
            {
842
                throw ExceptionUtilities.Unreachable;
843 844 845
            }
        }

846
        private async Task<CompilationCompletedEvent> ProcessCompilationEventsCoreAsync(AnalysisScope analysisScope, AnalysisState analysisStateOpt, bool prePopulatedEventQueue, CancellationToken cancellationToken)
847
        {
848
            try
849
            {
850
                CompilationCompletedEvent completedEvent = null;
851

852
                while (true)
853
                {
854 855
                    cancellationToken.ThrowIfCancellationRequested();

856
                    // NOTE: IsCompleted guarantees that Count will not increase
V
VSadov 已提交
857
                    //       the reverse is not true, so we need to check IsCompleted first and then check the Count
858 859
                    if ((prePopulatedEventQueue || CompilationEventQueue.IsCompleted) &&
                        CompilationEventQueue.Count == 0)
860
                    {
861
                        break;
862
                    }
863 864 865

                    CompilationEvent e;
                    try
866
                    {
867
                        if (!CompilationEventQueue.TryDequeue(out e))
868
                        {
869 870 871 872 873 874 875 876
                            if (!prePopulatedEventQueue)
                            {
                                e = await CompilationEventQueue.DequeueAsync(cancellationToken).ConfigureAwait(false);
                            }
                            else
                            {
                                return completedEvent;
                            }
877 878 879 880 881 882 883
                        }
                    }
                    catch (TaskCanceledException) when (!prePopulatedEventQueue)
                    {
                        // When the queue is completed with a pending DequeueAsync return then a 
                        // TaskCanceledException will be thrown.  This just signals the queue is 
                        // complete and we should finish processing it.
J
Jared Parsons 已提交
884 885 886

                        // This failure is being tracked by https://github.com/dotnet/roslyn/issues/5962
                        // Debug.Assert(CompilationEventQueue.IsCompleted, "DequeueAsync should never throw unless the AsyncQueue<T> is completed.");
887
                        break;
888
                    }
889

890 891 892 893 894 895 896 897 898
                    // Don't process the compilation completed event as other worker threads might still be processing other compilation events.
                    // The caller will wait for all workers to complete and finally process this event.
                    var compilationCompletedEvent = e as CompilationCompletedEvent;
                    if (compilationCompletedEvent != null)
                    {
                        completedEvent = compilationCompletedEvent;
                        continue;
                    }

899
                    ProcessEvent(e, analysisScope, analysisStateOpt, cancellationToken);
900
                }
901

902 903
                return completedEvent;
            }
904
            catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
905 906
            {
                throw ExceptionUtilities.Unreachable;
907 908 909
            }
        }

910
        private void ProcessEvent(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
911
        {
912 913 914 915
            if (TryProcessEventCore(e, analysisScope, analysisStateOpt, cancellationToken))
            {
                analysisStateOpt?.OnCompilationEventProcessed(e, analysisScope);
            }
916 917
        }

918
        private bool TryProcessEventCore(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
919
        {
920 921
            cancellationToken.ThrowIfCancellationRequested();

922 923 924
            var symbolEvent = e as SymbolDeclaredCompilationEvent;
            if (symbolEvent != null)
            {
925
                return TryProcessSymbolDeclared(symbolEvent, analysisScope, analysisStateOpt, cancellationToken);
926 927 928 929 930
            }

            var completedEvent = e as CompilationUnitCompletedEvent;
            if (completedEvent != null)
            {
931
                return TryProcessCompilationUnitCompleted(completedEvent, analysisScope, analysisStateOpt, cancellationToken);
932 933 934 935 936
            }

            var endEvent = e as CompilationCompletedEvent;
            if (endEvent != null)
            {
937
                return TryProcessCompilationCompleted(endEvent, analysisScope, analysisStateOpt, cancellationToken);
938 939
            }

940 941
            var startedEvent = e as CompilationStartedEvent;
            if (startedEvent != null)
942
            {
943
                return TryProcessCompilationStarted(startedEvent, analysisScope, analysisStateOpt, cancellationToken);
944 945 946 947 948
            }

            throw new InvalidOperationException("Unexpected compilation event of type " + e.GetType().Name);
        }

949 950 951 952 953 954 955 956
        /// <summary>
        /// Tries to execute symbol and declaration actions for the given symbol.
        /// </summary>
        /// <returns>
        /// True, if successfully executed the actions for the given analysis scope OR no actions were required to be executed for the given analysis scope.
        /// False, otherwise.
        /// </returns>
        private bool TryProcessSymbolDeclared(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
957 958 959
        {
            try
            {
960 961
                // Attempt to execute all analyzer actions.
                var success = true;
962
                var symbol = symbolEvent.Symbol;
963
                var isGeneratedCodeSymbol = IsGeneratedCodeSymbol(symbol);
964 965
                if (!AnalysisScope.ShouldSkipSymbolAnalysis(symbolEvent) &&
                    !TryExecuteSymbolActions(symbolEvent, analysisScope, analysisStateOpt, isGeneratedCodeSymbol, cancellationToken))
966
                {
967
                    success = false;
968 969
                }

970 971
                if (!AnalysisScope.ShouldSkipDeclarationAnalysis(symbol) &&
                    !TryExecuteDeclaringReferenceActions(symbolEvent, analysisScope, analysisStateOpt, isGeneratedCodeSymbol, cancellationToken))
972
                {
973
                    success = false;
974
                }
975 976

                return success;
977 978 979 980 981 982 983
            }
            finally
            {
                symbolEvent.FlushCache();
            }
        }

984 985 986 987 988 989 990 991
        /// <summary>
        /// Tries to execute symbol actions.
        /// </summary>
        /// <returns>
        /// True, if successfully executed the actions for the given analysis scope OR no actions were required to be executed for the given analysis scope.
        /// False, otherwise.
        /// </returns>
        private bool TryExecuteSymbolActions(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, bool isGeneratedCodeSymbol, CancellationToken cancellationToken)
992 993
        {
            var symbol = symbolEvent.Symbol;
994
            if (!analysisScope.ShouldAnalyze(symbol))
995
            {
996
                return true;
997 998
            }

999
            var success = true;
1000
            foreach (var analyzer in analysisScope.Analyzers)
1001
            {
1002 1003 1004 1005
                // Invoke symbol analyzers only for source symbols.
                ImmutableArray<ImmutableArray<SymbolAnalyzerAction>> actionsByKind;
                if (_symbolActionsByKind.TryGetValue(analyzer, out actionsByKind) && (int)symbol.Kind < actionsByKind.Length)
                {
1006 1007 1008 1009
                    if (!analyzerExecutor.TryExecuteSymbolActions(actionsByKind[(int)symbol.Kind], analyzer, symbolEvent, GetTopmostNodeForAnalysis, analysisScope, analysisStateOpt, isGeneratedCodeSymbol))
                    {
                        success = false;
                    }
1010
                }
1011
                else
1012
                {
1013
                    analysisStateOpt?.MarkSymbolComplete(symbol, analyzer);
1014
                }
1015
            }
1016 1017

            return success;
1018 1019
        }

1020
        private static SyntaxNode GetTopmostNodeForAnalysis(ISymbol symbol, SyntaxReference syntaxReference, Compilation compilation)
1021
        {
1022 1023
            var model = compilation.GetSemanticModel(syntaxReference.SyntaxTree);
            return model.GetTopmostNodeForDiagnosticAnalysis(symbol, syntaxReference.GetSyntax());
1024 1025
        }

1026
        protected abstract bool TryExecuteDeclaringReferenceActions(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, bool isGeneratedCodeSymbol, CancellationToken cancellationToken);
1027

1028 1029 1030 1031 1032 1033 1034 1035
        /// <summary>
        /// Tries to execute compilation unit actions.
        /// </summary>
        /// <returns>
        /// True, if successfully executed the actions for the given analysis scope OR no actions were required to be executed for the given analysis scope.
        /// False, otherwise.
        /// </returns>
        private bool TryProcessCompilationUnitCompleted(CompilationUnitCompletedEvent completedEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
1036 1037 1038 1039 1040
        {
            // When the compiler is finished with a compilation unit, we can run user diagnostics which
            // might want to ask the compiler for all the diagnostics in the source file, for example
            // to get information about unnecessary usings.

1041
            var semanticModel = analysisStateOpt != null ?
1042
                compilationData.GetOrCreateCachedSemanticModel(completedEvent.CompilationUnit, completedEvent.Compilation, cancellationToken) :
1043 1044 1045
                completedEvent.SemanticModel;

            if (!analysisScope.ShouldAnalyze(semanticModel.SyntaxTree))
1046
            {
1047
                return true;
1048
            }
1049

1050 1051 1052 1053
            var isGeneratedCode = IsGeneratedCode(semanticModel.SyntaxTree);
            if (isGeneratedCode && DoNotAnalyzeGeneratedCode)
            {
                analysisStateOpt?.MarkEventComplete(completedEvent, analysisScope.Analyzers);
1054
                return true;
1055 1056
            }

1057 1058
            try
            {
1059
                var success = true;
1060
                foreach (var analyzer in analysisScope.Analyzers)
1061
                {
1062 1063
                    ImmutableArray<SemanticModelAnalyzerAction> semanticModelActions;
                    if (_semanticModelActionsMap.TryGetValue(analyzer, out semanticModelActions))
1064 1065
                    {
                        // Execute actions for a given analyzer sequentially.
1066 1067 1068 1069
                        if (!analyzerExecutor.TryExecuteSemanticModelActions(semanticModelActions, analyzer, semanticModel, completedEvent, analysisScope, analysisStateOpt, isGeneratedCode))
                        {
                            success = false;
                        }
1070
                    }
1071
                    else
1072
                    {
1073
                        analysisStateOpt?.MarkEventComplete(completedEvent, analyzer);
1074
                    }
1075
                }
1076 1077

                return success;
1078 1079 1080 1081 1082 1083 1084
            }
            finally
            {
                completedEvent.FlushCache();
            }
        }

1085 1086 1087 1088 1089 1090 1091 1092
        /// <summary>
        /// Tries to execute compilation started actions.
        /// </summary>
        /// <returns>
        /// True, if successfully executed the actions for the given analysis scope OR no actions were required to be executed for the given analysis scope.
        /// False, otherwise.
        /// </returns>
        private bool TryProcessCompilationStarted(CompilationStartedEvent startedEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
1093
        {
1094
            return TryExecuteCompilationActions(_compilationActionsMap, startedEvent, analysisScope, analysisStateOpt, cancellationToken);
1095 1096
        }

1097 1098 1099 1100 1101 1102 1103 1104
        /// <summary>
        /// Tries to execute compilation completed actions.
        /// </summary>
        /// <returns>
        /// True, if successfully executed the actions for the given analysis scope OR no actions were required to be executed for the given analysis scope.
        /// False, otherwise.
        /// </returns>
        private bool TryProcessCompilationCompleted(CompilationCompletedEvent endEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
1105
        {
1106
            return TryExecuteCompilationActions(_compilationEndActionsMap, endEvent, analysisScope, analysisStateOpt, cancellationToken);
1107 1108
        }

1109 1110 1111 1112 1113 1114 1115 1116
        /// <summary>
        /// Tries to execute compilation actions.
        /// </summary>
        /// <returns>
        /// True, if successfully executed the actions for the given analysis scope OR no actions were required to be executed for the given analysis scope.
        /// False, otherwise.
        /// </returns>
        private bool TryExecuteCompilationActions(
1117 1118 1119 1120 1121 1122 1123 1124
            ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>> compilationActionsMap,
            CompilationEvent compilationEvent,
            AnalysisScope analysisScope,
            AnalysisState analysisStateOpt,
            CancellationToken cancellationToken)
        {
            Debug.Assert(compilationEvent is CompilationStartedEvent || compilationEvent is CompilationCompletedEvent);

1125 1126
            try
            {
1127
                var success = true;
1128 1129 1130 1131 1132
                foreach (var analyzer in analysisScope.Analyzers)
                {
                    ImmutableArray<CompilationAnalyzerAction> compilationActions;
                    if (compilationActionsMap.TryGetValue(analyzer, out compilationActions))
                    {
1133 1134 1135 1136
                        if (!analyzerExecutor.TryExecuteCompilationActions(compilationActions, analyzer, compilationEvent, analysisScope, analysisStateOpt))
                        {
                            success = false;
                        }
1137
                    }
1138
                    else
1139
                    {
1140
                        analysisStateOpt?.MarkEventComplete(compilationEvent, analyzer);
1141 1142
                    }
                }
1143 1144

                return success;
1145 1146 1147
            }
            finally
            {
1148
                compilationEvent.FlushCache();
1149 1150 1151
            }
        }

1152
        internal static Action<Diagnostic> GetDiagnosticSink(Action<Diagnostic> addDiagnosticCore, Compilation compilation)
1153
        {
1154
            return diagnostic =>
1155
            {
1156
                var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation);
1157
                if (filteredDiagnostic != null)
1158
                {
1159 1160 1161 1162
                    addDiagnosticCore(filteredDiagnostic);
                }
            };
        }
1163

1164
        internal static Action<Diagnostic, DiagnosticAnalyzer, bool> GetDiagnosticSink(Action<Diagnostic, DiagnosticAnalyzer, bool> addLocalDiagnosticCore, Compilation compilation)
1165 1166 1167
        {
            return (diagnostic, analyzer, isSyntaxDiagnostic) =>
            {
1168
                var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation);
1169 1170 1171 1172 1173
                if (filteredDiagnostic != null)
                {
                    addLocalDiagnosticCore(filteredDiagnostic, analyzer, isSyntaxDiagnostic);
                }
            };
1174 1175
        }

1176
        internal static Action<Diagnostic, DiagnosticAnalyzer> GetDiagnosticSink(Action<Diagnostic, DiagnosticAnalyzer> addDiagnosticCore, Compilation compilation)
1177
        {
1178
            return (diagnostic, analyzer) =>
1179
            {
1180
                var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation);
M
Manish Vasani 已提交
1181
                if (filteredDiagnostic != null)
1182
                {
1183
                    addDiagnosticCore(filteredDiagnostic, analyzer);
1184 1185 1186 1187
                }
            };
        }

1188
        private static Diagnostic GetFilteredDiagnostic(Diagnostic diagnostic, Compilation compilation)
1189
        {
1190
            return compilation.Options.FilterDiagnostic(diagnostic);
1191 1192
        }

1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
        private static ImmutableArray<DiagnosticAnalyzer> GetUnsuppressedAnalyzers(
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerManager analyzerManager,
            AnalyzerExecutor analyzerExecutor)
        {
            var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>();
            foreach (var analyzer in analyzers)
            {
                if (!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor))
                {
                    builder.Add(analyzer);
                }
            }

            return builder.ToImmutable();
        }

1210
        private static async Task<AnalyzerActions> GetAnalyzerActionsAsync(
1211 1212
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerManager analyzerManager,
M
Manish Vasani 已提交
1213
            AnalyzerExecutor analyzerExecutor)
1214
        {
1215 1216
            var allAnalyzerActions = new AnalyzerActions();
            foreach (var analyzer in analyzers)
1217
            {
1218 1219 1220 1221
                Debug.Assert(!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor));

                var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false);
                if (analyzerActions != null)
M
Manish Vasani 已提交
1222
                {
1223
                    allAnalyzerActions = allAnalyzerActions.Append(analyzerActions);
M
Manish Vasani 已提交
1224
                }
1225
            }
1226

1227
            return allAnalyzerActions;
1228 1229
        }

1230 1231 1232 1233
        private static async Task<ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim>> GetAnalyzerGateMapAsync(
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerManager analyzerManager,
            AnalyzerExecutor analyzerExecutor)
1234 1235
        {
            var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, SemaphoreSlim>();
1236
            foreach (var analyzer in analyzers)
1237
            {
1238 1239
                Debug.Assert(!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor));

1240 1241
                var isConcurrent = await analyzerManager.IsConcurrentAnalyzerAsync(analyzer, analyzerExecutor).ConfigureAwait(false);
                if (!isConcurrent)
1242
                {
1243
                    // Non-concurrent analyzers need their action callbacks from the analyzer driver to be guarded by a gate.
1244 1245 1246 1247 1248 1249
                    var gate = new SemaphoreSlim(initialCount: 1);
                    builder.Add(analyzer, gate);
                }
            }

            return builder.ToImmutable();
1250 1251
        }

1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
        private static async Task<ImmutableDictionary<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags>> GetGeneratedCodeAnalysisFlagsAsync(
            ImmutableArray<DiagnosticAnalyzer> analyzers,
            AnalyzerManager analyzerManager,
            AnalyzerExecutor analyzerExecutor)
        {
            var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, GeneratedCodeAnalysisFlags>();
            foreach (var analyzer in analyzers)
            {
                Debug.Assert(!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor));

                var generatedCodeAnalysisFlags = await analyzerManager.GetGeneratedCodeAnalysisFlagsAsync(analyzer, analyzerExecutor).ConfigureAwait(false);
                builder.Add(analyzer, generatedCodeAnalysisFlags);
            }

            return builder.ToImmutable();
        }

1269
        private bool IsGeneratedCodeSymbol(ISymbol symbol)
1270
        {
1271
            if (_treatAllCodeAsNonGeneratedCode)
1272
            {
1273
                return false;
1274 1275 1276 1277 1278 1279 1280 1281 1282
            }

            if (_generatedCodeAttribute != null && GeneratedCodeUtilities.IsGeneratedSymbolWithGeneratedCodeAttribute(symbol, _generatedCodeAttribute))
            {
                return true;
            }

            foreach (var declaringRef in symbol.DeclaringSyntaxReferences)
            {
1283
                if (!IsGeneratedOrHiddenCodeLocation(declaringRef.SyntaxTree, declaringRef.Span))
1284 1285 1286 1287 1288 1289 1290 1291
                {
                    return false;
                }
            }

            return true;
        }

1292 1293 1294
        [PerformanceSensitive(
            "https://github.com/dotnet/roslyn/pull/23637",
            AllowLocks = false)]
1295 1296
        protected bool IsGeneratedCode(SyntaxTree tree)
        {
1297 1298 1299 1300 1301 1302 1303
            if (_treatAllCodeAsNonGeneratedCode)
            {
                return false;
            }

            Debug.Assert(_lazyGeneratedCodeFilesMap != null);

1304 1305
            bool isGenerated;
            if (!_lazyGeneratedCodeFilesMap.TryGetValue(tree, out isGenerated))
1306
            {
1307 1308
                isGenerated = _isGeneratedCode(tree, analyzerExecutor.CancellationToken);
                _lazyGeneratedCodeFilesMap.TryAdd(tree, isGenerated);
1309
            }
1310 1311

            return isGenerated;
1312 1313 1314 1315
        }

        protected bool DoNotAnalyzeGeneratedCode => _doNotAnalyzeGeneratedCode;

1316
        // Location is in generated code if either the containing tree is a generated code file OR if it is a hidden source location.
1317 1318
        protected bool IsGeneratedOrHiddenCodeLocation(SyntaxTree syntaxTree, TextSpan span)
            => IsGeneratedCode(syntaxTree) || IsHiddenSourceLocation(syntaxTree, span);
1319

1320 1321 1322
        protected bool IsHiddenSourceLocation(SyntaxTree syntaxTree, TextSpan span)
            => HasHiddenRegions(syntaxTree) && 
               syntaxTree.IsHiddenPosition(span.Start);
1323

1324 1325 1326
        [PerformanceSensitive(
            "https://github.com/dotnet/roslyn/pull/23637",
            AllowLocks = false)]
1327 1328 1329 1330
        private bool HasHiddenRegions(SyntaxTree tree)
        {
            Debug.Assert(tree != null);

M
Manish Vasani 已提交
1331
            if (_lazyTreesWithHiddenRegionsMap == null)
1332 1333 1334 1335
            {
                return false;
            }

1336 1337
            bool hasHiddenRegions;
            if (!_lazyTreesWithHiddenRegionsMap.TryGetValue(tree, out hasHiddenRegions))
1338
            {
1339 1340
                hasHiddenRegions = tree.HasHiddenRegions();
                _lazyTreesWithHiddenRegionsMap.TryAdd(tree, hasHiddenRegions);
1341
            }
1342 1343

            return hasHiddenRegions;
1344 1345
        }

1346
        internal async Task<AnalyzerActionCounts> GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, CompilationOptions compilationOptions, CancellationToken cancellationToken)
1347 1348
        {
            var executor = analyzerExecutor.WithCancellationToken(cancellationToken);
1349 1350
            if (IsDiagnosticAnalyzerSuppressed(analyzer, compilationOptions, analyzerManager, executor))
            {
1351
                return AnalyzerActionCounts.Empty;
1352 1353
            }

1354
            var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, executor).ConfigureAwait(false);
1355
            return new AnalyzerActionCounts(analyzerActions);
1356 1357
        }

1358 1359 1360 1361 1362 1363
        /// <summary>
        /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options.
        /// </summary>
        internal static bool IsDiagnosticAnalyzerSuppressed(
            DiagnosticAnalyzer analyzer,
            CompilationOptions options,
M
Manish Vasani 已提交
1364 1365
            AnalyzerManager analyzerManager,
            AnalyzerExecutor analyzerExecutor)
1366
        {
1367
            return analyzerManager.IsDiagnosticAnalyzerSuppressed(analyzer, options, s_IsCompilerAnalyzerFunc, analyzerExecutor);
M
Manish Vasani 已提交
1368
        }
1369

M
Manish Vasani 已提交
1370 1371 1372
        private static bool IsCompilerAnalyzer(DiagnosticAnalyzer analyzer)
        {
            return analyzer is CompilerDiagnosticAnalyzer;
1373 1374 1375 1376
        }

        public void Dispose()
        {
1377 1378
            this.CompilationEventQueue?.TryComplete();
            this.DiagnosticQueue?.TryComplete();
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
            _queueRegistration.Dispose();
        }
    }

    /// <summary>
    /// Driver to execute diagnostic analyzers for a given compilation.
    /// It uses a <see cref="AsyncQueue{TElement}"/> of <see cref="CompilationEvent"/>s to drive its analysis.
    /// </summary>
    internal class AnalyzerDriver<TLanguageKindEnum> : AnalyzerDriver where TLanguageKindEnum : struct
    {
V
Vladimir Reshetnikov 已提交
1389
        private readonly Func<SyntaxNode, TLanguageKindEnum> _getKind;
V
Vladimir Reshetnikov 已提交
1390
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>>> _lazyNodeActionsByKind;
J
John Hamby 已提交
1391
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>>> _lazyOperationActionsByKind;
V
Vladimir Reshetnikov 已提交
1392
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CodeBlockStartAnalyzerAction<TLanguageKindEnum>>> _lazyCodeBlockStartActionsByAnalyzer;
1393 1394
        // Code block actions and code block end actions are kept separate so that it is easy to
        // execute the code block actions before the code block end actions.
V
Vladimir Reshetnikov 已提交
1395 1396
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CodeBlockAnalyzerAction>> _lazyCodeBlockEndActionsByAnalyzer;
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CodeBlockAnalyzerAction>> _lazyCodeBlockActionsByAnalyzer;
J
John Hamby 已提交
1397 1398 1399
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<OperationBlockStartAnalyzerAction>> _lazyOperationBlockStartActionsByAnalyzer;
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<OperationBlockAnalyzerAction>> _lazyOperationBlockActionsByAnalyzer;
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<OperationBlockAnalyzerAction>> _lazyOperationBlockEndActionsByAnalyzer;
1400

1401
        private static readonly ObjectPool<DeclarationAnalysisData> s_declarationAnalysisDataPool = new ObjectPool<DeclarationAnalysisData>(() => new DeclarationAnalysisData());
1402

1403
        /// <summary>
1404 1405 1406 1407
        /// Create an analyzer driver.
        /// </summary>
        /// <param name="analyzers">The set of analyzers to include in the analysis</param>
        /// <param name="getKind">A delegate that returns the language-specific kind for a given syntax node</param>
1408
        /// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param>
1409 1410 1411
        /// <param name="isComment">Delegate to identify if the given trivia is a comment.</param>
        internal AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, Func<SyntaxNode, TLanguageKindEnum> getKind, AnalyzerManager analyzerManager, Func<SyntaxTrivia, bool> isComment)
            : base(analyzers, analyzerManager, isComment)
1412 1413 1414 1415
        {
            _getKind = getKind;
        }

1416
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>>> NodeActionsByAnalyzerAndKind
1417 1418 1419 1420 1421
        {
            get
            {
                if (_lazyNodeActionsByKind == null)
                {
M
Manish Vasani 已提交
1422
                    var nodeActions = this.analyzerActions.GetSyntaxNodeActions<TLanguageKindEnum>();
1423 1424 1425 1426 1427
                    ImmutableDictionary<DiagnosticAnalyzer, ImmutableDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>>> analyzerActionsByKind;
                    if (nodeActions.Any())
                    {
                        var nodeActionsByAnalyzers = nodeActions.GroupBy(a => a.Analyzer);
                        var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>>>();
C
Charles Stoner 已提交
1428
                        foreach (var analyzerAndActions in nodeActionsByAnalyzers)
1429 1430
                        {
                            ImmutableDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> actionsByKind;
C
Charles Stoner 已提交
1431
                            if (analyzerAndActions.Any())
1432
                            {
C
Charles Stoner 已提交
1433
                                actionsByKind = AnalyzerExecutor.GetNodeActionsByKind(analyzerAndActions);
1434 1435 1436 1437 1438 1439
                            }
                            else
                            {
                                actionsByKind = ImmutableDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>>.Empty;
                            }

C
Charles Stoner 已提交
1440
                            builder.Add(analyzerAndActions.Key, actionsByKind);
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456
                        }

                        analyzerActionsByKind = builder.ToImmutable();
                    }
                    else
                    {
                        analyzerActionsByKind = ImmutableDictionary<DiagnosticAnalyzer, ImmutableDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>>>.Empty;
                    }

                    Interlocked.CompareExchange(ref _lazyNodeActionsByKind, analyzerActionsByKind, null);
                }

                return _lazyNodeActionsByKind;
            }
        }

J
John Hamby 已提交
1457
        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>>> OperationActionsByAnalyzerAndKind
1458 1459 1460
        {
            get
            {
J
John Hamby 已提交
1461
                if (_lazyOperationActionsByKind == null)
1462
                {
J
John Hamby 已提交
1463 1464 1465
                    var operationActions = this.analyzerActions.OperationActions;
                    ImmutableDictionary<DiagnosticAnalyzer, ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>>> analyzerActionsByKind;
                    if (operationActions.Any())
1466
                    {
J
John Hamby 已提交
1467 1468 1469
                        var operationActionsByAnalyzers = operationActions.GroupBy(a => a.Analyzer);
                        var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>>>();
                        foreach (var analyzerAndActions in operationActionsByAnalyzers)
1470
                        {
J
John Hamby 已提交
1471 1472 1473 1474
                            ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> actionsByKind;
                            if (analyzerAndActions.Any())
                            {
                                actionsByKind = AnalyzerExecutor.GetOperationActionsByKind(analyzerAndActions);
1475
                            }
J
John Hamby 已提交
1476 1477 1478 1479
                            else
                            {
                                actionsByKind = ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>>.Empty;
                            }
1480

J
John Hamby 已提交
1481
                            builder.Add(analyzerAndActions.Key, actionsByKind);
1482
                        }
J
John Hamby 已提交
1483 1484

                        analyzerActionsByKind = builder.ToImmutable();
1485 1486 1487
                    }
                    else
                    {
J
John Hamby 已提交
1488
                        analyzerActionsByKind = ImmutableDictionary<DiagnosticAnalyzer, ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>>>.Empty;
1489 1490
                    }

J
John Hamby 已提交
1491
                    Interlocked.CompareExchange(ref _lazyOperationActionsByKind, analyzerActionsByKind, null);
1492 1493
                }

J
John Hamby 已提交
1494
                return _lazyOperationActionsByKind;
1495 1496 1497
            }
        }

J
John Hamby 已提交
1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531

        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CodeBlockStartAnalyzerAction<TLanguageKindEnum>>> CodeBlockStartActionsByAnalyzer
        {
            get { return GetBlockActionsByAnalyzer(ref _lazyCodeBlockStartActionsByAnalyzer, this.analyzerActions.GetCodeBlockStartActions<TLanguageKindEnum>()); }
        }

        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CodeBlockAnalyzerAction>> CodeBlockEndActionsByAnalyzer
        {
            get { return GetBlockActionsByAnalyzer(ref _lazyCodeBlockEndActionsByAnalyzer, this.analyzerActions.CodeBlockEndActions); }
        }

        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CodeBlockAnalyzerAction>> CodeBlockActionsByAnalyzer
        {
            get { return GetBlockActionsByAnalyzer(ref _lazyCodeBlockActionsByAnalyzer, this.analyzerActions.CodeBlockActions); }
        }

        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<OperationBlockStartAnalyzerAction>> OperationBlockStartActionsByAnalyzer
        {
            get { return GetBlockActionsByAnalyzer(ref _lazyOperationBlockStartActionsByAnalyzer, this.analyzerActions.OperationBlockStartActions); }
        }

        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<OperationBlockAnalyzerAction>> OperationBlockEndActionsByAnalyzer
        {
            get { return GetBlockActionsByAnalyzer(ref _lazyOperationBlockEndActionsByAnalyzer, this.analyzerActions.OperationBlockEndActions); }
        }

        private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<OperationBlockAnalyzerAction>> OperationBlockActionsByAnalyzer
        {
            get { return GetBlockActionsByAnalyzer(ref _lazyOperationBlockActionsByAnalyzer, this.analyzerActions.OperationBlockActions); }
        }

        private static ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<ActionType>> GetBlockActionsByAnalyzer<ActionType>(
            ref ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<ActionType>> lazyCodeBlockActionsByAnalyzer,
            ImmutableArray<ActionType> codeBlockActions) where ActionType : AnalyzerAction
1532
        {
1533
            if (lazyCodeBlockActionsByAnalyzer == null)
1534
            {
J
John Hamby 已提交
1535
                ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<ActionType>> codeBlockActionsByAnalyzer;
1536
                if (!codeBlockActions.IsEmpty)
1537
                {
J
John Hamby 已提交
1538
                    var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<ActionType>>();
1539 1540
                    var actionsByAnalyzer = codeBlockActions.GroupBy(action => action.Analyzer);
                    foreach (var analyzerAndActions in actionsByAnalyzer)
1541
                    {
1542
                        builder.Add(analyzerAndActions.Key, analyzerAndActions.ToImmutableArrayOrEmpty());
1543 1544
                    }

1545 1546 1547 1548
                    codeBlockActionsByAnalyzer = builder.ToImmutable();
                }
                else
                {
J
John Hamby 已提交
1549
                    codeBlockActionsByAnalyzer = ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<ActionType>>.Empty;
1550 1551
                }

1552
                Interlocked.CompareExchange(ref lazyCodeBlockActionsByAnalyzer, codeBlockActionsByAnalyzer, null);
1553
            }
1554 1555 1556 1557

            return lazyCodeBlockActionsByAnalyzer;
        }

1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
        private bool ShouldExecuteSyntaxNodeActions(AnalysisScope analysisScope)
        {
            foreach (var analyzer in analysisScope.Analyzers)
            {
                if (this.NodeActionsByAnalyzerAndKind.ContainsKey(analyzer))
                {
                    return true;
                }
            }

            return false;
        }

J
John Hamby 已提交
1571 1572 1573 1574 1575 1576
        private bool ShouldExecuteOperationActions(AnalysisScope analysisScope)
        {
            return analysisScope.Analyzers.Any(analyzer => this.OperationActionsByAnalyzerAndKind.ContainsKey(analyzer));
        }

        private bool ShouldExecuteBlockActions<T0, T1>(ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<T0>> blockStartActions, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<T1>> blockActions, AnalysisScope analysisScope, ISymbol symbol)
1577 1578 1579 1580 1581
        {
            if (AnalyzerExecutor.CanHaveExecutableCodeBlock(symbol))
            {
                foreach (var analyzer in analysisScope.Analyzers)
                {
J
John Hamby 已提交
1582 1583
                    if (blockStartActions.ContainsKey(analyzer) ||
                        blockActions.ContainsKey(analyzer))
1584 1585 1586 1587 1588 1589 1590 1591 1592
                    {
                        return true;
                    }
                }
            }

            return false;
        }

J
John Hamby 已提交
1593 1594 1595 1596 1597 1598 1599 1600 1601 1602
        private bool ShouldExecuteCodeBlockActions(AnalysisScope analysisScope, ISymbol symbol)
        {
            return ShouldExecuteBlockActions(this.CodeBlockStartActionsByAnalyzer, this.CodeBlockActionsByAnalyzer, analysisScope, symbol);
        }

        private bool ShouldExecuteOperationBlockActions(AnalysisScope analysisScope, ISymbol symbol)
        {
            return ShouldExecuteBlockActions(this.OperationBlockStartActionsByAnalyzer, this.OperationBlockActionsByAnalyzer, analysisScope, symbol);
        }

1603 1604 1605 1606 1607 1608 1609 1610
        /// <summary>
        /// Tries to execute syntax node, code block and operation actions for all declarations for the given symbol.
        /// </summary>
        /// <returns>
        /// True, if successfully executed the actions for the given analysis scope OR no actions were required to be executed for the given analysis scope.
        /// False, otherwise.
        /// </returns>
        protected override bool TryExecuteDeclaringReferenceActions(
1611
            SymbolDeclaredCompilationEvent symbolEvent,
1612 1613
            AnalysisScope analysisScope,
            AnalysisState analysisStateOpt,
1614
            bool isGeneratedCodeSymbol,
1615 1616 1617
            CancellationToken cancellationToken)
        {
            var symbol = symbolEvent.Symbol;
1618 1619
            var executeSyntaxNodeActions = ShouldExecuteSyntaxNodeActions(analysisScope);
            var executeCodeBlockActions = ShouldExecuteCodeBlockActions(analysisScope, symbol);
1620
            var executeOperationActions = ShouldExecuteOperationActions(analysisScope);
J
John Hamby 已提交
1621
            var executeOperationBlockActions = ShouldExecuteOperationBlockActions(analysisScope, symbol);
1622

1623
            var success = true;
J
John Hamby 已提交
1624
            if (executeSyntaxNodeActions || executeOperationActions || executeCodeBlockActions || executeOperationBlockActions)
1625
            {
1626 1627
                var declaringReferences = symbolEvent.DeclaringSyntaxReferences;
                for (var i = 0; i < declaringReferences.Length; i++)
1628
                {
1629 1630
                    cancellationToken.ThrowIfCancellationRequested();

1631
                    var decl = declaringReferences[i];
1632 1633 1634 1635 1636
                    if (analysisScope.FilterTreeOpt != null && analysisScope.FilterTreeOpt != decl.SyntaxTree)
                    {
                        continue;
                    }

1637
                    var isInGeneratedCode = isGeneratedCodeSymbol || IsGeneratedOrHiddenCodeLocation(decl.SyntaxTree, decl.Span);
1638
                    if (isInGeneratedCode && DoNotAnalyzeGeneratedCode)
1639
                    {
1640
                        analysisStateOpt?.MarkDeclarationComplete(symbol, i, analysisScope.Analyzers);
1641
                        continue;
1642
                    }
1643

1644 1645 1646 1647
                    if (!TryExecuteDeclaringReferenceActions(decl, i, symbolEvent, analysisScope, analysisStateOpt, executeSyntaxNodeActions, executeOperationActions, executeCodeBlockActions, executeOperationBlockActions, isInGeneratedCode, cancellationToken))
                    {
                        success = false;
                    }
1648 1649
                }
            }
1650
            else if (analysisStateOpt != null)
1651
            {
1652
                cancellationToken.ThrowIfCancellationRequested();
1653
                analysisStateOpt.MarkDeclarationsComplete(symbol, analysisScope.Analyzers);
1654

1655 1656
                var declaringReferences = symbolEvent.DeclaringSyntaxReferences;
                for (var i = 0; i < declaringReferences.Length; i++)
1657
                {
1658 1659
                    var decl = declaringReferences[i];
                    ClearCachedAnalysisDataIfAnalyzed(decl, symbol, i, analysisStateOpt);
1660
                }
1661
            }
1662 1663

            return success;
1664 1665
        }

1666
        private void ClearCachedAnalysisDataIfAnalyzed(SyntaxReference declaration, ISymbol symbol, int declarationIndex, AnalysisState analysisState)
1667 1668 1669
        {
            Debug.Assert(analysisState != null);

1670
            if (!analysisState.IsDeclarationComplete(symbol, declarationIndex))
1671 1672 1673 1674
            {
                return;
            }

1675
            compilationData.ClearDeclarationAnalysisData(declaration);
1676 1677 1678 1679 1680 1681 1682
        }

        private DeclarationAnalysisData ComputeDeclarationAnalysisData(
            ISymbol symbol,
            SyntaxReference declaration,
            SemanticModel semanticModel,
            AnalysisScope analysisScope,
1683
            Func<DeclarationAnalysisData> allocateData,
1684 1685
            CancellationToken cancellationToken)
        {
1686 1687
            cancellationToken.ThrowIfCancellationRequested();

1688
            var declarationAnalysisData = allocateData();
1689 1690
            var builder = declarationAnalysisData.DeclarationsInNode;

1691 1692
            var declaringReferenceSyntax = declaration.GetSyntax(cancellationToken);
            var topmostNodeForAnalysis = semanticModel.GetTopmostNodeForDiagnosticAnalysis(symbol, declaringReferenceSyntax);
1693
            ComputeDeclarationsInNode(semanticModel, symbol, declaringReferenceSyntax, topmostNodeForAnalysis, builder, cancellationToken);
1694

1695
            var isPartialDeclAnalysis = analysisScope.FilterSpanOpt.HasValue && !analysisScope.ContainsSpan(topmostNodeForAnalysis.FullSpan);
H
Heejae Chang 已提交
1696
            var nodesToAnalyze = GetSyntaxNodesToAnalyze(topmostNodeForAnalysis, symbol, builder, analysisScope, isPartialDeclAnalysis, semanticModel, analyzerExecutor);
1697

1698 1699 1700 1701 1702 1703
            declarationAnalysisData.DeclaringReferenceSyntax = declaringReferenceSyntax;
            declarationAnalysisData.TopmostNodeForAnalysis = topmostNodeForAnalysis;
            declarationAnalysisData.DescendantNodesToAnalyze.AddRange(nodesToAnalyze);
            declarationAnalysisData.IsPartialAnalysis = isPartialDeclAnalysis;

            return declarationAnalysisData;
1704 1705
        }

1706
        private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymbol declaredSymbol, SyntaxNode declaringReferenceSyntax, SyntaxNode topmostNodeForAnalysis, List<DeclarationInfo> builder, CancellationToken cancellationToken)
1707 1708 1709
        {
            // We only care about the top level symbol declaration and its immediate member declarations.
            int? levelsToCompute = 2;
1710
            var getSymbol = topmostNodeForAnalysis != declaringReferenceSyntax || declaredSymbol.Kind == SymbolKind.Namespace;
1711
            semanticModel.ComputeDeclarationsInNode(topmostNodeForAnalysis, getSymbol, builder, cancellationToken, levelsToCompute);
1712 1713
        }

1714 1715 1716 1717 1718 1719 1720 1721
        /// <summary>
        /// Tries to execute syntax node, code block and operation actions for the given declaration.
        /// </summary>
        /// <returns>
        /// True, if successfully executed the actions for the given analysis scope OR no actions were required to be executed for the given analysis scope.
        /// False, otherwise.
        /// </returns>
        private bool TryExecuteDeclaringReferenceActions(
1722
            SyntaxReference decl,
1723
            int declarationIndex,
1724
            SymbolDeclaredCompilationEvent symbolEvent,
1725 1726
            AnalysisScope analysisScope,
            AnalysisState analysisStateOpt,
1727
            bool shouldExecuteSyntaxNodeActions,
J
John Hamby 已提交
1728
            bool shouldExecuteOperationActions,
1729
            bool shouldExecuteCodeBlockActions,
J
John Hamby 已提交
1730
            bool shouldExecuteOperationBlockActions,
1731
            bool isInGeneratedCode,
1732 1733
            CancellationToken cancellationToken)
        {
J
John Hamby 已提交
1734
            Debug.Assert(shouldExecuteSyntaxNodeActions || shouldExecuteOperationActions || shouldExecuteCodeBlockActions || shouldExecuteOperationBlockActions);
1735
            Debug.Assert(!isInGeneratedCode || !DoNotAnalyzeGeneratedCode);
1736 1737

            var symbol = symbolEvent.Symbol;
1738

1739
            SemanticModel semanticModel = analysisStateOpt != null ?
1740
                compilationData.GetOrCreateCachedSemanticModel(decl.SyntaxTree, symbolEvent.Compilation, cancellationToken) :
1741
                symbolEvent.SemanticModel(decl);
1742

1743 1744 1745
            var cacheAnalysisData = analysisScope.Analyzers.Length < analyzers.Length &&
                (!analysisScope.FilterSpanOpt.HasValue || analysisScope.FilterSpanOpt.Value.Length >= decl.SyntaxTree.GetRoot(cancellationToken).Span.Length);

1746
            var declarationAnalysisData = compilationData.GetOrComputeDeclarationAnalysisData(
1747
                decl,
H
Heejae Chang 已提交
1748
                allocateData => ComputeDeclarationAnalysisData(symbol, decl, semanticModel, analysisScope, allocateData, cancellationToken),
1749 1750 1751 1752
                cacheAnalysisData);

            if (!analysisScope.ShouldAnalyze(declarationAnalysisData.TopmostNodeForAnalysis))
            {
1753
                return true;
1754
            }
1755

1756
            var success = true;
1757

1758 1759 1760
            // Execute stateless syntax node actions.
            if (shouldExecuteSyntaxNodeActions)
            {
1761 1762
                var nodesToAnalyze = declarationAnalysisData.DescendantNodesToAnalyze;
                foreach (var analyzer in analysisScope.Analyzers)
1763
                {
1764 1765 1766
                    ImmutableDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> nodeActionsByKind;
                    if (this.NodeActionsByAnalyzerAndKind.TryGetValue(analyzer, out nodeActionsByKind))
                    {
1767 1768 1769 1770 1771
                        if (nodeActionsByKind.IsEmpty)
                        {
                            continue;
                        }

1772
                        if (!analyzerExecutor.TryExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind,
1773
                            analyzer, semanticModel, _getKind, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan,
1774 1775 1776 1777
                            decl, declarationIndex, symbol, analysisScope, analysisStateOpt, isInGeneratedCode))
                        {
                            success = false;
                        }
1778
                    }
1779 1780 1781 1782
                }
            }

            // Execute code block actions.
J
John Hamby 已提交
1783
            if (shouldExecuteCodeBlockActions || shouldExecuteOperationActions || shouldExecuteOperationBlockActions)
1784 1785 1786
            {
                // Compute the executable code blocks of interest.
                var executableCodeBlocks = ImmutableArray<SyntaxNode>.Empty;
J
John Hamby 已提交
1787
                IEnumerable<CodeBlockAnalyzerActions> codeBlockActions = null;
1788
                foreach (var declInNode in declarationAnalysisData.DeclarationsInNode)
1789
                {
1790
                    if (declInNode.DeclaredNode == declarationAnalysisData.TopmostNodeForAnalysis || declInNode.DeclaredNode == declarationAnalysisData.DeclaringReferenceSyntax)
1791 1792
                    {
                        executableCodeBlocks = declInNode.ExecutableCodeBlocks;
J
John Hamby 已提交
1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
                        if (executableCodeBlocks.Any())
                        {
                            if (shouldExecuteCodeBlockActions || shouldExecuteOperationBlockActions)
                            {
                                codeBlockActions = GetCodeBlockActions(analysisScope);
                            }

                            // Execute operation actions.
                            if (shouldExecuteOperationActions || shouldExecuteOperationBlockActions)
                            {
                                var operationBlocksToAnalyze = GetOperationBlocksToAnalyze(executableCodeBlocks, semanticModel, cancellationToken);
1804 1805 1806 1807 1808
                                var operationsToAnalyze = ImmutableArray<IOperation>.Empty;
                                try
                                {
                                    operationsToAnalyze = GetOperationsToAnalyze(operationBlocksToAnalyze);
                                }
1809
                                catch (Exception ex) when (ex is InsufficientExecutionStackException || FatalError.ReportWithoutCrashUnlessCanceled(ex))
1810 1811 1812 1813 1814 1815 1816 1817
                                {
                                    // the exception filter will short-circuit if `ex` is `InsufficientExecutionStackException` (from OperationWalker)
                                    // and no non-fatal-watson will be logged as a result.
                                    var diagnostic = AnalyzerExecutor.CreateDriverExceptionDiagnostic(ex);
                                    var analyzer = this.analyzers[0];

                                    analyzerExecutor.OnAnalyzerException(ex, analyzer, diagnostic);
                                }
J
John Hamby 已提交
1818 1819 1820 1821 1822 1823 1824 1825 1826 1827

                                if (!operationsToAnalyze.IsEmpty)
                                {
                                    if (shouldExecuteOperationActions)
                                    {
                                        foreach (var analyzer in analysisScope.Analyzers)
                                        {
                                            ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> operationActionsByKind;
                                            if (this.OperationActionsByAnalyzerAndKind.TryGetValue(analyzer, out operationActionsByKind))
                                            {
1828 1829 1830 1831 1832
                                                if (operationActionsByKind.IsEmpty)
                                                {
                                                    continue;
                                                }

1833
                                                if (!analyzerExecutor.TryExecuteOperationActions(operationsToAnalyze, operationActionsByKind,
1834
                                                    analyzer, semanticModel, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan,
1835 1836 1837 1838
                                                    decl, declarationIndex, symbol, analysisScope, analysisStateOpt, isInGeneratedCode))
                                                {
                                                    success = false;
                                                }
J
John Hamby 已提交
1839 1840 1841 1842 1843 1844 1845 1846
                                            }
                                        }
                                    }

                                    if (shouldExecuteOperationBlockActions)
                                    {
                                        foreach (var analyzerActions in codeBlockActions)
                                        {
1847 1848
                                            if (analyzerActions.OperationBlockStartActions.IsEmpty &&
                                                analyzerActions.OperationBlockActions.IsEmpty &&
C
Charles Stoner 已提交
1849
                                                analyzerActions.OperationBlockEndActions.IsEmpty)
1850 1851 1852 1853
                                            {
                                                continue;
                                            }

1854
                                            if (!analyzerExecutor.TryExecuteOperationBlockActions(
J
John Hamby 已提交
1855
                                                analyzerActions.OperationBlockStartActions, analyzerActions.OperationBlockActions,
C
Charles Stoner 已提交
1856
                                                analyzerActions.OperationBlockEndActions, analyzerActions.Analyzer, declarationAnalysisData.TopmostNodeForAnalysis, symbol,
1857 1858 1859 1860
                                                operationBlocksToAnalyze, operationsToAnalyze, semanticModel, decl, declarationIndex, analysisScope, analysisStateOpt, isInGeneratedCode))
                                            {
                                                success = false;
                                            }
J
John Hamby 已提交
1861 1862 1863 1864 1865
                                        }
                                    }
                                }
                            }

1866 1867
                            break;
                        }
1868 1869 1870
                    }
                }

J
John Hamby 已提交
1871
                if (executableCodeBlocks.Any() && shouldExecuteCodeBlockActions)
1872
                {
J
John Hamby 已提交
1873
                    foreach (var analyzerActions in codeBlockActions)
1874
                    {
1875 1876 1877 1878 1879 1880 1881
                        if (analyzerActions.CodeBlockStartActions.IsEmpty &&
                            analyzerActions.CodeBlockActions.IsEmpty &&
                            analyzerActions.CodeBlockEndActions.IsEmpty)
                        {
                            continue;
                        }

1882
                        if (!analyzerExecutor.TryExecuteCodeBlockActions(
1883 1884
                            analyzerActions.CodeBlockStartActions, analyzerActions.CodeBlockActions,
                            analyzerActions.CodeBlockEndActions, analyzerActions.Analyzer, declarationAnalysisData.TopmostNodeForAnalysis, symbol,
1885 1886 1887 1888
                            executableCodeBlocks, semanticModel, _getKind, decl, declarationIndex, analysisScope, analysisStateOpt, isInGeneratedCode))
                        {
                            success = false;
                        }
1889
                    }
1890 1891
                }
            }
1892

1893 1894
            // Mark completion if we successfully executed all actions and only if we are analyzing a span containing the entire syntax node.
            if (success && analysisStateOpt != null && !declarationAnalysisData.IsPartialAnalysis)
1895 1896 1897
            {
                foreach (var analyzer in analysisScope.Analyzers)
                {
1898
                    analysisStateOpt.MarkDeclarationComplete(symbol, declarationIndex, analyzer);
1899
                }
1900 1901 1902

                if (cacheAnalysisData)
                {
1903
                    ClearCachedAnalysisDataIfAnalyzed(decl, symbol, declarationIndex, analysisStateOpt);
1904
                }
1905
            }
1906 1907

            return success;
1908 1909
        }

V
Vladimir Reshetnikov 已提交
1910
        [StructLayout(LayoutKind.Auto)]
1911 1912 1913 1914 1915 1916
        private struct CodeBlockAnalyzerActions
        {
            public DiagnosticAnalyzer Analyzer;
            public ImmutableArray<CodeBlockStartAnalyzerAction<TLanguageKindEnum>> CodeBlockStartActions;
            public ImmutableArray<CodeBlockAnalyzerAction> CodeBlockActions;
            public ImmutableArray<CodeBlockAnalyzerAction> CodeBlockEndActions;
J
John Hamby 已提交
1917 1918
            public ImmutableArray<OperationBlockStartAnalyzerAction> OperationBlockStartActions;
            public ImmutableArray<OperationBlockAnalyzerAction> OperationBlockActions;
C
Charles Stoner 已提交
1919
            public ImmutableArray<OperationBlockAnalyzerAction> OperationBlockEndActions;
1920 1921
        }

1922
        private IEnumerable<CodeBlockAnalyzerActions> GetCodeBlockActions(AnalysisScope analysisScope)
1923
        {
1924
            foreach (var analyzer in analysisScope.Analyzers)
1925
            {
1926 1927 1928 1929 1930 1931
                ImmutableArray<CodeBlockStartAnalyzerAction<TLanguageKindEnum>> codeBlockStartActions;
                if (!this.CodeBlockStartActionsByAnalyzer.TryGetValue(analyzer, out codeBlockStartActions))
                {
                    codeBlockStartActions = ImmutableArray<CodeBlockStartAnalyzerAction<TLanguageKindEnum>>.Empty;
                }

1932
                ImmutableArray<CodeBlockAnalyzerAction> codeBlockActions;
1933
                if (!this.CodeBlockActionsByAnalyzer.TryGetValue(analyzer, out codeBlockActions))
1934 1935 1936 1937 1938
                {
                    codeBlockActions = ImmutableArray<CodeBlockAnalyzerAction>.Empty;
                }

                ImmutableArray<CodeBlockAnalyzerAction> codeBlockEndActions;
1939
                if (!this.CodeBlockEndActionsByAnalyzer.TryGetValue(analyzer, out codeBlockEndActions))
1940 1941 1942
                {
                    codeBlockEndActions = ImmutableArray<CodeBlockAnalyzerAction>.Empty;
                }
1943

J
John Hamby 已提交
1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962
                ImmutableArray<OperationBlockStartAnalyzerAction> operationBlockStartActions;
                if (!this.OperationBlockStartActionsByAnalyzer.TryGetValue(analyzer, out operationBlockStartActions))
                {
                    operationBlockStartActions = ImmutableArray<OperationBlockStartAnalyzerAction>.Empty;
                }

                ImmutableArray<OperationBlockAnalyzerAction> operationBlockActions;
                if (!this.OperationBlockActionsByAnalyzer.TryGetValue(analyzer, out operationBlockActions))
                {
                    operationBlockActions = ImmutableArray<OperationBlockAnalyzerAction>.Empty;
                }

                ImmutableArray<OperationBlockAnalyzerAction> operationBlockEndActions;
                if (!this.OperationBlockEndActionsByAnalyzer.TryGetValue(analyzer, out operationBlockEndActions))
                {
                    operationBlockEndActions = ImmutableArray<OperationBlockAnalyzerAction>.Empty;
                }

                if (!codeBlockStartActions.IsEmpty || !codeBlockActions.IsEmpty || !codeBlockEndActions.IsEmpty || !operationBlockStartActions.IsEmpty || !operationBlockActions.IsEmpty || !operationBlockEndActions.IsEmpty)
1963 1964
                {
                    yield return
V
Vladimir Reshetnikov 已提交
1965
                        new CodeBlockAnalyzerActions
1966
                        {
1967 1968 1969
                            Analyzer = analyzer,
                            CodeBlockStartActions = codeBlockStartActions,
                            CodeBlockActions = codeBlockActions,
J
John Hamby 已提交
1970 1971 1972
                            CodeBlockEndActions = codeBlockEndActions,
                            OperationBlockStartActions = operationBlockStartActions,
                            OperationBlockActions = operationBlockActions,
C
Charles Stoner 已提交
1973
                            OperationBlockEndActions = operationBlockEndActions
1974 1975 1976
                        };
                }
            }
1977 1978
        }

1979
        private static IEnumerable<SyntaxNode> GetSyntaxNodesToAnalyze(
1980 1981 1982
            SyntaxNode declaredNode,
            ISymbol declaredSymbol,
            IEnumerable<DeclarationInfo> declarationsInNode,
1983 1984
            AnalysisScope analysisScope,
            bool isPartialDeclAnalysis,
1985
            SemanticModel semanticModel,
M
Manish Vasani 已提交
1986
            AnalyzerExecutor analyzerExecutor)
1987
        {
J
John Hamby 已提交
1988 1989
            // Eliminate descendant member declarations within declarations.
            // There will be separate symbols declared for the members.
1990 1991 1992 1993
            HashSet<SyntaxNode> descendantDeclsToSkip = null;
            bool first = true;
            foreach (var declInNode in declarationsInNode)
            {
1994 1995
                analyzerExecutor.CancellationToken.ThrowIfCancellationRequested();

1996 1997
                if (declInNode.DeclaredNode != declaredNode)
                {
1998 1999 2000 2001 2002 2003
                    // Might be:
                    // (1) A field declaration statement with multiple fields declared.
                    //     If so, we execute syntax node analysis for entire field declaration (and its descendants)
                    //     if we processing the first field and skip syntax actions for remaining fields in the declaration.
                    // (2) A namespace declaration statement with qualified name "namespace A.B { }"
                    if (IsEquivalentSymbol(declaredSymbol, declInNode.DeclaredSymbol))
2004
                    {
V
Vladimir Reshetnikov 已提交
2005
                        if (first)
2006
                        {
V
Vladimir Reshetnikov 已提交
2007
                            break;
2008
                        }
V
Vladimir Reshetnikov 已提交
2009

2010
                        return ImmutableArray<SyntaxNode>.Empty;
2011 2012 2013 2014
                    }

                    // Compute the topmost node representing the syntax declaration for the member that needs to be skipped.
                    var declarationNodeToSkip = declInNode.DeclaredNode;
M
Manish Vasani 已提交
2015
                    var declaredSymbolOfDeclInNode = declInNode.DeclaredSymbol ?? semanticModel.GetDeclaredSymbol(declInNode.DeclaredNode, analyzerExecutor.CancellationToken);
2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029
                    if (declaredSymbolOfDeclInNode != null)
                    {
                        declarationNodeToSkip = semanticModel.GetTopmostNodeForDiagnosticAnalysis(declaredSymbolOfDeclInNode, declInNode.DeclaredNode);
                    }

                    descendantDeclsToSkip = descendantDeclsToSkip ?? new HashSet<SyntaxNode>();
                    descendantDeclsToSkip.Add(declarationNodeToSkip);
                }

                first = false;
            }

            var nodesToAnalyze = descendantDeclsToSkip == null ?
                declaredNode.DescendantNodesAndSelf(descendIntoTrivia: true) :
M
mavasani 已提交
2030
                GetSyntaxNodesToAnalyze(declaredNode, descendantDeclsToSkip);
2031 2032 2033 2034 2035 2036

            if (isPartialDeclAnalysis)
            {
                nodesToAnalyze = nodesToAnalyze.Where(node => analysisScope.ShouldAnalyze(node));
            }

2037
            return nodesToAnalyze;
2038
        }
M
mavasani 已提交
2039

2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055
        private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol otherSymbol)
        {
            if (declaredSymbol.Equals(otherSymbol))
            {
                return true;
            }

            // GetSymbolInfo(name syntax) for "A" in "namespace A.B { }" sometimes returns a symbol which doesn't match
            // the symbol declared in the compilation. So we do an equivalence check for such namespace symbols.
            return otherSymbol != null &&
                declaredSymbol.Kind == SymbolKind.Namespace &&
                otherSymbol.Kind == SymbolKind.Namespace &&
                declaredSymbol.Name == otherSymbol.Name &&
                declaredSymbol.ToDisplayString() == otherSymbol.ToDisplayString();
        }

J
John Hamby 已提交
2056 2057 2058 2059 2060 2061 2062 2063 2064
        private static ImmutableArray<IOperation> GetOperationBlocksToAnalyze(
            ImmutableArray<SyntaxNode> executableBlocks,
            SemanticModel semanticModel,
            CancellationToken cancellationToken)
        {
            ArrayBuilder<IOperation> operationBlocksToAnalyze = ArrayBuilder<IOperation>.GetInstance();

            foreach (SyntaxNode executableBlock in executableBlocks)
            {
2065
                IOperation operation = semanticModel.GetOperation(executableBlock, cancellationToken);
J
John Hamby 已提交
2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087
                if (operation != null)
                {
                    operationBlocksToAnalyze.AddRange(operation);
                }
            }

            return operationBlocksToAnalyze.ToImmutableAndFree();
        }

        private static ImmutableArray<IOperation> GetOperationsToAnalyze(
            ImmutableArray<IOperation> operationBlocks)
        {
            ArrayBuilder<IOperation> operationsToAnalyze = ArrayBuilder<IOperation>.GetInstance();

            foreach (IOperation operationBlock in operationBlocks)
            {
                operationsToAnalyze.AddRange(operationBlock.DescendantsAndSelf());
            }

            return operationsToAnalyze.ToImmutableAndFree();
        }

M
mavasani 已提交
2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100
        private static IEnumerable<SyntaxNode> GetSyntaxNodesToAnalyze(SyntaxNode declaredNode, HashSet<SyntaxNode> descendantDeclsToSkip)
        {
            Debug.Assert(declaredNode != null);
            Debug.Assert(descendantDeclsToSkip != null);

            foreach (var node in declaredNode.DescendantNodesAndSelf(n => !descendantDeclsToSkip.Contains(n), descendIntoTrivia: true))
            {
                if (!descendantDeclsToSkip.Contains(node))
                {
                    yield return node;
                }
            }
        }
2101 2102
    }
}