From 3f4dbb52a63318846010a5dc8d7f53e772483263 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 27 Mar 2018 10:02:40 -0700 Subject: [PATCH] Add BasicBlock.IsReachable property. (#25698) --- .../IOperationTests_IBranchOperation.cs | 2 +- .../IOperationTests_ICoalesceOperation.cs | 8 +- .../IOperationTests_IIfStatement.cs | 2 +- .../IOperationTests_IReturnStatement.cs | 8 +- .../IOperationTests_IThrowOperation.cs | 54 +- .../IOperation/IOperationTests_TryCatch.cs | 4357 ++++++++++++++++- .../Core/Portable/Operations/BasicBlock.cs | 2 + .../Operations/ControlFlowGraph.Region.cs | 5 + .../Operations/ControlFlowGraphBuilder.cs | 354 +- .../Core/Portable/PublicAPI.Unshipped.txt | 1 + .../IOperationTests_IBranchOperation.vb | 80 +- .../IOperationTests_ICoalesceOperation.vb | 6 +- .../IOperationTests_IEndOperation.vb | 20 +- .../IOperation/IOperationTests_TryCatch.vb | 215 + .../Compilation/ControlFlowGraphVerifier.cs | 11 +- 15 files changed, 5006 insertions(+), 119 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IBranchOperation.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IBranchOperation.cs index ec3769076a7..54d78da67ea 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IBranchOperation.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IBranchOperation.cs @@ -1050,7 +1050,7 @@ void M(bool x) Leaving: {R6} {R1} } -Block[B5] - Block +Block[B5] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true;') diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ICoalesceOperation.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ICoalesceOperation.cs index aea169444bd..65c96aa0265 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ICoalesceOperation.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ICoalesceOperation.cs @@ -622,7 +622,7 @@ void F(dynamic alternative, dynamic result) IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: null, Constant: null, IsImplicit) (Syntax: 'null') Next (Regular) Block[B2] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'null') @@ -717,7 +717,7 @@ void F(int alternative, int result) IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: null, Constant: null, IsInvalid, IsImplicit) (Syntax: 'null') Next (Regular) Block[B2] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'null') @@ -810,7 +810,7 @@ void F(int? alternative, int? result) IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: null, Constant: null, IsImplicit) (Syntax: 'null') Next (Regular) Block[B2] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'null') @@ -1203,7 +1203,7 @@ void F(object alternative, object result) IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.String, Constant: ""a"", IsImplicit) (Syntax: 'input') Next (Regular) Block[B4] -Block[B3] - Block +Block[B3] - Block [UnReachable] Predecessors: [B1] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'alternative') diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IIfStatement.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IIfStatement.cs index 7b825f0f20b..fef4533183f 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IIfStatement.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IIfStatement.cs @@ -1277,7 +1277,7 @@ void M() Next (Regular) Block[B3] Leaving: {R1} - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'condition = true;') diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IReturnStatement.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IReturnStatement.cs index c6d44db8db8..9ebc81f296f 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IReturnStatement.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IReturnStatement.cs @@ -223,7 +223,7 @@ void F(bool a) Block[B0] - Entry Statements (0) Next (Regular) Block[B2] -Block[B1] - Block +Block[B1] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'a = true;') @@ -273,7 +273,7 @@ int F(bool a) Statements (0) Next (Return) Block[B3] ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'a = true;') @@ -624,7 +624,7 @@ int F() Statements (0) Next (Return) Block[B3] ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Next (Return) Block[B3] @@ -1189,7 +1189,7 @@ System.Collections.Generic.IEnumerable F(bool a) Block[B0] - Entry Statements (0) Next (Regular) Block[B2] -Block[B1] - Block +Block[B1] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'a = true;') diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IThrowOperation.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IThrowOperation.cs index bf28e7d8b6f..ffe6c0539a1 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IThrowOperation.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IThrowOperation.cs @@ -39,7 +39,7 @@ void F() Predecessors: [B0] Statements (0) Next (ReThrow) Block[null] -Block[B2] - Exit +Block[B2] - Exit [UnReachable] Predecessors (0) Statements (0) "; @@ -88,7 +88,7 @@ void F(int x) ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Next (ReThrow) Block[null] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') @@ -100,7 +100,7 @@ void F(int x) ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') Next (Regular) Block[B3] -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B2] Statements (0) "; @@ -133,7 +133,7 @@ void F(System.Exception ex) Statements (0) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') -Block[B2] - Exit +Block[B2] - Exit [UnReachable] Predecessors (0) Statements (0) "; @@ -186,7 +186,7 @@ void F(System.Exception ex) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') @@ -201,7 +201,7 @@ void F(System.Exception ex) Leaving: {R1} } -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B2] Statements (0) "; @@ -249,7 +249,7 @@ void F(int x, System.Exception ex) IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception, IsInvalid) (Syntax: 'ex') Right: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x = throw ex + x;') @@ -267,7 +267,7 @@ void F(int x, System.Exception ex) IOperation: (OperationKind.None, Type: null, IsInvalid, IsImplicit) (Syntax: 'throw ex + x') Next (Regular) Block[B3] -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B2] Statements (0) "; @@ -308,7 +308,7 @@ void F(int x, System.Exception ex) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x = (throw ex) + x;') @@ -330,7 +330,7 @@ void F(int x, System.Exception ex) IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') Next (Regular) Block[B3] -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B2] Statements (0) "; @@ -375,7 +375,7 @@ void F(int x, System.Exception ex) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception, IsInvalid) (Syntax: 'ex') -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x = x + throw ex;') @@ -397,7 +397,7 @@ void F(int x, System.Exception ex) IOperation: (OperationKind.None, Type: null, IsInvalid, IsImplicit) (Syntax: 'throw ex') Next (Regular) Block[B3] -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B2] Statements (0) "; @@ -442,7 +442,7 @@ void F(int x, System.Exception ex) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x = x + (throw ex);') @@ -464,7 +464,7 @@ void F(int x, System.Exception ex) IOperation: (OperationKind.None, Type: null, IsInvalid, IsImplicit) (Syntax: 'throw ex') Next (Regular) Block[B3] -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B2] Statements (0) "; @@ -522,7 +522,7 @@ void F(object x, object y, System.Exception ex) Statements (0) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') -Block[B4] - Block +Block[B4] - Block [UnReachable] Predecessors (0) Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'throw ex') @@ -606,7 +606,7 @@ void F(object x, object y, object z, System.Exception ex) Statements (0) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') -Block[B4] - Block +Block[B4] - Block [UnReachable] Predecessors (0) Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'throw ex') @@ -779,7 +779,7 @@ void F(int u) ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') Next (ReThrow) Block[null] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'u = 3;') @@ -885,7 +885,7 @@ void F(object x, object y, object z, int u) Next (Throw) Block[null] IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '') Children(0) - Block[B5] - Block + Block[B5] - Block [UnReachable] Predecessors (0) Statements (1) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'throw') @@ -958,7 +958,7 @@ void F(System.Exception ex) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') -Block[B2] - Exit +Block[B2] - Exit [UnReachable] Predecessors (0) Statements (0) "; @@ -1072,7 +1072,7 @@ void F(System.Exception ex, bool a) Statements (0) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors (0) Statements (0) "; @@ -1195,7 +1195,7 @@ void F(System.Exception ex) Statements (0) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors (0) Statements (0) "; @@ -1345,7 +1345,7 @@ void F(System.Exception ex, int x) Statements (0) Next (Throw) Block[null] IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') @@ -1425,7 +1425,7 @@ void F(int x) Predecessors: [B3] Statements (0) Next (ReThrow) Block[null] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') @@ -1486,7 +1486,7 @@ int F(bool a, System.Exception ex1, System.Exception ex2) Statements (0) Next (Throw) Block[null] IParameterReferenceOperation: ex2 (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex2') -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors (0) Statements (0) "; @@ -1541,7 +1541,7 @@ void F(int x, System.Exception ex1, System.Exception ex2, bool a) Statements (0) Next (Throw) Block[null] IParameterReferenceOperation: ex2 (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex2') -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors (0) Statements (0) "; @@ -1843,7 +1843,7 @@ void F(int u) Next (ReThrow) Block[null] } -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B1] Statements (0) "; @@ -1906,7 +1906,7 @@ void F(int u, System.Exception ex) IParameterReferenceOperation: ex (OperationKind.ParameterReference, Type: System.Exception) (Syntax: 'ex') } -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B1] Statements (0) "; diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_TryCatch.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_TryCatch.cs index 8a36af99007..bb77ff101ea 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_TryCatch.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_TryCatch.cs @@ -989,7 +989,7 @@ void F() } .catch {R3} (System.Object) { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B3] @@ -1437,7 +1437,7 @@ void F() } .catch {R3} (System.Object) { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B3] @@ -1579,7 +1579,7 @@ void F() .catch {R5} (Exception1) { Locals: [Exception1 e] - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: '(Exception1 e)') @@ -1594,7 +1594,7 @@ void F() .locals {R6} { Locals: [System.Int32 j] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'j = 2;') @@ -1691,7 +1691,7 @@ void F() .catch {R3} (Exception1) { Locals: [Exception1 e] - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: '(Exception1 e)') @@ -1759,7 +1759,7 @@ void F() Locals: [Exception1 e] [System.Int32 i] .filter {R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: '(Exception1 e)') @@ -1786,7 +1786,7 @@ void F() .handler {R5} { Locals: [System.Int32 j] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'j = 2;') @@ -1856,7 +1856,7 @@ void F() Locals: [Exception1 e] [System.Int32 i] .filter {R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: '(Exception1 e)') @@ -1882,7 +1882,7 @@ void F() } .handler {R5} { - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (0) Next (Regular) Block[B4] @@ -1945,7 +1945,7 @@ void F() Locals: [System.Int32 i] .filter {R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Jump if True (Regular) to Block[B3] @@ -1966,7 +1966,7 @@ void F() .handler {R5} { Locals: [System.Int32 j] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'j = 2;') @@ -2036,7 +2036,7 @@ void F() Locals: [System.Int32 i] .filter {R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Jump if True (Regular) to Block[B3] @@ -2056,7 +2056,7 @@ void F() } .handler {R5} { - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (0) Next (Regular) Block[B4] @@ -2119,7 +2119,7 @@ void F() Locals: [System.Int32 i] .filter {R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Jump if True (Regular) to Block[B3] @@ -2140,7 +2140,7 @@ void F() .handler {R5} { Locals: [System.Int32 j] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'j = 2;') @@ -2210,7 +2210,7 @@ void F() Locals: [System.Int32 i] .filter {R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Jump if True (Regular) to Block[B3] @@ -2230,7 +2230,7 @@ void F() } .handler {R5} { - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (0) Next (Regular) Block[B4] @@ -2289,7 +2289,7 @@ void F() .catch {R3} (Exception1) { Locals: [System.Int32 j] - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'j = 2;') @@ -2353,7 +2353,7 @@ void F() } .catch {R3} (Exception1) { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B3] @@ -2415,7 +2415,7 @@ void F() Locals: [System.Int32 i] .filter {R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Jump if True (Regular) to Block[B3] @@ -2436,7 +2436,7 @@ void F() .handler {R5} { Locals: [System.Int32 j] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'j = 2;') @@ -2506,7 +2506,7 @@ void F() Locals: [System.Int32 i] .filter {R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Jump if True (Regular) to Block[B3] @@ -2526,7 +2526,7 @@ void F() } .handler {R5} { - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors: [B2] Statements (0) Next (Regular) Block[B4] @@ -3399,6 +3399,4317 @@ void F() VerifyFlowGraphForTest(compilation, expectedGraph); } + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_01() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B3] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Exit + Predecessors: [B1] [B2] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_02() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + if (ThisCanThrow()) return; + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R2} {R1} + + Next (Regular) Block[B3] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Exit + Predecessors: [B1] [B2] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_03() + { + string source = @" +class P +{ + bool M(bool b) +/**/{ + try + { + return ThisCanThrow(); + } + catch + { + b = true; + } + + return false; + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B4] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Return) Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') +Block[B4] - Exit + Predecessors: [B1] [B3] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_04() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + throw null; + } + catch + { + b = true; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Exit + Predecessors: [B2] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_05() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + if (true) throw null; + } + catch + { + b = true; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R2} {R1} + + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Exit + Predecessors: [B1] [B2] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_06() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + if (false) throw null; + } + catch + { + b = true; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + Leaving: {R2} {R1} + + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') +} +.catch {R3} (System.Object) +{ + Block[B2] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Exit + Predecessors: [B1] [B2] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_07() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch + { + try + { + if (true) throw; + } + catch + { + b = true; + } + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .try {R4, R5} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R5} {R4} {R3} {R1} + + Next (ReThrow) Block[null] + } + .catch {R6} (System.Object) + { + Block[B3] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Leaving: {R6} {R4} {R3} {R1} + } +} + +Block[B4] - Exit + Predecessors: [B1] [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_08() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch + { + try + { + if (false) throw; + } + catch + { + b = true; + } + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .try {R4, R5} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + Leaving: {R5} {R4} {R3} {R1} + + Next (ReThrow) Block[null] + } + .catch {R6} (System.Object) + { + Block[B3] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Leaving: {R6} {R4} {R3} {R1} + } +} + +Block[B4] - Exit + Predecessors: [B1] [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_09() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + if (false) return; + } + catch + { + b = true; + } + }/**/ + + static bool[] ThisCanThrow() => null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean[] P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean[]) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + Leaving: {R2} {R1} + + Next (Regular) Block[B3] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Exit + Predecessors: [B1] [B2] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(9,24): warning CS0162: Unreachable code detected + // if (false) return; + Diagnostic(ErrorCode.WRN_UnreachableCode, "return").WithLocation(9, 24) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_10() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + if (true) return; + } + catch + { + b = true; + } + }/**/ + + static bool[] ThisCanThrow() => null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean[] P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean[]) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R2} {R1} + + Next (Regular) Block[B3] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Exit + Predecessors: [B1] [B2] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_11() + { + string source = @" +class P +{ + bool[] M(bool b) +/**/{ + try + { + if (true) return ThisCanThrow(); + } + catch + { + b = true; + } + + return null; + }/**/ + + static bool[] ThisCanThrow() => null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R2} {R1} + + Next (Return) Block[B4] + IInvocationOperation (System.Boolean[] P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean[]) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Block + Predecessors: [B1] [B2] + Statements (0) + Next (Return) Block[B4] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Boolean[], Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') +Block[B4] - Exit + Predecessors: [B1] [B3] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_12() + { + string source = @" +class P +{ + bool[] M(bool b) +/**/{ + try + { + if (false) return ThisCanThrow(); + } + catch + { + b = true; + } + + return null; + }/**/ + + static bool[] ThisCanThrow() => null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + Leaving: {R2} {R1} + + Next (Return) Block[B4] + IInvocationOperation (System.Boolean[] P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean[]) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + Block[B2] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B3] + Leaving: {R3} {R1} +} + +Block[B3] - Block + Predecessors: [B1] [B2] + Statements (0) + Next (Return) Block[B4] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Boolean[], Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') +Block[B4] - Exit + Predecessors: [B1] [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(8,24): warning CS0162: Unreachable code detected + // if (false) return ThisCanThrow(); + Diagnostic(ErrorCode.WRN_UnreachableCode, "return").WithLocation(8, 24) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_13() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + } + finally + { + ThisCanThrow(); + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B4] + Finalizing: {R5} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (StructuredExceptionHandling) Block[null] + } +} +.catch {R6} (System.Object) +{ + Block[B3] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Leaving: {R6} {R1} +} + +Block[B4] - Exit + Predecessors: [B1] [B3] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_14() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Next (Regular) Block[B4] + Leaving: {R5} {R3} {R2} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B3] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Leaving: {R6} {R1} +} + +Block[B4] - Exit + Predecessors: [B1] [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_15() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch when (true) + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B5] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + .filter {R6} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R6} + Entering: {R7} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R7} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Leaving: {R7} {R5} {R3} {R2} {R1} + } + } +} +.catch {R8} (System.Object) +{ + Block[B4] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B5] + Leaving: {R8} {R1} +} + +Block[B5] - Exit + Predecessors: [B1] [B3] [B4] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(12,25): warning CS7095: Filter expression is a constant 'true', consider removing the filter + // catch when (true) + Diagnostic(ErrorCode.WRN_FilterIsConstantTrue, "true").WithLocation(12, 25) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_16() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch when (false) + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B5] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + .filter {R6} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + Leaving: {R6} + Entering: {R7} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R7} + { + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Leaving: {R7} {R5} {R3} {R2} {R1} + } + } +} +.catch {R8} (System.Object) +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B5] + Leaving: {R8} {R1} +} + +Block[B5] - Exit + Predecessors: [B1] [B3] [B4] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(12,25): warning CS8360: Filter expression is a constant 'false', consider removing the try-catch block + // catch when (false) + Diagnostic(ErrorCode.WRN_FilterIsConstantFalseRedundantTryCatch, "false").WithLocation(12, 25) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_17() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch when (ThisCanThrow()) + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B5] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + .filter {R6} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B3] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R6} + Entering: {R7} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R7} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Leaving: {R7} {R5} {R3} {R2} {R1} + } + } +} +.catch {R8} (System.Object) +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B5] + Leaving: {R8} {R1} +} + +Block[B5] - Exit + Predecessors: [B1] [B3] [B4] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_18() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch when (ThisCanThrow() || true) + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B6] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + .filter {R6} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B4] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R6} + Entering: {R7} + + Next (Regular) Block[B3] + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Jump if True (Regular) to Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R6} + Entering: {R7} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R7} + { + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Next (Regular) Block[B6] + Leaving: {R7} {R5} {R3} {R2} {R1} + } + } +} +.catch {R8} (System.Object) +{ + Block[B5] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B6] + Leaving: {R8} {R1} +} + +Block[B6] - Exit + Predecessors: [B1] [B4] [B5] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_19() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch when (true || ThisCanThrow()) + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B6] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + .filter {R6} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R6} + Entering: {R7} + + Next (Regular) Block[B3] + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Jump if True (Regular) to Block[B4] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R6} + Entering: {R7} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R7} + { + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Next (Regular) Block[B6] + Leaving: {R7} {R5} {R3} {R2} {R1} + } + } +} +.catch {R8} (System.Object) +{ + Block[B5] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B6] + Leaving: {R8} {R1} +} + +Block[B6] - Exit + Predecessors: [B1] [B4] [B5] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_20() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch when (ThisCanThrow() && false) + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B7] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + .filter {R6} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B4] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B3] + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Jump if True (Regular) to Block[B5] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + Leaving: {R6} + Entering: {R7} + + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Next (StructuredExceptionHandling) Block[null] + } + .handler {R7} + { + Block[B5] - Block [UnReachable] + Predecessors: [B3] + Statements (0) + Next (Regular) Block[B7] + Leaving: {R7} {R5} {R3} {R2} {R1} + } + } +} +.catch {R8} (System.Object) +{ + Block[B6] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B7] + Leaving: {R8} {R1} +} + +Block[B7] - Exit + Predecessors: [B1] [B5] [B6] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_21() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch when (false && ThisCanThrow()) + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B7] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + .filter {R6} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + + Next (Regular) Block[B3] + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Jump if True (Regular) to Block[B5] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R6} + Entering: {R7} + + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Next (StructuredExceptionHandling) Block[null] + } + .handler {R7} + { + Block[B5] - Block [UnReachable] + Predecessors: [B3] + Statements (0) + Next (Regular) Block[B7] + Leaving: {R7} {R5} {R3} {R2} {R1} + } + } +} +.catch {R8} (System.Object) +{ + Block[B6] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B7] + Leaving: {R8} {R1} +} + +Block[B7] - Exit + Predecessors: [B1] [B5] [B6] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_22() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + Block[B2] - Block + Predecessors (0) + Statements (0) + Next (Regular) Block[B4] + Leaving: {R3} {R1} +} +.catch {R4} (System.Object) +{ + Block[B3] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Leaving: {R4} {R1} +} + +Block[B4] - Exit + Predecessors: [B1] [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(13,9): error CS1017: Catch clauses cannot follow the general catch clause of a try statement + // catch + Diagnostic(ErrorCode.ERR_TooManyCatches, "catch").WithLocation(13, 9) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_23() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch when (true) + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B5] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .filter {R4} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R4} + Entering: {R5} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R5} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Leaving: {R5} {R3} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B4] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B5] + Leaving: {R6} {R1} +} + +Block[B5] - Exit + Predecessors: [B1] [B3] [B4] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(10,21): warning CS7095: Filter expression is a constant 'true', consider removing the filter + // catch when (true) + Diagnostic(ErrorCode.WRN_FilterIsConstantTrue, "true").WithLocation(10, 21) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_24() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch when (false) + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B5] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .filter {R4} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + Leaving: {R4} + Entering: {R5} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R5} + { + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Leaving: {R5} {R3} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B5] + Leaving: {R6} {R1} +} + +Block[B5] - Exit + Predecessors: [B1] [B3] [B4] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(10,21): warning CS8359: Filter expression is a constant 'false', consider removing the catch clause + // catch when (false) + Diagnostic(ErrorCode.WRN_FilterIsConstantFalse, "false").WithLocation(10, 21) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_25() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch when (ThisCanThrow()) + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B5] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .filter {R4} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B3] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R4} + Entering: {R5} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R5} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Leaving: {R5} {R3} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B5] + Leaving: {R6} {R1} +} + +Block[B5] - Exit + Predecessors: [B1] [B3] [B4] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_26() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch when (ThisCanThrow()) + { + } + catch + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B6] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + .filter {R6} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B3] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R6} + Entering: {R7} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R7} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B6] + Leaving: {R7} {R5} {R3} {R2} {R1} + } + } + .catch {R8} (System.Object) + { + Block[B4] - Block + Predecessors (0) + Statements (0) + Next (Regular) Block[B6] + Leaving: {R8} {R3} {R2} {R1} + } +} +.catch {R9} (System.Object) +{ + Block[B5] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B6] + Leaving: {R9} {R1} +} + +Block[B6] - Exit + Predecessors: [B1] [B3] [B4] [B5] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_27() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch when (ThisCanThrow() || true) + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B6] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .filter {R4} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B4] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R4} + Entering: {R5} + + Next (Regular) Block[B3] + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Jump if True (Regular) to Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R4} + Entering: {R5} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R5} + { + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Next (Regular) Block[B6] + Leaving: {R5} {R3} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B5] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B6] + Leaving: {R6} {R1} +} + +Block[B6] - Exit + Predecessors: [B1] [B4] [B5] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_28() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch when (true || ThisCanThrow()) + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B6] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .filter {R4} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R4} + Entering: {R5} + + Next (Regular) Block[B3] + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Jump if True (Regular) to Block[B4] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R4} + Entering: {R5} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R5} + { + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Next (Regular) Block[B6] + Leaving: {R5} {R3} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B5] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B6] + Leaving: {R6} {R1} +} + +Block[B6] - Exit + Predecessors: [B1] [B4] [B5] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_29() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch when (ThisCanThrow() && false) + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B7] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .filter {R4} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B4] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B3] + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Jump if True (Regular) to Block[B5] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + Leaving: {R4} + Entering: {R5} + + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Next (StructuredExceptionHandling) Block[null] + } + .handler {R5} + { + Block[B5] - Block [UnReachable] + Predecessors: [B3] + Statements (0) + Next (Regular) Block[B7] + Leaving: {R5} {R3} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B6] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B7] + Leaving: {R6} {R1} +} + +Block[B7] - Exit + Predecessors: [B1] [B5] [B6] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_30() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch when (false && ThisCanThrow()) + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B7] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .filter {R4} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B4] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + + Next (Regular) Block[B3] + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Jump if True (Regular) to Block[B5] + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + Leaving: {R4} + Entering: {R5} + + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Next (StructuredExceptionHandling) Block[null] + } + .handler {R5} + { + Block[B5] - Block [UnReachable] + Predecessors: [B3] + Statements (0) + Next (Regular) Block[B7] + Leaving: {R5} {R3} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B6] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B7] + Leaving: {R6} {R1} +} + +Block[B7] - Exit + Predecessors: [B1] [B5] [B6] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_31() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch + { + ThisCanThrow(); + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.Object) + { + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R5} {R3} {R2} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B3] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Leaving: {R6} {R1} +} + +Block[B4] - Exit + Predecessors: [B1] [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_32() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch (System.NullReferenceException e) + { + ThisCanThrow(); + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R2} {R1} +} +.catch {R3} (System.NullReferenceException) +{ + Locals: [System.NullReferenceException e] + Block[B2] - Block + Predecessors (0) + Statements (2) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: '(System.Nul ... xception e)') + Left: + ILocalReferenceOperation: e (IsDeclaration: True) (OperationKind.LocalReference, Type: System.NullReferenceException, IsImplicit) (Syntax: '(System.Nul ... xception e)') + Right: + ICaughtExceptionOperation (OperationKind.CaughtException, Type: System.NullReferenceException, IsImplicit) (Syntax: '(System.Nul ... xception e)') + + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R3} {R1} +} +.catch {R4} (System.Object) +{ + Block[B3] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Leaving: {R4} {R1} +} + +Block[B4] - Exit + Predecessors: [B1] [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(10,46): warning CS0168: The variable 'e' is declared but never used + // catch (System.NullReferenceException e) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(10, 46) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_33() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + try + { + ThisCanThrow(); + } + catch (System.NullReferenceException e) + { + } + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Leaving: {R4} {R3} {R2} {R1} + } + .catch {R5} (System.NullReferenceException) + { + Locals: [System.NullReferenceException e] + Block[B2] - Block + Predecessors (0) + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: '(System.Nul ... xception e)') + Left: + ILocalReferenceOperation: e (IsDeclaration: True) (OperationKind.LocalReference, Type: System.NullReferenceException, IsImplicit) (Syntax: '(System.Nul ... xception e)') + Right: + ICaughtExceptionOperation (OperationKind.CaughtException, Type: System.NullReferenceException, IsImplicit) (Syntax: '(System.Nul ... xception e)') + + Next (Regular) Block[B4] + Leaving: {R5} {R3} {R2} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B3] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Leaving: {R6} {R1} +} + +Block[B4] - Exit + Predecessors: [B1] [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(12,50): warning CS0168: The variable 'e' is declared but never used + // catch (System.NullReferenceException e) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(12, 50) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_34() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + } + finally + { + if (true) throw null; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B4] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B4] - Exit [UnReachable] + Predecessors: [B1] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_35() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + } + finally + { + if (false) throw null; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B4] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B4] - Exit + Predecessors: [B1] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_36() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + } + finally + { + if (b) throw null; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B4] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B3] + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B4] - Exit + Predecessors: [B1] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_37() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + } + finally + { + if (true) goto label1; + throw null; +label1: ; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B4] + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Block[B4] - Block + Predecessors: [B2] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B5] - Exit + Predecessors: [B1] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_38() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + } + finally + { + if (false) goto label1; + throw null; +label1: ; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R3} + Leaving: {R2} {R1} +} +.finally {R3} +{ + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Block[B4] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Next (StructuredExceptionHandling) Block[null] +} + +Block[B5] - Exit [UnReachable] + Predecessors: [B1] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(11,24): warning CS0162: Unreachable code detected + // if (false) goto label1; + Diagnostic(ErrorCode.WRN_UnreachableCode, "goto").WithLocation(11, 24), + // file.cs(13,1): warning CS0162: Unreachable code detected + // label1: ; + Diagnostic(ErrorCode.WRN_UnreachableCode, "label1").WithLocation(13, 1) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_39() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch + { + try + { + ThisCanThrow(); + } + finally + { + if (false) goto label1; + throw; + label1: ; + } + + b = false; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B5] + Leaving: {R2} {R1} +} +.catch {R3} (System.Object) +{ + .try {R4, R5} + { + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B4] + Finalizing: {R6} + Leaving: {R5} {R4} + } + .finally {R6} + { + Block[B3] - Block + Predecessors (0) + Statements (0) + Jump if False (ReThrow) to Block[null] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + + Next (StructuredExceptionHandling) Block[null] + } + + Block[B4] - Block [UnReachable] + Predecessors: [B2] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = false;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = false') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') + + Next (Regular) Block[B5] + Leaving: {R3} {R1} +} + +Block[B5] - Exit + Predecessors: [B1] [B4] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(19,17): error CS0724: A throw statement with no arguments is not allowed in a finally clause that is nested inside the nearest enclosing catch clause + // throw; + Diagnostic(ErrorCode.ERR_BadEmptyThrowInFinally, "throw").WithLocation(19, 17), + // file.cs(18,28): warning CS0162: Unreachable code detected + // if (false) goto label1; + Diagnostic(ErrorCode.WRN_UnreachableCode, "goto").WithLocation(18, 28), + // file.cs(20,5): warning CS0162: Unreachable code detected + // label1: ; + Diagnostic(ErrorCode.WRN_UnreachableCode, "label1").WithLocation(20, 5), + // file.cs(23,13): warning CS0162: Unreachable code detected + // b = false; + Diagnostic(ErrorCode.WRN_UnreachableCode, "b").WithLocation(23, 13) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_40() + { + string source = @" +class P +{ + void M(bool b) +/**/{ + try + { + ThisCanThrow(); + } + catch (System.NullReferenceException) when (true) + { + } + catch + { + b = true; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + +.try {R1, R2} +{ + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ThisCanThrow();') + Expression: + IInvocationOperation (System.Boolean P.ThisCanThrow()) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'ThisCanThrow()') + Instance Receiver: + null + Arguments(0) + + Next (Regular) Block[B5] + Leaving: {R2} {R1} +} +.catch {R3} (System.NullReferenceException) +{ + .filter {R4} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Jump if True (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + Leaving: {R4} + Entering: {R5} + + Next (StructuredExceptionHandling) Block[null] + } + .handler {R5} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Leaving: {R5} {R3} {R1} + } +} +.catch {R6} (System.Object) +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = true;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = true') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B5] + Leaving: {R6} {R1} +} + +Block[B5] - Exit + Predecessors: [B1] [B3] [B4] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(10,53): warning CS7095: Filter expression is a constant 'true', consider removing the filter + // catch (System.NullReferenceException) when (true) + Diagnostic(ErrorCode.WRN_FilterIsConstantTrue, "true").WithLocation(10, 53) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_41() + { + string source = @" +class P +{ + void M(int x) +/**/{ + try + { + try + { + throw null; + } + finally + { + x = 1; + } + + x = 2; + } + finally + { + x = 3; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + } + .finally {R5} + { + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 1;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 1') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + + Next (StructuredExceptionHandling) Block[null] + } + + Block[B3] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 2') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + + Next (Regular) Block[B5] + Finalizing: {R6} + Leaving: {R2} {R1} +} +.finally {R6} +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 3;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 3') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + + Next (StructuredExceptionHandling) Block[null] +} + +Block[B5] - Exit [UnReachable] + Predecessors: [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(17,13): warning CS0162: Unreachable code detected + // x = 2; + Diagnostic(ErrorCode.WRN_UnreachableCode, "x").WithLocation(17, 13) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_42() + { + string source = @" +class P +{ + void M(int x) +/**/{ + try + { + try + { + throw null; + } + finally + { + throw null; + } + + x = 2; + } + finally + { + x = 3; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + } + .finally {R5} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + } + + Block[B3] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 2') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + + Next (Regular) Block[B5] + Finalizing: {R6} + Leaving: {R2} {R1} +} +.finally {R6} +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 3;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 3') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + + Next (StructuredExceptionHandling) Block[null] +} + +Block[B5] - Exit [UnReachable] + Predecessors: [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(17,13): warning CS0162: Unreachable code detected + // x = 2; + Diagnostic(ErrorCode.WRN_UnreachableCode, "x").WithLocation(17, 13) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_43() + { + string source = @" +class P +{ + void M(int x) +/**/{ + try + { + try + { + throw null; + } + finally + { + while (true) {} + } + + x = 2; + } + finally + { + x = 3; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + } + .finally {R5} + { + Block[B2] - Block + Predecessors: [B2] + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B2] + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Next (StructuredExceptionHandling) Block[null] + } + + Block[B4] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 2') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + + Next (Regular) Block[B6] + Finalizing: {R6} + Leaving: {R2} {R1} +} +.finally {R6} +{ + Block[B5] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 3;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 3') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + + Next (StructuredExceptionHandling) Block[null] +} + +Block[B6] - Exit [UnReachable] + Predecessors: [B4] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(17,13): warning CS0162: Unreachable code detected + // x = 2; + Diagnostic(ErrorCode.WRN_UnreachableCode, "x").WithLocation(17, 13) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void ExceptionDispatch_44() + { + string source = @" +class P +{ + void M() +/**/{ + try + { + try + { + try + { + } + finally + { + return; + } + } + catch + { + } + } + finally + { + throw null; + } + }/**/ +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} {R5} {R6} + +.try {R1, R2} +{ + .try {R3, R4} + { + .try {R5, R6} + { + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R7} {R9} + Leaving: {R6} {R5} {R4} {R3} {R2} {R1} + } + .finally {R7} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R9} + Leaving: {R7} {R5} {R4} {R3} {R2} {R1} + } + } + .catch {R8} (System.Object) + { + Block[B3] - Block [UnReachable] + Predecessors (0) + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R9} + Leaving: {R8} {R3} {R2} {R1} + } +} +.finally {R9} +{ + Block[B4] - Block + Predecessors (0) + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') +} + +Block[B5] - Exit [UnReachable] + Predecessors: [B1] [B2] [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(15,21): error CS0157: Control cannot leave the body of a finally clause + // return; + Diagnostic(ErrorCode.ERR_BadFinallyLeave, "return").WithLocation(15, 21) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void FinallyDispatch_01() + { + string source = @" +class P +{ + void M(int x) +/**/{ + try + { + try + { + return; + } + finally + { + x = 1; + } + + x = 2; + } + finally + { + x = 3; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R5} {R6} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B2] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 1;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 1') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + + Next (StructuredExceptionHandling) Block[null] + } + + Block[B3] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 2') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + + Next (Regular) Block[B5] + Finalizing: {R6} + Leaving: {R2} {R1} +} +.finally {R6} +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 3;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 3') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + + Next (StructuredExceptionHandling) Block[null] +} + +Block[B5] - Exit + Predecessors: [B1] [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(17,13): warning CS0162: Unreachable code detected + // x = 2; + Diagnostic(ErrorCode.WRN_UnreachableCode, "x").WithLocation(17, 13) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void FinallyDispatch_02() + { + string source = @" +class P +{ + void M(int x) +/**/{ + try + { + try + { + return; + } + finally + { + throw null; + } + + x = 2; + } + finally + { + x = 3; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R5} {R6} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B2] - Block + Predecessors (0) + Statements (0) + Next (Throw) Block[null] + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + } + + Block[B3] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 2') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + + Next (Regular) Block[B5] + Finalizing: {R6} + Leaving: {R2} {R1} +} +.finally {R6} +{ + Block[B4] - Block + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 3;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 3') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + + Next (StructuredExceptionHandling) Block[null] +} + +Block[B5] - Exit [UnReachable] + Predecessors: [B1] [B3] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(17,13): warning CS0162: Unreachable code detected + // x = 2; + Diagnostic(ErrorCode.WRN_UnreachableCode, "x").WithLocation(17, 13) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void FinallyDispatch_03() + { + string source = @" +class P +{ + void M(int x) +/**/{ + try + { + try + { + return; + } + finally + { + while (true) {} + } + + x = 2; + } + finally + { + x = 3; + } + }/**/ + + static bool ThisCanThrow() => throw null; +} +"; + string expectedGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} {R4} + +.try {R1, R2} +{ + .try {R3, R4} + { + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Regular) Block[B6] + Finalizing: {R5} {R6} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B2] - Block + Predecessors: [B2] + Statements (0) + Jump if False (Regular) to Block[B3] + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + + Next (Regular) Block[B2] + Block[B3] - Block [UnReachable] + Predecessors: [B2] + Statements (0) + Next (StructuredExceptionHandling) Block[null] + } + + Block[B4] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 2') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + + Next (Regular) Block[B6] + Finalizing: {R6} + Leaving: {R2} {R1} +} +.finally {R6} +{ + Block[B5] - Block [UnReachable] + Predecessors (0) + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 3;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'x = 3') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + + Next (StructuredExceptionHandling) Block[null] +} + +Block[B6] - Exit [UnReachable] + Predecessors: [B1] [B4] + Statements (0) +"; + var expectedDiagnostics = new[] { + // file.cs(17,13): warning CS0162: Unreachable code detected + // x = 2; + Diagnostic(ErrorCode.WRN_UnreachableCode, "x").WithLocation(17, 13) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); + } + // PROTOTYPE(dataflow): Add flow graph tests to VB. } } diff --git a/src/Compilers/Core/Portable/Operations/BasicBlock.cs b/src/Compilers/Core/Portable/Operations/BasicBlock.cs index 2f052a6fe31..3511491fb3d 100644 --- a/src/Compilers/Core/Portable/Operations/BasicBlock.cs +++ b/src/Compilers/Core/Portable/Operations/BasicBlock.cs @@ -54,6 +54,8 @@ public BasicBlock(BasicBlockKind kind) public int Ordinal { get; internal set; } = -1; + public bool IsReachable { get; internal set; } = false; + /// /// Enclosing region /// diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraph.Region.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraph.Region.cs index 1fc79a4f492..2a551c86be1 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraph.Region.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraph.Region.cs @@ -190,6 +190,11 @@ public sealed class Region } #endif } + + internal bool ContainsBlock(int destinationOrdinal) + { + return FirstBlockOrdinal <= destinationOrdinal && LastBlockOrdinal >= destinationOrdinal; + } } } } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index ff1fa944dcc..afb24bded83 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -1,7 +1,6 @@ // 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; -using System.Collections.Concurrent; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -58,6 +57,7 @@ public static ControlFlowGraph Create(IBlockOperation body) ControlFlowGraph.Region region = root.ToImmutableRegionAndFree(blocks); root = null; CalculateBranchLeaveEnterLists(blocks); + MarkReachableBlocks(blocks); Debug.Assert(builder._evalStack.Count == 0); builder._evalStack.Free(); @@ -67,6 +67,351 @@ public static ControlFlowGraph Create(IBlockOperation body) return new ControlFlowGraph(blocks.ToImmutableAndFree(), region); } + private static void MarkReachableBlocks(ArrayBuilder blocks) + { + var continueDispatchAfterFilterOrFinally = PooledDictionary.GetInstance(); + var dispatchedExceptionsFromRegions = PooledHashSet.GetInstance(); + MarkReachableBlocks(blocks, firstBlockOrdinal: 0, lastBlockOrdinal: blocks.Count - 1, + outOfRangeBlocksToVisit: null, + continueDispatchAfterFilterOrFinally, + dispatchedExceptionsFromRegions, + out _); + continueDispatchAfterFilterOrFinally.Free(); + dispatchedExceptionsFromRegions.Free(); + } + + private static BitVector MarkReachableBlocks( + ArrayBuilder blocks, + int firstBlockOrdinal, + int lastBlockOrdinal, + ArrayBuilder outOfRangeBlocksToVisit, + PooledDictionary continueDispatchAfterFilterOrFinally, + PooledHashSet dispatchedExceptionsFromRegions, + out bool fellThrough) + { + var visited = BitVector.Empty; + var toVisit = ArrayBuilder.GetInstance(); + + fellThrough = false; + toVisit.Push(blocks[firstBlockOrdinal]); + + do + { + BasicBlock current = toVisit.Pop(); + + if (current.Ordinal < firstBlockOrdinal || current.Ordinal > lastBlockOrdinal) + { + outOfRangeBlocksToVisit.Push(current); + continue; + } + + if (visited[current.Ordinal]) + { + continue; + } + + visited[current.Ordinal] = true; + current.IsReachable = true; + bool canThrow = false; + bool fallThrough = true; + + foreach (IOperation operation in current.Statements) + { + if (operationCanThrow(operation)) + { + canThrow = true; + break; + } + } + + (IOperation Condition, bool JumpIfTrue, BasicBlock.Branch Branch) conditional = current.Conditional; + if (conditional.Condition != null) + { + if (conditional.Condition.ConstantValue.HasValue && conditional.Condition.ConstantValue.Value is bool constant) + { + if (constant == conditional.JumpIfTrue) + { + followBranch(current, conditional.Branch, ref canThrow); + fallThrough = false; + } + } + else + { + if (operationCanThrow(conditional.Condition)) + { + canThrow = true; + } + + followBranch(current, conditional.Branch, ref canThrow); + } + } + + if (fallThrough) + { + (IOperation Value, BasicBlock.Branch Branch) next = current.Next; + + if (operationCanThrow(next.Value)) + { + canThrow = true; + } + + followBranch(current, current.Next.Branch, ref canThrow); + + if (current.Ordinal == lastBlockOrdinal) + { + fellThrough = true; + } + } + + if (canThrow) + { + dispatchException(current.Region); + } + } + while (toVisit.Count != 0); + + toVisit.Free(); + return visited; + + // A simplified, imprecise way to detect if operation can throw + bool operationCanThrow(IOperation operation) + { + if (operation == null) + { + return false; + } + + // PROTOTYPE(dataflow): Should we treat only primitive value type constant values as non throwing. + // For example, strings, decimal, datetime constants are not primitive value + // type constants. + if (operation.ConstantValue.HasValue) + { + return false; + } + + // Treat an exception assignment to a local or a parameter as non-throwing + if (operation.Kind == OperationKind.SimpleAssignment) + { + var assignment = (ISimpleAssignmentOperation)operation; + + if (assignment.Value.Kind == OperationKind.CaughtException && + (assignment.Target.Kind == OperationKind.LocalReference || assignment.Target.Kind == OperationKind.ParameterReference)) + { + return false; + } + } + + return true; + } + + void followBranch(BasicBlock current, BasicBlock.Branch branch, ref bool canThrow) + { + switch (branch.Kind) + { + case BasicBlock.BranchKind.None: + case BasicBlock.BranchKind.ProgramTermination: + case BasicBlock.BranchKind.StructuredExceptionHandling: + Debug.Assert(branch.Destination == null); + return; + + case BasicBlock.BranchKind.Throw: + case BasicBlock.BranchKind.ReThrow: + Debug.Assert(branch.Destination == null); + canThrow = true; + return; + + case BasicBlock.BranchKind.Regular: + case BasicBlock.BranchKind.Return: + Debug.Assert(branch.Destination != null); + + if (stepThroughFinally(current.Region, branch.Destination)) + { + toVisit.Add(branch.Destination); + } + + return; + + default: + throw ExceptionUtilities.UnexpectedValue(branch.Kind); + } + } + + // Returns whether we should proceed to the destination after finallies were taken care of. + bool stepThroughFinally(ControlFlowGraph.Region region, BasicBlock destination) + { + int destinationOrdinal = destination.Ordinal; + while (!region.ContainsBlock(destinationOrdinal)) + { + ControlFlowGraph.Region enclosing = region.Enclosing; + if (region.Kind == ControlFlowGraph.RegionKind.Try && enclosing.Kind == ControlFlowGraph.RegionKind.TryAndFinally) + { + Debug.Assert(enclosing.Regions[0] == region); + Debug.Assert(enclosing.Regions[1].Kind == ControlFlowGraph.RegionKind.Finally); + if (!stepThroughFilterOrFinally(enclosing.Regions[1])) + { + // The point that continues dispatch is not reachable. Cancel the dispatch. + return false; + } + } + + region = enclosing; + } + + return true; + } + + // Returns whether we should proceed with dispatch after filter or finally was taken care of. + bool stepThroughFilterOrFinally(ControlFlowGraph.Region filterOrFinally) + { + Debug.Assert(filterOrFinally.Kind == ControlFlowGraph.RegionKind.Finally || + filterOrFinally.Kind == ControlFlowGraph.RegionKind.Filter); + + if (!continueDispatchAfterFilterOrFinally.TryGetValue(filterOrFinally, out bool continueDispatch)) + { + // For simplicity, we do a complete walk of the finally/filter region in isolation + // to make sure that the resume dispatch point is reachable from its beginning. + // It could also be reachable through invalid branches into the finally and we don't want to consider + // these cases for regular finally handling. + BitVector isolated = MarkReachableBlocks(blocks, + filterOrFinally.FirstBlockOrdinal, + filterOrFinally.LastBlockOrdinal, + outOfRangeBlocksToVisit: toVisit, + continueDispatchAfterFilterOrFinally, + dispatchedExceptionsFromRegions, + out bool isolatedFellThrough); + visited.UnionWith(isolated); + + continueDispatch = isolatedFellThrough && + blocks[filterOrFinally.LastBlockOrdinal].Next.Branch.Kind == BasicBlock.BranchKind.StructuredExceptionHandling; + + continueDispatchAfterFilterOrFinally.Add(filterOrFinally, continueDispatch); + } + + return continueDispatch; + } + + void dispatchException(ControlFlowGraph.Region fromRegion) + { + do + { + if (!dispatchedExceptionsFromRegions.Add(fromRegion)) + { + return; + } + + ControlFlowGraph.Region enclosing = fromRegion.Enclosing; + if (fromRegion.Kind == ControlFlowGraph.RegionKind.Try) + { + switch (enclosing.Kind) + { + case ControlFlowGraph.RegionKind.TryAndFinally: + Debug.Assert(enclosing.Regions[0] == fromRegion); + Debug.Assert(enclosing.Regions[1].Kind == ControlFlowGraph.RegionKind.Finally); + if (!stepThroughFilterOrFinally(enclosing.Regions[1])) + { + // The point that continues dispatch is not reachable. Cancel the dispatch. + return; + } + break; + + case ControlFlowGraph.RegionKind.TryAndCatch: + Debug.Assert(enclosing.Regions[0] == fromRegion); + if (dispatchExceptionThroughCatches(enclosing, startAt: 1)) + { + // Stop dispatch, an exception is handled + return; + } + break; + + default: + throw ExceptionUtilities.UnexpectedValue(enclosing.Kind); + } + } + else if (fromRegion.Kind == ControlFlowGraph.RegionKind.Filter) + { + // If filter throws, dispatch is resumed at the next catch with an original exception + Debug.Assert(enclosing.Kind == ControlFlowGraph.RegionKind.FilterAndHandler); + ControlFlowGraph.Region tryAndCatch = enclosing.Enclosing; + Debug.Assert(tryAndCatch.Kind == ControlFlowGraph.RegionKind.TryAndCatch); + + int index = tryAndCatch.Regions.IndexOf(enclosing, startIndex: 1); + + if (index > 0) + { + if (dispatchExceptionThroughCatches(tryAndCatch, startAt: index + 1)) + { + // Stop dispatch, an exception is handled + return; + } + + fromRegion = tryAndCatch; + continue; + } + + throw ExceptionUtilities.Unreachable; + } + + fromRegion = enclosing; + } + while (fromRegion != null); + } + + // Returns true if exception is handled + bool dispatchExceptionThroughCatches(ControlFlowGraph.Region tryAndCatch, int startAt) + { + Debug.Assert(tryAndCatch.Kind == ControlFlowGraph.RegionKind.TryAndCatch); + Debug.Assert(startAt > 0); + Debug.Assert(startAt <= tryAndCatch.Regions.Length); + + for (int i = startAt; i < tryAndCatch.Regions.Length; i++) + { + ControlFlowGraph.Region @catch = tryAndCatch.Regions[i]; + + switch (@catch.Kind) + { + case ControlFlowGraph.RegionKind.Catch: + toVisit.Add(blocks[@catch.FirstBlockOrdinal]); + + if (isCatchAllException(@catch.ExceptionType)) + { + // Stop dispatch, an exception is handled + return true; + } + break; + + case ControlFlowGraph.RegionKind.FilterAndHandler: + BasicBlock entryBlock = blocks[@catch.FirstBlockOrdinal]; + ControlFlowGraph.Region filter = @catch.Regions[0]; + Debug.Assert(filter.Kind == ControlFlowGraph.RegionKind.Filter); + Debug.Assert(entryBlock.Ordinal == filter.FirstBlockOrdinal); + + toVisit.Add(entryBlock); + + if (isCatchAllException(@catch.ExceptionType)) + { + if (!stepThroughFilterOrFinally(filter)) + { + // Stop dispatch, an exception is handled + return true; + } + } + break; + + default: + throw ExceptionUtilities.UnexpectedValue(@catch.Kind); + } + } + + return false; + } + + bool isCatchAllException(ITypeSymbol exceptionType) + { + // PROTOTYPE(dataflow): Should we also consider System.Exception as catch all, at least for VB? + return exceptionType == null || exceptionType.SpecialType == SpecialType.System_Object; + } + } + private static void CalculateBranchLeaveEnterLists(ArrayBuilder blocks) { var builder = ArrayBuilder.GetInstance(); @@ -111,7 +456,7 @@ void collectRegions(int destinationOrdinal, ControlFlowGraph.Region source) { builder.Clear(); - while (source.FirstBlockOrdinal > destinationOrdinal || source.LastBlockOrdinal < destinationOrdinal) + while (!source.ContainsBlock(destinationOrdinal)) { builder.Add(source); source = source.Enclosing; @@ -393,9 +738,6 @@ private static bool PackBlocks(ArrayBuilder blocks, PooledDictionary // It is safe to drop an unreachable empty basic block if (predecessors.Count > 0) { - // PROTOTYPE(dataflow): It should be safe to merge other branches with null destination, even when there are more than one predecessor - // and more than one incoming branch - if (predecessors.Count != 1) { continue; @@ -411,6 +753,8 @@ private static bool PackBlocks(ArrayBuilder blocks, PooledDictionary // Do not merge StructuredExceptionHandling into the middle of the filter or finally, // Do not merge StructuredExceptionHandling into conditional branch // Do not merge StructuredExceptionHandling into a different region + // It is much easier to walk the graph when we can rely on the fact that a StructuredExceptionHandling + // branch is only in the last block in the region, if it is present. continue; } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 2df5cd9c9bb..dfff4537479 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -32,6 +32,7 @@ Microsoft.CodeAnalysis.Operations.BasicBlock.BranchKind.Return = 2 -> Microsoft. Microsoft.CodeAnalysis.Operations.BasicBlock.BranchKind.StructuredExceptionHandling = 3 -> Microsoft.CodeAnalysis.Operations.BasicBlock.BranchKind Microsoft.CodeAnalysis.Operations.BasicBlock.BranchKind.Throw = 5 -> Microsoft.CodeAnalysis.Operations.BasicBlock.BranchKind Microsoft.CodeAnalysis.Operations.BasicBlock.Conditional.get -> (Microsoft.CodeAnalysis.IOperation Condition, bool JumpIfTrue, Microsoft.CodeAnalysis.Operations.BasicBlock.Branch Branch) +Microsoft.CodeAnalysis.Operations.BasicBlock.IsReachable.get -> bool Microsoft.CodeAnalysis.Operations.BasicBlock.Kind.get -> Microsoft.CodeAnalysis.Operations.BasicBlockKind Microsoft.CodeAnalysis.Operations.BasicBlock.Next.get -> (Microsoft.CodeAnalysis.IOperation Value, Microsoft.CodeAnalysis.Operations.BasicBlock.Branch Branch) Microsoft.CodeAnalysis.Operations.BasicBlock.Ordinal.get -> int diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IBranchOperation.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IBranchOperation.vb index bd2d9d6ad5e..0384d0b60a2 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IBranchOperation.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IBranchOperation.vb @@ -909,7 +909,7 @@ Block[B0] - Entry { .try {R3, R4} { - Block[B1] - Block + Block[B1] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B3] @@ -936,7 +936,7 @@ Block[B0] - Entry Entering: {R2} {R3} {R5} } -Block[B4] - Block +Block[B4] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true') @@ -948,7 +948,7 @@ Block[B4] - Block ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') Next (Regular) Block[B5] -Block[B5] - Exit +Block[B5] - Exit [UnReachable] Predecessors: [B4] Statements (0) ]]>.Value @@ -985,7 +985,7 @@ Block[B0] - Entry .try {R1, R2} { - Block[B1] - Block + Block[B1] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B3] @@ -1000,7 +1000,7 @@ Block[B0] - Entry Next (StructuredExceptionHandling) Block[null] } -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B1] Statements (0) ]]>.Value @@ -1048,7 +1048,7 @@ Block[B0] - Entry { .try {R3, R4} { - Block[B1] - Block + Block[B1] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B4] @@ -1057,7 +1057,7 @@ Block[B0] - Entry } .catch {R5} (System.Exception) { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B3] @@ -1073,7 +1073,7 @@ Block[B0] - Entry Next (StructuredExceptionHandling) Block[null] } -Block[B4] - Exit +Block[B4] - Exit [UnReachable] Predecessors: [B1] Statements (0) ]]>.Value @@ -1112,7 +1112,7 @@ Block[B0] - Entry Statements (0) Next (Regular) Block[B3] Entering: {R1} {R3} -Block[B1] - Block +Block[B1] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true') @@ -1128,7 +1128,7 @@ Block[B1] - Block .try {R1, R2} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (0) Next (Regular) Block[B3] @@ -1143,7 +1143,7 @@ Block[B1] - Block Next (StructuredExceptionHandling) Block[null] } -Block[B4] - Exit +Block[B4] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value @@ -1181,7 +1181,7 @@ Block[B0] - Entry .try {R1, R2} { - Block[B1] - Block + Block[B1] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B3] @@ -1248,7 +1248,7 @@ Block[B0] - Entry { .try {R3, R4} { - Block[B1] - Block + Block[B1] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B4] @@ -1275,7 +1275,7 @@ Block[B0] - Entry Entering: {R2} {R3} {R5} } -Block[B4] - Exit +Block[B4] - Exit [UnReachable] Predecessors: [B1] [B2] Statements (0) ]]>.Value @@ -1322,7 +1322,7 @@ Block[B0] - Entry Statements (0) Next (Regular) Block[B3] Entering: {R1} {R2} {R3} {R5} -Block[B1] - Block +Block[B1] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true') @@ -1340,7 +1340,7 @@ Block[B1] - Block { .try {R3, R4} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (0) Next (Regular) Block[B3] @@ -1367,7 +1367,7 @@ Block[B1] - Block Entering: {R2} {R3} {R5} } -Block[B5] - Exit +Block[B5] - Exit [UnReachable] Predecessors: [B3] Statements (0) ]]>.Value @@ -1402,7 +1402,7 @@ Block[B0] - Entry Statements (0) Next (Regular) Block[B2] Entering: {R1} {R2} -Block[B1] - Block +Block[B1] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true') @@ -1426,7 +1426,7 @@ Block[B1] - Block } .catch {R3} (System.Exception) { - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B4] @@ -1474,7 +1474,7 @@ Block[B0] - Entry } .catch {R3} (System.Exception) { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B1] @@ -1599,7 +1599,7 @@ Block[B0] - Entry Statements (0) Next (Regular) Block[B2] Entering: {R1} {R2} -Block[B1] - Block +Block[B1] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true') @@ -1632,7 +1632,7 @@ Block[B1] - Block Entering: {R2} } -Block[B4] - Exit +Block[B4] - Exit [UnReachable] Predecessors: [B2] Statements (0) ]]>.Value @@ -1698,7 +1698,7 @@ Block[B1] - Block } .catch {R5} (System.Exception) { - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B6] @@ -1715,7 +1715,7 @@ Block[B1] - Block Leaving: {R6} {R1} } -Block[B5] - Block +Block[B5] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true') @@ -1801,7 +1801,7 @@ Block[B0] - Entry Next (Regular) Block[B2] Leaving: {R8} {R7} {R6} Entering: {R2} {R3} {R5} - Block[B4] - Block + Block[B4] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true') @@ -1817,20 +1817,20 @@ Block[B0] - Entry } .catch {R9} (System.Exception) { - Block[B5] - Block + Block[B5] - Block [UnReachable] Predecessors (0) Statements (0) Next (Regular) Block[B6] Leaving: {R9} {R7} } - Block[B6] - Block + Block[B6] - Block [UnReachable] Predecessors: [B4] [B5] Statements (0) Next (StructuredExceptionHandling) Block[null] } -Block[B7] - Exit +Block[B7] - Exit [UnReachable] Predecessors: [B1] [B2] Statements (0) ]]>.Value @@ -1857,7 +1857,7 @@ End Class]]>.Value Block[B0] - Entry Statements (0) Next (Regular) Block[B2] -Block[B1] - Block +Block[B1] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = true') @@ -1898,7 +1898,7 @@ Block[B1] - Block Predecessors: [B0] [B1] Statements (0) Next (Regular) Block[B1] -Block[B2] - Exit +Block[B2] - Exit [UnReachable] Predecessors (0) Statements (0)]]>.Value @@ -1952,7 +1952,7 @@ Block[B0] - Entry Predecessors: [B2] [B3] Statements (0) Next (Regular) Block[B2] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = false') @@ -2022,7 +2022,7 @@ Block[B0] - Entry Predecessors: [B2] [B3] Statements (0) Next (Regular) Block[B2] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = false') @@ -2036,7 +2036,7 @@ Block[B0] - Entry Next (Regular) Block[B2] } -Block[B4] - Exit +Block[B4] - Exit [UnReachable] Predecessors: [B1] Statements (0) ]]>.Value @@ -2181,7 +2181,7 @@ Block[B0] - Entry Next (Regular) Block[B2] } -Block[B4] - Exit +Block[B4] - Exit [UnReachable] Predecessors: [B1] Statements (0) ]]>.Value @@ -2242,7 +2242,7 @@ Block[B0] - Entry Leaving: {R3} {R1} Next (Regular) Block[B2] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = false') @@ -2317,7 +2317,7 @@ Block[B0] - Entry IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'x') Next (Regular) Block[B2] - Block[B3] - Block + Block[B3] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = false') @@ -2381,7 +2381,7 @@ Block[B2] - Block ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') Next (Regular) Block[B1] -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value @@ -2408,7 +2408,7 @@ End Class]]>.Value Block[B0] - Entry Statements (0) Next (Regular) Block[B2] -Block[B1] - Block +Block[B1] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = false') @@ -2463,7 +2463,7 @@ Block[B1] - Block IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'x') Next (Regular) Block[B1] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = false') @@ -2475,7 +2475,7 @@ Block[B2] - Block ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false') Next (Regular) Block[B1] -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ICoalesceOperation.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ICoalesceOperation.vb index 3b09222e48d..6aebfd8fe03 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ICoalesceOperation.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ICoalesceOperation.vb @@ -659,7 +659,7 @@ Block[B1] - Block IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: null, Constant: null, IsImplicit) (Syntax: 'Nothing') Next (Regular) Block[B2] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'Nothing') @@ -755,7 +755,7 @@ Block[B1] - Block IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: null, Constant: null, IsImplicit) (Syntax: 'Nothing') Next (Regular) Block[B2] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'Nothing') @@ -851,7 +851,7 @@ Block[B1] - Block IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: null, Constant: null, IsImplicit) (Syntax: 'Nothing') Next (Regular) Block[B2] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors: [B1] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'Nothing') diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IEndOperation.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IEndOperation.vb index f66e650fa8b..ded586a4946 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IEndOperation.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IEndOperation.vb @@ -28,7 +28,7 @@ Block[B1] - Block Predecessors: [B0] Statements (0) Next (ProgramTermination) Block[null] -Block[B2] - Exit +Block[B2] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value @@ -69,7 +69,7 @@ Block[B1] - Block ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Next (ProgramTermination) Block[null] -Block[B2] - Block +Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 2') @@ -81,7 +81,7 @@ Block[B2] - Block ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') Next (Regular) Block[B3] -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B2] Statements (0) ]]>.Value @@ -139,7 +139,7 @@ Block[B0] - Entry Next (ProgramTermination) Block[null] } -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors: [B1] Statements (0) ]]>.Value @@ -182,7 +182,7 @@ Block[B1] - Block ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Next (ProgramTermination) Block[null] -Block[B2] - Exit +Block[B2] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value @@ -218,7 +218,7 @@ Block[B1] - Block IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'x') Next (ProgramTermination) Block[null] -Block[B2] - Exit +Block[B2] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value @@ -276,7 +276,7 @@ Block[B3] - Block Predecessors: [B1] [B2] Statements (0) Next (ProgramTermination) Block[null] -Block[B4] - Exit +Block[B4] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value @@ -324,7 +324,7 @@ Block[B1] - Block IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'a') Next (ProgramTermination) Block[null] -Block[B2] - Exit +Block[B2] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value @@ -463,7 +463,7 @@ Block[B0] - Entry } .finally {R3} { - Block[B2] - Block + Block[B2] - Block [UnReachable] Predecessors (0) Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x = 1') @@ -477,7 +477,7 @@ Block[B0] - Entry Next (StructuredExceptionHandling) Block[null] } -Block[B3] - Exit +Block[B3] - Exit [UnReachable] Predecessors (0) Statements (0) ]]>.Value diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_TryCatch.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_TryCatch.vb index a2b104d5cf5..d8018e54f64 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_TryCatch.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_TryCatch.vb @@ -1172,5 +1172,220 @@ IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (S VerifyOperationTreeAndDiagnosticsForTest(Of ExpressionStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub + + + Public Sub ExceptionDispatch_45() + Dim source = .Value + + Dim expectedDiagnostics = String.Empty + + Dim expectedFlowGraph = .Value + + VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics, TestOptions.ReleaseExe) + End Sub + + + + Public Sub FinallyDispatch_04() + Dim source = .Value + + Dim expectedDiagnostics = .Value + + Dim expectedFlowGraph = .Value + + VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics) + End Sub + End Class End Namespace diff --git a/src/Test/Utilities/Portable/Compilation/ControlFlowGraphVerifier.cs b/src/Test/Utilities/Portable/Compilation/ControlFlowGraphVerifier.cs index 1a59033a92f..dbe693f4dcd 100644 --- a/src/Test/Utilities/Portable/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/ControlFlowGraphVerifier.cs @@ -57,6 +57,7 @@ public static string GetFlowGraph(Compilation compilation, ControlFlowGraph grap Assert.Null(currentRegion.ExceptionType); Assert.Empty(currentRegion.Locals); Assert.Equal(ControlFlowGraph.RegionKind.Root, currentRegion.Kind); + Assert.True(block.IsReachable); break; case BasicBlockKind.Exit: @@ -85,7 +86,7 @@ public static string GetFlowGraph(Compilation compilation, ControlFlowGraph grap stringBuilder.AppendLine(); } - appendLine($"Block[B{i}] - {block.Kind}"); + appendLine($"Block[B{i}] - {block.Kind}{(block.IsReachable ? "" : " [UnReachable]")}"); var predecessors = block.Predecessors; @@ -130,6 +131,7 @@ public static string GetFlowGraph(Compilation compilation, ControlFlowGraph grap Assert.Same(blocks[conditionalBranch.Destination.Ordinal], conditionalBranch.Destination); } + Assert.NotEqual(BasicBlock.BranchKind.StructuredExceptionHandling, conditionalBranch.Kind); appendLine($" Jump if {(block.Conditional.JumpIfTrue ? "True" : "False")} ({conditionalBranch.Kind}) to Block[{getDestinationString(ref conditionalBranch)}]"); IOperation value = block.Conditional.Condition; @@ -153,6 +155,13 @@ public static string GetFlowGraph(Compilation compilation, ControlFlowGraph grap Assert.Same(blocks[nextBranch.Destination.Ordinal], nextBranch.Destination); } + if (nextBranch.Kind == BasicBlock.BranchKind.StructuredExceptionHandling) + { + Assert.Null(nextBranch.Destination); + Assert.Equal(block.Region.LastBlockOrdinal, block.Ordinal); + Assert.True(block.Region.Kind == ControlFlowGraph.RegionKind.Filter || block.Region.Kind == ControlFlowGraph.RegionKind.Finally); + } + appendLine($" Next ({nextBranch.Kind}) Block[{getDestinationString(ref nextBranch)}]"); IOperation value = block.Next.Value; -- GitLab