|
// 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;
}
}
|