diff --git a/python/src/com/jetbrains/python/inspections/PyDataclassInspection.kt b/python/src/com/jetbrains/python/inspections/PyDataclassInspection.kt index d5e2d8058b4e..ca19fb8f64fd 100644 --- a/python/src/com/jetbrains/python/inspections/PyDataclassInspection.kt +++ b/python/src/com/jetbrains/python/inspections/PyDataclassInspection.kt @@ -68,7 +68,20 @@ class PyDataclassInspection : PyInspection() { processPostInitDefinition(postInit, dataclassParameters, initVars) } - PyNamedTupleInspection.inspectFieldsOrder(node, myTypeEvalContext, this::registerProblem) + PyNamedTupleInspection.inspectFieldsOrder( + node, + this::registerProblem, + { !PyTypingTypeProvider.isClassVar(it, myTypeEvalContext) }, + { + val fieldStub = PyDataclassFieldStubImpl.create(it) + if (fieldStub != null) { + fieldStub.hasDefault() || fieldStub.hasDefaultFactory() + } + else { + it.hasAssignedValue() + } + } + ) } } } diff --git a/python/src/com/jetbrains/python/inspections/PyNamedTupleInspection.kt b/python/src/com/jetbrains/python/inspections/PyNamedTupleInspection.kt index ebfe57ae331f..a69ac569fb5a 100644 --- a/python/src/com/jetbrains/python/inspections/PyNamedTupleInspection.kt +++ b/python/src/com/jetbrains/python/inspections/PyNamedTupleInspection.kt @@ -9,18 +9,19 @@ import com.intellij.psi.PsiElementVisitor import com.intellij.psi.ResolveState import com.intellij.psi.scope.PsiScopeProcessor import com.jetbrains.python.codeInsight.stdlib.PyStdlibTypeProvider -import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider import com.jetbrains.python.psi.LanguageLevel import com.jetbrains.python.psi.PyClass import com.jetbrains.python.psi.PyTargetExpression -import com.jetbrains.python.psi.types.TypeEvalContext import java.util.* class PyNamedTupleInspection : PyInspection() { companion object { - fun inspectFieldsOrder(cls: PyClass, context: TypeEvalContext, callback: (PsiElement, String, ProblemHighlightType) -> Unit) { - val fieldsProcessor = FieldsProcessor(context) + fun inspectFieldsOrder(cls: PyClass, + callback: (PsiElement, String, ProblemHighlightType) -> Unit, + filter: (PyTargetExpression) -> Boolean = { true }, + hasAssignedValue: (PyTargetExpression) -> Boolean = PyTargetExpression::hasAssignedValue) { + val fieldsProcessor = FieldsProcessor(filter, hasAssignedValue) cls.processClassLevelDeclarations(fieldsProcessor) @@ -54,12 +55,13 @@ class PyNamedTupleInspection : PyInspection() { if (node != null && LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON36) && PyStdlibTypeProvider.isTypingNamedTupleDirectInheritor(node, myTypeEvalContext)) { - inspectFieldsOrder(node, myTypeEvalContext, this::registerProblem) + inspectFieldsOrder(node, this::registerProblem) } } } - private class FieldsProcessor(private val context: TypeEvalContext) : PsiScopeProcessor { + private class FieldsProcessor(private val filter: (PyTargetExpression) -> Boolean, + private val hasAssignedValue: (PyTargetExpression) -> Boolean) : PsiScopeProcessor { val lastFieldWithoutDefaultValue: PyTargetExpression? get() = lastFieldWithoutDefaultValueBox.result @@ -74,13 +76,9 @@ class PyNamedTupleInspection : PyInspection() { } override fun execute(element: PsiElement, state: ResolveState): Boolean { - if (element is PyTargetExpression) { - if (PyTypingTypeProvider.isClassVar(element, context)) { - return true - } - + if (element is PyTargetExpression && filter(element)) { when { - element.findAssignedValue() != null -> fieldsWithDefaultValue.add(element) + hasAssignedValue(element) -> fieldsWithDefaultValue.add(element) else -> lastFieldWithoutDefaultValueBox.apply(element) } } diff --git a/python/testData/inspections/PyDataclassInspection/fieldsOrder.py b/python/testData/inspections/PyDataclassInspection/fieldsOrder.py index 514f23bb0d65..06c0c3cf6492 100644 --- a/python/testData/inspections/PyDataclassInspection/fieldsOrder.py +++ b/python/testData/inspections/PyDataclassInspection/fieldsOrder.py @@ -36,4 +36,22 @@ class A4: foo1: int bar2: ClassVar = 2 baz2: int - foo2: int = 3 \ No newline at end of file + foo2: int = 3 + + +@dataclasses.dataclass +class B1: + a: int = dataclasses.field() + b: int + + +@dataclasses.dataclass +class B2: + a: int = dataclasses.field(default=1) + b: int = dataclasses.field() + + +@dataclasses.dataclass +class B3: + a: int = dataclasses.field(default_factory=int) + b: int = dataclasses.field() \ No newline at end of file