mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Raise a warning on Finals overriding (PEP 591) (PY-34945)
GitOrigin-RevId: 53c741c0ba2467b0fde342ec7e95bca952651522
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0509475adc
commit
b70415af67
@@ -52,6 +52,8 @@ class PyFinalInspection : PyInspection() {
|
||||
checkClassLevelFinalsAreInitialized(classLevelFinals, initAttributes)
|
||||
checkSameNameClassAndInstanceFinals(classLevelFinals, initAttributes)
|
||||
}
|
||||
|
||||
checkOverridingInheritedFinalWithNewOne(node)
|
||||
}
|
||||
|
||||
override fun visitPyFunction(node: PyFunction) {
|
||||
@@ -140,6 +142,28 @@ class PyFinalInspection : PyInspection() {
|
||||
return Pair(classLevelFinals, initAttributes)
|
||||
}
|
||||
|
||||
private fun getDeclaredClassAndInstanceFinals(cls: PyClass): Pair<Map<String, PyTargetExpression>, Map<String, PyTargetExpression>> {
|
||||
val classFinals = mutableMapOf<String, PyTargetExpression>()
|
||||
val instanceFinals = mutableMapOf<String, PyTargetExpression>()
|
||||
|
||||
for (classAttribute in cls.classAttributes) {
|
||||
val name = classAttribute.name ?: continue
|
||||
|
||||
if (isFinal(classAttribute)) {
|
||||
val mapToPut = if (classAttribute.hasAssignedValue()) classFinals else instanceFinals
|
||||
mapToPut[name] = classAttribute
|
||||
}
|
||||
}
|
||||
|
||||
cls.findMethodByName(PyNames.INIT, false, myTypeEvalContext)?.let { init ->
|
||||
val attributesInInit = mutableMapOf<String, PyTargetExpression>()
|
||||
PyClassImpl.collectInstanceAttributes(init, attributesInInit, instanceFinals.keys)
|
||||
instanceFinals += attributesInInit.filterValues { isFinal(it) }
|
||||
}
|
||||
|
||||
return Pair(classFinals, instanceFinals)
|
||||
}
|
||||
|
||||
private fun checkClassLevelFinalsAreInitialized(classLevelFinals: Map<String?, PyTargetExpression>,
|
||||
initAttributes: Map<String, PyTargetExpression>) {
|
||||
classLevelFinals.forEach { (name, psi) ->
|
||||
@@ -167,6 +191,35 @@ class PyFinalInspection : PyInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOverridingInheritedFinalWithNewOne(cls: PyClass) {
|
||||
val (newClassFinals, newInstanceFinals) = getDeclaredClassAndInstanceFinals(cls)
|
||||
|
||||
val notRegisteredClassFinals = newClassFinals.keys.toMutableSet()
|
||||
val notRegisteredInstanceFinals = newInstanceFinals.keys.toMutableSet()
|
||||
if (notRegisteredClassFinals.isEmpty() && notRegisteredInstanceFinals.isEmpty()) return
|
||||
|
||||
for (ancestor in cls.getAncestorClasses(myTypeEvalContext)) {
|
||||
val (inheritedClassFinals, inheritedInstanceFinals) = getDeclaredClassAndInstanceFinals(ancestor)
|
||||
|
||||
checkOverridingInheritedFinalWithNewOne(newClassFinals, inheritedClassFinals, ancestor.name, notRegisteredClassFinals)
|
||||
checkOverridingInheritedFinalWithNewOne(newInstanceFinals, inheritedInstanceFinals, ancestor.name, notRegisteredInstanceFinals)
|
||||
|
||||
if (notRegisteredClassFinals.isEmpty() && notRegisteredInstanceFinals.isEmpty()) break
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOverridingInheritedFinalWithNewOne(newFinals: Map<String, PyTargetExpression>,
|
||||
inheritedFinals: Map<String, PyTargetExpression>,
|
||||
ancestorName: String?,
|
||||
notRegistered: MutableSet<String>) {
|
||||
if (notRegistered.isEmpty()) return
|
||||
|
||||
for (commonFinal in newFinals.keys.intersect(inheritedFinals.keys)) {
|
||||
registerProblem(newFinals[commonFinal], "'$ancestorName.$commonFinal' is 'Final' and could not be overridden")
|
||||
notRegistered.remove(commonFinal)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInstanceFinalsOutsideInit(method: PyFunction) {
|
||||
if (PyUtil.isInit(method)) return
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from typing_extensions import Final
|
||||
from b import A
|
||||
|
||||
class B(A):
|
||||
<warning descr="'A.a' is 'Final' and could not be overridden">a</warning>: Final[str] = "3"
|
||||
@@ -0,0 +1,4 @@
|
||||
from typing_extensions import Final
|
||||
|
||||
class A:
|
||||
a: Final[int] = 1
|
||||
@@ -0,0 +1,14 @@
|
||||
from typing_extensions import Final
|
||||
from b import A
|
||||
|
||||
class B(A):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
<warning descr="'A.a' is 'Final' and could not be overridden">self.a</warning>: Final[str] = "2"
|
||||
|
||||
class C(A):
|
||||
<warning descr="'A.a' is 'Final' and could not be overridden">a</warning>: Final[str]
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.a = "3"
|
||||
@@ -0,0 +1,7 @@
|
||||
from typing_extensions import Final
|
||||
|
||||
class A:
|
||||
a: Final[int]
|
||||
|
||||
def __init__(self):
|
||||
self.a = 1
|
||||
@@ -415,6 +415,56 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
// PY-34945
|
||||
public void testClassFinalOverriding() {
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON36,
|
||||
() -> doTestByText("from typing_extensions import Final\n" +
|
||||
"\n" +
|
||||
"class A:\n" +
|
||||
" a: Final[int] = 1\n" +
|
||||
"\n" +
|
||||
"class B(A):\n" +
|
||||
" <warning descr=\"'A.a' is 'Final' and could not be overridden\">a</warning>: Final[str] = \"3\"\n")
|
||||
);
|
||||
}
|
||||
|
||||
// PY-34945
|
||||
public void testImportedClassFinalOverriding() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON36, this::doMultiFileTest);
|
||||
}
|
||||
|
||||
// PY-34945
|
||||
public void testInstanceFinalOverriding() {
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON36,
|
||||
() -> doTestByText("from typing_extensions import Final\n" +
|
||||
"\n" +
|
||||
"class A:\n" +
|
||||
" a: Final[int]\n" +
|
||||
"\n" +
|
||||
" def __init__(self):\n" +
|
||||
" self.a = 1\n" +
|
||||
"\n" +
|
||||
"class B(A):\n" +
|
||||
" def __init__(self):\n" +
|
||||
" super().__init__()\n" +
|
||||
" <warning descr=\"'A.a' is 'Final' and could not be overridden\">self.a</warning>: Final[str] = \"2\"\n" +
|
||||
"\n" +
|
||||
"class C(A):\n" +
|
||||
" <warning descr=\"'A.a' is 'Final' and could not be overridden\">a</warning>: Final[str]\n" +
|
||||
"\n" +
|
||||
" def __init__(self):\n" +
|
||||
" super().__init__()\n" +
|
||||
" self.a = \"3\"")
|
||||
);
|
||||
}
|
||||
|
||||
// PY-34945
|
||||
public void testImportedInstanceFinalOverriding() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON36, this::doMultiFileTest);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user