diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs index 713026ce03622e9c1180ad28d352994cd58a234b..871af43202187691dbaff2fc58f6b88d9da7839c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs @@ -145,7 +145,7 @@ public static partial class SymbolFinder var bestMethod = sourceMethod.Symbol != null ? sourceMethod : m; var implementations = await type.FindImplementationsForInterfaceMemberAsync( - bestMethod.Symbol, solution, cancellationToken).ConfigureAwait(false); + bestMethod, solution, cancellationToken).ConfigureAwait(false); foreach (var implementation in implementations) { if (implementation.Symbol != null && @@ -249,7 +249,7 @@ public static partial class SymbolFinder ImmutableArray.Builder results = null; foreach (var t in allTypes.Convert()) { - var implementations = await t.FindImplementationsForInterfaceMemberAsync(symbolAndProjectId.Symbol, solution, cancellationToken).ConfigureAwait(false); + var implementations = await t.FindImplementationsForInterfaceMemberAsync(symbolAndProjectId, solution, cancellationToken).ConfigureAwait(false); foreach (var implementation in implementations) { var sourceDef = await FindSourceDefinitionAsync(implementation, solution, cancellationToken).ConfigureAwait(false); @@ -515,8 +515,12 @@ public static async Task> FindCallersAsync(ISymbol } } - // Now check forwarded types in symbolToMatchCompilation. - verifiedCount += VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies, symbolToMatchCompilation, verifiedKeys, isSearchSymbolCompilation: false); + if (symbolToMatchCompilation != null || TryGetCompilation(symbolToMatch, solution, out symbolToMatchCompilation, cancellationToken)) + { + // Now check forwarded types in symbolToMatchCompilation. + verifiedCount += VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies, symbolToMatchCompilation, verifiedKeys, isSearchSymbolCompilation: false); + } + return verifiedCount == count; } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ITypeSymbolExtensions.cs index 135a9b51dad4d69a06cd613eb99f328fcc16aff1..20c36c05a015b9e938682e61cd168f0920e48475 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ITypeSymbolExtensions.cs @@ -95,7 +95,7 @@ public static ITypeSymbol RemoveNullableIfPresent(this ITypeSymbol symbol) /// public static async Task> FindImplementationsForInterfaceMemberAsync( this SymbolAndProjectId typeSymbolAndProjectId, - ISymbol interfaceMember, + SymbolAndProjectId interfaceMemberAndProjectId, Solution solution, CancellationToken cancellationToken) { @@ -109,6 +109,7 @@ public static ITypeSymbol RemoveNullableIfPresent(this ITypeSymbol symbol) // results in C. var arrBuilder = ArrayBuilder.GetInstance(); + var interfaceMember = interfaceMemberAndProjectId.Symbol; // TODO(cyrusn): Implement this using the actual code for // TypeSymbol.FindImplementationForInterfaceMember @@ -167,20 +168,21 @@ public static ITypeSymbol RemoveNullableIfPresent(this ITypeSymbol symbol) // if they are considered to be the same type, which provides a more accurate // implementations list for interfaces. var typeSymbolProject = solution.GetProject(typeSymbolAndProjectId.ProjectId); - var typeSymbolCompilation = typeSymbolProject == null ? - null : - await typeSymbolProject.GetCompilationAsync(cancellationToken).ConfigureAwait(false); + var interfaceMemberProject = solution.GetProject(interfaceMemberAndProjectId.ProjectId); + + var typeSymbolCompilation = await GetCompilationOrNullAsync(typeSymbolProject, cancellationToken).ConfigureAwait(false); + var interfaceMemberCompilation = await GetCompilationOrNullAsync(interfaceMemberProject, cancellationToken).ConfigureAwait(false); foreach (var constructedInterface in constructedInterfaces) { cancellationToken.ThrowIfCancellationRequested(); - var constructedInterfaceMember = constructedInterface.GetMembers().FirstOrDefault(m => + var constructedInterfaceMember = constructedInterface.GetMembers().FirstOrDefault(typeSymbol => SymbolFinder.OriginalSymbolsMatch( - m, + typeSymbol, interfaceMember, solution, typeSymbolCompilation, - symbolToMatchCompilation: null, + interfaceMemberCompilation, cancellationToken)); if (constructedInterfaceMember == null) @@ -210,6 +212,11 @@ public static ITypeSymbol RemoveNullableIfPresent(this ITypeSymbol symbol) } return arrBuilder.ToImmutableAndFree(); + + // local functions + + static Task GetCompilationOrNullAsync(Project project, CancellationToken cancellationToken) + => project?.GetCompilationAsync(cancellationToken) ?? SpecializedTasks.Default(); } diff --git a/src/Workspaces/CoreTest/FindReferencesTests.cs b/src/Workspaces/CoreTest/FindReferencesTests.cs index 4bed8b94fcbcaaabe3a3e8d1d05a25c97471b9fc..f018bfda602b5e585809932dbd8900e442dccc64 100644 --- a/src/Workspaces/CoreTest/FindReferencesTests.cs +++ b/src/Workspaces/CoreTest/FindReferencesTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using System.Windows.Markup; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -32,6 +33,25 @@ private Solution GetSingleDocumentSolution(string sourceText) .AddDocument(did, "goo.cs", SourceText.From(sourceText)); } + private Solution GetMultipleDocumentSolution(string[] sourceTexts) + { + var pid = ProjectId.CreateNewId(); + + var solution = CreateSolution() + .AddProject(pid, "goo", "goo", LanguageNames.CSharp) + .AddMetadataReference(pid, MscorlibRef); + + int docCounter = 1; + + foreach (var sourceText in sourceTexts) + { + var did = DocumentId.CreateNewId(pid); + solution = solution.AddDocument(did, $"goo{docCounter++}.cs", SourceText.From(sourceText)); + } + + return solution; + } + [Fact] public async Task FindFieldReferencesInSingleDocumentProject() { @@ -310,6 +330,52 @@ public System.Uri Get() Assert.True(references.Any(r => r.DefinitionAndProjectId.ProjectId == desktopProject.Id)); } + [Fact, WorkItem(35786, "https://github.com/dotnet/roslyn/issues/35786")] + public async Task FindReferences_MultipleInterfaceInheritence() + { + var implText = @"namespace A +{ + class C : ITest + { + public string Name { get; } + public System.Uri Uri { get; } + } +}"; + + var interface1Text = @"namespace A +{ + interface ITest : ITestBase + { + string Name { get; } + } +}"; + + var interface2Text = @"namespace A +{ + interface ITestBase + { + System.Uri Uri { get; } + } +}"; + + var solution = GetMultipleDocumentSolution(new[] { implText, interface1Text, interface2Text }); + solution = solution.AddMetadataReferences(solution.ProjectIds.Single(), new[] { MscorlibRef_v46, Net46StandardFacade, SystemRef_v46, NetStandard20Ref }); + + var project = solution.Projects.Single(); + var compilation = await project.GetCompilationAsync(); + var nameProperty = compilation.GetTypeByMetadataName("A.C").GetMembers("Uri").Single(); + + var references = await SymbolFinder.FindReferencesAsync(nameProperty, solution); + + // References are: + // A.C.Uri + // A.ITestBase.Uri + // k__backingField + // A.C.get_Uri + // A.ITestBase.get_Uri + Assert.Equal(5, references.Count()); + } + [WorkItem(4936, "https://github.com/dotnet/roslyn/issues/4936")] [Fact] public async Task OverriddenMethodsFromPortableToDesktop()