提交 9623d557 编写于 作者: B Brett V. Forsgren

Merge pull request #9056 from brettfo/type-equality

Use smarter type equality in `DependentTypeFinder`
......@@ -735,7 +735,7 @@
<Compile Include="Shared\Preview\PreviewSolutionCrawlerRegistrationService.cs" />
<Compile Include="Shared\Preview\PredefinedPreviewTaggerKeys.cs" />
<Compile Include="Shared\Preview\PreviewWorkspace.cs" />
<Compile Include="Shared\IDocumentSupportsSuggestionService.cs" />
<Compile Include="Shared\IDocumentSupportsFeatureService.cs" />
<Compile Include="Shared\Tagging\EventSources\AbstractTaggerEventSource.cs" />
<Compile Include="Shared\Tagging\EventSources\AbstractWorkspaceTrackingTaggerEventSource.cs" />
<Compile Include="Shared\Tagging\EventSources\TaggerEventSources.WorkspaceRegistrationChangedEventSource.cs" />
......
......@@ -159,7 +159,7 @@ internal static class DependentTypeFinder
type,
solution,
null,
(t1, t2) => t1.BaseType == t2,
(candidate, baseType) => OriginalSymbolsMatch(candidate.BaseType, baseType, solution, cancellationToken),
s_derivedClassesCache,
cancellationToken);
}
......@@ -179,7 +179,7 @@ internal static class DependentTypeFinder
type,
solution,
null,
(i1, i2) => i1.Interfaces.Contains(i2),
(candidate, baseInterface) => candidate.Interfaces.Any(i => OriginalSymbolsMatch(i, baseInterface, solution, cancellationToken)),
s_derivedInterfacesCache,
cancellationToken);
}
......@@ -341,5 +341,257 @@ private static List<SymbolKey> GetOrAddDependentTypes(ConditionalWeakTable<Compi
.GetOrAdd(typeId, dependentTypeIds);
}
}
internal static bool OriginalSymbolsMatch(
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
CancellationToken cancellationToken)
{
if (ReferenceEquals(searchSymbol, symbolToMatch))
{
return true;
}
if (searchSymbol == null || symbolToMatch == null)
{
return false;
}
Compilation symbolToMatchCompilation = null;
if (!TryGetCompilation(symbolToMatch, solution, out symbolToMatchCompilation, cancellationToken))
{
return false;
}
return OriginalSymbolsMatch(searchSymbol, symbolToMatch, solution, null, symbolToMatchCompilation, cancellationToken);
}
internal static bool OriginalSymbolsMatch(
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
if (symbolToMatch == null)
{
return false;
}
if (OriginalSymbolsMatchCore(searchSymbol, symbolToMatch, solution, searchSymbolCompilation, symbolToMatchCompilation, cancellationToken))
{
return true;
}
if (searchSymbol.Kind == SymbolKind.Namespace && symbolToMatch.Kind == SymbolKind.Namespace)
{
// if one of them is a merged namespace symbol and other one is its constituent namespace symbol, they are equivalent.
var namespace1 = (INamespaceSymbol)searchSymbol;
var namespace2 = (INamespaceSymbol)symbolToMatch;
var namespace1Count = namespace1.ConstituentNamespaces.Length;
var namespace2Count = namespace2.ConstituentNamespaces.Length;
if (namespace1Count != namespace2Count)
{
if ((namespace1Count > 1 &&
namespace1.ConstituentNamespaces.Any(n => NamespaceSymbolsMatch(n, namespace2, solution, cancellationToken))) ||
(namespace2Count > 1 &&
namespace2.ConstituentNamespaces.Any(n2 => NamespaceSymbolsMatch(namespace1, n2, solution, cancellationToken))))
{
return true;
}
}
}
if (searchSymbol.Kind == SymbolKind.NamedType && symbolToMatch.IsConstructor())
{
return OriginalSymbolsMatch(searchSymbol, symbolToMatch.ContainingType, solution, searchSymbolCompilation, symbolToMatchCompilation, cancellationToken);
}
return false;
}
private static bool OriginalSymbolsMatchCore(
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
if (searchSymbol == null || symbolToMatch == null)
{
return false;
}
searchSymbol = searchSymbol.GetOriginalUnreducedDefinition();
symbolToMatch = symbolToMatch.GetOriginalUnreducedDefinition();
// We compare the given searchSymbol and symbolToMatch for equivalence using SymbolEquivalenceComparer
// as follows:
// 1) We compare the given symbols using the SymbolEquivalenceComparer.IgnoreAssembliesInstance,
// which ignores the containing assemblies for named types equivalence checks. This is required
// to handle equivalent named types which are forwarded to completely different assemblies.
// 2) If the symbols are NOT equivalent ignoring assemblies, then they cannot be equivalent.
// 3) Otherwise, if the symbols ARE equivalent ignoring assemblies, they may or may not be equivalent
// if containing assemblies are NOT ignored. We need to perform additional checks to ensure they
// are indeed equivalent:
//
// (a) If IgnoreAssembliesInstance.Equals equivalence visitor encountered any pair of non-nested
// named types which were equivalent in all aspects, except that they resided in different
// assemblies, we need to ensure that all such pairs are indeed equivalent types. Such a pair
// of named types is equivalent if and only if one of them is a type defined in either
// searchSymbolCompilation(C1) or symbolToMatchCompilation(C2), say defined in reference assembly
// A (version v1) in compilation C1, and the other type is a forwarded type, such that it is
// forwarded from reference assembly A (version v2) to assembly B in compilation C2.
// (b) Otherwise, if no such named type pairs were encountered, symbols ARE equivalent.
using (var equivalentTypesWithDifferingAssemblies = SharedPools.Default<Dictionary<INamedTypeSymbol, INamedTypeSymbol>>().GetPooledObject())
{
// 1) Compare searchSymbol and symbolToMatch using SymbolEquivalenceComparer.IgnoreAssembliesInstance
if (!SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(searchSymbol, symbolToMatch, equivalentTypesWithDifferingAssemblies.Object))
{
// 2) If the symbols are NOT equivalent ignoring assemblies, then they cannot be equivalent.
return false;
}
// 3) If the symbols ARE equivalent ignoring assemblies, they may or may not be equivalent if containing assemblies are NOT ignored.
if (equivalentTypesWithDifferingAssemblies.Object.Count > 0)
{
// Step 3a) Ensure that all pairs of named types in equivalentTypesWithDifferingAssemblies are indeed equivalent types.
return VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies.Object, searchSymbol, symbolToMatch,
solution, searchSymbolCompilation, symbolToMatchCompilation, cancellationToken);
}
// 3b) If no such named type pairs were encountered, symbols ARE equivalent.
return true;
}
}
private static bool NamespaceSymbolsMatch(
INamespaceSymbol namespace1,
INamespaceSymbol namespace2,
Solution solution,
CancellationToken cancellationToken)
{
return OriginalSymbolsMatch(namespace1, namespace2, solution, cancellationToken);
}
// Verifies that all pairs of named types in equivalentTypesWithDifferingAssemblies are equivalent forwarded types.
private static bool VerifyForwardedTypes(
Dictionary<INamedTypeSymbol, INamedTypeSymbol> equivalentTypesWithDifferingAssemblies,
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
var verifiedKeys = new HashSet<INamedTypeSymbol>();
var count = equivalentTypesWithDifferingAssemblies.Count;
int verifiedCount = 0;
// First check forwarded types in searchSymbolCompilation.
if (searchSymbolCompilation != null || TryGetCompilation(searchSymbol, solution, out searchSymbolCompilation, cancellationToken))
{
verifiedCount = VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies, searchSymbolCompilation, verifiedKeys, isSearchSymbolCompilation: true);
if (verifiedCount == count)
{
// All equivalent types verified.
return true;
}
}
// Now check forwarded types in symbolToMatchCompilation.
verifiedCount += VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies, symbolToMatchCompilation, verifiedKeys, isSearchSymbolCompilation: false);
return verifiedCount == count;
}
private static int VerifyForwardedTypes(
Dictionary<INamedTypeSymbol, INamedTypeSymbol> equivalentTypesWithDifferingAssemblies,
Compilation compilation,
HashSet<INamedTypeSymbol> verifiedKeys,
bool isSearchSymbolCompilation)
{
Contract.ThrowIfNull(compilation);
Contract.ThrowIfNull(equivalentTypesWithDifferingAssemblies);
Contract.ThrowIfTrue(!equivalentTypesWithDifferingAssemblies.Any());
// Must contain equivalents named types residing in different assemblies.
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => !SymbolEquivalenceComparer.Instance.Equals(kvp.Key.ContainingAssembly, kvp.Value.ContainingAssembly)));
// Must contain non-nested named types.
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => kvp.Key.ContainingType == null));
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => kvp.Value.ContainingType == null));
var referencedAssemblies = new MultiDictionary<string, IAssemblySymbol>();
foreach (var assembly in compilation.GetReferencedAssemblySymbols())
{
referencedAssemblies.Add(assembly.Name, assembly);
}
int verifiedCount = 0;
foreach (var kvp in equivalentTypesWithDifferingAssemblies)
{
if (!verifiedKeys.Contains(kvp.Key))
{
INamedTypeSymbol originalType, expectedForwardedType;
if (isSearchSymbolCompilation)
{
originalType = kvp.Value.OriginalDefinition;
expectedForwardedType = kvp.Key.OriginalDefinition;
}
else
{
originalType = kvp.Key.OriginalDefinition;
expectedForwardedType = kvp.Value.OriginalDefinition;
}
foreach (var referencedAssembly in referencedAssemblies[originalType.ContainingAssembly.Name])
{
var fullyQualifiedTypeName = originalType.MetadataName;
if (originalType.ContainingNamespace != null)
{
fullyQualifiedTypeName = originalType.ContainingNamespace.ToDisplayString(SymbolDisplayFormats.SignatureFormat) +
"." + fullyQualifiedTypeName;
}
// Resolve forwarded type and verify that the types from different assembly are indeed equivalent.
var forwardedType = referencedAssembly.ResolveForwardedType(fullyQualifiedTypeName);
if (forwardedType == expectedForwardedType)
{
verifiedKeys.Add(kvp.Key);
verifiedCount++;
}
}
}
}
return verifiedCount;
}
private static bool TryGetCompilation(
ISymbol symbol,
Solution solution,
out Compilation definingCompilation,
CancellationToken cancellationToken)
{
var definitionProject = solution.GetProject(symbol.ContainingAssembly, cancellationToken);
if (definitionProject == null)
{
definingCompilation = null;
return false;
}
// compilation from definition project must already exist.
if (!definitionProject.TryGetCompilation(out definingCompilation))
{
Contract.Requires(false, "How can compilation not exist?");
return false;
}
return true;
}
}
}
......@@ -217,8 +217,6 @@ protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string n
protected static Func<SyntaxNode, SemanticModel, ValueTuple<bool, CandidateReason>> GetStandardSymbolsNodeMatchFunction(
ISymbol searchSymbol, Solution solution, CancellationToken cancellationToken)
{
Compilation searchSymbolCompilation = null;
Func<SyntaxNode, SemanticModel, ValueTuple<bool, CandidateReason>> symbolsMatch =
(node, model) =>
{
......@@ -227,11 +225,11 @@ protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string n
var symbolToMatch = symbolInfoToMatch.Symbol;
var symbolToMatchCompilation = model.Compilation;
if (OriginalSymbolsMatch(searchSymbol, symbolInfoToMatch.Symbol, solution, ref searchSymbolCompilation, symbolToMatchCompilation, cancellationToken))
if (DependentTypeFinder.OriginalSymbolsMatch(searchSymbol, symbolInfoToMatch.Symbol, solution, null, symbolToMatchCompilation, cancellationToken))
{
return ValueTuple.Create(true, CandidateReason.None);
}
else if (symbolInfoToMatch.CandidateSymbols.Any(s => OriginalSymbolsMatch(searchSymbol, s, solution, ref searchSymbolCompilation, symbolToMatchCompilation, cancellationToken)))
else if (symbolInfoToMatch.CandidateSymbols.Any(s => DependentTypeFinder.OriginalSymbolsMatch(searchSymbol, s, solution, null, symbolToMatchCompilation, cancellationToken)))
{
return ValueTuple.Create(true, symbolInfoToMatch.CandidateReason);
}
......@@ -244,236 +242,6 @@ protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string n
return symbolsMatch;
}
private static bool OriginalSymbolsMatch(
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
ref Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
if (symbolToMatch == null)
{
return false;
}
if (OriginalSymbolsMatchCore(searchSymbol, symbolToMatch, solution, ref searchSymbolCompilation, symbolToMatchCompilation, cancellationToken))
{
return true;
}
if (searchSymbol.Kind == SymbolKind.Namespace && symbolToMatch.Kind == SymbolKind.Namespace)
{
// if one of them is a merged namespace symbol and other one is its constituent namespace symbol, they are equivalent.
var namespace1 = (INamespaceSymbol)searchSymbol;
var namespace2 = (INamespaceSymbol)symbolToMatch;
var namespace1Count = namespace1.ConstituentNamespaces.Length;
var namespace2Count = namespace2.ConstituentNamespaces.Length;
if (namespace1Count != namespace2Count)
{
if ((namespace1Count > 1 &&
namespace1.ConstituentNamespaces.Any(n => NamespaceSymbolsMatch(n, namespace2, solution, cancellationToken))) ||
(namespace2Count > 1 &&
namespace2.ConstituentNamespaces.Any(n2 => NamespaceSymbolsMatch(namespace1, n2, solution, cancellationToken))))
{
return true;
}
}
}
if (searchSymbol.Kind == SymbolKind.NamedType && symbolToMatch.IsConstructor())
{
return OriginalSymbolsMatch(searchSymbol, symbolToMatch.ContainingType, solution, ref searchSymbolCompilation, symbolToMatchCompilation, cancellationToken);
}
return false;
}
private static bool NamespaceSymbolsMatch(
INamespaceSymbol namespace1,
INamespaceSymbol namespace2,
Solution solution,
CancellationToken cancellationToken)
{
Compilation definingCompilation = null;
return OriginalSymbolsMatch(namespace1, namespace2, solution, ref definingCompilation, null, cancellationToken);
}
private static bool OriginalSymbolsMatchCore(
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
ref Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
if (searchSymbol == null || symbolToMatch == null)
{
return false;
}
searchSymbol = searchSymbol.GetOriginalUnreducedDefinition();
symbolToMatch = symbolToMatch.GetOriginalUnreducedDefinition();
// We compare the given searchSymbol and symbolToMatch for equivalence using SymbolEquivalenceComparer
// as follows:
// 1) We compare the given symbols using the SymbolEquivalenceComparer.IgnoreAssembliesInstance,
// which ignores the containing assemblies for named types equivalence checks. This is required
// to handle equivalent named types which are forwarded to completely different assemblies.
// 2) If the symbols are NOT equivalent ignoring assemblies, then they cannot be equivalent.
// 3) Otherwise, if the symbols ARE equivalent ignoring assemblies, they may or may not be equivalent
// if containing assemblies are NOT ignored. We need to perform additional checks to ensure they
// are indeed equivalent:
//
// (a) If IgnoreAssembliesInstance.Equals equivalence visitor encountered any pair of non-nested
// named types which were equivalent in all aspects, except that they resided in different
// assemblies, we need to ensure that all such pairs are indeed equivalent types. Such a pair
// of named types is equivalent if and only if one of them is a type defined in either
// searchSymbolCompilation(C1) or symbolToMatchCompilation(C2), say defined in reference assembly
// A (version v1) in compilation C1, and the other type is a forwarded type, such that it is
// forwarded from reference assembly A (version v2) to assembly B in compilation C2.
// (b) Otherwise, if no such named type pairs were encountered, symbols ARE equivalent.
using (var equivalentTypesWithDifferingAssemblies = SharedPools.Default<Dictionary<INamedTypeSymbol, INamedTypeSymbol>>().GetPooledObject())
{
// 1) Compare searchSymbol and symbolToMatch using SymbolEquivalenceComparer.IgnoreAssembliesInstance
if (!SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(searchSymbol, symbolToMatch, equivalentTypesWithDifferingAssemblies.Object))
{
// 2) If the symbols are NOT equivalent ignoring assemblies, then they cannot be equivalent.
return false;
}
// 3) If the symbols ARE equivalent ignoring assemblies, they may or may not be equivalent if containing assemblies are NOT ignored.
if (equivalentTypesWithDifferingAssemblies.Object.Count > 0)
{
// Step 3a) Ensure that all pairs of named types in equivalentTypesWithDifferingAssemblies are indeed equivalent types.
return VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies.Object, searchSymbol, symbolToMatch,
solution, ref searchSymbolCompilation, symbolToMatchCompilation, cancellationToken);
}
// 3b) If no such named type pairs were encountered, symbols ARE equivalent.
return true;
}
}
// Verifies that all pairs of named types in equivalentTypesWithDifferingAssemblies are equivalent forwarded types.
private static bool VerifyForwardedTypes(
Dictionary<INamedTypeSymbol, INamedTypeSymbol> equivalentTypesWithDifferingAssemblies,
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
ref Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
var verifiedKeys = new HashSet<INamedTypeSymbol>();
var count = equivalentTypesWithDifferingAssemblies.Count;
int verifiedCount = 0;
// First check forwarded types in searchSymbolCompilation.
if (TryGetCompilation(searchSymbol, solution, ref searchSymbolCompilation, cancellationToken))
{
verifiedCount = VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies, searchSymbolCompilation, verifiedKeys, isSearchSymbolCompilation: true);
if (verifiedCount == count)
{
// All equivalent types verified.
return true;
}
}
// Now check forwarded types in symbolToMatchCompilation.
verifiedCount += VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies, symbolToMatchCompilation, verifiedKeys, isSearchSymbolCompilation: false);
return verifiedCount == count;
}
private static int VerifyForwardedTypes(
Dictionary<INamedTypeSymbol, INamedTypeSymbol> equivalentTypesWithDifferingAssemblies,
Compilation compilation,
HashSet<INamedTypeSymbol> verifiedKeys,
bool isSearchSymbolCompilation)
{
Contract.ThrowIfNull(compilation);
Contract.ThrowIfNull(equivalentTypesWithDifferingAssemblies);
Contract.ThrowIfTrue(!equivalentTypesWithDifferingAssemblies.Any());
// Must contain equivalents named types residing in different assemblies.
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => !SymbolEquivalenceComparer.Instance.Equals(kvp.Key.ContainingAssembly, kvp.Value.ContainingAssembly)));
// Must contain non-nested named types.
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => kvp.Key.ContainingType == null));
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => kvp.Value.ContainingType == null));
var referencedAssemblies = new MultiDictionary<string, IAssemblySymbol>();
foreach (var assembly in compilation.GetReferencedAssemblySymbols())
{
referencedAssemblies.Add(assembly.Name, assembly);
}
int verifiedCount = 0;
foreach (var kvp in equivalentTypesWithDifferingAssemblies)
{
if (!verifiedKeys.Contains(kvp.Key))
{
INamedTypeSymbol originalType, expectedForwardedType;
if (isSearchSymbolCompilation)
{
originalType = kvp.Value.OriginalDefinition;
expectedForwardedType = kvp.Key.OriginalDefinition;
}
else
{
originalType = kvp.Key.OriginalDefinition;
expectedForwardedType = kvp.Value.OriginalDefinition;
}
foreach (var referencedAssembly in referencedAssemblies[originalType.ContainingAssembly.Name])
{
var fullyQualifiedTypeName = originalType.MetadataName;
if (originalType.ContainingNamespace != null)
{
fullyQualifiedTypeName = originalType.ContainingNamespace.ToDisplayString(SymbolDisplayFormats.SignatureFormat) +
"." + fullyQualifiedTypeName;
}
// Resolve forwarded type and verify that the types from different assembly are indeed equivalent.
var forwardedType = referencedAssembly.ResolveForwardedType(fullyQualifiedTypeName);
if (forwardedType == expectedForwardedType)
{
verifiedKeys.Add(kvp.Key);
verifiedCount++;
}
}
}
}
return verifiedCount;
}
private static bool TryGetCompilation(
ISymbol symbol,
Solution solution,
ref Compilation definingCompilation,
CancellationToken cancellationToken)
{
if (definingCompilation == null)
{
var definitionProject = solution.GetProject(symbol.ContainingAssembly, cancellationToken);
if (definitionProject == null)
{
return false;
}
// compilation from definition project must already exist.
if (!definitionProject.TryGetCompilation(out definingCompilation))
{
Contract.Requires(false, "How can compilation not exist?");
return false;
}
}
return true;
}
protected Task<IEnumerable<ReferenceLocation>> FindReferencesInTokensAsync(
TSymbol symbol,
Document document,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.UnitTests
{
public class DependentTypeFinderTests : TestBase
{
private Solution AddProject(Solution solution, string projectName, string languageName, string code, MetadataReference metadataReference, params ProjectId[] projectReferences)
{
var suffix = languageName == LanguageNames.CSharp ? "cs" : "vb";
var pid = ProjectId.CreateNewId();
var did = DocumentId.CreateNewId(pid);
var pi = ProjectInfo.Create(
pid,
VersionStamp.Default,
projectName,
projectName,
languageName,
metadataReferences: new[] { metadataReference },
projectReferences: projectReferences.Select(p => new ProjectReference(p)));
return solution.AddProject(pi).AddDocument(did, $"{projectName}.{suffix}", SourceText.From(code));
}
[WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
[Fact]
public async Task ImmediatelyDerivedTypes_CSharp()
{
var solution = new AdhocWorkspace().CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProject(solution, "PortableProject", LanguageNames.CSharp, @"
namespace N
{
public abstract class BaseClass { }
}
", MscorlibRefPortable);
// create a normal assembly with a type derived from the portable abstract base
solution = AddProject(solution, "NormalProject", LanguageNames.CSharp, @"
using N;
namespace M
{
public class DerivedClass : BaseClass { }
}
", MscorlibRef, solution.Projects.Single(pid => pid.Name == "PortableProject").Id);
// get symbols for types
var portableCompilation = await solution.Projects.Single(p => p.Name == "PortableProject").GetCompilationAsync();
var baseClassSymbol = portableCompilation.GetTypeByMetadataName("N.BaseClass");
var normalCompilation = await solution.Projects.Single(p => p.Name == "NormalProject").GetCompilationAsync();
var derivedClassSymbol = normalCompilation.GetTypeByMetadataName("M.DerivedClass");
// verify that the symbols are different (due to retargeting)
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
[Fact]
public async Task ImmediatelyDerivedTypes_CSharp_PortableProfile7()
{
var solution = new AdhocWorkspace().CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProject(solution, "PortableProject", LanguageNames.CSharp, @"
namespace N
{
public abstract class BaseClass { }
}
", MscorlibRefPortable);
// create a normal assembly with a type derived from the portable abstract base
solution = AddProject(solution, "NormalProject", LanguageNames.CSharp, @"
using N;
namespace M
{
public class DerivedClass : BaseClass { }
}
", SystemRuntimePP7Ref, solution.Projects.Single(pid => pid.Name == "PortableProject").Id);
// get symbols for types
var portableCompilation = await solution.Projects.Single(p => p.Name == "PortableProject").GetCompilationAsync();
var baseClassSymbol = portableCompilation.GetTypeByMetadataName("N.BaseClass");
var normalCompilation = await solution.Projects.Single(p => p.Name == "NormalProject").GetCompilationAsync();
var derivedClassSymbol = normalCompilation.GetTypeByMetadataName("M.DerivedClass");
// verify that the symbols are different (due to retargeting)
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
[Fact]
public async Task ImmediatelyDerivedTypes_VisualBasic()
{
var solution = new AdhocWorkspace().CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProject(solution, "PortableProject", LanguageNames.VisualBasic, @"
Namespace N
Public MustInherit Class BaseClass
End Class
End Namespace
", MscorlibRefPortable);
// create a normal assembly with a type derived from the portable abstract base
solution = AddProject(solution, "NormalProject", LanguageNames.VisualBasic, @"
Imports N
Namespace M
Public Class DerivedClass
Inherits BaseClass
End Class
End Namespace
", MscorlibRef, solution.Projects.Single(pid => pid.Name == "PortableProject").Id);
// get symbols for types
var portableCompilation = await solution.Projects.Single(p => p.Name == "PortableProject").GetCompilationAsync();
var baseClassSymbol = portableCompilation.GetTypeByMetadataName("N.BaseClass");
var normalCompilation = await solution.Projects.Single(p => p.Name == "NormalProject").GetCompilationAsync();
var derivedClassSymbol = normalCompilation.GetTypeByMetadataName("M.DerivedClass");
// verify that the symbols are different (due to retargeting)
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
[Fact]
public async Task ImmediatelyDerivedTypes_CrossLanguage()
{
var solution = new AdhocWorkspace().CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProject(solution, "PortableProject", LanguageNames.CSharp, @"
namespace N
{
public abstract class BaseClass { }
}
", MscorlibRefPortable);
// create a normal assembly with a type derived from the portable abstract base
solution = AddProject(solution, "NormalProject", LanguageNames.VisualBasic, @"
Imports N
Namespace M
Public Class DerivedClass
Inherits BaseClass
End Class
End Namespace
", MscorlibRef, solution.Projects.Single(pid => pid.Name == "PortableProject").Id);
// get symbols for types
var portableCompilation = await solution.Projects.Single(p => p.Name == "PortableProject").GetCompilationAsync();
var baseClassSymbol = portableCompilation.GetTypeByMetadataName("N.BaseClass");
var normalCompilation = await solution.Projects.Single(p => p.Name == "NormalProject").GetCompilationAsync();
var derivedClassSymbol = normalCompilation.GetTypeByMetadataName("M.DerivedClass");
// verify that the symbols are different (due to retargeting)
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
[Fact]
public async Task ImmediatelyDerivedInterfaces_CSharp()
{
var solution = new AdhocWorkspace().CurrentSolution;
// create portable assembly with an interface
solution = AddProject(solution, "PortableProject", LanguageNames.CSharp, @"
namespace N
{
public interface IBaseInterface { }
}
", MscorlibRefPortable);
// create a normal assembly with a type implementing that interface
solution = AddProject(solution, "NormalProject", LanguageNames.CSharp, @"
using N;
namespace M
{
public class ImplementingClass : IBaseInterface { }
}
", MscorlibRef, solution.Projects.Single(pid => pid.Name == "PortableProject").Id);
// get symbols for types
var portableCompilation = await solution.Projects.Single(p => p.Name == "PortableProject").GetCompilationAsync();
var baseInterfaceSymbol = portableCompilation.GetTypeByMetadataName("N.IBaseInterface");
var normalCompilation = await solution.Projects.Single(p => p.Name == "NormalProject").GetCompilationAsync();
var implementingClassSymbol = normalCompilation.GetTypeByMetadataName("M.ImplementingClass");
// verify that the symbols are different (due to retargeting)
Assert.NotEqual(baseInterfaceSymbol, implementingClassSymbol.Interfaces.Single());
// verify that the implementing types of `N.IBaseInterface` correctly resolve to `M.ImplementingClass`
var typesThatImplementInterface = await DependentTypeFinder.GetTypesImmediatelyDerivedFromInterfacesAsync(baseInterfaceSymbol, solution, CancellationToken.None);
Assert.Equal(implementingClassSymbol, typesThatImplementInterface.Single());
}
[WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
[Fact]
public async Task ImmediatelyDerivedInterfaces_VisualBasic()
{
var solution = new AdhocWorkspace().CurrentSolution;
// create portable assembly with an interface
solution = AddProject(solution, "PortableProject", LanguageNames.VisualBasic, @"
Namespace N
Public Interface IBaseInterface
End Interface
End Namespace
", MscorlibRefPortable);
// create a normal assembly with a type implementing that interface
solution = AddProject(solution, "NormalProject", LanguageNames.VisualBasic, @"
Imports N
Namespace M
Public Class ImplementingClass
Implements IBaseInterface
End Class
End Namespace
", MscorlibRef, solution.Projects.Single(pid => pid.Name == "PortableProject").Id);
// get symbols for types
var portableCompilation = await solution.Projects.Single(p => p.Name == "PortableProject").GetCompilationAsync();
var baseInterfaceSymbol = portableCompilation.GetTypeByMetadataName("N.IBaseInterface");
var normalCompilation = await solution.Projects.Single(p => p.Name == "NormalProject").GetCompilationAsync();
var implementingClassSymbol = normalCompilation.GetTypeByMetadataName("M.ImplementingClass");
// verify that the symbols are different (due to retargeting)
Assert.NotEqual(baseInterfaceSymbol, implementingClassSymbol.Interfaces.Single());
// verify that the implementing types of `N.IBaseInterface` correctly resolve to `M.ImplementingClass`
var typesThatImplementInterface = await DependentTypeFinder.GetTypesImmediatelyDerivedFromInterfacesAsync(baseInterfaceSymbol, solution, CancellationToken.None);
Assert.Equal(implementingClassSymbol, typesThatImplementInterface.Single());
}
[WorkItem(4973, "https://github.com/dotnet/roslyn/issues/4973")]
[Fact]
public async Task ImmediatelyDerivedInterfaces_CrossLanguage()
{
var solution = new AdhocWorkspace().CurrentSolution;
// create portable assembly with an interface
solution = AddProject(solution, "PortableProject", LanguageNames.VisualBasic, @"
Namespace N
Public Interface IBaseInterface
End Interface
End Namespace
", MscorlibRefPortable);
// create a normal assembly with a type implementing that interface
solution = AddProject(solution, "NormalProject", LanguageNames.CSharp, @"
using N;
namespace M
{
public class ImplementingClass : IBaseInterface { }
}
", MscorlibRef, solution.Projects.Single(pid => pid.Name == "PortableProject").Id);
// get symbols for types
var portableCompilation = await solution.Projects.Single(p => p.Name == "PortableProject").GetCompilationAsync();
var baseInterfaceSymbol = portableCompilation.GetTypeByMetadataName("N.IBaseInterface");
var normalCompilation = await solution.Projects.Single(p => p.Name == "NormalProject").GetCompilationAsync();
var implementingClassSymbol = normalCompilation.GetTypeByMetadataName("M.ImplementingClass");
// verify that the symbols are different (due to retargeting)
Assert.NotEqual(baseInterfaceSymbol, implementingClassSymbol.Interfaces.Single());
// verify that the implementing types of `N.IBaseInterface` correctly resolve to `M.ImplementingClass`
var typesThatImplementInterface = await DependentTypeFinder.GetTypesImmediatelyDerivedFromInterfacesAsync(baseInterfaceSymbol, solution, CancellationToken.None);
Assert.Equal(implementingClassSymbol, typesThatImplementInterface.Single());
}
}
}
......@@ -66,6 +66,7 @@
<CodeAnalysisRuleSet>..\..\..\build\TestProjectRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Include="DependentTypeFinderTests.cs" />
<Compile Include="Editting\SyntaxEditorTests.cs" />
<Compile Include="ExtensionOrdererTests.cs" />
<Compile Include="Host\WorkspaceServices\TestProjectCacheService.cs" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册