diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 88ba5d387bca..08b3988cb7c0 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -80,6 +80,7 @@ public class PyTypeChecker { } } if (expected instanceof PyInstantiableType && actual instanceof PyInstantiableType + && !(expected instanceof PyGenericType && typeVarAcceptsBothClassAndInstanceTypes((PyGenericType)expected)) && ((PyInstantiableType)expected).isDefinition() ^ ((PyInstantiableType)actual).isDefinition()) { return false; } @@ -288,6 +289,10 @@ public class PyTypeChecker { return matchNumericTypes(expected, actual); } + private static boolean typeVarAcceptsBothClassAndInstanceTypes(@NotNull PyGenericType typeVar) { + return !typeVar.isDefinition() && typeVar.getBound() == null; + } + private static boolean consistsOfSameElementNumberTuples(@NotNull PyUnionType unionType, int elementCount) { for (PyType type : unionType.getMembers()) { if (type instanceof PyTupleType) { diff --git a/python/testData/inspections/PyTypeCheckerInspection/UnboundTypeVarsMatchClassObjectTypes.py b/python/testData/inspections/PyTypeCheckerInspection/UnboundTypeVarsMatchClassObjectTypes.py new file mode 100644 index 000000000000..05f1376366ea --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/UnboundTypeVarsMatchClassObjectTypes.py @@ -0,0 +1,38 @@ +from typing import TypeVar + +T = TypeVar('T') +B = TypeVar('B', str) + + +def f1(p: T): + return p + + +f1(str) + + +def f2(p: B): + return p + + +f2(str) + + +def g1(p): + """ + :type p: T + """ + + +g1(str) + + +def g2(p): + """ + :type p: T <= str + """ + + +g2(str) + +xs = list([str]) diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index 9342dd00adfe..12420ba99c6e 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -868,6 +868,30 @@ public class PyTypingTest extends PyTestCase { " expr = x"); } + // PY-23053 + public void testUnboundGenericMatchesClassObjectTypes() { + doTest("Type[str]", + "from typing import Generic, TypeVar\n" + + "\n" + + "T = TypeVar('T')\n" + + "\n" + + "class Holder(Generic[T]):\n" + + " def __init__(self, value: T):\n" + + " self._value = value\n" + + "\n" + + " def get(self) -> T:\n" + + " return self._value\n" + + "\n" + + "expr = Holder(str).get()\n"); + } + + // PY-23053 + public void testListContainingClasses() { + doTest("Type[str]", + "xs = [str]\n" + + "expr = xs.pop()"); + } + private void doTestNoInjectedText(@NotNull String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject()); diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index 9de54dc04f70..a86c8023dfde 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -223,4 +223,9 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { public void testReplaceCalledOnUnionOfStrAndBytesWithStrArguments() { doTest(); } + + // PY-23053 + public void testUnboundTypeVarsMatchClassObjectTypes() { + doTest(); + } }