File: FindSymbols\FindReferences\DependentTypeFinder_ImplementingTypes.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
 
namespace Microsoft.CodeAnalysis.FindSymbols;
 
internal static partial class DependentTypeFinder
{
    private static async Task<ImmutableArray<INamedTypeSymbol>> FindImplementingTypesInCurrentProcessAsync(
        INamedTypeSymbol type,
        Solution solution,
        IImmutableSet<Project>? projects,
        bool transitive,
        CancellationToken cancellationToken)
    {
        // Only an interface can be implemented.
        if (type?.TypeKind == TypeKind.Interface)
        {
            // Note: it is intentional that we do TypeHasBaseTypeInSet even though we're searching for interfaces
            // (which are never in the BaseType chain of a type).  The reason for this is the following case:
            //
            //  interface IGoo { }
            //
            //  class Base : IGoo { }
            //
            //  class Derived : Base { }
            //
            // In this case, IGoo has two implementations (Base and Derived).  When searching we'll first find the
            // 'Base' match and will add to the set.  Then, we'll look for types that have 'Base' in their
            // inheritance chain, and we need to match that by looking in the .BaseType inheritance chain when
            // looking at 'Derived'.
            static bool TypeMatches(INamedTypeSymbol type, HashSet<INamedTypeSymbol> set)
                => TypeHasBaseTypeInSet(type, set) || TypeHasInterfaceInSet(type, set);
 
            // As long as we keep hitting derived interfaces or implementing non-sealed classes we need to keep
            // looking (as those types themselves may have more derived classes that would b implementations of this
            // interface).  If we hit structs/sealed-classes though we can stop as they can't have any more types
            // that inherit from them.
            var allTypes = await DescendInheritanceTreeAsync(type, solution, projects,
                typeMatches: TypeMatches,
                shouldContinueSearching: s_isInterfaceOrNonSealedClass,
                transitive: transitive,
                cancellationToken: cancellationToken).ConfigureAwait(false);
 
            // Only classes/struct/delegates/enums implement interface types.  Derived interfaces can be found with
            // FindDerivedInterfacesAsync.  Delegates/Enums only happen in a few corner cases.  For example, enums
            // implement IComparable, and delegates implement ICloneable.
            return allTypes.WhereAsArray(
                t => t.TypeKind is TypeKind.Class or
                     TypeKind.Struct or
                     TypeKind.Delegate or
                     TypeKind.Enum);
        }
 
        return [];
    }
}