/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyProvider.cs
185 строк
9 KB
Deepak Singh Rathore
Fix Call Hierarchy cross-thread exception when displaying field initializers (#82127)
28 апр 2026, 20:25
Не верифицирован
28 апр 2026, 20:25
4dfff2e
Код
Авторство
О чём код?
// 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; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CallHierarchy; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; 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 sealed 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; GlyphService = glyphService; _streamingPresenter = streamingPresenter; } public async Task<CallHierarchyItem?> CreateItemAsync( ISymbol symbol, Project project, ImmutableArray<Location> callsites, CancellationToken cancellationToken) { var service = project.GetRequiredLanguageService<ICallHierarchyService>(); var descriptor = await service.CreateItemAsync(symbol, project, cancellationToken).ConfigureAwait(false); return descriptor != null ? await CreateItemAsync(descriptor, project.Solution.Workspace, callsites, cancellationToken).ConfigureAwait(false) : null; } public async Task<CallHierarchyItem?> CreateItemAsync( CallHierarchyItemDescriptor descriptor, Workspace workspace, ImmutableArray<Location> callsites, CancellationToken cancellationToken) { var resolved = await descriptor.ItemId.TryResolveAsync(workspace.CurrentSolution, cancellationToken).ConfigureAwait(false); if (resolved == null) return null; var (symbol, project) = resolved.Value; var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync( symbol, project.Solution, this.ThreadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); return new CallHierarchyItem( this, descriptor, location, await CreateSearchCategoryEntriesAsync(descriptor, symbol, workspace.CurrentSolution, cancellationToken).ConfigureAwait(false), () => descriptor.Glyph.GetImageSource(GlyphService), symbol.ToDisplayString(), project.Name, callsites, project); } public FieldInitializerItem CreateInitializerItem(IEnumerable<CallHierarchyDetail> details) { return new FieldInitializerItem(EditorFeaturesResources.Initializers, "__" + EditorFeaturesResources.Initializers, () => Glyph.FieldPublic.GetImageSource(GlyphService), details); } public async Task<ImmutableArray<CallHierarchySearchResult>> SearchAsync( Workspace workspace, CallHierarchySearchDescriptor searchDescriptor, CallHierarchySearchScope searchScope, IImmutableSet<Document>? documents, CancellationToken cancellationToken) { var project = workspace.CurrentSolution.GetProject(searchDescriptor.ItemId.ProjectId); if (project == null) return []; documents ??= IncludeDocuments(searchScope, project); var service = project.GetRequiredLanguageService<ICallHierarchyService>(); return await service.SearchIncomingCallsAsync(workspace.CurrentSolution, searchDescriptor, documents, cancellationToken).ConfigureAwait(false); } private static IImmutableSet<Document>? IncludeDocuments(CallHierarchySearchScope scope, Project project) { if (scope is CallHierarchySearchScope.CurrentDocument or CallHierarchySearchScope.CurrentProject) { var documentTrackingService = project.Solution.Services.GetRequiredService<IDocumentTrackingService>(); var activeDocument = documentTrackingService.TryGetActiveDocument(); if (activeDocument != null) { if (scope == CallHierarchySearchScope.CurrentProject) { var currentProject = project.Solution.GetProject(activeDocument.ProjectId); if (currentProject != null) return ImmutableHashSet.CreateRange(currentProject.Documents); } else { var currentDocument = project.Solution.GetDocument(activeDocument); if (currentDocument != null) return ImmutableHashSet.Create(currentDocument); } return ImmutableHashSet<Document>.Empty; } } return null; } private static async Task<ImmutableArray<CallHierarchySearchCategoryEntry>> CreateSearchCategoryEntriesAsync( CallHierarchyItemDescriptor descriptor, ISymbol symbol, Solution solution, CancellationToken cancellationToken) { var builder = ImmutableArray.CreateBuilder<CallHierarchySearchCategoryEntry>(descriptor.SupportedSearchDescriptors.Length); foreach (var searchDescriptor in descriptor.SupportedSearchDescriptors) { builder.Add(await CreateSearchCategoryEntryAsync(searchDescriptor, symbol, solution, cancellationToken).ConfigureAwait(false)); } return builder.MoveToImmutable(); } private static async Task<CallHierarchySearchCategoryEntry> CreateSearchCategoryEntryAsync( CallHierarchySearchDescriptor descriptor, ISymbol symbol, Solution solution, CancellationToken cancellationToken) { var relatedSymbol = descriptor.Relationship switch { CallHierarchyRelationshipKind.BaseMember or CallHierarchyRelationshipKind.InterfaceImplementations => (await descriptor.ItemId.TryResolveAsync(solution, cancellationToken).ConfigureAwait(false))?.Symbol, _ => null, }; var displayName = descriptor.Relationship switch { CallHierarchyRelationshipKind.Callers => string.Format(EditorFeaturesResources.Calls_To_0, symbol.Name), CallHierarchyRelationshipKind.CallsToOverrides => EditorFeaturesResources.Calls_To_Overrides, CallHierarchyRelationshipKind.BaseMember => string.Format(EditorFeaturesResources.Calls_To_Base_Member_0, relatedSymbol?.ToDisplayString() ?? symbol.ToDisplayString()), CallHierarchyRelationshipKind.InterfaceImplementations => string.Format(EditorFeaturesResources.Calls_To_Interface_Implementation_0, relatedSymbol?.ToDisplayString() ?? symbol.ToDisplayString()), CallHierarchyRelationshipKind.Implementations => string.Format(EditorFeaturesResources.Implements_0, symbol.Name), CallHierarchyRelationshipKind.Overrides => EditorFeaturesResources.Overrides_, CallHierarchyRelationshipKind.FieldReferences => string.Format(EditorFeaturesResources.References_To_Field_0, symbol.Name), _ => throw new InvalidOperationException(), }; var searchCategory = descriptor.Relationship switch { CallHierarchyRelationshipKind.Callers => CallHierarchyPredefinedSearchCategoryNames.Callers, CallHierarchyRelationshipKind.InterfaceImplementations => CallHierarchyPredefinedSearchCategoryNames.InterfaceImplementations, CallHierarchyRelationshipKind.Overrides => CallHierarchyPredefinedSearchCategoryNames.Overrides, _ => displayName, }; return new CallHierarchySearchCategoryEntry(descriptor, searchCategory, displayName); } }