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;
using Microsoft.CodeAnalysis.PooledObjects;
 
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)
    {
        using var _ = ArrayBuilder<TypeParameterConstraintClauseSyntax>.GetInstance(out var clauses);
 
        foreach (var typeParameter in typeParameters)
            AddConstraintClauses(clauses, typeParameter);
 
        return [.. clauses];
    }
 
    private static void AddConstraintClauses(
        ArrayBuilder<TypeParameterConstraintClauseSyntax> clauses,
        ITypeParameterSymbol typeParameter)
    {
        using var _ = ArrayBuilder<TypeParameterConstraintSyntax>.GetInstance(out var constraints);
 
        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]));
    }
}