mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Raise a warning on same name class and instance level Finals (PEP 591) (PY-34945)
GitOrigin-RevId: e8eaad47c5a06cb8471e06d7c1e0890f02266820
This commit is contained in:
committed by
intellij-monorepo-bot
parent
66defb9207
commit
f2ca243d2e
@@ -54,7 +54,9 @@ class PyFinalInspection : PyInspection() {
|
||||
)
|
||||
}
|
||||
else {
|
||||
checkClassLevelFinalsAreInitialized(node)
|
||||
val (classLevelFinals, initAttributes) = getClassLevelFinalsAndInitAttributes(node)
|
||||
checkClassLevelFinalsAreInitialized(classLevelFinals, initAttributes)
|
||||
checkSameNameClassAndInstanceFinals(classLevelFinals, initAttributes)
|
||||
}
|
||||
|
||||
checkRedeclarationsInScope(node)
|
||||
@@ -140,24 +142,39 @@ class PyFinalInspection : PyInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkClassLevelFinalsAreInitialized(cls: PyClass) {
|
||||
val notInitializedFinals = mutableMapOf<String?, PyTargetExpression>()
|
||||
private fun getClassLevelFinalsAndInitAttributes(cls: PyClass): Pair<Map<String?, PyTargetExpression>, Map<String, PyTargetExpression>> {
|
||||
val classLevelFinals = mutableMapOf<String?, PyTargetExpression>()
|
||||
cls.classAttributes.forEach { if (isFinal(it)) classLevelFinals[it.name] = it }
|
||||
|
||||
cls.classAttributes.forEach {
|
||||
if (!it.hasAssignedValue() && isFinal(it)) {
|
||||
notInitializedFinals[it.name] = it
|
||||
val initAttributes = mutableMapOf<String, PyTargetExpression>()
|
||||
cls.findMethodByName(PyNames.INIT, false, myTypeEvalContext)?.let { PyClassImpl.collectInstanceAttributes(it, initAttributes) }
|
||||
|
||||
return Pair(classLevelFinals, initAttributes)
|
||||
}
|
||||
|
||||
private fun checkClassLevelFinalsAreInitialized(classLevelFinals: Map<String?, PyTargetExpression>,
|
||||
initAttributes: Map<String, PyTargetExpression>) {
|
||||
classLevelFinals.forEach { (name, psi) ->
|
||||
if (!psi.hasAssignedValue() && name !in initAttributes) {
|
||||
registerProblem(psi, "'Final' name should be initialized with a value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notInitializedFinals.isNotEmpty()) {
|
||||
cls.findMethodByName(PyNames.INIT, false, myTypeEvalContext)?.let {
|
||||
val initializedAttributes = mutableMapOf<String, PyTargetExpression>()
|
||||
PyClassImpl.collectInstanceAttributes(it, initializedAttributes)
|
||||
notInitializedFinals -= initializedAttributes.keys
|
||||
}
|
||||
private fun checkSameNameClassAndInstanceFinals(classLevelFinals: Map<String?, PyTargetExpression>,
|
||||
initAttributes: Map<String, PyTargetExpression>) {
|
||||
initAttributes.forEach { (name, initAttribute) ->
|
||||
val sameNameClassLevelFinal = classLevelFinals[name]
|
||||
|
||||
notInitializedFinals.values.forEach {
|
||||
registerProblem(it, "'Final' name should be initialized with a value")
|
||||
if (sameNameClassLevelFinal != null && isFinal(initAttribute)) {
|
||||
if (sameNameClassLevelFinal.hasAssignedValue()) {
|
||||
registerProblem(initAttribute, "Already declared name could not be redefined as 'Final'")
|
||||
}
|
||||
else {
|
||||
val message = "Either instance attribute or class attribute could be type hinted as 'Final'"
|
||||
registerProblem(sameNameClassLevelFinal, message)
|
||||
registerProblem(initAttribute, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +288,25 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
// PY-34945
|
||||
public void testSameNameClassAndInstanceLevelFinals() {
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON36,
|
||||
() -> doTestByText(
|
||||
"from typing_extensions import Final\n" +
|
||||
"\n" +
|
||||
"class A:\n" +
|
||||
" a: Final[int] = 1\n" +
|
||||
" b: Final[str] = \"1\"\n" +
|
||||
" <warning descr=\"Either instance attribute or class attribute could be type hinted as 'Final'\">c</warning>: Final[int]\n" +
|
||||
"\n" +
|
||||
" def __init__(self):\n" +
|
||||
" <warning descr=\"Already declared name could not be redefined as 'Final'\">self.a</warning>: Final[int] = 2\n" +
|
||||
" self.b = \"2\"\n" +
|
||||
" <warning descr=\"Either instance attribute or class attribute could be type hinted as 'Final'\">self.c</warning>: Final[int] = 2")
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user