File: Microsoft.CodeQuality.Analyzers\QualityGuidelines\DoNotRaiseExceptionsInExceptionClauses.cs
Web Access
Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Collections.Immutable;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeQuality.Analyzers.QualityGuidelines
{
    using static MicrosoftCodeQualityAnalyzersResources;

    /// <summary>
    /// CA2219: <inheritdoc cref="DoNotRaiseExceptionsInExceptionClausesTitle"/>
    /// </summary>
    /// <remarks>
    /// The original FxCop implementation of this rule finds violations of this rule inside 
    /// filter and fault blocks. However in both C# and VB there's no way to throw an exception
    /// inside a filter block and there is no language representation for fault blocks in either language.
    /// So this analyzer just checks for throw statements inside finally blocks.
    /// </remarks>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed class DoNotRaiseExceptionsInExceptionClausesAnalyzer : DiagnosticAnalyzer
    {
        internal const string RuleId = "CA2219";

        internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
            RuleId,
            CreateLocalizableResourceString(nameof(DoNotRaiseExceptionsInExceptionClausesTitle)),
            CreateLocalizableResourceString(nameof(DoNotRaiseExceptionsInExceptionClausesMessageFinally)),
            DiagnosticCategory.Usage,
            RuleLevel.IdeSuggestion,
            description: CreateLocalizableResourceString(nameof(DoNotRaiseExceptionsInExceptionClausesDescription)),
            isPortedFxCopRule: true,
            isDataflowRule: false);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);

        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterOperationBlockAction(operationBlockContext =>
            {
                foreach (var block in operationBlockContext.OperationBlocks)
                {
                    var walker = new ThrowInsideFinallyWalker();
                    walker.Visit(block);

                    foreach (var throwStatement in walker.ThrowExpressions)
                    {
                        operationBlockContext.ReportDiagnostic(throwStatement.Syntax.CreateDiagnostic(Rule));
                    }
                }
            });
        }

        /// <summary>
        /// Walks an IOperation tree to find throw expressions inside finally blocks.
        /// </summary>
        private class ThrowInsideFinallyWalker : OperationWalker
        {
            private int _finallyBlockNestingDepth;

            public List<IThrowOperation> ThrowExpressions { get; private set; } = new List<IThrowOperation>();

            public override void VisitTry(ITryOperation operation)
            {
                Visit(operation.Body);
                foreach (var catchClause in operation.Catches)
                {
                    Visit(catchClause);
                }

                _finallyBlockNestingDepth++;
                Visit(operation.Finally);
                _finallyBlockNestingDepth--;
            }

            public override void VisitThrow(IThrowOperation operation)
            {
                if (_finallyBlockNestingDepth > 0)
                {
                    ThrowExpressions.Add(operation);
                }

                base.VisitThrow(operation);
            }
        }
    }
}