Raise a warning on inherited Finals reassignment on class level (PEP 591) (PY-34945)

GitOrigin-RevId: 8d06617819ab21c83a053a0c36da5495b503288b
This commit is contained in:
Semyon Proshev
2019-07-02 06:52:16 +03:00
committed by intellij-monorepo-bot
parent b70415af67
commit 3cb186667e
3 changed files with 29 additions and 3 deletions
@@ -111,6 +111,14 @@ class PyFinalInspection : PyInspection() {
else if (PyUtil.multiResolveTopPriority(node, resolveContext).any { it != node && it is PyTargetExpression && isFinal(it) }) {
registerProblem(node, "'${node.name}' is 'Final' and could not be reassigned")
}
else {
if (!node.isQualified) {
val scopeOwner = ScopeUtil.getScopeOwner(node)
if (scopeOwner is PyClass) {
checkInheritedClassFinalReassignmentOnClassLevel(node, scopeOwner)
}
}
}
}
if (isFinal(node) && PyUtil.multiResolveTopPriority(node, resolveContext).any { it != node }) {
@@ -265,6 +273,19 @@ class PyFinalInspection : PyInspection() {
}
}
private fun checkInheritedClassFinalReassignmentOnClassLevel(target: PyTargetExpression, cls: PyClass) {
val name = target.name ?: return
for (ancestor in cls.getAncestorClasses(myTypeEvalContext)) {
val ancestorClassAttribute = ancestor.findClassAttribute(name, false, myTypeEvalContext)
if (ancestorClassAttribute != null && ancestorClassAttribute.hasAssignedValue() && isFinal(ancestorClassAttribute)) {
registerProblem(target, "'${ancestor.name}.$name' is 'Final' and could not be reassigned")
break
}
}
}
private fun checkFinalIsOuterMost(node: PyReferenceExpression) {
if (isTopLevelInAnnotationOrTypeComment(node)) return
(node.parent as? PySubscriptionExpression)?.let {
@@ -7,4 +7,7 @@ class B(A):
def my_cls_method(cls):
<warning descr="'a' is 'Final' and could not be reassigned">cls.a</warning> = 6
<warning descr="'a' is 'Final' and could not be reassigned">B.a</warning> = 7
<warning descr="'a' is 'Final' and could not be reassigned">B.a</warning> = 7
class C(A):
<warning descr="'A.a' is 'Final' and could not be reassigned">a</warning> = 8
@@ -350,8 +350,10 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
" def my_cls_method(cls):\n" +
" <warning descr=\"'a' is 'Final' and could not be reassigned\">cls.a</warning> = 6\n" +
"\n" +
"<warning descr=\"'a' is 'Final' and could not be reassigned\">" +
"B.a</warning> = 7")
"<warning descr=\"'a' is 'Final' and could not be reassigned\">B.a</warning> = 7\n" +
"\n" +
"class C(A):\n" +
" <warning descr=\"'A.a' is 'Final' and could not be reassigned\">a</warning> = 8\n")
);
}