Repository: dotnet/roslyn
File: src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs (C#)

Code before:

        public static bool IsAtLeastAsVisibleAs(this TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
        {
            var visitTypeData = s_visitTypeDataPool.Allocate();

            try
            {
                visitTypeData.UseSiteInfo = useSiteInfo;
                visitTypeData.Symbol = sym;

                var result = type.VisitType(static (type1, arg, unused) => IsTypeLessVisibleThan(type1, arg.Symbol!, ref arg.UseSiteInfo),
                                            arg: visitTypeData,
                                            canDigThroughNullable: true); // System.Nullable is public

                useSiteInfo = visitTypeData.UseSiteInfo;
                return result is null;
            }
            finally
            {
                visitTypeData.UseSiteInfo = default;
                visitTypeData.Symbol = null;
                s_visitTypeDataPool.Free(visitTypeData);
            }
        }

        public static List<TypeSymbol> FindAllLessVisible(this TypeSymbol type, TypeSymbol sym, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
        {
            List<TypeSymbol> result = [];
            var visitTypeData = s_visitTypeDataPool.Allocate();

            try
            {
                visitTypeData.UseSiteInfo = useSiteInfo;
                visitTypeData.Symbol = type;

                sym.VisitType(addToResultIfLessVisibleAndContinueSearchPredicate,
                              arg: visitTypeData,
                              canDigThroughNullable: true); // System.Nullable is public

                useSiteInfo = visitTypeData.UseSiteInfo;
                return result;
            }
            finally
            {
                visitTypeData.UseSiteInfo = default;
                visitTypeData.Symbol = null;
                s_visitTypeDataPool.Free(visitTypeData);
            }

            bool addToResultIfLessVisibleAndContinueSearchPredicate(TypeSymbol type1, VisitTypeData arg, bool unused)
            {

Review comment (refactor):
I think ideally, what we would do, instead of creating a list, is create a new type and pool similar to `VisitTypeData`, which contains whatever objects are needed to report the diagnostics.

See https://github.com/dotnet/roslyn/blob/06f2643e1268e4a7fcdf1221c052f9c8cce20b60/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs#L585 for an example of a VisitType pass which reports diagnostics. It feels like that case is much more complex than what you would need for this, but, may still be useful as an example.

Also, the predicate being passed to VisitType, should probably be marked `static`, so that a new delegate doesn't need to be allocated on each call. (In the past I think it was necessary to store a lambda in a static field to ensure this, I think the codegen has been improved somewhere along the way.)

Unsure if I am striking the right balance with this suggestion, or if it is overkill. Curious what other @dotnet/roslyn-compiler folks think.


Code after:

        public static bool IsAtLeastAsVisibleAs(this TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
        {
            return type.FindTypeLessVisibleThan(sym, ref useSiteInfo) is null;
        }

        public static TypeSymbol? FindTypeLessVisibleThan(this TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
        {
            var visitTypeData = s_visitTypeDataPool.Allocate();

            try
            {
                visitTypeData.UseSiteInfo = useSiteInfo;
                visitTypeData.Symbol = sym;

                var result = type.VisitType(static (type1, arg, unused) => IsTypeLessVisibleThan(type1, arg.Symbol!, ref arg.UseSiteInfo),
                                            arg: visitTypeData,
                                            canDigThroughNullable: true); // System.Nullable is public

                useSiteInfo = visitTypeData.UseSiteInfo;
                return result;
            }
            finally
            {
                visitTypeData.UseSiteInfo = default;
                visitTypeData.Symbol = null;
                s_visitTypeDataPool.Free(visitTypeData);
            }
        }

        private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
        {
            switch (type.TypeKind)
            {
                case TypeKind.Class:
                case TypeKind.Struct:
                case TypeKind.Interface:
                case TypeKind.Enum:
                case TypeKind.Delegate:
                case TypeKind.Submission:
                    return !IsAsRestrictive((NamedTypeSymbol)type, sym, ref useSiteInfo);

                default:
                    return false;
            }
        }

        /// <summary>
        /// Visit the given type and, in the case of compound types, visit all "sub type"
        /// (such as A in A[], or { A&lt;T&gt;, T, U } in A&lt;T&gt;.B&lt;U&gt;) invoking 'predicate'
        /// with the type and 'arg' at each sub type. If the predicate returns true for any type,