提交 d408866d 编写于 作者: C Cyrus Najmabadi

Fix goto-impl multi-targetting

上级 24911dad
......@@ -29,17 +29,16 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext)
Document document, int position, IFindUsagesContext context)
{
var cancellationToken = context.CancellationToken;
var tuple = await FindUsagesHelpers.FindImplementationsAsync(
var tupleOpt = await FindUsagesHelpers.FindSourceImplementationsAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (tuple == null)
if (tupleOpt == null)
{
await context.ReportMessageAsync(
EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret).ConfigureAwait(false);
return;
}
var message = tuple.Value.message;
var (symbolAndProjectId, implementations, message) = tupleOpt.Value;
if (message != null)
{
await context.ReportMessageAsync(message).ConfigureAwait(false);
......@@ -48,13 +47,13 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext)
await context.SetSearchTitleAsync(
string.Format(EditorFeaturesResources._0_implementations,
FindUsagesHelpers.GetDisplayName(tuple.Value.symbol))).ConfigureAwait(false);
FindUsagesHelpers.GetDisplayName(symbolAndProjectId.Symbol))).ConfigureAwait(false);
var project = tuple.Value.project;
foreach (var implementation in tuple.Value.implementations)
var solution = document.Project.Solution;
foreach (var implementation in implementations)
{
var definitionItem = await implementation.ToClassifiedDefinitionItemAsync(
project, includeHiddenLocations: false,
var definitionItem = await implementation.Symbol.ToClassifiedDefinitionItemAsync(
solution.GetProject(implementation.ProjectId), includeHiddenLocations: false,
FindReferencesSearchOptions.Default, cancellationToken: cancellationToken).ConfigureAwait(false);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
}
......@@ -128,8 +127,12 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext)
if (symbolAndProject == null)
return;
var solution = document.Project.Solution;
await FindSymbolReferencesAsync(
_threadingContext, context, symbolAndProject.Value.symbol, symbolAndProject.Value.project, cancellationToken).ConfigureAwait(false);
_threadingContext, context,
symbolAndProject.Value.Symbol,
solution.GetRequiredProject(symbolAndProject.Value.ProjectId),
cancellationToken).ConfigureAwait(false);
}
/// <summary>
......
......@@ -9,6 +9,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.SymbolMapping;
using Roslyn.Utilities;
......@@ -29,16 +30,14 @@ public static string GetDisplayName(ISymbol symbol)
/// there may be symbol mapping involved (for example in Metadata-As-Source
/// scenarios).
/// </summary>
public static async Task<(ISymbol symbol, Project project)?> GetRelevantSymbolAndProjectAtPositionAsync(
public static async Task<SymbolAndProjectId?> GetRelevantSymbolAndProjectAtPositionAsync(
Document document, int position, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbol == null)
{
return null;
}
// If this document is not in the primary workspace, we may want to search for results
// in a solution different from the one we started in. Use the starting workspace's
......@@ -47,53 +46,58 @@ public static string GetDisplayName(ISymbol symbol)
var mapping = await mappingService.MapSymbolAsync(document, symbol, cancellationToken).ConfigureAwait(false);
if (mapping == null)
{
return null;
}
return (mapping.Symbol, mapping.Project);
return new SymbolAndProjectId(mapping.Symbol, mapping.Project.Id);
}
public static async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, string message)?> FindImplementationsAsync(Document document, int position, CancellationToken cancellationToken)
public static async Task<(SymbolAndProjectId symboAndProjectId, ImmutableArray<SymbolAndProjectId> implementations, string message)?> FindSourceImplementationsAsync(Document document, int position, CancellationToken cancellationToken)
{
var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null)
{
return null;
}
return await FindImplementationsAsync(
symbolAndProject?.symbol, symbolAndProject?.project, cancellationToken).ConfigureAwait(false);
return await FindSourceImplementationsAsync(
symbolAndProject.Value, document.Project.Solution, cancellationToken).ConfigureAwait(false);
}
private static async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, string message)?> FindImplementationsAsync(
ISymbol symbol, Project project, CancellationToken cancellationToken)
private static async Task<(SymbolAndProjectId symbolAndProjectId, ImmutableArray<SymbolAndProjectId> implementations, string message)?> FindSourceImplementationsAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution, CancellationToken cancellationToken)
{
var implementations = await FindImplementationsWorkerAsync(
symbol, project, cancellationToken).ConfigureAwait(false);
var linkedSymbols = await SymbolFinder.FindLinkedSymbolsAsync(
symbolAndProjectId.Symbol, solution, cancellationToken).ConfigureAwait(false);
var filteredSymbols = implementations.WhereAsArray(
s => s.Locations.Any(l => l.IsInSource));
// If we're in a linked file, try to find all the symbols this links to, and find all the implementations of
// each of those linked symbols. De-dupe the results so the user only gets unique results.
var builder = new HashSet<SymbolAndProjectId>(SymbolAndProjectIdComparer.SymbolEquivalenceInstance);
foreach (var linkedSymbol in linkedSymbols)
{
var implementations = await FindImplementationsWorkerAsync(
linkedSymbol, solution, cancellationToken).ConfigureAwait(false);
builder.AddRange(implementations.Where(s => s.Symbol.Locations.Any(l => l.IsInSource)));
}
return filteredSymbols.Length == 0
? (symbol, project, filteredSymbols, EditorFeaturesResources.The_symbol_has_no_implementations)
: (symbol, project, filteredSymbols, null);
var result = builder.ToImmutableArray();
return result.Length == 0
? (symbolAndProjectId, result, EditorFeaturesResources.The_symbol_has_no_implementations)
: (symbolAndProjectId, result, null);
}
private static async Task<ImmutableArray<ISymbol>> FindImplementationsWorkerAsync(
ISymbol symbol, Project project, CancellationToken cancellationToken)
private static async Task<ImmutableArray<SymbolAndProjectId>> FindImplementationsWorkerAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution, CancellationToken cancellationToken)
{
var solution = project.Solution;
if (symbol.IsInterfaceType() || symbol.IsImplementableMember())
if (symbolAndProjectId.Symbol.IsInterfaceType() || symbolAndProjectId.Symbol.IsImplementableMember())
{
var implementations = await SymbolFinder.FindImplementationsAsync(
symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
symbolAndProjectId, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
// It's important we use a HashSet here -- we may have cases in an inheritance hierarchy where more than one method
// in an overrides chain implements the same interface method, and we want to duplicate those. The easiest way to do it
// is to just use a HashSet.
var implementationsAndOverrides = new HashSet<ISymbol>();
var implementationsAndOverrides = new HashSet<SymbolAndProjectId>();
foreach (var implementation in implementations)
{
......@@ -101,7 +105,7 @@ public static async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbo
// FindImplementationsAsync will only return the base virtual/abstract method, not that method and the overrides
// of the method. We should also include those.
if (implementation.IsOverridable())
if (implementation.Symbol.IsOverridable())
{
var overrides = await SymbolFinder.FindOverridesAsync(
implementation, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
......@@ -109,33 +113,36 @@ public static async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbo
}
}
if (!symbol.IsInterfaceType() && !symbol.IsAbstract)
if (!symbolAndProjectId.Symbol.IsInterfaceType() &&
!symbolAndProjectId.Symbol.IsAbstract)
{
implementationsAndOverrides.Add(symbol);
implementationsAndOverrides.Add(symbolAndProjectId);
}
return implementationsAndOverrides.ToImmutableArray();
}
else if ((symbol as INamedTypeSymbol)?.TypeKind == TypeKind.Class)
else if ((symbolAndProjectId.Symbol as INamedTypeSymbol)?.TypeKind == TypeKind.Class)
{
var derivedClasses = await SymbolFinder.FindDerivedClassesAsync(
(INamedTypeSymbol)symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
var implementations = derivedClasses.Concat(symbol);
symbolAndProjectId.WithSymbol((INamedTypeSymbol)symbolAndProjectId.Symbol),
solution, cancellationToken: cancellationToken).ConfigureAwait(false);
var implementations = derivedClasses.SelectAsArray(s => (SymbolAndProjectId)s).Concat(symbolAndProjectId);
return implementations.ToImmutableArray();
return implementations;
}
else if (symbol.IsOverridable())
else if (symbolAndProjectId.Symbol.IsOverridable())
{
var overrides = await SymbolFinder.FindOverridesAsync(
symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
var implementations = overrides.Concat(symbol);
symbolAndProjectId, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
var implementations = overrides.Concat(symbolAndProjectId);
return implementations.ToImmutableArray();
return implementations;
}
else
{
// This is something boring like a regular method or type, so we'll just go there directly
return ImmutableArray.Create(symbol);
return ImmutableArray.Create(symbolAndProjectId);
}
}
......
......@@ -7,6 +7,7 @@
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.Editor.GoToBase
{
......@@ -15,26 +16,29 @@ internal abstract partial class AbstractGoToBaseService : IGoToBaseService
public async Task FindBasesAsync(Document document, int position, IFindUsagesContext context)
{
var cancellationToken = context.CancellationToken;
var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
var symbolAndProjectIdOpt = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null)
if (symbolAndProjectIdOpt == null)
{
await context.ReportMessageAsync(
EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret).ConfigureAwait(false);
return;
}
var symbol = symbolAndProject.Value.symbol;
var solution = document.Project.Solution;
var symbolAndProjectId = symbolAndProjectIdOpt.Value;
var symbol = symbolAndProjectId.Symbol;
var bases = FindBaseHelpers.FindBases(
symbol, symbolAndProject.Value.project, cancellationToken);
symbolAndProjectId.Symbol,
solution.GetRequiredProject(symbolAndProjectId.ProjectId),
cancellationToken);
await context.SetSearchTitleAsync(
string.Format(EditorFeaturesResources._0_bases,
FindUsagesHelpers.GetDisplayName(symbol))).ConfigureAwait(false);
var project = document.Project;
var solution = project.Solution;
var projectId = project.Id;
var found = false;
......@@ -45,11 +49,11 @@ public async Task FindBasesAsync(Document document, int position, IFindUsagesCon
foreach (var baseSymbol in bases)
{
var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync(
SymbolAndProjectId.Create(baseSymbol, projectId), solution, cancellationToken).ConfigureAwait(false);
if (sourceDefinition.Symbol != null)
baseSymbol, solution, cancellationToken).ConfigureAwait(false);
if (sourceDefinition != null)
{
var definitionItem = await sourceDefinition.Symbol.ToClassifiedDefinitionItemAsync(
solution.GetProject(sourceDefinition.ProjectId), includeHiddenLocations: false,
var definitionItem = await sourceDefinition.ToClassifiedDefinitionItemAsync(
project, includeHiddenLocations: false,
FindReferencesSearchOptions.Default, cancellationToken: cancellationToken)
.ConfigureAwait(false);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
......
......@@ -16,7 +16,7 @@
namespace Microsoft.CodeAnalysis.FindSymbols
{
using RelatedTypeCache = ConditionalWeakTable<Solution, ConcurrentDictionary<(SymbolKey, IImmutableSet<Project>), AsyncLazy<ImmutableArray<(SymbolKey, ProjectId)>>>>;
using RelatedTypeCache = ConditionalWeakTable<Solution, ConcurrentDictionary<(SymbolKey, ProjectId, IImmutableSet<Project>), AsyncLazy<ImmutableArray<(SymbolKey, ProjectId)>>>>;
using SymbolAndProjectIdSet = HashSet<SymbolAndProjectId<INamedTypeSymbol>>;
/// <summary>
......@@ -69,7 +69,7 @@ internal static partial class DependentTypeFinder
// Do a quick lookup first to avoid the allocation. If it fails, go through the
// slower allocating path.
var key = (type.Symbol.GetSymbolKey(), projects);
var key = (type.Symbol.GetSymbolKey(), type.ProjectId, projects);
if (!dictionary.TryGetValue(key, out var lazy))
{
lazy = dictionary.GetOrAdd(key,
......
......@@ -252,6 +252,7 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C
foreach (var location in symbol.DeclaringSyntaxReferences)
{
var originalDocument = solution.GetDocument(location.SyntaxTree);
linkedSymbols.Add(new SymbolAndProjectId(symbol, originalDocument.Project.Id));
// GetDocument will return null for locations in #load'ed trees. TODO: Remove this check and add logic
// to fetch the #load'ed tree's Document once https://github.com/dotnet/roslyn/issues/5260 is fixed.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册