/// <summary> /// Gets all constraint types for the given <paramref name="typeParameterSymbol"/>. /// </summary> /// <param name="typeParameterSymbol">The typeParameterSymbol to act on.</param> /// <param name="includeInterfaces">(Optional) True to include, false to exclude the interfaces.</param> /// <returns/> public static IEnumerable <ITypeSymbol> GetAllConstraintTypes(this ITypeParameterSymbol typeParameterSymbol, bool includeInterfaces = true) { typeParameterSymbol.ThrowOnNull(nameof(typeParameterSymbol)); var constraintTypes = includeInterfaces ? GetAllConstraintTypesImplementation(typeParameterSymbol) : GetAllConstraintTypesImplementation(typeParameterSymbol) .Where(type => type.TypeKind != TypeKind.Interface); return(constraintTypes.Distinct()); //---------------------------------Local Functions-------------------------------------------------------- IEnumerable <ITypeSymbol> GetAllConstraintTypesImplementation(ITypeParameterSymbol typeParameter, int recursionLevel = 0) { const int maxRecursionLevel = 40; if (recursionLevel > maxRecursionLevel || typeParameter.ConstraintTypes.Length == 0) { yield break; } foreach (ITypeSymbol constraintType in typeParameter.ConstraintTypes) { if (constraintType is ITypeParameterSymbol constraintTypeParameter) { var nextOrderTypeParams = GetAllConstraintTypesImplementation(constraintTypeParameter, recursionLevel + 1); foreach (ITypeSymbol type in nextOrderTypeParams) { yield return(type); } } else { yield return(constraintType); } } } }