File: src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\Extensions\ITypeParameterSymbolExtensions.cs
Web Access
Project: src\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
 
namespace Microsoft.CodeAnalysis.CSharp.Extensions;
 
using static SyntaxFactory;
 
internal static class ITypeParameterSymbolExtensions
{
    public static SyntaxList<TypeParameterConstraintClauseSyntax> GenerateConstraintClauses(
        this ImmutableArray<ITypeParameterSymbol> typeParameters)
    {
        return typeParameters.AsEnumerable().GenerateConstraintClauses();
    }
 
    public static SyntaxList<TypeParameterConstraintClauseSyntax> GenerateConstraintClauses(
        this IEnumerable<ITypeParameterSymbol> typeParameters)
    {
        var clauses = new List<TypeParameterConstraintClauseSyntax>();
 
        foreach (var typeParameter in typeParameters)
        {
            AddConstraintClauses(clauses, typeParameter);
        }
 
        return [.. clauses];
    }
 
    private static void AddConstraintClauses(
        List<TypeParameterConstraintClauseSyntax> clauses,
        ITypeParameterSymbol typeParameter)
    {
        var constraints = new List<TypeParameterConstraintSyntax>();
 
        if (typeParameter.HasReferenceTypeConstraint)
        {
            constraints.Add(ClassOrStructConstraint(SyntaxKind.ClassConstraint));
        }
        else if (typeParameter.HasUnmanagedTypeConstraint)
        {
            constraints.Add(TypeConstraint(IdentifierName("unmanaged")));
        }
        else if (typeParameter.HasValueTypeConstraint)
        {
            constraints.Add(ClassOrStructConstraint(SyntaxKind.StructConstraint));
        }
        else if (typeParameter.HasNotNullConstraint)
        {
            constraints.Add(TypeConstraint(IdentifierName("notnull")));
        }
 
        var constraintTypes =
            typeParameter.ConstraintTypes.Where(t => t.TypeKind == TypeKind.Class).Concat(
            typeParameter.ConstraintTypes.Where(t => t.TypeKind == TypeKind.Interface).Concat(
            typeParameter.ConstraintTypes.Where(t => t.TypeKind is not TypeKind.Class and not TypeKind.Interface)));
 
        foreach (var type in constraintTypes)
        {
            if (type.SpecialType != SpecialType.System_Object)
            {
                constraints.Add(TypeConstraint(type.GenerateTypeSyntax()));
            }
        }
 
        if (typeParameter.HasConstructorConstraint)
        {
            constraints.Add(ConstructorConstraint());
        }
 
        if (typeParameter.AllowsRefLikeType)
        {
            // "allows ref struct" anti-constraint must be last
            constraints.Add(AllowsConstraintClause([RefStructConstraint()]));
        }
 
        if (constraints.Count == 0)
        {
            return;
        }
 
        clauses.Add(TypeParameterConstraintClause(
            typeParameter.Name.ToIdentifierName(),
            [.. constraints]));
    }
}