File: Writers\CSharp\CSDeclarationWriter.Generics.cs
Web Access
Project: src\src\Microsoft.Cci.Extensions\Microsoft.Cci.Extensions.csproj (Microsoft.Cci.Extensions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Cci.Extensions.CSharp;
using Microsoft.Cci.Writers.Syntax;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
 
namespace Microsoft.Cci.Writers.CSharp
{
    public partial class CSDeclarationWriter
    {
        private void WriteGenericParameters(IEnumerable<IGenericParameter> genericParameters)
        {
            if (!genericParameters.Any())
                return;
 
            WriteSymbol("<");
            _writer.WriteList(genericParameters, p => WriteGenericParameter(p));
            WriteSymbol(">");
        }
 
        private void WriteGenericParameter(IGenericParameter param)
        {
            switch (param.Variance)
            {
                case TypeParameterVariance.Contravariant:
                    WriteKeyword("in"); break;
                case TypeParameterVariance.Covariant:
                    WriteKeyword("out"); break;
            }
            WriteTypeName(param, noSpace: true);
        }
 
        private void WriteGenericContraints(IEnumerable<IGenericParameter> genericParams, byte? methodNullableContextValue = null)
        {
            if (!genericParams.Any())
                return;
 
            foreach (IGenericParameter param in genericParams)
            {
                Action[] constraints = GetConstraints(param, methodNullableContextValue).ToArray();
 
                if (constraints.Length <= 0)
                    continue;
 
                WriteSpace();
                WriteKeyword("where");
                WriteTypeName(param);
                WriteSymbol(":", true);
 
                _writer.WriteList(constraints, c => c());
            }
        }
 
        private IEnumerable<Action> GetConstraints(IGenericParameter parameter, byte? methodNullableContextValue)
        {
            parameter.Attributes.TryGetAttributeOfType(CSharpCciExtensions.NullableAttributeFullName, out ICustomAttribute nullableAttribute);
            object nullableAttributeValue = nullableAttribute.GetAttributeArgumentValue<byte>() ?? methodNullableContextValue ?? TypeNullableContextValue ?? ModuleNullableContextValue;
 
            if (parameter.MustBeValueType)
                yield return () => WriteKeyword("struct", noSpace: true);
            else
            {
                if (parameter.MustBeReferenceType)
                    yield return () =>
                    {
                        WriteKeyword("class", noSpace: true);
 
                        if (nullableAttribute != null)
                        {
                            WriteNullableSymbolForReferenceType(nullableAttributeValue, arrayIndex: 0);
                        }
                    };
            }
 
            // If there are no struct or class constraints and contains a nullableAttributeValue then it might have a notnull constraint
            if (!parameter.MustBeValueType && !parameter.MustBeReferenceType && nullableAttributeValue != null)
            {
                if (((byte)nullableAttributeValue & 1) != 0)
                {
                    yield return () => WriteKeyword("notnull", noSpace: true);
                }
            }
 
            var assemblyLocation = parameter.Locations.FirstOrDefault()?.Document?.Location;
 
            int constraintIndex = 0;
            foreach (var constraint in parameter.Constraints)
            {
                // Skip valuetype because we should get it above.
                if (!TypeHelper.TypesAreEquivalent(constraint, constraint.PlatformType.SystemValueType) && !parameter.MustBeValueType)
                {
                    if (assemblyLocation != null)
                    {
                        nullableAttributeValue = parameter.GetGenericParameterConstraintConstructorArgument(constraintIndex, assemblyLocation, _metadataReaderCache, CSharpCciExtensions.NullableConstructorArgumentParser) ?? nullableAttributeValue;
                    }
 
                    constraintIndex++;
                    yield return () => WriteTypeName(constraint, noSpace: true, nullableAttributeArgument: nullableAttributeValue ?? methodNullableContextValue ?? TypeNullableContextValue ?? ModuleNullableContextValue);
                }
            }
 
            // new constraint cannot be put on structs and needs to be the last constraint
            if (!parameter.MustBeValueType && parameter.MustHaveDefaultConstructor)
                yield return () => { WriteKeyword("new", noSpace: true); WriteSymbol("("); WriteSymbol(")"); };
        }
    }
}