diff --git a/python-checks/src/main/java/org/sonar/python/checks/AfterJumpStatementCheck.java b/python-checks/src/main/java/org/sonar/python/checks/AfterJumpStatementCheck.java index 7f4768101..e7a5d468d 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/AfterJumpStatementCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/AfterJumpStatementCheck.java @@ -58,7 +58,7 @@ private static void checkCfg(@Nullable ControlFlowGraph cfg, SubscriptionContext .filter(block -> cfgBlock.equals(block.syntacticSuccessor())) .map(block -> block.elements().get(block.elements().size() - 1)) .collect(Collectors.toList()); - if (isInsideFinallyClause(firstElement) || jumpStatements.stream().anyMatch(AfterJumpStatementCheck::isRaiseInsideWithStatement)) { + if (isInsideFinallyClause(firstElement)) { continue; } Tree lastElement = cfgBlock.elements().get(cfgBlock.elements().size() - 1); @@ -68,12 +68,6 @@ private static void checkCfg(@Nullable ControlFlowGraph cfg, SubscriptionContext } } - // To avoid FP, we assume that exception raised inside with statement will be suppressed by __exit__() method of Context Manager - // see https://docs.python.org/3/reference/compound_stmts.html#the-with-statement - private static boolean isRaiseInsideWithStatement(Tree element) { - return element.is(Kind.RAISE_STMT) && TreeUtils.firstAncestorOfKind(element, Kind.WITH_STMT) != null; - } - // due to CFG limitation on jump statements inside try blocks, we exclude finally clause to avoid FP. // TODO: After SONARPY-448 is implemented, we should remove this exclusion private static boolean isInsideFinallyClause(Tree element) { diff --git a/python-checks/src/test/resources/checks/afterJumpStatement.py b/python-checks/src/test/resources/checks/afterJumpStatement.py index d81895057..859864720 100644 --- a/python-checks/src/test/resources/checks/afterJumpStatement.py +++ b/python-checks/src/test/resources/checks/afterJumpStatement.py @@ -176,3 +176,8 @@ def while_dead_code_in_else_clause_condition_true(x): else: print('dead code!') # FN, we don't take into account while condition being always true print("end of loop") + +def code_after_with(): + with A(): + return e + return False diff --git a/python-squid/src/main/java/org/sonar/python/cfg/ControlFlowGraphBuilder.java b/python-squid/src/main/java/org/sonar/python/cfg/ControlFlowGraphBuilder.java index fe0ced7b1..8cc667f57 100644 --- a/python-squid/src/main/java/org/sonar/python/cfg/ControlFlowGraphBuilder.java +++ b/python-squid/src/main/java/org/sonar/python/cfg/ControlFlowGraphBuilder.java @@ -129,7 +129,7 @@ private PythonCfgBlock build(List statements, PythonCfgBlock successo private PythonCfgBlock build(Statement statement, PythonCfgBlock currentBlock) { switch (statement.getKind()) { case WITH_STMT: - return build(((WithStatement) statement).statements().statements(), currentBlock); + return buildWithStatement((WithStatement) statement, currentBlock); case CLASSDEF: return build(((ClassDef) statement).body().statements(), currentBlock); case RETURN_STMT: @@ -155,6 +155,13 @@ private PythonCfgBlock build(Statement statement, PythonCfgBlock currentBlock) { return currentBlock; } + private PythonCfgBlock buildWithStatement(WithStatement withStatement, PythonCfgBlock successor) { + PythonCfgBlock withBodyBlock = build(withStatement.statements().statements(), createSimpleBlock(successor)); + // exceptions may be raised inside with block and be caught by context manager + // see https://docs.python.org/3/reference/compound_stmts.html#the-with-statement + return createBranchingBlock(withStatement, withBodyBlock, successor); + } + private PythonCfgBlock tryStatement(TryStatement tryStatement, PythonCfgBlock successor) { PythonCfgBlock finallyOrAfterTryBlock = successor; FinallyClause finallyClause = tryStatement.finallyClause(); diff --git a/python-squid/src/test/java/org/sonar/plugins/python/api/cfg/ControlFlowGraphTest.java b/python-squid/src/test/java/org/sonar/plugins/python/api/cfg/ControlFlowGraphTest.java index 92a3476fb..679a3820c 100644 --- a/python-squid/src/test/java/org/sonar/plugins/python/api/cfg/ControlFlowGraphTest.java +++ b/python-squid/src/test/java/org/sonar/plugins/python/api/cfg/ControlFlowGraphTest.java @@ -407,9 +407,9 @@ public void raise_in_try() { @Test public void with_statement() { verifyCfg( - "before(succ = [if_body, END])", + "before(succ = [with_block, END])", "with A() as a:", - " if cond:", + " if with_block(succ = [if_body, END]):", " if_body(succ = [END])" ); }