File: TypeUtilities.cs
Web Access
Project: ..\..\..\src\MSBuild\MSBuild.csproj (MSBuild)
// 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.Linq;
using System.Reflection;
 
namespace Microsoft.Build.Shared
{
    /// <summary>
    /// Utility extension methods for working with <see cref="Type"/> metadata in a resilient manner.
    /// </summary>
    /// <remarks>
    /// These helpers intentionally:
    /// - Catch and suppress non-critical reflection/type loading exceptions so callers can probe for attributes safely
    ///   even when some referenced types cannot be loaded in the current load context.
    /// - Compare attribute types by their simple <see cref="MemberInfo.Name"/> (e.g. <c>ObsoleteAttribute</c>) rather than
    ///   full name or assembly-qualified name. This mirrors existing MSBuild behavior but means ambiguous attribute
    ///   short names across different assemblies cannot be distinguished here.
    /// </remarks>
    internal static class TypeUtilities
    {
        /// <summary>
        /// Determines whether the specified <paramref name="type"/> is decorated with an attribute of type <typeparamref name="T"/>.
        /// </summary>
        public static bool HasAttribute<T>(this Type type)
            where T : Attribute
        {
            return type.HasAttribute(typeof(T).Name);
        }
 
        /// <summary>
        /// Determines whether the specified <paramref name="type"/> is decorated with an attribute whose simple type name
        /// equals <paramref name="attributeName"/>.
        /// </summary>
        public static bool HasAttribute(this Type type, string attributeName)
        {
            if (type == null)
            {
                return false;
            }
 
            try
            {
                return CustomAttributeData
                    .GetCustomAttributes(type)
                    .Any(attr => SafeGetAttributeName(attr) == attributeName);
            }
            catch (Exception e) when (!ExceptionHandling.IsCriticalException(e))
            {
                // Skip this attribute - it references a type that can't be loaded/found
                // It might be available in the child node.
                return false;
            }
        }
 
        /// <summary>
        /// Safely retrieves the simple name of an attribute's type, swallowing non-critical reflection exceptions.
        /// </summary>
        /// <param name="attr">The attribute metadata.</param>
        /// <returns>The simple attribute type name, or <c>null</c> if it cannot be resolved.</returns>
        private static string? SafeGetAttributeName(CustomAttributeData attr)
        {
            try
            {
                return attr.AttributeType?.Name;
            }
            catch (Exception e) when (!ExceptionHandling.IsCriticalException(e))
            {
                // Skip this attribute - it references a type that can't be loaded/found
                // It might be available in the child node.
                return null;
            }
        }
    }
}