diff --git a/analyzers/src/SonarAnalyzer.CFG/CfgSerializer/CfgSerializer.RoslynLvaWalker.cs b/analyzers/src/SonarAnalyzer.CFG/CfgSerializer/CfgSerializer.RoslynLvaWalker.cs index aaf4765cb41..67ad81dfc9e 100644 --- a/analyzers/src/SonarAnalyzer.CFG/CfgSerializer/CfgSerializer.RoslynLvaWalker.cs +++ b/analyzers/src/SonarAnalyzer.CFG/CfgSerializer/CfgSerializer.RoslynLvaWalker.cs @@ -38,7 +38,7 @@ protected override void WriteEdges(BasicBlock block) { foreach (var predecessor in lva.BlockPredecessors[block.Ordinal].Where(x => !block.Predecessors.Any(y => y.Source == x))) { - writer.WriteEdge(BlockId(predecessor), BlockId(block), "LVA"); + writer.WriteEdge(BlockId(predecessor), BlockId(block), "LVA\" fontcolor=\"blue\" penwidth=\"2\" color=\"blue"); } base.WriteEdges(block); } diff --git a/analyzers/src/SonarAnalyzer.CFG/LiveVariableAnalysis/RoslynLiveVariableAnalysis.cs b/analyzers/src/SonarAnalyzer.CFG/LiveVariableAnalysis/RoslynLiveVariableAnalysis.cs index 3e8ffc6a8b5..03cfe1964d6 100644 --- a/analyzers/src/SonarAnalyzer.CFG/LiveVariableAnalysis/RoslynLiveVariableAnalysis.cs +++ b/analyzers/src/SonarAnalyzer.CFG/LiveVariableAnalysis/RoslynLiveVariableAnalysis.cs @@ -141,9 +141,9 @@ private void BuildBranches(BasicBlock block) AddPredecessorsOutsideRegion(finallyBlock); } } - if (block.IsEnclosedIn(ControlFlowRegionKind.Catch) && block.Successors.Any(x => x.Semantics == ControlFlowBranchSemantics.Rethrow)) + if (block.IsEnclosedIn(ControlFlowRegionKind.Catch) && block.Successors.Any(x => x.Semantics is ControlFlowBranchSemantics.Rethrow or ControlFlowBranchSemantics.Throw)) { - BuildBranchesNestedCatchRethrow(block); + BuildBranchesRethrow(block); } void AddPredecessorsOutsideRegion(BasicBlock destination) @@ -168,13 +168,18 @@ private void BuildBranchesFinally(BasicBlock source, ControlFlowRegion finallyRe } } - private void BuildBranchesNestedCatchRethrow(BasicBlock block) + private void BuildBranchesRethrow(BasicBlock block) { - var reachableHandlers = block.EnclosingRegion(ControlFlowRegionKind.TryAndCatch).NestedRegion(ControlFlowRegionKind.Try).ReachableHandlers(); - foreach (var catchBlock in reachableHandlers.Where(x => x.Kind == ControlFlowRegionKind.Catch && x.FirstBlockOrdinal > block.Ordinal).SelectMany(x => x.Blocks(Cfg))) + var reachableHandlerRegions = block.EnclosingRegion(ControlFlowRegionKind.TryAndCatch).NestedRegion(ControlFlowRegionKind.Try).ReachableHandlers(); + var reachableCatchAndFinallyBlocks = reachableHandlerRegions.Where(x => x.FirstBlockOrdinal > block.Ordinal).SelectMany(x => x.Blocks(Cfg)); + foreach (var catchBlock in reachableCatchAndFinallyBlocks.Where(x => x.EnclosingRegion.Kind is ControlFlowRegionKind.Catch)) { AddBranch(block, catchBlock); } + if (reachableCatchAndFinallyBlocks.FirstOrDefault() is { EnclosingRegion.Kind: ControlFlowRegionKind.Finally } finallyBlock) + { + AddBranch(block, finallyBlock); + } } private void AddBranch(BasicBlock source, BasicBlock destination) diff --git a/analyzers/tests/SonarAnalyzer.Test/CFG/Roslyn/RoslynLvaSerializerTest.cs b/analyzers/tests/SonarAnalyzer.Test/CFG/Roslyn/RoslynLvaSerializerTest.cs index a5b64a93791..699582f7c0a 100644 --- a/analyzers/tests/SonarAnalyzer.Test/CFG/Roslyn/RoslynLvaSerializerTest.cs +++ b/analyzers/tests/SonarAnalyzer.Test/CFG/Roslyn/RoslynLvaSerializerTest.cs @@ -85,14 +85,14 @@ void Use(int v) {} cfg0_block0 [shape=record label="{ENTRY #0}"] cfg0_block5 [shape=record label="{EXIT #5}"] cfg0_block1 -> cfg0_block2 - cfg0_block2 -> cfg0_block3 [label="LVA"] - cfg0_block1 -> cfg0_block3 [label="LVA"] - cfg0_block2 -> cfg0_block4 [label="LVA"] - cfg0_block3 -> cfg0_block4 [label="LVA"] + cfg0_block2 -> cfg0_block3 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block1 -> cfg0_block3 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block2 -> cfg0_block4 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block3 -> cfg0_block4 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] cfg0_block4 -> NoDestination_cfg0_block4 [label="StructuredExceptionHandling"] cfg0_block0 -> cfg0_block1 - cfg0_block4 -> cfg0_block5 [label="LVA"] - cfg0_block4 -> cfg0_block5 [label="LVA"] + cfg0_block4 -> cfg0_block5 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block4 -> cfg0_block5 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] cfg0_block2 -> cfg0_block5 cfg0_block3 -> cfg0_block5 } @@ -160,13 +160,14 @@ void Use(int v) {} cfg0_block0 [shape=record label="{ENTRY #0}"] cfg0_block5 [shape=record label="{EXIT #5}"] cfg0_block1 -> cfg0_block2 - cfg0_block2 -> cfg0_block3 [label="LVA"] - cfg0_block1 -> cfg0_block3 [label="LVA"] + cfg0_block2 -> cfg0_block3 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block1 -> cfg0_block3 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] cfg0_block3 -> NoDestination_cfg0_block3 [label="Rethrow"] - cfg0_block2 -> cfg0_block4 [label="LVA"] + cfg0_block2 -> cfg0_block4 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block3 -> cfg0_block4 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] cfg0_block4 -> NoDestination_cfg0_block4 [label="StructuredExceptionHandling"] cfg0_block0 -> cfg0_block1 - cfg0_block4 -> cfg0_block5 [label="LVA"] + cfg0_block4 -> cfg0_block5 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] cfg0_block2 -> cfg0_block5 } @@ -265,17 +266,17 @@ void Use(int v) {} cfg0_block3 -> cfg0_block4 [label="Else"] cfg0_block2 -> cfg0_block3 cfg0_block4 -> cfg0_block3 - cfg0_block3 -> cfg0_block5 [label="LVA"] - cfg0_block3 -> cfg0_block5 [label="LVA"] - cfg0_block2 -> cfg0_block5 [label="LVA"] - cfg0_block4 -> cfg0_block5 [label="LVA"] + cfg0_block3 -> cfg0_block5 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block3 -> cfg0_block5 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block2 -> cfg0_block5 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] + cfg0_block4 -> cfg0_block5 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] cfg0_block5 -> cfg0_block6 [label="Else"] cfg0_block5 -> cfg0_block7 [label="WhenTrue"] cfg0_block6 -> cfg0_block7 cfg0_block7 -> NoDestination_cfg0_block7 [label="StructuredExceptionHandling"] cfg0_block1 -> cfg0_block2 cfg0_block0 -> cfg0_block1 - cfg0_block7 -> cfg0_block8 [label="LVA"] + cfg0_block7 -> cfg0_block8 [label="LVA" fontcolor="blue" penwidth="2" color="blue"] cfg0_block3 -> cfg0_block8 [label="WhenFalse"] } diff --git a/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.TryCatchFinally.cs b/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.TryCatchFinally.cs index 03bc3450350..eb37acd66c7 100644 --- a/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.TryCatchFinally.cs +++ b/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.TryCatchFinally.cs @@ -65,7 +65,7 @@ public void Using_LiveInUntilTheEnd(string usingStatement, string suffix) [TestMethod] public void Using_Nested_Block_LiveInUntilTheEnd() { - var code = """ + const string code = """ using (var msOuter = new MemoryStream()) { if (boolParameter) @@ -95,7 +95,7 @@ public void Using_Nested_Block_LiveInUntilTheEnd() [TestMethod] public void Using_Nested_Declaration_LiveInUntilTheEnd() { - var code = """ + const string code = """ using var msOuter = new MemoryStream(); if (boolParameter) { @@ -124,7 +124,7 @@ public void Using_Nested_Declaration_LiveInUntilTheEnd() [TestMethod] public void Catch_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -146,7 +146,7 @@ public void Catch_LiveIn() [TestMethod] public void Catch_TryHasLocalLifetimeRegion_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -169,7 +169,7 @@ public void Catch_TryHasLocalLifetimeRegion_LiveIn() [TestMethod] public void Catch_VariableUsedAfter_LiveIn_LiveOut() { - var code = """ + const string code = """ try { Method(0); @@ -191,7 +191,7 @@ public void Catch_VariableUsedAfter_LiveIn_LiveOut() [TestMethod] public void Catch_WithThrowStatement_LiveIn() { - var code = """ + const string code = """ var usedInTry = 42; var usedInTryUnreachable = 42; var usedInCatch = 42; @@ -224,7 +224,7 @@ public void Catch_WithThrowStatement_LiveIn() [TestMethod] public void Catch_WithThrowStatement_Conditional_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -253,7 +253,7 @@ public void Catch_WithThrowStatement_Conditional_LiveIn() [TestMethod] public void Catch_Rethrow_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -276,7 +276,7 @@ public void Catch_Rethrow_LiveIn() [TestMethod] public void Catch_NotLiveIn_NotLiveOut() { - var code = """ + const string code = """ try { Method(0); @@ -299,7 +299,7 @@ public void Catch_NotLiveIn_NotLiveOut() [TestMethod] public void Catch_Nested_LiveIn() { - var code = """ + const string code = """ var outer = 42; var inner = 42; try @@ -341,7 +341,7 @@ public void Catch_InvalidSyntax_LiveIn() * | * Exit 2 */ - var code = """ + const string code = """ // Error CS1003 Syntax error, 'try' expected // Error CS1514 { expected // Error CS1513 } expected @@ -361,7 +361,7 @@ public void Catch_InvalidSyntax_LiveIn() [TestMethod] public void Catch_Loop_Propagates_LiveIn_LiveOut() { - var code = """ + const string code = """ var variableUsedInCatch = 42; A: Method(intParameter); @@ -390,7 +390,7 @@ public void Catch_Loop_Propagates_LiveIn_LiveOut() [TestMethod] public void Catch_ExVariable_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -416,7 +416,7 @@ public void Catch_ExVariable_LiveIn() [TestMethod] public void Catch_SingleType_LiveIn() { - var code = """ + const string code = """ var usedAfter = 42; var usedInTry = 42; var usedInCatch = 42; @@ -441,7 +441,7 @@ public void Catch_SingleType_LiveIn() [TestMethod] public void Catch_SingleTypeWhenCondition_LiveIn() { - var code = """ + const string code = """ var usedAfter = 42; var usedInTry = 42; var usedInCatch = 42; @@ -466,7 +466,7 @@ public void Catch_SingleTypeWhenCondition_LiveIn() [TestMethod] public void Catch_SingleTypeWhenConditionReferencingArgument_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -488,7 +488,7 @@ public void Catch_SingleTypeWhenConditionReferencingArgument_LiveIn() [TestMethod] public void Catch_MultipleTypes_LiveIn() { - var code = """ + const string code = """ var usedAfter = 42; var usedInTry = 42; var usedInCatchA = 42; @@ -522,7 +522,7 @@ public void Catch_MultipleTypes_LiveIn() [TestMethod] public void Catch_AssignedInTry_LiveOut() { - var code = """ + const string code = """ int variable = 42; Method(0); try @@ -547,7 +547,7 @@ public void Catch_AssignedInTry_LiveOut() [TestMethod] public void Catch_When_AssignedInTry_LiveOut() { - var code = """ + const string code = """ int variable = 42; Method(0); try @@ -573,7 +573,7 @@ public void Catch_When_AssignedInTry_LiveOut() [TestMethod] public void Catch_Loop_Propagates_LiveIn() { - var code = """ + const string code = """ var variable = 0; Method(0); while (variable < 5) @@ -606,7 +606,7 @@ public void Catch_Loop_Propagates_LiveIn() [TestMethod] public void Throw_NestedCatch_LiveOut() { - var code = """ + const string code = """ var value = 100; try { @@ -638,7 +638,7 @@ public void Throw_NestedCatch_LiveOut() [TestMethod] public void Throw_NestedCatch_LiveInInConsecutiveOuterCatch() { - var code = """ + const string code = """ var value = 100; try { @@ -680,9 +680,48 @@ public void Throw_NestedCatch_LiveInInConsecutiveOuterCatch() } [TestMethod] - public void Throw_NestedCatch_OuterCatchRethrows_LiveOutOuterCathc() + public void Throw_NestedCatch_LiveInInConsecutiveOuterCatchNewThrow() { - var code = """ + const string code = """ + var value = 100; + try + { + try + { + Method(value); + Method(0); + } + catch (ArgumentNullException ex) + { + Method(value); + Method(1); + throw new Exception("Message", ex); + } + } + catch (IOException) + { + Method(value); + Method(2); + } + catch (NullReferenceException) + { + Method(value); + Method(3); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("Method(0);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(1);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(2);", LiveIn("value")); + context.Validate("Method(3);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void Throw_NestedCatch_OuterCatchRethrows_LiveOutOuterCatch() + { + const string code = """ var value = 100; try { @@ -724,7 +763,7 @@ public void Throw_NestedCatch_OuterCatchRethrows_LiveOutOuterCathc() [TestMethod] public void Finally_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -746,7 +785,7 @@ public void Finally_LiveIn() [TestMethod] public void Finally_VariableUsedAfter_LiveIn_LiveOut() { - var code = """ + const string code = """ try { Method(0); @@ -768,7 +807,7 @@ public void Finally_VariableUsedAfter_LiveIn_LiveOut() [TestMethod] public void Finally_VariableUsedAfter_FinallyHasLocalLifetimeRegion_LiveIn_LiveOut() { - var code = """ + const string code = """ try { Method(0); @@ -791,7 +830,7 @@ public void Finally_VariableUsedAfter_FinallyHasLocalLifetimeRegion_LiveIn_LiveO [TestMethod] public void Finally_WithThrowStatement_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -817,7 +856,7 @@ public void Finally_WithThrowStatement_LiveIn() [TestMethod] public void Finally_WithThrowStatementAsSingleExit_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -841,7 +880,7 @@ public void Finally_WithThrowStatementAsSingleExit_LiveIn() [TestMethod] public void Finally_WithThrowStatement_Conditional_LiveIn() { - var code = """ + const string code = """ try { Method(0); @@ -870,7 +909,7 @@ public void Finally_WithThrowStatement_Conditional_LiveIn() [TestMethod] public void Finally_WithThrowStatementInTry_LiveOut() { - var code = """ + const string code = """ int variable = 42; Method(0); try @@ -895,7 +934,7 @@ public void Finally_WithThrowStatementInTry_LiveOut() [TestMethod] public void Finally_WithThrowStatementInTry_LiveOut_FilteredCatch() { - var code = """ + const string code = """ int variable = 42; Method(0); try @@ -957,7 +996,7 @@ public void Finally_WithThrowStatementInTry_LiveOut_CatchAll(string catchAll) [TestMethod] public void Finally_NotLiveIn_NotLiveOut() { - var code = """ + const string code = """ try { Method(0); @@ -980,7 +1019,7 @@ public void Finally_NotLiveIn_NotLiveOut() [TestMethod] public void Finally_Nested_LiveIn() { - var code = """ + const string code = """ var outer = 42; var inner = 42; try @@ -1014,7 +1053,7 @@ public void Finally_Nested_LiveIn() [TestMethod] public void Finally_Nested_NoInstructionBetweenFinally_LiveIn() { - var code = """ + const string code = """ var outer = 42; var inner = 42; try @@ -1047,7 +1086,7 @@ public void Finally_Nested_NoInstructionBetweenFinally_LiveIn() [TestMethod] public void Finally_ForEach_LiveIn() { - var code = """ + const string code = """ var outer = 42; try { @@ -1082,7 +1121,7 @@ public void Finally_InvalidSyntax_LiveIn() * | * Exit 2 */ - var code = """ + const string code = """ // Error CS1003 Syntax error, 'try' expected // Error CS1514 { expected // Error CS1513 } expected @@ -1102,7 +1141,7 @@ public void Finally_InvalidSyntax_LiveIn() [TestMethod] public void Finally_Loop_Propagates_LiveIn_LiveOut() { - var code = """ + const string code = """ A: Method(intParameter); if (boolParameter) @@ -1130,7 +1169,7 @@ public void Finally_Loop_Propagates_LiveIn_LiveOut() [TestMethod] public void Finally_Loop_Propagates_FinallyHasLocalLifetimeRegion_LiveIn_LiveOut() { - var code = """ + const string code = """ A: Method(intParameter); if (boolParameter) @@ -1159,7 +1198,7 @@ public void Finally_Loop_Propagates_FinallyHasLocalLifetimeRegion_LiveIn_LiveOut [TestMethod] public void Finally_AssignedInTry_LiveOut() { - var code = """ + const string code = """ int variable = 42; Method(0); try @@ -1184,7 +1223,7 @@ public void Finally_AssignedInTry_LiveOut() [TestMethod] public void TryCatchFinally_LiveIn() { - var code = """ + const string code = """ var usedInTry = 42; var usedInCatch = 42; var usedInFinally = 42; @@ -1211,4 +1250,402 @@ public void TryCatchFinally_LiveIn() context.Validate("Method(usedAfter);", LiveIn("usedAfter")); context.ValidateExit(); } + + [TestMethod] + public void TryCatchFinally_Rethrow_ValueLivesOut() + { + const string code = """ + var value = 0; + try + { + Method(0); + value = 42; + } + catch + { + Method(value); + value = 1; + throw; + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 1;", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void TryCatchFinally_RethrowOuterFinally() + { + const string code = """ + var value = 0; + try + { + Method(0); + try + { + Method(0); + value = 42; + } + catch + { + Method(value); + value = 2; + throw; + } + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 2;", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void TryCatchFinally_RethrowOuterCatchFinally() + { + const string code = """ + var value = 0; + try + { + Method(0); + try + { + Method(1); + value = 42; + } + catch + { + value = 2; + throw; + } + } + catch + { + Method(2); + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 2;", LiveOut("value")); + context.Validate("Method(2);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void TryCatchFinally_RethrowOuterCatchRethrowFinally() + { + const string code = """ + var value = 0; + try + { + Method(0); + try + { + Method(1); + value = 42; + } + catch + { + value = 2; + throw; + } + } + catch + { + Method(2); + throw; + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 2;", LiveOut("value")); + context.Validate("Method(2);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void TryCatchFinally_RethrowCatchRethrowOuterFinally() + { + const string code = """ + var value = 0; + try + { + Method(0); + try + { + Method(1); + value = 42; + } + catch + { + value = 2; + throw; + } + finally + { + Method(value + 1); + } + } + finally + { + Method(value + 2); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 2;", LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 2);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void TryCatchFinally_ConsecutiveCatchRethrowFinally() + { + const string code = """ + var value = 0; + try + { + Method(0); + value = 42; + } + catch (IOException) + { + Method(value); + value = 1; + throw; + } + catch + { + Method(value); + value = 2; + throw; + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 1;", LiveIn("value"), LiveOut("value")); + context.Validate("value = 2;", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void TryCatchFinally_FilteredCatchRethrowFinally() + { + const string code = """ + var value = 0; + try + { + Method(0); + value = 42; + } + catch (IOException) + { + Method(value); + value = 1; + throw; + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 1;", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void TryCatchFinally_MultipleFilteredCatchRethrowFinally() + { + const string code = """ + var value = 0; + try + { + Method(0); + value = 42; + } + catch (IOException) + { + Method(value); + value = 1; + throw; + } + catch (ArgumentException) + { + Method(value); + value = 2; + throw; + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 1;", LiveIn("value"), LiveOut("value")); + context.Validate("value = 2;", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void TryCatchFinally_MultipleFilteredCatchNewThrowFinally() + { + const string code = """ + var value = 0; + try + { + Method(0); + value = 42; + } + catch (IOException ex) + { + Method(value); + value = 1; + throw new Exception("Message", ex); + } + catch (ArgumentException) + { + Method(value); + value = 2; + throw; + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("value = 1;", LiveIn("value"), LiveOut("value")); + context.Validate("value = 2;", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void Throw_NestedCatch_LiveInInConsecutiveOuterCatchFinally() + { + const string code = """ + var value = 100; + try + { + try + { + Method(value); + Method(0); + } + catch + { + value = 200; + throw; + } + } + catch (ArgumentNullException) + { + Method(value); + Method(1); + } + catch (IOException) + { + Method(value); + Method(2); + } + catch (NullReferenceException) + { + Method(value); + Method(3); + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("Method(0);", LiveIn("value"), LiveOut("value")); + context.Validate("value = 200;", LiveOut("value")); + context.Validate("Method(1);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(2);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(3);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } + + [TestMethod] + public void Throw_NestedCatch_OuterCatchRethrows_LiveOutOuterCatchFinally() + { + const string code = """ + var value = 100; + try + { + try + { + try + { + Method(value); + Method(0); + } + catch + { + value = 200; + throw; + } + } + catch // Outer catch + { + Method(value); + Method(1); + throw; + } + } + catch + { + Method(value); + Method(2); + } + finally + { + Method(value + 1); + } + """; + var context = CreateContextCS(code); + context.ValidateEntry(); + context.Validate("Method(0);", LiveIn("value"), LiveOut("value")); + context.Validate("value = 200;", LiveOut("value")); + context.Validate("Method(1);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(2);", LiveIn("value"), LiveOut("value")); + context.Validate("Method(value + 1);", LiveIn("value")); + context.ValidateExit(); + } } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/DeadStores.RoslynCfg.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/DeadStores.RoslynCfg.cs index 255c240fb07..507318064f0 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/DeadStores.RoslynCfg.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/DeadStores.RoslynCfg.cs @@ -1530,7 +1530,7 @@ public int ReadInFinallyAfterCatch() } catch { - value = 1; // Noncompliant FP, used in finally after the throw + value = 1; throw; } finally