File: RequiresISymbolExtensions.cs
Web Access
Project: src\src\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj (ILLink.RoslynAnalyzer)
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
 
namespace ILLink.RoslynAnalyzer
{
    public static class RequiresISymbolExtensions
    {
        // TODO: Consider sharing with ILLink DoesMemberRequire method
        /// <summary>
        /// True if the target of a call is considered to be annotated with the Requires... attribute
        /// </summary>
        public static bool DoesMemberRequire(this ISymbol member, string requiresAttribute, [NotNullWhen(returnValue: true)] out AttributeData? requiresAttributeData)
        {
            requiresAttributeData = null;
            if (!member.IsStaticConstructor() && member.TryGetAttribute(requiresAttribute, out requiresAttributeData))
                return true;
 
            if (member is IMethodSymbol { AssociatedSymbol: { } associated } && associated.TryGetAttribute(requiresAttribute, out requiresAttributeData))
                return true;
 
            // Also check the containing type
            if (member.IsStatic || member.IsConstructor())
                return member.ContainingType.TryGetAttribute(requiresAttribute, out requiresAttributeData);
 
            return false;
        }
 
        public static bool IsInRequiresScope(this ISymbol member, string attributeName)
        {
            return member.IsInRequiresScope(attributeName, out _);
        }
 
        // TODO: Consider sharing with ILLink IsInRequiresScope method
        /// <summary>
        /// True if the source of a call is considered to be annotated with the Requires... attribute
        /// </summary>
        public static bool IsInRequiresScope(this ISymbol member, string attributeName, [NotNullWhen(true)] out AttributeData? requiresAttribute)
        {
            // Requires attribute on a type does not silence warnings that originate
            // from the type directly. We also only check the containing type for members
            // below, not of nested types.
            if (member is ITypeSymbol)
            {
                requiresAttribute = null;
                return false;
            }
 
            while (true)
            {
                if (member.TryGetAttribute(attributeName, out requiresAttribute) && !member.IsStaticConstructor())
                    return true;
                if (member.ContainingSymbol is not IMethodSymbol method)
                    break;
                member = method;
            }
 
            if (member.ContainingType is ITypeSymbol containingType && containingType.TryGetAttribute(attributeName, out requiresAttribute))
                return true;
 
            if (member is IMethodSymbol { AssociatedSymbol: { } associated } && associated.TryGetAttribute(attributeName, out requiresAttribute))
                return true;
 
            // When using instance fields suppress the warning if the constructor has already the Requires annotation
            if (member is IFieldSymbol field && !field.IsStatic)
            {
                foreach (var constructor in field.ContainingType.InstanceConstructors)
                {
                    if (!constructor.TryGetAttribute(attributeName, out requiresAttribute))
                    {
                        requiresAttribute = null;
                        return false;
                    }
                }
                return requiresAttribute != null;
            }
 
            requiresAttribute = null;
            return false;
        }
    }
}