File: Utilities\SymbolExtensions.cs
Web Access
Project: src\src\Analyzers\Microsoft.Analyzers.Extra\Microsoft.Analyzers.Extra.csproj (Microsoft.Analyzers.Extra)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.CodeAnalysis;
 
namespace Microsoft.Extensions.ExtraAnalyzers.Utilities;
 
internal static class SymbolExtensions
{
    /// <summary>
    /// Determines whether the current instance is an ancestor type of the parameter.
    /// </summary>
    /// <param name="potentialAncestor">The potential ancestor being inspected.</param>
    /// <param name="potentialDescendant">The type to test.</param>
    /// <returns><see langword="true"/> if <paramref name="potentialDescendant"/> derives directly or indirectly from <paramref name="potentialAncestor"/>.</returns>
    public static bool IsAncestorOf(this ITypeSymbol potentialAncestor, ITypeSymbol potentialDescendant)
    {
        ITypeSymbol? t = potentialDescendant;
        while (true)
        {
            t = t.BaseType;
            if (t == null)
            {
                return false;
            }
 
            if (SymbolEqualityComparer.Default.Equals(t, potentialAncestor))
            {
                return true;
            }
        }
    }
 
    /// <summary>
    /// True if the symbol is externally visible outside this assembly.
    /// </summary>
    public static bool IsExternallyVisible(this ISymbol symbol)
    {
        while (symbol.Kind != SymbolKind.Namespace)
        {
            switch (symbol.DeclaredAccessibility)
            {
                // If we see anything private, then the symbol is private.
                case Accessibility.NotApplicable:
                case Accessibility.Private:
                    return false;
 
                // If we see anything internal, then knock it down from public to
                // internal.
                case Accessibility.Internal:
                case Accessibility.ProtectedAndInternal:
                    return false;
            }
 
            symbol = symbol.ContainingSymbol;
        }
 
        return true;
    }
 
    public static bool ImplementsPublicInterface(this IMethodSymbol method)
    {
        foreach (var iface in method.ContainingType.AllInterfaces)
        {
            if (iface.IsExternallyVisible())
            {
                foreach (var member in iface.GetMembers().OfType<IMethodSymbol>())
                {
                    var impl = method.ContainingType.FindImplementationForInterfaceMember(member);
                    if (SymbolEqualityComparer.Default.Equals(impl, method))
                    {
                        return true;
                    }
                }
            }
        }
 
        return false;
    }
}