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();
+ }
}