GenericSubstitutions (@Nullable PyExpression receiver, @NotNull TypeEvalContext context) { // Collect generic params of object type final var substitutions = new GenericSubstitutions(); if (receiver != null) { // TODO properly handle union types here PyType receiverType = context.getType(receiver); if (receiverType instanceof PyClassType) { substitutions.qualifierType = ((PyClassType)receiverType).toInstance(); } else { substitutions.qualifierType = receiverType; } PyTypeUtil.toStream(receiverType) .select(PyClassType.class) .map(type -> collectTypeSubstitutions(type, context)) .forEach(newSubstitutions -> { for (Map.Entry<PyGenericType, PyType> typeVarMapping : newSubstitutions.typeVars.entrySet()) { substitutions.typeVars.putIfAbsent(typeVarMapping.getKey(), typeVarMapping.getValue()); } for (Map.Entry<PyTypeVarTupleType, PyVariadicType> typeVarMapping : newSubstitutions.typeVarTuples.entrySet()) { substitutions.typeVarTuples.putIfAbsent(typeVarMapping.getKey(), typeVarMapping.getValue()); } for (Map.Entry<PyParamSpecType, PyParamSpecType> paramSpecMapping : newSubstitutions.paramSpecs.entrySet()) { substitutions.paramSpecs.putIfAbsent(paramSpecMapping.getKey(), paramSpecMapping.getValue()); } }); } return substitutions; }