File: UseSiteAttributeProvider.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 Microsoft.CodeAnalysis;
 
namespace Microsoft.Interop
{
    /// <summary>
    /// Parses information for all use-site attributes and tracks their usage.
    /// </summary>
    public sealed class UseSiteAttributeProvider
    {
        private readonly ImmutableDictionary<int, UseSiteAttributeData> _useSiteAttributesByIndirectionDepth;
        private readonly int _maxIndirectionLevelDataProvided;
        private readonly GeneratorDiagnosticsBag _diagnostics;
        private int _maxIndirectionLevelUsed;
 
        /// <summary>
        /// Construct a new <see cref="UseSiteAttributeProvider"/> for a given usage site.
        /// </summary>
        /// <param name="useSiteAttributeParsers">The parsers for the attributes at the given usage site.</param>
        /// <param name="useSiteAttributes">The attributes at the usage site.</param>
        /// <param name="elementInfoProvider">The provider for additional element information, used by the attribute parsers.</param>
        /// <param name="diagnostics">Diagnostics sink for any invalid configurations.</param>
        /// <param name="getMarshallingInfoCallback">A callback to get marshalling information for other elements. Used by <paramref name="elementInfoProvider"/>.</param>
        internal UseSiteAttributeProvider(
            ImmutableArray<IUseSiteAttributeParser> useSiteAttributeParsers,
            IEnumerable<AttributeData> useSiteAttributes,
            IElementInfoProvider elementInfoProvider,
            GeneratorDiagnosticsBag diagnostics,
            GetMarshallingInfoCallback getMarshallingInfoCallback)
        {
            ImmutableDictionary<int, UseSiteAttributeData>.Builder useSiteAttributesByIndirectionDepth = ImmutableDictionary.CreateBuilder<int, UseSiteAttributeData>();
            _maxIndirectionLevelDataProvided = 0;
            foreach (AttributeData attribute in useSiteAttributes)
            {
                UseSiteAttributeData? useSiteAttributeData = GetUseSiteInfoForAttribute(attribute);
                if (useSiteAttributeData is not null)
                {
                    int indirectionDepth = useSiteAttributeData.IndirectionDepth;
                    if (useSiteAttributesByIndirectionDepth.ContainsKey(indirectionDepth))
                    {
                        diagnostics.ReportInvalidMarshallingAttributeInfo(attribute, nameof(SR.DuplicateMarshallingInfo), indirectionDepth.ToString());
                    }
                    else
                    {
                        useSiteAttributesByIndirectionDepth.Add(indirectionDepth, useSiteAttributeData);
                        _maxIndirectionLevelDataProvided = Math.Max(_maxIndirectionLevelDataProvided, indirectionDepth);
                    }
                }
            }
            _useSiteAttributesByIndirectionDepth = useSiteAttributesByIndirectionDepth.ToImmutable();
            _diagnostics = diagnostics;
 
            UseSiteAttributeData? GetUseSiteInfoForAttribute(AttributeData attribute)
            {
                foreach (var parser in useSiteAttributeParsers)
                {
                    // Automatically ignore invalid attributes.
                    // The compiler will already error on them.
                    if (attribute.AttributeConstructor is not null && parser.CanParseAttributeType(attribute.AttributeClass))
                    {
                        return parser.ParseAttribute(attribute, elementInfoProvider, getMarshallingInfoCallback);
                    }
                }
                return null;
            }
        }
 
        /// <summary>
        /// Get the <see cref="UseSiteAttributeData"/> provided for a given <paramref name="indirectionDepth"/>, if it exists.
        /// </summary>
        /// <param name="indirectionDepth">The indirection depth to retrieve info for.</param>
        /// <param name="useSiteInfo">The use site information, if it exists.</param>
        /// <returns><c>true</c> if an attribute was provided for the given indirection depth.</returns>
        public bool TryGetUseSiteAttributeInfo(int indirectionDepth, out UseSiteAttributeData useSiteInfo)
        {
            _maxIndirectionLevelUsed = Math.Max(indirectionDepth, _maxIndirectionLevelUsed);
            return _useSiteAttributesByIndirectionDepth.TryGetValue(indirectionDepth, out useSiteInfo);
        }
 
        /// <summary>
        /// Call when no more of the use-site attribute information will be used.
        /// Records any information or diagnostics about unused marshalling information.
        /// </summary>
        internal void OnAttributeUsageFinished()
        {
            if (_maxIndirectionLevelUsed < _maxIndirectionLevelDataProvided)
            {
                _diagnostics.ReportInvalidMarshallingAttributeInfo(
                    _useSiteAttributesByIndirectionDepth[_maxIndirectionLevelDataProvided].AttributeData,
                    nameof(SR.ExtraneousMarshallingInfo),
                    _maxIndirectionLevelDataProvided.ToString(),
                    _maxIndirectionLevelUsed.ToString());
            }
        }
    }
}