GenericSubstitutions (@NotNull PyClassType classType, @NotNull TypeEvalContext context) { GenericSubstitutions result = new GenericSubstitutions(); for (PyTypeProvider provider : PyTypeProvider.EP_NAME.getExtensionList()) { Map<PyType, PyType> substitutionsFromClassDefinition = provider.getGenericSubstitutions(classType.getPyClass(), context); for (Map.Entry<PyType, PyType> entry : substitutionsFromClassDefinition.entrySet()) { if (entry.getKey() instanceof PyGenericType) { result.typeVars.put((PyGenericType)entry.getKey(), entry.getValue()); } else if (entry.getKey() instanceof PyTypeVarTupleType typeVarTuple) { assert entry.getValue() instanceof PyVariadicType; result.typeVarTuples.put(typeVarTuple, (PyVariadicType)entry.getValue()); } // TODO Handle ParamSpecs here } if (!classType.isDefinition()) { PyCollectionType genericDefinitionType = as(provider.getGenericType(classType.getPyClass(), context), PyCollectionType.class); // TODO Re-use PyTypeParameterMapping, at the moment C[*Ts] <- C leads to *Ts being mapped to *tuple[], which breaks inference later on if (genericDefinitionType != null) { List<PyType> definitionTypeParameters = genericDefinitionType.getElementTypes(); if (!(classType instanceof PyCollectionType genericType)) { for (PyType typeParameter : definitionTypeParameters) { if (typeParameter instanceof PyTypeVarTupleType typeVarTupleType) { result.typeVarTuples.put(typeVarTupleType, null); } else if (typeParameter instanceof PyParamSpecType paramSpecType) { result.paramSpecs.put(paramSpecType, null); } else if (typeParameter instanceof PyTypeVarType typeVarType) { result.typeVars.put((PyGenericType)typeVarType, null); } } } else { PyTypeParameterMapping mapping = PyTypeParameterMapping.mapByShape(definitionTypeParameters, genericType.getElementTypes(), Option.MAP_UNMATCHED_EXPECTED_TYPES_TO_ANY); if (mapping != null) { for (Couple<PyType> pair : mapping.getMappedTypes()) { PyType typeParameter = pair.getFirst(); PyType typeArgument = pair.getSecond(); if (typeParameter instanceof PyGenericType) { result.typeVars.put((PyGenericType)typeParameter, typeArgument); } else if (typeParameter instanceof PyTypeVarTupleType typeVarTuple) { assert typeArgument instanceof PyVariadicType || typeArgument == null; result.typeVarTuples.put(typeVarTuple, (PyVariadicType)typeArgument); } else if (typeParameter instanceof PyParamSpecType) { result.getParamSpecs().put((PyParamSpecType)typeParameter, as(typeArgument, PyParamSpecType.class)); } } } } } } if (!result.typeVars.isEmpty() || !result.typeVarTuples.isEmpty() || !result.paramSpecs.isEmpty()) { return result; } } return result; }