File: ITypeSymbolExtensions.cs
Web Access
Project: 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;
using ILLink.Shared.TypeSystemProxy;
using Microsoft.CodeAnalysis;

namespace ILLink.RoslynAnalyzer
{
    internal static class ITypeSymbolExtensions
    {
        [Flags]
        private enum HierarchyFlags
        {
            IsSystemType = 0x01,
            IsSystemReflectionIReflect = 0x02,
        }

        public static bool IsTypeInterestingForDataflow(this ITypeSymbol type, bool isByRef)
        {
            if (type.SpecialType is SpecialType.System_String && !isByRef)
                return true;

            if (type is not INamedTypeSymbol namedType)
                return false;

            var flags = GetFlags(namedType);
            return IsSystemType(flags) || IsSystemReflectionIReflect(flags);
        }

        private static HierarchyFlags GetFlags(INamedTypeSymbol type)
        {
            HierarchyFlags flags = 0;
            if (type.IsTypeOf(WellKnownType.System_Reflection_IReflect))
            {
                flags |= HierarchyFlags.IsSystemReflectionIReflect;
            }

            ITypeSymbol? baseType = type;
            while (baseType != null)
            {
                if (baseType.IsTypeOf(WellKnownType.System_Type))
                    flags |= HierarchyFlags.IsSystemType;

                foreach (var iface in baseType.Interfaces)
                {
                    if (iface.IsTypeOf(WellKnownType.System_Reflection_IReflect))
                    {
                        flags |= HierarchyFlags.IsSystemReflectionIReflect;
                    }
                }

                baseType = baseType.BaseType;
            }
            return flags;
        }

        private static bool IsSystemType(HierarchyFlags flags) => (flags & HierarchyFlags.IsSystemType) != 0;

        private static bool IsSystemReflectionIReflect(HierarchyFlags flags) => (flags & HierarchyFlags.IsSystemReflectionIReflect) != 0;

        public static bool IsTypeOf(this ITypeSymbol symbol, string @namespace, string name)
        {
            return symbol.MetadataName == name && symbol.ContainingNamespace?.GetDisplayName() == @namespace;
        }

        public static bool IsTypeOf(this ITypeSymbol symbol, WellKnownType wellKnownType)
        {
            switch (symbol.SpecialType)
            {
                case SpecialType.System_String:
                    return wellKnownType == WellKnownType.System_String;
                case SpecialType.System_Nullable_T:
                    return wellKnownType == WellKnownType.System_Nullable_T;
                case SpecialType.System_Array:
                    return wellKnownType == WellKnownType.System_Array;
                case SpecialType.System_Object:
                    return wellKnownType == WellKnownType.System_Object;
            }

            if ((uint)wellKnownType >= (uint)WellKnownType.NextAvailable)
            {
                return false;
            }

            (string @namespace, string name) = wellKnownType.GetNamespaceAndName();
            return symbol.IsTypeOf(@namespace, name);
        }
    }
}