diff --git a/python/python-psi-impl/resources/messages/PyPsiBundle.properties b/python/python-psi-impl/resources/messages/PyPsiBundle.properties index 6e557ad5c9ff..1825ca92b96d 100644 --- a/python/python-psi-impl/resources/messages/PyPsiBundle.properties +++ b/python/python-psi-impl/resources/messages/PyPsiBundle.properties @@ -1373,6 +1373,7 @@ INSP.patterns.pattern.can.be.simplified=Pattern can be simplified INSP.patterns.class.does.not.support.pattern.matching.with.positional.arguments=Class {0} does not support pattern matching with positional arguments INSP.patterns.too.many.positional.patterns.expected=Too many positional patterns, expected {0} INSP.patterns.attribute.already.specified.as.positional.pattern.at.position=Attribute ''{0}'' is already specified as positional pattern at position {1} +INSP.patterns.not.a.class=Class pattern requires a class, but ''{0}'' can be ''{1}'' QFIX.simplify.as.pattern=Simplify 'as' pattern QFIX.NAME.remove.element=Remove element QFIX.remove.pattern=Remove pattern diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyPatternInspection.kt b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyPatternInspection.kt index b5cdabd85821..f947be839bc4 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyPatternInspection.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyPatternInspection.kt @@ -15,10 +15,7 @@ import com.jetbrains.python.psi.* import com.jetbrains.python.psi.impl.PyBuiltinCache import com.jetbrains.python.psi.impl.PyClassPatternImpl import com.jetbrains.python.psi.impl.PyPsiUtils -import com.jetbrains.python.psi.types.PyClassType -import com.jetbrains.python.psi.types.PyTupleType -import com.jetbrains.python.psi.types.PyTypeChecker -import com.jetbrains.python.psi.types.TypeEvalContext +import com.jetbrains.python.psi.types.* class PyPatternInspection : PyInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { @@ -49,7 +46,20 @@ private class PyPatternInspectionVisitor(holder: ProblemsHolder, context: TypeEv override fun visitPyClassPattern(node: PyClassPattern) { - val classType = myTypeEvalContext.getType(node.classNameReference) as? PyClassType ?: return + val type = myTypeEvalContext.getType(node.classNameReference) + val types = PyTypeUtil.toStream(type).toList() + if (types.isNotEmpty() && types.none { PyTypeChecker.isUnknown(it, myTypeEvalContext) }) { + val invalidTypes = types.filter { it !is PyClassType || !it.isDefinition } + if (invalidTypes.isNotEmpty()) { + val invalidTypesUnion = PyUnionType.union(invalidTypes) + val invalidTypeName = PythonDocumentationProvider.getTypeName(invalidTypesUnion, myTypeEvalContext) + holder.problem(node.classNameReference, + PyPsiBundle.message("INSP.patterns.not.a.class", node.classNameReference.text, invalidTypeName)).register() + return + } + } + + val classType = type as? PyClassType ?: return val pyClass = classType.pyClass if (pyClass.name in PyClassPattern.SPECIAL_BUILTINS) return diff --git a/python/testSrc/com/jetbrains/python/inspections/PyPatternInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyPatternInspectionTest.java index 7f875e732b5c..6b81419d137f 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyPatternInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyPatternInspectionTest.java @@ -323,6 +323,70 @@ def f(c): """); } + // PY-86019 + public void testFunctionPattern() { + doTestByText(""" + def f(): + pass + + match 1: + case f(): + pass + """); + } + + // PY-86019 + public void testInstancePattern() { + doTestByText(""" + x = 1 + match 1: + case x(): + pass + """); + } + + // PY-86019 + public void testUnionOfClassesPattern() { + doTestByText(""" + class A: pass + class B: pass + + def g(cond): + if cond: + c = A + else: + c = B + match 1: + case c(): + pass + """); + } + + // PY-86019 + public void testUnionOfClassAndFunctionPattern() { + doTestByText(""" + class A: pass + def f(): pass + + def g(cond): + c = A if cond else f + match 1: + case c(): + pass + """); + } + + // PY-86019 + public void testAnyPattern() { + doTestByText(""" + from typing import Any + def g(c: Any): + match 1: + case c(): + pass + """); + } + @NotNull @Override protected Class getInspectionClass() {