GenericSubstitutions (@NotNull Collection<PyCallableParameter> parameters, @Nullable PyType returnType, @Nullable GenericSubstitutions substitutions, @NotNull TypeEvalContext context) { GenericSubstitutions existingSubstitutions = substitutions == null ? new GenericSubstitutions() : substitutions; Generics typeParamsFromReturnType = collectGenerics(returnType, context); // TODO Handle unmatched ParamSpecs and TypeVarTuples here as well if (typeParamsFromReturnType.typeVars.isEmpty()) { return existingSubstitutions; } Set<PyType> visited = new HashSet<>(); Generics typeParamsFromParameterTypes = new Generics(); for (PyCallableParameter parameter : parameters) { collectGenerics(parameter.getArgumentType(context), context, typeParamsFromParameterTypes, visited); } for (PyGenericType returnTypeParam : typeParamsFromReturnType.typeVars) { boolean canGetBoundFromArguments = typeParamsFromParameterTypes.typeVars.contains(returnTypeParam) || typeParamsFromParameterTypes.typeVars.contains(invert(returnTypeParam)); boolean isAlreadyBound = existingSubstitutions.typeVars.containsKey(returnTypeParam) || existingSubstitutions.typeVars.containsKey(invert(returnTypeParam)); if (canGetBoundFromArguments && !isAlreadyBound) { existingSubstitutions.typeVars.put(returnTypeParam, null); } } return existingSubstitutions; }