未验证 提交 a094703b 编写于 作者: A Andrew Hall 提交者: GitHub

Update FindImplementationsForInterfaceMemberAsync to correctly handle...

Update FindImplementationsForInterfaceMemberAsync to correctly handle ambiguous type returns (#35853)

FindImplementationsForInterfaceMemberAsync checks for forwarded types using SymbolFinder.OriginalSymbolsMatch, which expects non-null compilation for both the project containing the interface and the project containing the symbol to compare to. Until now, most instances have found equivalence without falling through, but in cases where the type forwarding isn't completely verified a non-null compilation for the interface symbol is needed as well. See VerifyForwardedTypes for the logic being used.

In #35786 the type System.Text.Encoding is provided by a a NuGet reference and could be ambiguous based on the build target. The OOP service attempts to find the correct type resolution but needs the original symbol compilation to verify.

Without this fix, FindAllReferences may cause a null deref and fail.

Fixes #35786
上级 968fae55
......@@ -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<SymbolAndProjectId>.Builder results = null;
foreach (var t in allTypes.Convert<INamedTypeSymbol, ITypeSymbol>())
{
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<IEnumerable<SymbolCallerInfo>> 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;
}
......
......@@ -95,7 +95,7 @@ public static ITypeSymbol RemoveNullableIfPresent(this ITypeSymbol symbol)
/// </summary>
public static async Task<ImmutableArray<SymbolAndProjectId>> FindImplementationsForInterfaceMemberAsync(
this SymbolAndProjectId<ITypeSymbol> 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<SymbolAndProjectId>.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<Compilation> GetCompilationOrNullAsync(Project project, CancellationToken cancellationToken)
=> project?.GetCompilationAsync(cancellationToken) ?? SpecializedTasks.Default<Compilation>();
}
......
......@@ -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()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册