mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
[pycharm] remove NoReturn logic from control flow builder
GitOrigin-RevId: 85ba28fd8b86fe0b6337703798a0b869bb793e88
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8a4dfe563c
commit
2625d2472b
@@ -0,0 +1,33 @@
|
||||
package com.jetbrains.python.codeInsight.controlflow
|
||||
|
||||
import com.intellij.codeInsight.controlflow.ControlFlowBuilder
|
||||
import com.intellij.codeInsight.controlflow.Instruction
|
||||
import com.intellij.codeInsight.controlflow.impl.InstructionImpl
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
|
||||
import com.jetbrains.python.psi.PyCallExpression
|
||||
import com.jetbrains.python.psi.PyFunction
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
|
||||
class CallInstruction(builder: ControlFlowBuilder, call: PyCallExpression) : InstructionImpl(builder, call) {
|
||||
override fun getElement(): PyCallExpression {
|
||||
return super.getElement() as PyCallExpression
|
||||
}
|
||||
|
||||
fun isNoReturnCall(context: TypeEvalContext): Boolean {
|
||||
val callees = element.multiResolveCalleeFunction(PyResolveContext.defaultContext(context))
|
||||
if (callees.size == 1) {
|
||||
val pyFunction = callees.single()
|
||||
if (pyFunction is PyFunction) {
|
||||
return PyTypingTypeProvider.isNoReturn(pyFunction, context)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun allPredWithoutNoReturn(instruction: Instruction, typeEvalContext: TypeEvalContext): List<Instruction> {
|
||||
return instruction.allPred().filter { it !is CallInstruction || !it.isNoReturnCall(typeEvalContext) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,10 @@ import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.*;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
import com.jetbrains.python.psi.impl.PyAugAssignmentStatementNavigator;
|
||||
import com.jetbrains.python.psi.impl.PyEvaluator;
|
||||
import com.jetbrains.python.psi.impl.PyImportStatementNavigator;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import kotlin.Triple;
|
||||
@@ -167,15 +170,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
final PyExpression callee = node.getCallee();
|
||||
final var callNodeType = getCalleeNodeType(callee);
|
||||
|
||||
// Flow abrupted
|
||||
if (callNodeType instanceof NoReturnCallKind) {
|
||||
callee.accept(this);
|
||||
for (PyExpression expression : node.getArguments()) {
|
||||
expression.accept(this);
|
||||
}
|
||||
abruptFlow(node);
|
||||
}
|
||||
else if (callNodeType instanceof TypeGuardCallKind typeGuardCallKind && node.getArguments().length > 0) {
|
||||
if (callNodeType instanceof TypeGuardCallKind typeGuardCallKind && node.getArguments().length > 0) {
|
||||
expressionToGuards.put(node, typeGuardCallKind.pyFunction);
|
||||
super.visitPyCallExpression(node);
|
||||
}
|
||||
@@ -183,6 +178,9 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
super.visitPyCallExpression(node);
|
||||
}
|
||||
|
||||
var callInstruction = new CallInstruction(myBuilder, node);
|
||||
myBuilder.addNodeAndCheckPending(callInstruction);
|
||||
|
||||
if (node.isCalleeText(PyNames.ASSERT_IS_INSTANCE)) {
|
||||
final PyTypeAssertionEvaluator assertionEvaluator = new PyTypeAssertionEvaluator();
|
||||
node.accept(assertionEvaluator);
|
||||
@@ -1072,9 +1070,9 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
.of(PyResolveUtil.resolveQualifiedNameInScope(qName, scopeOwner, context))
|
||||
.select(PyFunction.class)
|
||||
.map(function -> {
|
||||
if (PyTypingTypeProvider.isNoReturn(function, context)) {
|
||||
return NoReturnCallKind.INSTANCE;
|
||||
}
|
||||
//if (PyTypingTypeProvider.isNoReturn(function, context)) {
|
||||
// return NoReturnCallKind.INSTANCE;
|
||||
//}
|
||||
if (PyTypingTypeProvider.isTypeGuard(function, context)) {
|
||||
return new TypeGuardCallKind(function);
|
||||
}
|
||||
@@ -1111,11 +1109,6 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
|
||||
private interface CallTypeKind { }
|
||||
|
||||
private static class NoReturnCallKind implements CallTypeKind {
|
||||
private NoReturnCallKind() {}
|
||||
public static final NoReturnCallKind INSTANCE = new NoReturnCallKind();
|
||||
}
|
||||
|
||||
private record TypeGuardCallKind(@NotNull PyFunction pyFunction) implements CallTypeKind {}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.jetbrains.python.PyPsiBundle;
|
||||
import com.jetbrains.python.codeInsight.controlflow.CallInstruction;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.psi.PyStatementListContainer;
|
||||
@@ -45,7 +46,7 @@ public final class PyUnreachableCodeInspection extends PyInspection {
|
||||
final List<PsiElement> unreachable = new ArrayList<>();
|
||||
if (instructions.length > 0) {
|
||||
ControlFlowUtil.iteratePrev(instructions.length - 1, instructions, instruction -> {
|
||||
if (instruction.allPred().isEmpty() && !PyInspectionsUtil.isFirstInstruction(instruction)) {
|
||||
if (CallInstruction.Companion.allPredWithoutNoReturn(instruction, myTypeEvalContext).isEmpty() && !PyInspectionsUtil.isFirstInstruction(instruction)) {
|
||||
unreachable.add(unwrapStatementListContainer(instruction.getElement()));
|
||||
}
|
||||
return ControlFlowUtil.Operation.NEXT;
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.jetbrains.python.codeInsight.controlflow.CallInstruction;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
@@ -76,6 +77,9 @@ public final class PyDefUseUtil {
|
||||
final Collection<Instruction> result = new LinkedHashSet<>();
|
||||
ControlFlowUtil.iteratePrev(instr, instructions,
|
||||
instruction -> {
|
||||
if (instruction.num() < instructions[instr].num() && instruction instanceof CallInstruction callInstruction) {
|
||||
if (callInstruction.isNoReturnCall(context)) return ControlFlowUtil.Operation.CONTINUE;
|
||||
}
|
||||
final PsiElement element = instruction.getElement();
|
||||
final PyImplicitImportNameDefiner implicit = PyUtil.as(element, PyImplicitImportNameDefiner.class);
|
||||
if (instruction instanceof ReadWriteInstruction rwInstruction) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
from util import panic
|
||||
from util import Alarmist
|
||||
|
||||
|
||||
def foo():
|
||||
x = Alarmist()
|
||||
x.panic("Error!")
|
||||
<warning descr="This code is unreachable">print("Should be reported as unreachable")</warning>
|
||||
@@ -0,0 +1,6 @@
|
||||
from typing import NoReturn
|
||||
|
||||
class Alarmist:
|
||||
def panic(m) -> NoReturn:
|
||||
print(f'Help: {m}')
|
||||
raise SystemExit
|
||||
@@ -2004,6 +2004,26 @@ public class Py3TypeTest extends PyTestCase {
|
||||
""");
|
||||
}
|
||||
|
||||
public void testNoReturn() {
|
||||
doTest("Bar",
|
||||
"""
|
||||
from typing import NoReturn
|
||||
|
||||
class Foo:
|
||||
def stop(self) -> NoReturn:
|
||||
raise RuntimeError('no way')
|
||||
|
||||
class Bar:
|
||||
...
|
||||
|
||||
def foo(x):
|
||||
f = Foo()
|
||||
if not isinstance(x, Bar):
|
||||
f.stop()
|
||||
expr = x # expr is Bar, not Union[Bar, Any]
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-61137
|
||||
public void testLiteralStringIsNotInferredWithoutExplicitAnnotation() {
|
||||
doTest("list[str]",
|
||||
|
||||
@@ -237,6 +237,10 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase {
|
||||
doMultiFileTest();
|
||||
}
|
||||
|
||||
public void testUnreachableCodeReportedNoReturnInClassMember() {
|
||||
doMultiFileTest();
|
||||
}
|
||||
|
||||
// PY-53703
|
||||
public void testUnreachableCodeReportedAfterNever() {
|
||||
runWithLanguageLevel(LanguageLevel.getLatest(), () -> {
|
||||
|
||||
Reference in New Issue
Block a user