File: MarshalUsingAttributeParser.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj (Microsoft.Interop.SourceGeneration)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
 
namespace Microsoft.Interop
{
    /// <summary>
    /// This class suppports parsing a System.Runtime.InteropServices.Marshalling.MarshalUsingAttribute.
    /// </summary>
    public sealed class MarshalUsingAttributeParser : IMarshallingInfoAttributeParser, IUseSiteAttributeParser
    {
        private readonly Compilation _compilation;
        private readonly GeneratorDiagnosticsBag _diagnostics;
 
        public MarshalUsingAttributeParser(Compilation compilation, GeneratorDiagnosticsBag diagnostics)
        {
            _compilation = compilation;
            _diagnostics = diagnostics;
        }
 
        public bool CanParseAttributeType(INamedTypeSymbol attributeType) => attributeType.ToDisplayString() == TypeNames.MarshalUsingAttribute;
 
        MarshallingInfo? IMarshallingInfoAttributeParser.ParseAttribute(AttributeData attributeData, ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
        {
            Debug.Assert(attributeData.AttributeClass!.ToDisplayString() == TypeNames.MarshalUsingAttribute);
            CountInfo countInfo = NoCountInfo.Instance;
            if (useSiteAttributes.TryGetUseSiteAttributeInfo(indirectionDepth, out UseSiteAttributeData useSiteInfo))
            {
                countInfo = useSiteInfo.CountInfo;
            }
 
            if (attributeData.ConstructorArguments.Length == 0)
            {
                // This attribute only has count information.
                // It does not provide any marshalling info.
                // Return null here to respresent the lack of any marshalling info,
                // instead of the presence of invalid marshalling info.
                return null;
            }
 
            if (attributeData.ConstructorArguments[0].Value is not INamedTypeSymbol namedType)
            {
                return NoMarshallingInfo.Instance;
            }
 
            return CustomMarshallingInfoHelper.CreateNativeMarshallingInfo(
                type,
                namedType,
                attributeData,
                useSiteAttributes,
                marshallingInfoCallback,
                indirectionDepth,
                countInfo,
                _diagnostics,
                _compilation
                );
        }
 
        UseSiteAttributeData IUseSiteAttributeParser.ParseAttribute(AttributeData attributeData, IElementInfoProvider elementInfoProvider, GetMarshallingInfoCallback marshallingInfoCallback)
        {
            ImmutableDictionary<string, TypedConstant> namedArgs = ImmutableDictionary.CreateRange(attributeData.NamedArguments);
            CountInfo countInfo = ParseCountInfo(attributeData, elementInfoProvider, marshallingInfoCallback);
            int elementIndirectionDepth = namedArgs.TryGetValue(ManualTypeMarshallingHelper.MarshalUsingProperties.ElementIndirectionDepth, out TypedConstant value) ? (int)value.Value! : 0;
            return new UseSiteAttributeData(elementIndirectionDepth, countInfo, attributeData);
        }
 
        private CountInfo ParseCountInfo(AttributeData attributeData, IElementInfoProvider elementInfoProvider, GetMarshallingInfoCallback marshallingInfoCallback)
        {
            int? constSize = null;
            string? elementName = null;
            foreach (KeyValuePair<string, TypedConstant> arg in attributeData.NamedArguments)
            {
                if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.ConstantElementCount)
                {
                    constSize = (int)arg.Value.Value!;
                }
                else if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName)
                {
                    if (arg.Value.Value is null)
                    {
                        _diagnostics.ReportConfigurationNotSupported(attributeData, ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName, "null");
                        return NoCountInfo.Instance;
                    }
                    elementName = (string)arg.Value.Value!;
                }
            }
 
            if (constSize is not null && elementName is not null)
            {
                _diagnostics.ReportInvalidMarshallingAttributeInfo(attributeData, nameof(SR.ConstantAndElementCountInfoDisallowed));
            }
            else if (constSize is not null)
            {
                return new ConstSizeCountInfo(constSize.Value);
            }
            else if (elementName is not null)
            {
                if (!elementInfoProvider.TryGetInfoForElementName(attributeData, elementName, marshallingInfoCallback, out TypePositionInfo elementInfo))
                {
                    _diagnostics.ReportConfigurationNotSupported(attributeData, ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName, elementName);
                    return NoCountInfo.Instance;
                }
                return new CountElementCountInfo(elementInfo);
            }
 
            return NoCountInfo.Instance;
        }
    }
}