mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Inspect access to dataclasses' init only fields (PY-27398)
This commit is contained in:
@@ -7,6 +7,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.jetbrains.python.codeInsight.stdlib.DATACLASSES_INITVAR_TYPE
|
||||
import com.jetbrains.python.codeInsight.stdlib.parseDataclassParameters
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
|
||||
import com.jetbrains.python.psi.*
|
||||
@@ -126,6 +127,32 @@ class PyDataclassInspection : PyInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitPyReferenceExpression(node: PyReferenceExpression?) {
|
||||
super.visitPyReferenceExpression(node)
|
||||
|
||||
if (node != null && node.isQualified) {
|
||||
val cls = getInstancePyClass(node.qualifier) ?: return
|
||||
|
||||
if (parseDataclassParameters(cls, myTypeEvalContext) != null) {
|
||||
cls.processClassLevelDeclarations { element, _ ->
|
||||
if (element is PyTargetExpression && element.name == node.name) {
|
||||
val type = myTypeEvalContext.getType(element)
|
||||
|
||||
if (type is PyClassType && type.classQName == DATACLASSES_INITVAR_TYPE) {
|
||||
registerProblem(node.lastChild,
|
||||
"'${cls.name}' object could have no attribute '${element.name}' because it is declared as init-only",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
|
||||
return@processClassLevelDeclarations false
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInstancePyClass(element: PyTypedElement?): PyClass? {
|
||||
val type = element?.let { myTypeEvalContext.getType(it) } as? PyClassType
|
||||
return if (type != null && !type.isDefinition) type.pyClass else null
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import dataclasses
|
||||
|
||||
@dataclasses.dataclass
|
||||
class A:
|
||||
a: int
|
||||
b: dataclasses.InitVar[str]
|
||||
|
||||
def __post_init__(self, b: str):
|
||||
pass
|
||||
|
||||
|
||||
a = A(1, "a")
|
||||
print(a.a)
|
||||
print(a.<warning descr="'A' object could have no attribute 'b' because it is declared as init-only">b</warning>)
|
||||
@@ -1,3 +1,11 @@
|
||||
class _InitVarMeta(type):
|
||||
def __getitem__(self, params):
|
||||
return self
|
||||
|
||||
class InitVar(metaclass=_InitVarMeta):
|
||||
pass
|
||||
|
||||
|
||||
def dataclass(_cls=None, *, init=True, repr=True, eq=True, order=False,
|
||||
hash=None, frozen=False):
|
||||
pass
|
||||
|
||||
@@ -51,6 +51,11 @@ public class PyDataclassInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-27398
|
||||
public void testAccessToInitVar() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doTest() {
|
||||
runWithLanguageLevel(
|
||||
|
||||
Reference in New Issue
Block a user