File: CallHierarchy\CallHierarchyProvider.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_pxr0p0dn_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.CallHierarchy.Finders;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.GoToDefinition;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Language.CallHierarchy;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Microsoft.VisualStudio.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.CallHierarchy;
 
[Export(typeof(CallHierarchyProvider))]
internal partial class CallHierarchyProvider
{
    public readonly IAsynchronousOperationListener AsyncListener;
    public readonly IUIThreadOperationExecutor ThreadOperationExecutor;
    private readonly Lazy<IStreamingFindUsagesPresenter> _streamingPresenter;
 
    public IThreadingContext ThreadingContext { get; }
    public IGlyphService GlyphService { get; }
 
    [ImportingConstructor]
    [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    public CallHierarchyProvider(
        IThreadingContext threadingContext,
        IUIThreadOperationExecutor threadOperationExecutor,
        IAsynchronousOperationListenerProvider listenerProvider,
        IGlyphService glyphService,
        Lazy<IStreamingFindUsagesPresenter> streamingPresenter)
    {
        AsyncListener = listenerProvider.GetListener(FeatureAttribute.CallHierarchy);
        ThreadingContext = threadingContext;
        ThreadOperationExecutor = threadOperationExecutor;
        this.GlyphService = glyphService;
        _streamingPresenter = streamingPresenter;
    }
 
    public async Task<ICallHierarchyMemberItem> CreateItemAsync(
        ISymbol symbol, Project project, ImmutableArray<Location> callsites, CancellationToken cancellationToken)
    {
        if (symbol.Kind is SymbolKind.Method or
                           SymbolKind.Property or
                           SymbolKind.Event or
                           SymbolKind.Field)
        {
            symbol = GetTargetSymbol(symbol);
 
            var finders = await CreateFindersAsync(symbol, project, cancellationToken).ConfigureAwait(false);
            var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync(
                symbol, project.Solution, this.ThreadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false);
            ICallHierarchyMemberItem item = new CallHierarchyItem(
                this,
                symbol,
                location,
                finders,
                () => symbol.GetGlyph().GetImageSource(GlyphService),
                callsites,
                project);
 
            return item;
        }
 
        return null;
    }
 
    private static ISymbol GetTargetSymbol(ISymbol symbol)
    {
        if (symbol is IMethodSymbol methodSymbol)
        {
            methodSymbol = methodSymbol.ReducedFrom ?? methodSymbol;
            methodSymbol = methodSymbol.ConstructedFrom ?? methodSymbol;
            return methodSymbol;
        }
 
        return symbol;
    }
 
    public FieldInitializerItem CreateInitializerItem(IEnumerable<CallHierarchyDetail> details)
    {
        return new FieldInitializerItem(EditorFeaturesResources.Initializers,
                                        "__" + EditorFeaturesResources.Initializers,
                                        Glyph.FieldPublic.GetImageSource(GlyphService),
                                        details);
    }
 
    public async Task<IEnumerable<AbstractCallFinder>> CreateFindersAsync(ISymbol symbol, Project project, CancellationToken cancellationToken)
    {
        if (symbol.Kind is SymbolKind.Property or
                SymbolKind.Event or
                SymbolKind.Method)
        {
            var finders = new List<AbstractCallFinder>
            {
                new MethodCallFinder(symbol, project.Id, AsyncListener, this)
            };
 
            if (symbol.IsVirtual || symbol.IsAbstract)
            {
                finders.Add(new OverridingMemberFinder(symbol, project.Id, AsyncListener, this));
            }
 
            var @overrides = await SymbolFinder.FindOverridesAsync(symbol, project.Solution, cancellationToken: cancellationToken).ConfigureAwait(false);
            if (overrides.Any())
            {
                finders.Add(new CallToOverrideFinder(symbol, project.Id, AsyncListener, this));
            }
 
            if (symbol.GetOverriddenMember() != null)
            {
                finders.Add(new BaseMemberFinder(symbol.GetOverriddenMember(), project.Id, AsyncListener, this));
            }
 
            var implementedInterfaceMembers = await SymbolFinder.FindImplementedInterfaceMembersAsync(symbol, project.Solution, cancellationToken: cancellationToken).ConfigureAwait(false);
            foreach (var implementedInterfaceMember in implementedInterfaceMembers)
            {
                finders.Add(new InterfaceImplementationCallFinder(implementedInterfaceMember, project.Id, AsyncListener, this));
            }
 
            if (symbol.IsImplementableMember())
            {
                finders.Add(new ImplementerFinder(symbol, project.Id, AsyncListener, this));
            }
 
            return finders;
        }
 
        if (symbol.Kind == SymbolKind.Field)
        {
            return [new FieldReferenceFinder(symbol, project.Id, AsyncListener, this)];
        }
 
        return null;
    }
}