File: Lowering\LocalRewriter\LocalRewriter_TryStatement.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Symbols;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    internal sealed partial class LocalRewriter
    {
        public override BoundNode VisitTryStatement(BoundTryStatement node)
        {
            BoundBlock? tryBlock = (BoundBlock?)this.Visit(node.TryBlock);
            Debug.Assert(tryBlock is { });
 
            var origSawAwait = _sawAwait;
            _sawAwait = false;
 
            var optimizing = _compilation.Options.OptimizationLevel == OptimizationLevel.Release;
            ImmutableArray<BoundCatchBlock> catchBlocks =
                // When optimizing and we have a try block without side-effects, we can discard the catch blocks.
                (optimizing && !HasSideEffects(tryBlock)) ? ImmutableArray<BoundCatchBlock>.Empty
                : this.VisitList(node.CatchBlocks);
            BoundBlock? finallyBlockOpt = (BoundBlock?)this.Visit(node.FinallyBlockOpt);
 
            _sawAwaitInExceptionHandler |= _sawAwait;
            _sawAwait |= origSawAwait;
 
            if (optimizing && !HasSideEffects(finallyBlockOpt))
            {
                finallyBlockOpt = null;
            }
 
            return (catchBlocks.IsDefaultOrEmpty && finallyBlockOpt == null)
                ? (BoundNode)tryBlock
                : (BoundNode)node.Update(tryBlock, catchBlocks, finallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler);
        }
 
        /// <summary>
        /// Is there any code to execute in the given statement that could have side-effects,
        /// such as throwing an exception? This implementation is conservative, in the sense
        /// that it may return true when the statement actually may have no side effects.
        /// </summary>
        private static bool HasSideEffects([NotNullWhen(true)] BoundStatement? statement)
        {
            if (statement == null) return false;
            switch (statement.Kind)
            {
                case BoundKind.NoOpStatement:
                    return false;
                case BoundKind.Block:
                    {
                        var block = (BoundBlock)statement;
                        foreach (var stmt in block.Statements)
                        {
                            if (HasSideEffects(stmt)) return true;
                        }
                        return false;
                    }
                case BoundKind.SequencePoint:
                    {
                        var sequence = (BoundSequencePoint)statement;
                        return HasSideEffects(sequence.StatementOpt);
                    }
                case BoundKind.SequencePointWithSpan:
                    {
                        var sequence = (BoundSequencePointWithSpan)statement;
                        return HasSideEffects(sequence.StatementOpt);
                    }
                default:
                    return true;
            }
        }
 
        public override BoundNode? VisitCatchBlock(BoundCatchBlock node)
        {
            if (node.ExceptionFilterOpt?.ConstantValueOpt?.BooleanValue == false)
            {
                return null;
            }
 
            BoundExpression? rewrittenExceptionSourceOpt = (BoundExpression?)this.Visit(node.ExceptionSourceOpt);
            BoundStatementList? rewrittenFilterPrologue = (BoundStatementList?)this.Visit(node.ExceptionFilterPrologueOpt);
            BoundExpression? rewrittenFilter = (BoundExpression?)this.Visit(node.ExceptionFilterOpt);
            BoundBlock? rewrittenBody = (BoundBlock?)this.Visit(node.Body);
            Debug.Assert(rewrittenBody is { });
            TypeSymbol? rewrittenExceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt);
 
            if (Instrument)
            {
                Instrumenter.InstrumentCatchBlock(
                    node,
                    ref rewrittenExceptionSourceOpt,
                    ref rewrittenFilterPrologue,
                    ref rewrittenFilter,
                    ref rewrittenBody,
                    ref rewrittenExceptionTypeOpt,
                    _factory);
            }
 
            return node.Update(
                node.Locals,
                rewrittenExceptionSourceOpt,
                rewrittenExceptionTypeOpt,
                rewrittenFilterPrologue,
                rewrittenFilter,
                rewrittenBody,
                node.IsSynthesizedAsyncCatchAll);
        }
    }
}