File: Symbols\Attributes\AttributeUsageInfo.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// 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;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis
{
    internal readonly struct AttributeUsageInfo : IEquatable<AttributeUsageInfo>
    {
        [Flags()]
        private enum PackedAttributeUsage
        {
            None = 0,
            Assembly = AttributeTargets.Assembly,
            Module = AttributeTargets.Module,
            Class = AttributeTargets.Class,
            Struct = AttributeTargets.Struct,
            Enum = AttributeTargets.Enum,
            Constructor = AttributeTargets.Constructor,
            Method = AttributeTargets.Method,
            Property = AttributeTargets.Property,
            Field = AttributeTargets.Field,
            Event = AttributeTargets.Event,
            Interface = AttributeTargets.Interface,
            Parameter = AttributeTargets.Parameter,
            Delegate = AttributeTargets.Delegate,
            ReturnValue = AttributeTargets.ReturnValue,
            GenericParameter = AttributeTargets.GenericParameter,
            All = AttributeTargets.All,
 
            // NOTE: VB allows AttributeUsageAttribute with no valid target, i.e. <AttributeUsageAttribute(0)>, and doesn't generate any diagnostics.
            // We use PackedAttributeUsage.Initialized field to differentiate between uninitialized AttributeUsageInfo and initialized AttributeUsageInfo with no valid target.
            Initialized = GenericParameter << 1,
 
            AllowMultiple = Initialized << 1,
            Inherited = AllowMultiple << 1
        }
 
        private readonly PackedAttributeUsage _flags;
 
        /// <summary>
        /// Default attribute usage for attribute types:
        /// (a) Valid targets: AttributeTargets.All
        /// (b) AllowMultiple: false
        /// (c) Inherited: true
        /// </summary>
        internal static readonly AttributeUsageInfo Default = new AttributeUsageInfo(validTargets: AttributeTargets.All, allowMultiple: false, inherited: true);
 
        internal static readonly AttributeUsageInfo Null = default(AttributeUsageInfo);
 
        internal AttributeUsageInfo(AttributeTargets validTargets, bool allowMultiple, bool inherited)
        {
            // NOTE: VB allows AttributeUsageAttribute with no valid target, i.e. <AttributeUsageAttribute(0)>, and doesn't generate any diagnostics.
            // We use PackedAttributeUsage.Initialized field to differentiate between uninitialized AttributeUsageInfo and initialized AttributeUsageInfo with no valid targets.
            _flags = (PackedAttributeUsage)validTargets | PackedAttributeUsage.Initialized;
 
            if (allowMultiple)
            {
                _flags |= PackedAttributeUsage.AllowMultiple;
            }
 
            if (inherited)
            {
                _flags |= PackedAttributeUsage.Inherited;
            }
        }
 
        public bool IsNull
        {
            get
            {
                return (_flags & PackedAttributeUsage.Initialized) == 0;
            }
        }
 
        internal AttributeTargets ValidTargets
        {
            get
            {
                return (AttributeTargets)(_flags & PackedAttributeUsage.All);
            }
        }
 
        internal bool AllowMultiple
        {
            get
            {
                return (_flags & PackedAttributeUsage.AllowMultiple) != 0;
            }
        }
 
        internal bool Inherited
        {
            get
            {
                return (_flags & PackedAttributeUsage.Inherited) != 0;
            }
        }
 
        public static bool operator ==(AttributeUsageInfo left, AttributeUsageInfo right)
        {
            return left._flags == right._flags;
        }
 
        public static bool operator !=(AttributeUsageInfo left, AttributeUsageInfo right)
        {
            return left._flags != right._flags;
        }
 
        public override bool Equals(object? obj)
        {
            if (obj is AttributeUsageInfo)
            {
                return this.Equals((AttributeUsageInfo)obj);
            }
 
            return false;
        }
 
        public bool Equals(AttributeUsageInfo other)
        {
            return this == other;
        }
 
        public override int GetHashCode()
        {
            return ((int)_flags).GetHashCode();
        }
 
        internal bool HasValidAttributeTargets
        {
            get
            {
                var value = (int)ValidTargets;
                return value != 0 && (value & (int)~AttributeTargets.All) == 0;
            }
        }
 
        internal object GetValidTargetsErrorArgument()
        {
            var validTargetsInt = (int)ValidTargets;
            if (!HasValidAttributeTargets)
            {
                return string.Empty;
            }
 
            var builder = ArrayBuilder<string>.GetInstance();
            int flag = 0;
            while (validTargetsInt > 0)
            {
                if ((validTargetsInt & 1) != 0)
                {
                    builder.Add(GetErrorDisplayNameResourceId((AttributeTargets)(1 << flag)));
                }
 
                validTargetsInt >>= 1;
                flag++;
            }
 
            return new ValidTargetsStringLocalizableErrorArgument(builder.ToArrayAndFree());
        }
 
        private readonly struct ValidTargetsStringLocalizableErrorArgument : IFormattable
        {
            private readonly string[]? _targetResourceIds;
 
            internal ValidTargetsStringLocalizableErrorArgument(string[] targetResourceIds)
            {
                RoslynDebug.Assert(targetResourceIds != null);
                _targetResourceIds = targetResourceIds;
            }
 
            public override string ToString()
            {
                return ToString(null, null);
            }
 
            public string ToString(string? format, IFormatProvider? formatProvider)
            {
                var builder = PooledStringBuilder.GetInstance();
                var culture = formatProvider as System.Globalization.CultureInfo;
 
                if (_targetResourceIds != null)
                {
                    foreach (string id in _targetResourceIds)
                    {
                        if (builder.Builder.Length > 0)
                        {
                            builder.Builder.Append(", ");
                        }
 
                        builder.Builder.Append(CodeAnalysisResources.ResourceManager.GetString(id, culture));
                    }
                }
 
                var message = builder.Builder.ToString();
                builder.Free();
 
                return message;
            }
        }
 
        private static string GetErrorDisplayNameResourceId(AttributeTargets target)
        {
            switch (target)
            {
                case AttributeTargets.Assembly: return nameof(CodeAnalysisResources.Assembly);
                case AttributeTargets.Class: return nameof(CodeAnalysisResources.Class1);
                case AttributeTargets.Constructor: return nameof(CodeAnalysisResources.Constructor);
                case AttributeTargets.Delegate: return nameof(CodeAnalysisResources.Delegate1);
                case AttributeTargets.Enum: return nameof(CodeAnalysisResources.Enum1);
                case AttributeTargets.Event: return nameof(CodeAnalysisResources.Event1);
                case AttributeTargets.Field: return nameof(CodeAnalysisResources.Field);
                case AttributeTargets.GenericParameter: return nameof(CodeAnalysisResources.TypeParameter);
                case AttributeTargets.Interface: return nameof(CodeAnalysisResources.Interface1);
                case AttributeTargets.Method: return nameof(CodeAnalysisResources.Method);
                case AttributeTargets.Module: return nameof(CodeAnalysisResources.Module);
                case AttributeTargets.Parameter: return nameof(CodeAnalysisResources.Parameter);
                case AttributeTargets.Property: return nameof(CodeAnalysisResources.Property);
                case AttributeTargets.ReturnValue: return nameof(CodeAnalysisResources.Return1);
                case AttributeTargets.Struct: return nameof(CodeAnalysisResources.Struct1);
                default:
                    throw ExceptionUtilities.UnexpectedValue(target);
            }
        }
    }
}