diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 9f85131a8da5..5ac746146a46 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -754,13 +754,21 @@ public class PyTypeChecker { for (PyClassType type : toPossibleClassTypes(qualifierType)) { for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { final PyType genericType = provider.getGenericType(type.getPyClass(), context); + final Set providedTypeGenerics = new LinkedHashSet<>(); + if (genericType != null) { match(genericType, type, context, substitutions); + collectGenerics(genericType, context, providedTypeGenerics, new HashSet<>()); } + for (Map.Entry entry : provider.getGenericSubstitutions(type.getPyClass(), context).entrySet()) { final PyGenericType genericKey = as(entry.getKey(), PyGenericType.class); final PyType value = entry.getValue(); - if (genericKey != null && value != null && !substitutions.containsKey(genericKey)) { + + if (genericKey != null && + value != null && + !substitutions.containsKey(genericKey) && + !providedTypeGenerics.contains(genericKey)) { substitutions.put(genericKey, value); } } diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index b121ef99669a..67361ae53ea8 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -953,6 +953,27 @@ public class Py3TypeTest extends PyTestCase { ); } + // PY-27783 + public void testApplyingSuperSubstituionToGenericClass() { + runWithLanguageLevel( + LanguageLevel.PYTHON36, + () -> doTest("Dict[T, int]", + "from typing import TypeVar, Generic, Dict, List\n" + + "\n" + + "T = TypeVar('T')\n" + + "\n" + + "class A(Generic[T]):\n" + + " pass\n" + + "\n" + + "class B(A[List[T]], Generic[T]):\n" + + " def __init__(self) -> None:\n" + + " self.value_set: Dict[T, int] = {}\n" + + "\n" + + " def foo(self) -> None:\n" + + " expr = self.value_set") + ); + } + private void doTest(final String expectedType, final String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class);