PY-83047 False positive "This code is unreachable" after attribute assignment to None under an if

Merge-request: IJ-MR-173356
Merged-by: Aleksandr Govenko <aleksandr.govenko@jetbrains.com>

GitOrigin-RevId: 08669d49a831548c354096fbb5cbec1a155da4e2
This commit is contained in:
Aleksandr.Govenko
2025-09-30 07:37:59 +00:00
committed by intellij-monorepo-bot
parent 7d7c958cde
commit 9bbd8452a0
9 changed files with 249 additions and 127 deletions
@@ -805,7 +805,7 @@ public class Py3ResolveTest extends PyResolveTestCase {
public void testInstanceAttrOtherMethodAndAbove() {
final PyTargetExpression target = assertResolvesTo(PyTargetExpression.class, "foo");
final PyFunction function = assertInstanceOf(ScopeUtil.getScopeOwner(target), PyFunction.class);
assertEquals("f", function.getName());
assertEquals("g", function.getName());
}
public void testInstanceAttrBelowAndOtherMethodAbove() {
@@ -17,6 +17,108 @@ import java.util.Map;
public class Py3TypeTest extends PyTestCase {
public static final String TEST_DIRECTORY = "/types/";
// See PyReferenceExpressionImpl.getQualifiedReferenceType for explanations.
public void testQualifiedNameResolution() {
doTest("str", """
class C:
def m(self):
self.t = 5
def f(self: C, x: float):
self.t = "foo"
expr = self.t
""");
doTest("int", """
class C:
def m(self):
self.t: int = 5
def f(self: C, x: float):
self.t = "foo"
expr = self.t
""");
doTest("int", """
class C:
def __init__(self):
self.t: int = 5
def f(self: C, x: float):
self.t = "foo"
expr = self.t
""");
}
// PY-83047
public void testQualifiedReferenceTypeNarrowing() {
doTest("int | None", """
class C:
def __init__(self):
self.t: int | None = 5
def f(self, x: float):
if x < 0:
self.t = None
expr = self.t
""");
doTest("int", """
class C:
def __init__(self):
self.t: int | None = 5
def f(self, x: float):
if self.t is not None:
expr = self.t
""");
doTest("None", """
class C:
def __init__(self):
self.t: int | None = 5
def f(self, x: float):
if self.t is None:
expr = self.t
""");
// Same, but as a separate function
doTest("int | None", """
class C:
def __init__(self):
self.t: int | None = 5
def f(self: C, x: float):
if x < 0:
self.t = None
expr = self.t
""");
doTest("int", """
class C:
def __init__(self):
self.t: int | None = 5
def f(self: C, x: float):
if self.t is not None:
expr = self.t
""");
doTest("None", """
class C:
def __init__(self):
self.t: int | None = 5
def f(self: C, x: float):
if self.t is None:
expr = self.t
""");
}
/**
Overload signatures for dict.get and dict.pop in builtins.pyi differ slightly,
@@ -353,7 +353,7 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
self.a: Final[str] = "str"
def method(self):
<warning descr="'Final' attribute should be declared in class body or '__init__'">self.a</warning>: Final[int] = 10
<warning descr="'Final' attribute should be declared in class body or '__init__'"><warning descr="Already declared name could not be redefined as 'Final'">self.a</warning></warning>: Final[int] = 10
<warning descr="'Final' attribute should be declared in class body or '__init__'">self.b</warning>: Final[int] = 10""")
);
}