mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
[python] PY-81674 Highlight all unreachable code gray
GitOrigin-RevId: 5a8dba3a51b7e9d415deb5401213ad3b79ddd3c5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cd9e39eb6e
commit
b0ca1ef976
+2
-1
@@ -699,11 +699,12 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
|
||||
@Override
|
||||
public void visitPyRaiseStatement(final @NotNull PyRaiseStatement node) {
|
||||
myBuilder.addNodeAndCheckPending(new PyRaiseInstruction(myBuilder, node));
|
||||
myBuilder.startNode(node);
|
||||
final PyExpression[] expressions = node.getExpressions();
|
||||
for (PyExpression expression : expressions) {
|
||||
expression.accept(this);
|
||||
}
|
||||
myBuilder.addNode(new PyRaiseInstruction(myBuilder, node));
|
||||
myBuilder.addPendingEdge(null, myBuilder.prevInstruction);
|
||||
myBuilder.flowAbrupted();
|
||||
}
|
||||
|
||||
+73
-6
@@ -4,7 +4,10 @@ import com.intellij.codeInsight.controlflow.ControlFlow
|
||||
import com.intellij.codeInsight.controlflow.ControlFlowUtil
|
||||
import com.intellij.codeInsight.controlflow.Instruction
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil
|
||||
import com.jetbrains.python.psi.*
|
||||
import com.jetbrains.python.psi.impl.PyEvaluator
|
||||
import com.jetbrains.python.psi.types.PyNeverType
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -50,13 +53,77 @@ class PyDataFlow(controlFlow: ControlFlow, private val context: TypeEvalContext)
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiElement.isUnreachable(context: TypeEvalContext): Boolean {
|
||||
/**
|
||||
* Checks if inspections should flag a Python element as unreachable.
|
||||
*
|
||||
* This method considers special cases where code might be technically unreachable
|
||||
* but should not be reported as an issue.
|
||||
* In particular, the first terminating statement in a sequence is considered valid
|
||||
* and should not be reported as unreachable.
|
||||
*
|
||||
* Terminating statements include:
|
||||
* - `raise` statements
|
||||
* - `assert False`
|
||||
* - calls to functions annotated with `NoReturn`
|
||||
*/
|
||||
fun PsiElement.isUnreachableForInspection(context: TypeEvalContext): Boolean {
|
||||
return isUnreachableByControlFlow(context) && !isFirstTerminatingStatement(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the element is unreachable by control flow analysis.
|
||||
* If the element does not have corresponding instruction in CFG, searches for the nearest parent that has.
|
||||
*/
|
||||
fun PsiElement.isUnreachableByControlFlow(context: TypeEvalContext): Boolean {
|
||||
return PyUtil.getParameterizedCachedValue(this, context) { this.isUnreachableByControlFlowNoCache(it) }
|
||||
}
|
||||
|
||||
private fun PsiElement.isUnreachableByControlFlowNoCache(context: TypeEvalContext): Boolean {
|
||||
val scope = ScopeUtil.getScopeOwner(this)
|
||||
if (scope != null) {
|
||||
val flow = ControlFlowCache.getControlFlow(scope).getInstructions()
|
||||
val idx = ControlFlowUtil.findInstructionNumberByElement(flow, this)
|
||||
if (idx < 0) return false
|
||||
return ControlFlowCache.getDataFlow(scope, context).isUnreachable(flow[idx])
|
||||
val flow = ControlFlowCache.getDataFlow(scope, context)
|
||||
val instructions = flow.instructions
|
||||
val idx = ControlFlowUtil.findInstructionNumberByElement(instructions, this)
|
||||
if (idx < 0 || instructions[idx].isAuxiliary()) {
|
||||
val parent = this.parent
|
||||
return parent != null && parent.isUnreachableByControlFlow(context)
|
||||
}
|
||||
return flow.isUnreachable(instructions[idx])
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.isFirstTerminatingStatement(context: TypeEvalContext): Boolean {
|
||||
if (this.isTerminatingStatement(context)) {
|
||||
val prevSibling = prevSiblingOfType<PyElement>() ?: return true
|
||||
return !prevSibling.isTerminatingStatement(context) && !prevSibling.isUnreachableByControlFlow(context)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun PsiElement.isTerminatingStatement(context: TypeEvalContext): Boolean {
|
||||
return when (this) {
|
||||
is PyRaiseStatement -> true
|
||||
is PyAssertStatement -> getArguments().firstOrNull()?.asBooleanNoResolve() == false
|
||||
is PyExpressionStatement -> expression is PyCallExpression && context.getType(expression) is PyNeverType
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun Instruction.isAuxiliary(): Boolean {
|
||||
return when (this) {
|
||||
is PyRaiseInstruction -> true
|
||||
is PyWithContextExitInstruction -> true
|
||||
is PyFinallyFailExitInstruction -> true
|
||||
is ReadWriteInstruction -> access.isAssertTypeAccess
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun PyExpression.asBooleanNoResolve(): Boolean? {
|
||||
return PyEvaluator.evaluateAsBooleanNoResolve(this)
|
||||
}
|
||||
|
||||
private inline fun <reified T: PsiElement> PsiElement.prevSiblingOfType(): T? {
|
||||
return PsiTreeUtil.getPrevSiblingOfType(this, T::class.java)
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.intellij.codeInsight.controlflow.ControlFlow;
|
||||
import com.intellij.codeInsight.controlflow.ControlFlowUtil;
|
||||
import com.intellij.codeInsight.controlflow.Instruction;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.codeInsight.controlflow.*;
|
||||
import com.jetbrains.python.psi.PyStatementListContainer;
|
||||
import com.jetbrains.python.psi.types.PyNeverType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class PyInspectionsUtil {
|
||||
|
||||
/**
|
||||
* Collects a list of unreachable elements, iterating through CFG backwards
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static @NotNull List<PsiElement> collectUnreachable(@NotNull ScopeOwner owner, @Nullable PsiElement anchor, @NotNull TypeEvalContext context) {
|
||||
final ControlFlow flow = ControlFlowCache.getControlFlow(owner);
|
||||
final Instruction[] instructions = flow.getInstructions();
|
||||
final List<PsiElement> unreachable = new ArrayList<>();
|
||||
final int start = anchor != null ? ControlFlowUtil.findInstructionNumberByElement(instructions, anchor) : instructions.length - 1;
|
||||
if (start >= 0) {
|
||||
ControlFlowUtil.iteratePrev(start, instructions, instruction -> {
|
||||
if (getReachablePredecessors(instruction, context).isEmpty() && instruction.num() != 0) {
|
||||
ContainerUtil.addIfNotNull(unreachable, getRelevantElement(instruction));
|
||||
}
|
||||
return ControlFlowUtil.Operation.NEXT;
|
||||
});
|
||||
}
|
||||
return unreachable;
|
||||
}
|
||||
|
||||
private static @NotNull List<Instruction> getReachablePredecessors(@NotNull Instruction instruction, @NotNull TypeEvalContext context) {
|
||||
// TODO: merge this with PyDataFlow
|
||||
return ContainerUtil.filter(instruction.allPred(), it -> {
|
||||
if (it instanceof CallInstruction ci && ci.isNoReturnCall(context)) return false;
|
||||
if (it instanceof PyWithContextExitInstruction wi && !wi.isSuppressingExceptions(context)) return false;
|
||||
if (it instanceof ReadWriteInstruction rw && rw.getAccess().isAssertTypeAccess()) {
|
||||
var type = rw.getType(context, null);
|
||||
return !(type != null && type.get() instanceof PyNeverType);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static @Nullable PsiElement getRelevantElement(@NotNull Instruction instruction) {
|
||||
if (instruction instanceof PyWithContextExitInstruction) {
|
||||
return null;
|
||||
}
|
||||
if (instruction instanceof ReadWriteInstruction rw && rw.getAccess().isAssertTypeAccess()) {
|
||||
return null;
|
||||
}
|
||||
PsiElement element = instruction.getElement();
|
||||
if (element instanceof PyStatementListContainer) {
|
||||
return ((PyStatementListContainer)element).getStatementList();
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
private PyInspectionsUtil() {
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -67,7 +67,7 @@ public final class PyUnboundLocalVariableInspection extends PyInspection {
|
||||
if (PsiTreeUtil.getParentOfType(node, PyImportStatementBase.class) != null) {
|
||||
return;
|
||||
}
|
||||
if (PyDataFlowKt.isUnreachable(node, myTypeEvalContext)) {
|
||||
if (PyDataFlowKt.isUnreachableForInspection(node, myTypeEvalContext)) {
|
||||
return;
|
||||
}
|
||||
final String name = node.getReferencedName();
|
||||
|
||||
+11
-7
@@ -2,11 +2,13 @@
|
||||
package com.jetbrains.python.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.util.findParentInFile
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner
|
||||
import com.jetbrains.python.codeInsight.controlflow.isUnreachableForInspection
|
||||
import com.jetbrains.python.psi.PyElement
|
||||
|
||||
/**
|
||||
* Detects unreachable code using control flow graph
|
||||
@@ -18,13 +20,15 @@ class PyUnreachableCodeInspection : PyInspection() {
|
||||
session: LocalInspectionToolSession
|
||||
): PsiElementVisitor {
|
||||
return object : PyInspectionVisitor(holder, getContext(session)) {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
if (element is ScopeOwner) {
|
||||
for (e in PyInspectionsUtil.collectUnreachable(element, null, myTypeEvalContext)) {
|
||||
registerProblem(e, PyPsiBundle.message("INSP.unreachable.code"))
|
||||
override fun visitPyElement(node: PyElement) {
|
||||
if (node.isUnreachableForInspection(myTypeEvalContext)) {
|
||||
if (node.findParentInFile { it.isUnreachableForInspection(myTypeEvalContext) } != null) {
|
||||
// We only want to highlight top level unreachable code
|
||||
return
|
||||
}
|
||||
registerProblem(node, PyPsiBundle.message("INSP.unreachable.code"), ProblemHighlightType.LIKE_UNUSED_SYMBOL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -193,7 +193,7 @@ public abstract class PyUnresolvedReferencesVisitor extends PyInspectionVisitor
|
||||
return;
|
||||
}
|
||||
if (!expr.isQualified()) {
|
||||
if (PyDataFlowKt.isUnreachable(expr, myTypeEvalContext)) {
|
||||
if (PyDataFlowKt.isUnreachableForInspection(expr, myTypeEvalContext)) {
|
||||
return;
|
||||
}
|
||||
ContainerUtil.addIfNotNull(fixes, getTrueFalseQuickFix(refText));
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
14(15) element: PyExpressionStatement
|
||||
15(16) READ ACCESS: sys
|
||||
16(4,17) element: PyCallExpression: sys.exit
|
||||
17(18) raise: PyRaiseStatement
|
||||
17(18) element: PyRaiseStatement
|
||||
18(19) READ ACCESS: Exception
|
||||
19(20) element: PyCallExpression: Exception
|
||||
20() element: null
|
||||
20(21) raise: PyRaiseStatement
|
||||
21() element: null
|
||||
@@ -2,26 +2,27 @@
|
||||
1(2) element: PyTryExceptStatement
|
||||
2(3,5) element: PyTryPart
|
||||
3(4,5) element: PyFromImportStatement
|
||||
4(5,9) WRITE ACCESS: lsprof
|
||||
4(5,10) WRITE ACCESS: lsprof
|
||||
5(6) element: PyExceptPart
|
||||
6(7) READ ACCESS: ImportError
|
||||
7(8) raise: PyRaiseStatement
|
||||
8(26) READ ACCESS: Error
|
||||
9(10) element: PyAssignmentStatement
|
||||
10(11) WRITE ACCESS: p
|
||||
11(12) element: PyTryExceptStatement
|
||||
12(13,17) element: PyTryPart
|
||||
13(14,17) element: PyReturnStatement
|
||||
14(17) READ ACCESS: foo
|
||||
15(16,17) element: PyAssignmentStatement
|
||||
16(17,21) WRITE ACCESS: x
|
||||
17(18) element: PyFinallyPart
|
||||
18(19) element: PyPrintStatement
|
||||
19(20) READ ACCESS: p
|
||||
20(26) finally fail exit
|
||||
21(22) element: PyFinallyPart
|
||||
22(23) element: PyPrintStatement
|
||||
23(24) READ ACCESS: p
|
||||
24(25) element: PyAssignmentStatement
|
||||
25(26) WRITE ACCESS: y
|
||||
26() element: null
|
||||
7(8) element: PyRaiseStatement
|
||||
8(9) READ ACCESS: Error
|
||||
9(27) raise: PyRaiseStatement
|
||||
10(11) element: PyAssignmentStatement
|
||||
11(12) WRITE ACCESS: p
|
||||
12(13) element: PyTryExceptStatement
|
||||
13(14,18) element: PyTryPart
|
||||
14(15,18) element: PyReturnStatement
|
||||
15(18) READ ACCESS: foo
|
||||
16(17,18) element: PyAssignmentStatement
|
||||
17(18,22) WRITE ACCESS: x
|
||||
18(19) element: PyFinallyPart
|
||||
19(20) element: PyPrintStatement
|
||||
20(21) READ ACCESS: p
|
||||
21(27) finally fail exit
|
||||
22(23) element: PyFinallyPart
|
||||
23(24) element: PyPrintStatement
|
||||
24(25) READ ACCESS: p
|
||||
25(26) element: PyAssignmentStatement
|
||||
26(27) WRITE ACCESS: y
|
||||
27() element: null
|
||||
@@ -1,19 +1,20 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyIfStatement
|
||||
2(3,4) READ ACCESS: markup
|
||||
3(15) element: null. Condition: markup:false
|
||||
3(16) element: null. Condition: markup:false
|
||||
4(5) element: null. Condition: markup:true
|
||||
5(6) ASSERTTYPE ACCESS: markup
|
||||
6(7) element: PyStatementList
|
||||
7(8) element: PyTryExceptStatement
|
||||
8(9,11) element: PyTryPart
|
||||
9(10,11) element: PyReturnStatement
|
||||
10(11,18) READ ACCESS: renderer
|
||||
10(11,19) READ ACCESS: renderer
|
||||
11(12) element: PyExceptPart
|
||||
12(13) READ ACCESS: KeyError
|
||||
13(14) raise: PyRaiseStatement
|
||||
14(18) READ ACCESS: Error
|
||||
15(16) element: PyStatementList
|
||||
16(17) element: PyReturnStatement
|
||||
17(18) READ ACCESS: body
|
||||
18() element: null
|
||||
13(14) element: PyRaiseStatement
|
||||
14(15) READ ACCESS: Error
|
||||
15(19) raise: PyRaiseStatement
|
||||
16(17) element: PyStatementList
|
||||
17(18) element: PyReturnStatement
|
||||
18(19) READ ACCESS: body
|
||||
19() element: null
|
||||
@@ -7,14 +7,15 @@
|
||||
6(7) element: PyStatementList
|
||||
7(8) element: PyForStatement
|
||||
8(9) READ ACCESS: range
|
||||
9(10,17) element: PyCallExpression: range
|
||||
9(10,18) element: PyCallExpression: range
|
||||
10(11) element: PyTargetExpression: _
|
||||
11(12) WRITE ACCESS: _
|
||||
12(10,17) element: PyPrintStatement
|
||||
12(10,18) element: PyPrintStatement
|
||||
13(14) element: PyStatementList
|
||||
14(15) raise: PyRaiseStatement
|
||||
14(15) element: PyRaiseStatement
|
||||
15(16) READ ACCESS: Exception
|
||||
16(19) element: PyCallExpression: Exception
|
||||
17(18) element: PyReturnStatement
|
||||
18(19) READ ACCESS: True
|
||||
19() element: null
|
||||
16(17) element: PyCallExpression: Exception
|
||||
17(20) raise: PyRaiseStatement
|
||||
18(19) element: PyReturnStatement
|
||||
19(20) READ ACCESS: True
|
||||
20() element: null
|
||||
@@ -1,14 +1,16 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyTryExceptStatement
|
||||
2(3,6) element: PyTryPart
|
||||
3(4,6) raise: PyRaiseStatement
|
||||
4(5,6) READ ACCESS: Exception
|
||||
5(6,13) element: PyCallExpression: Exception
|
||||
6(7) element: PyExceptPart
|
||||
7(8) element: PyAssignmentStatement
|
||||
8(9) READ ACCESS: Exception
|
||||
9(10) element: PyCallExpression: Exception
|
||||
10(11) WRITE ACCESS: foo
|
||||
11(12) raise: PyRaiseStatement
|
||||
12(13) READ ACCESS: foo
|
||||
13() element: null
|
||||
2(3,7) element: PyTryPart
|
||||
3(4,7) element: PyRaiseStatement
|
||||
4(5,7) READ ACCESS: Exception
|
||||
5(6,7) element: PyCallExpression: Exception
|
||||
6(7,15) raise: PyRaiseStatement
|
||||
7(8) element: PyExceptPart
|
||||
8(9) element: PyAssignmentStatement
|
||||
9(10) READ ACCESS: Exception
|
||||
10(11) element: PyCallExpression: Exception
|
||||
11(12) WRITE ACCESS: foo
|
||||
12(13) element: PyRaiseStatement
|
||||
13(14) READ ACCESS: foo
|
||||
14(15) raise: PyRaiseStatement
|
||||
15() element: null
|
||||
@@ -16,7 +16,7 @@
|
||||
15(16,19,27,30) READ ACCESS: s
|
||||
16(17,19,27,30) element: PyCallExpression: s.strip
|
||||
17(18,19,27,30) element: PyCallExpression: int
|
||||
18(19,27,30,36) WRITE ACCESS: i
|
||||
18(19,27,30,37) WRITE ACCESS: i
|
||||
19(20) element: PyExceptPart
|
||||
20(21) READ ACCESS: IOError
|
||||
21(22) WRITE ACCESS: errno
|
||||
@@ -24,14 +24,15 @@
|
||||
23(24) element: PyPrintStatement
|
||||
24(25) READ ACCESS: errno
|
||||
25(26) READ ACCESS: strerror
|
||||
26(36) element: PyCallExpression: "I/O error({0}): {1}".format
|
||||
26(37) element: PyCallExpression: "I/O error({0}): {1}".format
|
||||
27(28) element: PyExceptPart
|
||||
28(29) READ ACCESS: ValueError
|
||||
29(36) element: PyPrintStatement
|
||||
29(37) element: PyPrintStatement
|
||||
30(31) element: PyExceptPart
|
||||
31(32) element: PyPrintStatement
|
||||
32(33) element: PySubscriptionExpression
|
||||
33(34) READ ACCESS: sys
|
||||
34(35) element: PyCallExpression: sys.exc_info
|
||||
35(36) raise: PyRaiseStatement
|
||||
36() element: null
|
||||
35(36) element: PyRaiseStatement
|
||||
36(37) raise: PyRaiseStatement
|
||||
37() element: null
|
||||
@@ -2,13 +2,14 @@
|
||||
1(2) element: PyTryExceptStatement
|
||||
2(3,8) element: PyTryPart
|
||||
3(4,8) element: PyForStatement
|
||||
4(5,8,12) READ ACCESS: bar
|
||||
4(5,8,13) READ ACCESS: bar
|
||||
5(6,8) element: PyTargetExpression: i
|
||||
6(7,8) WRITE ACCESS: i
|
||||
7(12) element: PyBreakStatement
|
||||
7(13) element: PyBreakStatement
|
||||
8(9) element: PyExceptPart
|
||||
9(10) raise: PyRaiseStatement
|
||||
9(10) element: PyRaiseStatement
|
||||
10(11) READ ACCESS: Exception
|
||||
11(13) element: PyCallExpression: Exception
|
||||
12(13) element: PyReturnStatement
|
||||
13() element: null
|
||||
11(12) element: PyCallExpression: Exception
|
||||
12(14) raise: PyRaiseStatement
|
||||
13(14) element: PyReturnStatement
|
||||
14() element: null
|
||||
@@ -5,16 +5,17 @@
|
||||
4(5,8,12) WRITE ACCESS: value
|
||||
5(6) element: PyElsePart
|
||||
6(7) element: PyAssignmentStatement
|
||||
7(17) WRITE ACCESS: value
|
||||
7(18) WRITE ACCESS: value
|
||||
8(9) element: PyExceptPart
|
||||
9(10) READ ACCESS: ValueError
|
||||
10(11) element: PyAssignmentStatement
|
||||
11(17) WRITE ACCESS: value
|
||||
11(18) WRITE ACCESS: value
|
||||
12(13) element: PyExceptPart
|
||||
13(14) READ ACCESS: SomethingElse
|
||||
14(15) element: PyAssignmentStatement
|
||||
15(16) WRITE ACCESS: value
|
||||
16(19) raise: PyRaiseStatement
|
||||
17(18) element: PyPrintStatement
|
||||
18(19) READ ACCESS: value
|
||||
19() element: null
|
||||
16(17) element: PyRaiseStatement
|
||||
17(20) raise: PyRaiseStatement
|
||||
18(19) element: PyPrintStatement
|
||||
19(20) READ ACCESS: value
|
||||
20() element: null
|
||||
@@ -1,9 +1,10 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyTryExceptStatement
|
||||
2(3,5) element: PyTryPart
|
||||
3(4,5) raise: PyRaiseStatement
|
||||
4(5) READ ACCESS: KeyboardInterrupt
|
||||
5(6) element: PyFinallyPart
|
||||
6(7) element: PyPrintStatement
|
||||
7(8) finally fail exit
|
||||
8() element: null
|
||||
2(3,6) element: PyTryPart
|
||||
3(4,6) element: PyRaiseStatement
|
||||
4(5,6) READ ACCESS: KeyboardInterrupt
|
||||
5(6) raise: PyRaiseStatement
|
||||
6(7) element: PyFinallyPart
|
||||
7(8) element: PyPrintStatement
|
||||
8(9) finally fail exit
|
||||
9() element: null
|
||||
@@ -2,80 +2,81 @@
|
||||
1(2) element: PyAssignmentStatement
|
||||
2(3) WRITE ACCESS: a
|
||||
3(4) element: PyTryExceptStatement
|
||||
4(5,71) element: PyTryPart
|
||||
5(6,71) element: PyAssignmentStatement
|
||||
6(7,71) WRITE ACCESS: b
|
||||
7(8,71) element: PyForStatement
|
||||
8(9,71) element: PyTargetExpression: x
|
||||
9(10,71) WRITE ACCESS: x
|
||||
10(11,71) element: PyTryExceptStatement
|
||||
11(12,60) element: PyTryPart
|
||||
12(13,60) element: PyAssignmentStatement
|
||||
13(14,60) WRITE ACCESS: c
|
||||
14(15,60) element: PyTryExceptStatement
|
||||
15(16,51) element: PyTryPart
|
||||
16(17,51) element: PyAssignmentStatement
|
||||
17(18,51) WRITE ACCESS: d
|
||||
18(19,51) element: PyIfStatement
|
||||
19(20,22,51) READ ACCESS: x
|
||||
4(5,72) element: PyTryPart
|
||||
5(6,72) element: PyAssignmentStatement
|
||||
6(7,72) WRITE ACCESS: b
|
||||
7(8,72) element: PyForStatement
|
||||
8(9,72) element: PyTargetExpression: x
|
||||
9(10,72) WRITE ACCESS: x
|
||||
10(11,72) element: PyTryExceptStatement
|
||||
11(12,61) element: PyTryPart
|
||||
12(13,61) element: PyAssignmentStatement
|
||||
13(14,61) WRITE ACCESS: c
|
||||
14(15,61) element: PyTryExceptStatement
|
||||
15(16,52) element: PyTryPart
|
||||
16(17,52) element: PyAssignmentStatement
|
||||
17(18,52) WRITE ACCESS: d
|
||||
18(19,52) element: PyIfStatement
|
||||
19(20,22,52) READ ACCESS: x
|
||||
20(21) element: null. Condition: x == 0:false
|
||||
21(51,26) ASSERTTYPE ACCESS: x
|
||||
21(52,26) ASSERTTYPE ACCESS: x
|
||||
22(23) element: null. Condition: x == 0:true
|
||||
23(51,24) ASSERTTYPE ACCESS: x
|
||||
23(52,24) ASSERTTYPE ACCESS: x
|
||||
24(25) element: PyStatementList
|
||||
25(55) element: PyBreakStatement
|
||||
26(27,29,51) READ ACCESS: x
|
||||
25(56) element: PyBreakStatement
|
||||
26(27,29,52) READ ACCESS: x
|
||||
27(28) element: null. Condition: x == 1:false
|
||||
28(51,33) ASSERTTYPE ACCESS: x
|
||||
28(52,33) ASSERTTYPE ACCESS: x
|
||||
29(30) element: null. Condition: x == 1:true
|
||||
30(51,31) ASSERTTYPE ACCESS: x
|
||||
30(52,31) ASSERTTYPE ACCESS: x
|
||||
31(32) element: PyStatementList
|
||||
32(7,55) element: PyContinueStatement
|
||||
33(34,36,51) READ ACCESS: x
|
||||
32(7,56) element: PyContinueStatement
|
||||
33(34,36,52) READ ACCESS: x
|
||||
34(35) element: null. Condition: x == 2:false
|
||||
35(51,42) ASSERTTYPE ACCESS: x
|
||||
35(52,43) ASSERTTYPE ACCESS: x
|
||||
36(37) element: null. Condition: x == 2:true
|
||||
37(51,38) ASSERTTYPE ACCESS: x
|
||||
37(52,38) ASSERTTYPE ACCESS: x
|
||||
38(39) element: PyStatementList
|
||||
39(40,51) raise: PyRaiseStatement
|
||||
40(41,51) READ ACCESS: Exception
|
||||
41(51) element: PyCallExpression: Exception
|
||||
42(43,45,51) READ ACCESS: x
|
||||
43(44) element: null. Condition: x == 3:false
|
||||
44(51,49) ASSERTTYPE ACCESS: x
|
||||
45(46) element: null. Condition: x == 3:true
|
||||
46(51,47) ASSERTTYPE ACCESS: x
|
||||
47(48) element: PyStatementList
|
||||
48(51) element: PyReturnStatement
|
||||
49(50,51) element: PyAssignmentStatement
|
||||
50(51,55) WRITE ACCESS: e
|
||||
51(52,60) element: PyFinallyPart
|
||||
52(53,60) element: PyAssignmentStatement
|
||||
53(54,60) WRITE ACCESS: f
|
||||
54(60) finally fail exit
|
||||
55(56,60) element: PyFinallyPart
|
||||
56(57,60) element: PyAssignmentStatement
|
||||
57(60,64,58) WRITE ACCESS: f
|
||||
58(59,60) element: PyAssignmentStatement
|
||||
59(60,64) WRITE ACCESS: g
|
||||
60(61,71) element: PyFinallyPart
|
||||
61(62,71) element: PyAssignmentStatement
|
||||
62(63,71) WRITE ACCESS: h
|
||||
63(71) finally fail exit
|
||||
64(65,71) element: PyFinallyPart
|
||||
65(66,71) element: PyAssignmentStatement
|
||||
66(71,67,69) WRITE ACCESS: h
|
||||
67(68,71) element: PyAssignmentStatement
|
||||
68(8,71,69) WRITE ACCESS: i
|
||||
69(70,71) element: PyAssignmentStatement
|
||||
70(71,75) WRITE ACCESS: j
|
||||
71(72) element: PyFinallyPart
|
||||
72(73) element: PyAssignmentStatement
|
||||
73(74) WRITE ACCESS: k
|
||||
74(80) finally fail exit
|
||||
75(76) element: PyFinallyPart
|
||||
76(77) element: PyAssignmentStatement
|
||||
77(78) WRITE ACCESS: k
|
||||
78(79) element: PyAssignmentStatement
|
||||
79(80) WRITE ACCESS: l
|
||||
80() element: null
|
||||
39(40,52) element: PyRaiseStatement
|
||||
40(41,52) READ ACCESS: Exception
|
||||
41(42,52) element: PyCallExpression: Exception
|
||||
42(52) raise: PyRaiseStatement
|
||||
43(44,46,52) READ ACCESS: x
|
||||
44(45) element: null. Condition: x == 3:false
|
||||
45(52,50) ASSERTTYPE ACCESS: x
|
||||
46(47) element: null. Condition: x == 3:true
|
||||
47(52,48) ASSERTTYPE ACCESS: x
|
||||
48(49) element: PyStatementList
|
||||
49(52) element: PyReturnStatement
|
||||
50(51,52) element: PyAssignmentStatement
|
||||
51(52,56) WRITE ACCESS: e
|
||||
52(53,61) element: PyFinallyPart
|
||||
53(54,61) element: PyAssignmentStatement
|
||||
54(55,61) WRITE ACCESS: f
|
||||
55(61) finally fail exit
|
||||
56(57,61) element: PyFinallyPart
|
||||
57(58,61) element: PyAssignmentStatement
|
||||
58(61,65,59) WRITE ACCESS: f
|
||||
59(60,61) element: PyAssignmentStatement
|
||||
60(61,65) WRITE ACCESS: g
|
||||
61(62,72) element: PyFinallyPart
|
||||
62(63,72) element: PyAssignmentStatement
|
||||
63(64,72) WRITE ACCESS: h
|
||||
64(72) finally fail exit
|
||||
65(66,72) element: PyFinallyPart
|
||||
66(67,72) element: PyAssignmentStatement
|
||||
67(72,68,70) WRITE ACCESS: h
|
||||
68(69,72) element: PyAssignmentStatement
|
||||
69(8,72,70) WRITE ACCESS: i
|
||||
70(71,72) element: PyAssignmentStatement
|
||||
71(72,76) WRITE ACCESS: j
|
||||
72(73) element: PyFinallyPart
|
||||
73(74) element: PyAssignmentStatement
|
||||
74(75) WRITE ACCESS: k
|
||||
75(81) finally fail exit
|
||||
76(77) element: PyFinallyPart
|
||||
77(78) element: PyAssignmentStatement
|
||||
78(79) WRITE ACCESS: k
|
||||
79(80) element: PyAssignmentStatement
|
||||
80(81) WRITE ACCESS: l
|
||||
81() element: null
|
||||
@@ -1,18 +1,19 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyWithStatement
|
||||
2(4) READ ACCESS: context_manager
|
||||
3(15) exit context manager: context_manager
|
||||
3(16) exit context manager: context_manager
|
||||
4(5,3) element: PyIfStatement
|
||||
5(6,7,3) READ ACCESS: c
|
||||
6(12) element: null. Condition: c:false
|
||||
6(13) element: null. Condition: c:false
|
||||
7(8) element: null. Condition: c:true
|
||||
8(3,9) ASSERTTYPE ACCESS: c
|
||||
9(10) element: PyStatementList
|
||||
10(11,3) raise: PyRaiseStatement
|
||||
11(17,3) READ ACCESS: ValueError
|
||||
12(13,3) element: PyAssignmentStatement
|
||||
13(14,3) READ ACCESS: c
|
||||
14(15,3) WRITE ACCESS: val
|
||||
15(16) element: PyPrintStatement
|
||||
16(17) READ ACCESS: val
|
||||
17() element: null
|
||||
10(11,3) element: PyRaiseStatement
|
||||
11(12,3) READ ACCESS: ValueError
|
||||
12(18,3) raise: PyRaiseStatement
|
||||
13(14,3) element: PyAssignmentStatement
|
||||
14(15,3) READ ACCESS: c
|
||||
15(16,3) WRITE ACCESS: val
|
||||
16(17) element: PyPrintStatement
|
||||
17(18) READ ACCESS: val
|
||||
18() element: null
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
def test_exit():
|
||||
exit()
|
||||
<warning descr="This code is unreachable">print("should be reported as unreachable")</warning>
|
||||
return True
|
||||
<warning descr="This code is unreachable">return True</warning>
|
||||
+1
-1
@@ -2,4 +2,4 @@ match 42:
|
||||
case <error descr="Pattern makes remaining case clauses unreachable">x</error>:
|
||||
pass
|
||||
case <warning descr="This code is unreachable">42</warning>:
|
||||
pass
|
||||
<warning descr="This code is unreachable">pass</warning>
|
||||
+64
-3
@@ -25,6 +25,64 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON26, () -> doTest());
|
||||
}
|
||||
|
||||
// PY-81674
|
||||
public void testFinallyEarlyExit() {
|
||||
doTestByText("""
|
||||
def f():
|
||||
try:
|
||||
print("Hello, world!")
|
||||
finally:
|
||||
assert False
|
||||
<warning descr="This code is unreachable">print("Goodbye, world!")</warning>
|
||||
|
||||
<warning descr="This code is unreachable">print("This is unreachable")</warning>
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-81674
|
||||
public void testConsecutiveTerminating() {
|
||||
doTestByText("""
|
||||
def f1():
|
||||
exit()
|
||||
<warning descr="This code is unreachable">raise Exception()</warning>
|
||||
<warning descr="This code is unreachable">print("unreachable")</warning>
|
||||
<warning descr="This code is unreachable">assert False</warning>
|
||||
|
||||
def f2():
|
||||
raise Exception()
|
||||
<warning descr="This code is unreachable">assert False</warning>
|
||||
<warning descr="This code is unreachable">print("unreachable")</warning>
|
||||
<warning descr="This code is unreachable">exit()</warning>
|
||||
|
||||
def f3():
|
||||
assert False
|
||||
<warning descr="This code is unreachable">exit()</warning>
|
||||
<warning descr="This code is unreachable">print("unreachable")</warning>
|
||||
<warning descr="This code is unreachable">raise Exception()</warning>
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-81674
|
||||
public void testNoNestedWarnings() {
|
||||
doTestByText("""
|
||||
from enum import Enum
|
||||
|
||||
class Foo(Enum):
|
||||
A = 0
|
||||
B = 1
|
||||
|
||||
<warning descr="This code is unreachable">print(exit())</warning>
|
||||
|
||||
<warning descr="This code is unreachable">def unreachable(foo: Foo) -> None:
|
||||
if foo is Foo.A:
|
||||
...
|
||||
elif foo is Foo.B:
|
||||
...
|
||||
else:
|
||||
print("also unreachable")</warning>
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-81593
|
||||
public void testReachabilityLogicalOperatorChaining() {
|
||||
doTestByText("""
|
||||
@@ -175,24 +233,26 @@ def sup2(b):
|
||||
assert False
|
||||
print("reachable")
|
||||
|
||||
def nosup(b):
|
||||
def nosupRaise(b):
|
||||
with NoSuppress():
|
||||
a = 42
|
||||
raise ValueError("Something went wrong")
|
||||
<warning descr="This code is unreachable">print("unreachable")</warning>
|
||||
|
||||
def nosupAssert(b):
|
||||
with NoSuppress():
|
||||
assert b
|
||||
a = 42
|
||||
assert False
|
||||
<warning descr="This code is unreachable">print("unreachable")</warning>
|
||||
|
||||
def nosup2(b):
|
||||
def nosup2Raise(b):
|
||||
with NoSuppress2():
|
||||
a = 42
|
||||
raise ValueError("Something went wrong")
|
||||
<warning descr="This code is unreachable">print("unreachable")</warning>
|
||||
|
||||
def nosup2Assert(b):
|
||||
with NoSuppress2():
|
||||
assert b
|
||||
a = 42
|
||||
@@ -256,12 +316,13 @@ async def sup(b):
|
||||
assert False
|
||||
print("reachable")
|
||||
|
||||
async def nosup(b):
|
||||
async def nosupRaise(b):
|
||||
async with AsyncNoSuppress():
|
||||
a = 42
|
||||
raise ValueError("Something went wrong")
|
||||
<warning descr="This code is unreachable">print("unreachable")</warning>
|
||||
|
||||
async def nosupAssertFalse(b):
|
||||
async with AsyncNoSuppress():
|
||||
assert b
|
||||
a = 42
|
||||
|
||||
Reference in New Issue
Block a user