Raise a warning on Final instance attributes outside __init__ (PEP 591) (PY-34945)

GitOrigin-RevId: 74f4aab3a879d74815945a6f33d5a66afabd25c7
This commit is contained in:
Semyon Proshev
2019-07-02 06:52:16 +03:00
committed by intellij-monorepo-bot
parent 1dd5ee7103
commit 66defb9207
2 changed files with 32 additions and 2 deletions
@@ -71,8 +71,12 @@ class PyFinalInspection : PyInspection() {
registerProblem(node.nameIdentifier, "'${(it as PyFunction).qualifiedName}' is marked as '@final' and should not be overridden")
}
if (!PyiUtil.isInsideStub(node) && isFinal(node) && PyiUtil.isOverload(node, myTypeEvalContext)) {
registerProblem(node.nameIdentifier, "'@final' should be placed on the implementation")
if (!PyiUtil.isInsideStub(node)) {
if (isFinal(node) && PyiUtil.isOverload(node, myTypeEvalContext)) {
registerProblem(node.nameIdentifier, "'@final' should be placed on the implementation")
}
checkInstanceFinalsOutsideInit(node)
}
}
else if (isFinal(node)) {
@@ -158,6 +162,16 @@ class PyFinalInspection : PyInspection() {
}
}
private fun checkInstanceFinalsOutsideInit(method: PyFunction) {
if (PyUtil.isInit(method)) return
val instanceAttributes = mutableMapOf<String, PyTargetExpression>()
PyClassImpl.collectInstanceAttributes(method, instanceAttributes)
instanceAttributes.values.forEach {
if (isFinal(it)) registerProblem(it, "'Final' attribute should be declared in class body or '__init__'")
}
}
private fun checkFinalIsOuterMost(node: PyReferenceExpression) {
if (isTopLevelInAnnotationOrTypeComment(node)) return
(node.parent as? PySubscriptionExpression)?.let {
@@ -272,6 +272,22 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
);
}
// PY-34945
public void testFinalInstanceAttributes() {
runWithLanguageLevel(
LanguageLevel.PYTHON36,
() -> doTestByText("from typing_extensions import Final\n" +
"\n" +
"class A:\n" +
" def __init__(self):\n" +
" self.a: Final[str] = \"str\"\n" +
"\n" +
" def method(self):\n" +
" <warning descr=\"'Final' attribute should be declared in class body or '__init__'\">self.a</warning>: Final[int] = 10\n" +
" <warning descr=\"'Final' attribute should be declared in class body or '__init__'\">self.b</warning>: Final[int] = 10")
);
}
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {