From 6116f322d72661da10362635b93f009fe3604626 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 15 Dec 2021 18:16:30 -0800 Subject: [PATCH 1/8] Implement GetTypesByMetadataName Closes https://github.com/dotnet/roslyn/issues/57802. Implements the GetTypesByMetadataName API, which returns all named types in the current compilation and referenced assemblies that match the given CLR metatdata name. Also updates the IDE's GetBestTypeByMetadataName to use this API under the hood, rather than the existing manual walk they were doing, simplyfing that codepath and proving out the API itself. --- .../Symbol/Compilation/CompilationAPITests.cs | 208 ++++++++++++++++++ .../Core/Portable/Compilation/Compilation.cs | 53 ++++- .../Core/Portable/PublicAPI.Unshipped.txt | 1 + .../Test/Symbol/CompilationAPITests.vb | 176 +++++++++++++++ .../Core/Extensions/CompilationExtensions.cs | 51 ++--- 5 files changed, 456 insertions(+), 33 deletions(-) create mode 100644 src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs index d1a03c60f4a71..f6f1ba1d252b1 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs @@ -3036,5 +3036,213 @@ void M1() } #endregion + + #region GetTypesByMetadataName Tests + + [Fact] + public void GetTypesByMetadataName_NotInSourceNotInReferences() + { + var comp = CreateCompilation(""); + comp.VerifyDiagnostics(); + + var types = comp.GetTypesByMetadataName("N.C`1"); + Assert.Empty(types); + } + + [Theory] + [CombinatorialData] + public void GetTypesByMetadataName_SingleInSourceNotInReferences( + bool useMetadataReference, + [CombinatorialValues("public", "internal")] string accessibility) + { + var referenceComp = CreateCompilation(""); + + var comp = CreateCompilation( +$@"namespace N; +{accessibility} class C {{}}", new[] { useMetadataReference ? referenceComp.ToMetadataReference() : referenceComp.EmitToImageReference() }); + + comp.VerifyDiagnostics(); + + var types = comp.GetTypesByMetadataName("N.C`1"); + + Assert.Single(types); + AssertEx.Equal("N.C", types[0].ToTestDisplayString()); + } + + [Theory] + [CombinatorialData] + public void GetTypesByMetadataName_MultipleInSourceNotInReferences( + bool useMetadataReference, + [CombinatorialValues("public", "internal")] string accessibility) + { + var referenceComp = CreateCompilation(""); + + var comp = CreateCompilation( +$@"namespace N; +{accessibility} class C {{}} +{accessibility} class C {{}}", new[] { useMetadataReference ? referenceComp.ToMetadataReference() : referenceComp.EmitToImageReference() }); + + comp.VerifyDiagnostics( + // (3,16): error CS0101: The namespace 'N' already contains a definition for 'C' + // internal class C {} + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "C").WithArguments("C", "N").WithLocation(3, 8 + accessibility.Length) + ); + + var types = comp.GetTypesByMetadataName("N.C`1"); + + Assert.Single(types); + AssertEx.Equal("N.C", types[0].ToTestDisplayString()); + Assert.Equal(2, types[0].Locations.Length); + } + + [Theory] + [CombinatorialData] + public void GetTypesByMetadataName_SingleInSourceSingleInReferences( + bool useMetadataReference, + [CombinatorialValues("public", "internal")] string accessibility) + { + string source = $@"namespace N; +{accessibility} class C {{}}"; + + var referenceComp = CreateCompilation(source); + + referenceComp.VerifyDiagnostics(); + + var comp = CreateCompilation(source, new[] { useMetadataReference ? referenceComp.ToMetadataReference() : referenceComp.EmitToImageReference() }); + comp.VerifyDiagnostics(); + + var types = comp.GetTypesByMetadataName("N.C`1"); + + Assert.Equal(2, types.Length); + AssertEx.Equal("N.C", types[0].ToTestDisplayString()); + Assert.Same(comp.Assembly.GetPublicSymbol(), types[0].ContainingAssembly); + AssertEx.Equal("N.C", types[1].ToTestDisplayString()); + if (useMetadataReference) + { + Assert.Same(referenceComp.Assembly.GetPublicSymbol(), types[1].ContainingAssembly); + } + else + { + Assert.False(types[1].IsInSource()); + } + } + + [Theory] + [CombinatorialData] + public void GetTypesByMetadataName_NotInSourceSingleInReferences( + bool useMetadataReference, + [CombinatorialValues("public", "internal")] string accessibility) + { + string source = @$"namespace N; +{accessibility} class C {{}}"; + + var referenceComp = CreateCompilation(source); + + referenceComp.VerifyDiagnostics(); + + var comp = CreateCompilation("", new[] { useMetadataReference ? referenceComp.ToMetadataReference() : referenceComp.EmitToImageReference() }); + comp.VerifyDiagnostics(); + + var types = comp.GetTypesByMetadataName("N.C`1"); + + + Assert.Single(types); + AssertEx.Equal("N.C", types[0].ToTestDisplayString()); + if (useMetadataReference) + { + Assert.Same(referenceComp.Assembly.GetPublicSymbol(), types[0].ContainingAssembly); + } + else + { + Assert.False(types[0].IsInSource()); + } + } + + [Theory] + [CombinatorialData] + public void GetTypesByMetadataName_NotInSourceMultipleInReferences( + bool useMetadataReference, + [CombinatorialValues("public", "internal")] string accessibility) + { + string source = @$"namespace N; +{accessibility} class C {{}}"; + + var referenceComp1 = CreateCompilation(source); + referenceComp1.VerifyDiagnostics(); + + var referenceComp2 = CreateCompilation(source); + referenceComp2.VerifyDiagnostics(); + + var comp = CreateCompilation("", new[] { getReference(referenceComp1), getReference(referenceComp2) }); + comp.VerifyDiagnostics(); + + var types = comp.GetTypesByMetadataName("N.C`1"); + + Assert.Equal(2, types.Length); + AssertEx.Equal("N.C", types[0].ToTestDisplayString()); + AssertEx.Equal("N.C", types[1].ToTestDisplayString()); + if (useMetadataReference) + { + Assert.Same(referenceComp1.Assembly.GetPublicSymbol(), types[0].ContainingAssembly); + Assert.Same(referenceComp2.Assembly.GetPublicSymbol(), types[1].ContainingAssembly); + } + else + { + Assert.False(types[0].IsInSource()); + Assert.False(types[1].IsInSource()); + Assert.NotSame(types[0].ContainingAssembly, types[1].ContainingAssembly); + } + + MetadataReference getReference(CSharpCompilation referenceComp1) + { + return useMetadataReference ? referenceComp1.ToMetadataReference() : referenceComp1.EmitToImageReference(); + } + } + + [Theory] + [CombinatorialData] + public void GetTypesByMetadataName_SingleInSourceMultipleInReferences( + bool useMetadataReference, + [CombinatorialValues("public", "internal")] string accessibility) + { + string source = @$"namespace N; +{accessibility} class C {{}}"; + + var referenceComp1 = CreateCompilation(source); + referenceComp1.VerifyDiagnostics(); + + var referenceComp2 = CreateCompilation(source); + referenceComp2.VerifyDiagnostics(); + + var comp = CreateCompilation(source, new[] { getReference(referenceComp1), getReference(referenceComp2) }); + comp.VerifyDiagnostics(); + + var types = comp.GetTypesByMetadataName("N.C`1"); + + Assert.Equal(3, types.Length); + AssertEx.Equal("N.C", types[0].ToTestDisplayString()); + Assert.Same(comp.Assembly.GetPublicSymbol(), types[0].ContainingAssembly); + AssertEx.Equal("N.C", types[1].ToTestDisplayString()); + AssertEx.Equal("N.C", types[2].ToTestDisplayString()); + + if (useMetadataReference) + { + Assert.Same(referenceComp1.Assembly.GetPublicSymbol(), types[1].ContainingAssembly); + Assert.Same(referenceComp2.Assembly.GetPublicSymbol(), types[2].ContainingAssembly); + } + else + { + Assert.False(types[1].IsInSource()); + Assert.False(types[2].IsInSource()); + Assert.NotSame(types[1].ContainingAssembly, types[2].ContainingAssembly); + } + + MetadataReference getReference(CSharpCompilation referenceComp1) + { + return useMetadataReference ? referenceComp1.ToMetadataReference() : referenceComp1.EmitToImageReference(); + } + } + + #endregion } } diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 037ea63240a7d..fe95c6d0b40f9 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1025,6 +1025,9 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) private readonly ConcurrentCache _getTypeCache = new ConcurrentCache(50, ReferenceEqualityComparer.Instance); + private readonly ConcurrentCache> _getTypesCache = + new ConcurrentCache>(50); + /// /// Gets the type within the compilation's assembly and all referenced assemblies (other than /// those that can only be referenced via an extern alias) using its canonical CLR metadata name. @@ -1038,15 +1041,58 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) if (!_getTypeCache.TryGetValue(fullyQualifiedMetadataName, out INamedTypeSymbol? val)) { val = CommonGetTypeByMetadataName(fullyQualifiedMetadataName); - // Ignore if someone added the same value before us - _ = _getTypeCache.TryAdd(fullyQualifiedMetadataName, val); + var result = _getTypeCache.TryAdd(fullyQualifiedMetadataName, val); + Debug.Assert(result || (_getTypeCache.TryGetValue(fullyQualifiedMetadataName, out var addedType) && ReferenceEquals(addedType, val))); } return val; } protected abstract INamedTypeSymbol? CommonGetTypeByMetadataName(string metadataName); -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + /// + /// Gets all types with the compilation's assembly and all referenced assemblies that have the + /// given canonical CLR metadta name. + /// + /// Empty array if no types match. Otherwise, all types that match the name, current assembly first if present. + public ImmutableArray GetTypesByMetadataName(string fullyQualifiedMetadataName) + { + if (!_getTypesCache.TryGetValue(fullyQualifiedMetadataName, out ImmutableArray val)) + { + val = getTypesByMetadataNameImpl(); + var result = _getTypesCache.TryAdd(fullyQualifiedMetadataName, val); + Debug.Assert(result + || (_getTypesCache.TryGetValue(fullyQualifiedMetadataName, out var addedArray) + && addedArray.Zip(val, (added, calculated) => (added, calculated)).All(el => ReferenceEquals(el.added, el.calculated)))); + } + + return val; + + ImmutableArray getTypesByMetadataNameImpl() + { + ArrayBuilder? typesByMetadataName = null; + + // Start with the current assembly, then look through all references, to mimic GetTypeByMetadataName search order. + + addIfNotNull(Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName)); + + foreach (var referencedAssembly in SourceModule.ReferencedAssemblySymbols) + { + addIfNotNull(referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName)); + } + + return typesByMetadataName?.ToImmutableAndFree() ?? ImmutableArray.Empty; + + void addIfNotNull(INamedTypeSymbol? toAdd) + { + if (toAdd != null) + { + typesByMetadataName ??= ArrayBuilder.GetInstance(); + typesByMetadataName.Add(toAdd); + } + } + } + } + /// /// Returns a new INamedTypeSymbol with the given element types and /// (optional) element names, locations, and nullable annotations. @@ -1087,7 +1133,6 @@ public INamedTypeSymbol CreateTupleTypeSymbol( return CommonCreateTupleTypeSymbol(elementTypes, elementNames, elementLocations, elementNullableAnnotations); } -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters /// /// Returns a new INamedTypeSymbol with the given element types, names, and locations. diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 02161a15fe460..ad76fcb80cd4d 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -9,6 +9,7 @@ const Microsoft.CodeAnalysis.WellKnownGeneratorOutputs.ImplementationSourceOutpu const Microsoft.CodeAnalysis.WellKnownGeneratorOutputs.SourceOutput = "SourceOutput" -> string! const Microsoft.CodeAnalysis.WellKnownMemberNames.PrintMembersMethodName = "PrintMembers" -> string! Microsoft.CodeAnalysis.Compilation.EmitDifference(Microsoft.CodeAnalysis.Emit.EmitBaseline! baseline, System.Collections.Generic.IEnumerable! edits, System.Func! isAddedSymbol, System.IO.Stream! metadataStream, System.IO.Stream! ilStream, System.IO.Stream! pdbStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitDifferenceResult! +Microsoft.CodeAnalysis.Compilation.GetTypesByMetadataName(string! fullyQualifiedMetadataName) -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Emit.EmitDifferenceResult.ChangedTypes.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Emit.EmitDifferenceResult.UpdatedMethods.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.Emit.SemanticEditKind.Replace = 4 -> Microsoft.CodeAnalysis.Emit.SemanticEditKind diff --git a/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb new file mode 100644 index 0000000000000..1d8220b41ed34 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb @@ -0,0 +1,176 @@ +' 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. + +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Roslyn.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests + + Public Class CompilationAPITests + Inherits BasicTestBase + + + Public Sub GetTypesByMetadtaName_NotInSourceNotInReferences() + Dim comp = CreateCompilation("") + comp.AssertNoDiagnostics() + + Dim types = comp.GetTypesByMetadataName("N.C`1") + Assert.Empty(types) + End Sub + + + Public Sub GetTypesByMetadtaName_SingleInSourceNotInReferences(useMetadataReferences As Boolean, accessibility As String) + Dim referenceComp = CreateCompilation("") + + Dim source = +$"Namespace N + {accessibility} Class C(Of T) + End Class +End Namespace" + + Dim comp = CreateCompilation(source, {If(useMetadataReferences, referenceComp.ToMetadataReference(), referenceComp.EmitToImageReference())}) + comp.AssertNoDiagnostics() + + Dim types = comp.GetTypesByMetadataName("N.C`1") + + Assert.Single(types) + AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) + End Sub + + + Public Sub GetTypesByMetadtaName_MultipleInSourceNotInReferences(useMetadataReferences As Boolean, accessibility As String) + Dim referenceComp = CreateCompilation("") + + Dim source = +$"Namespace N + {accessibility} Class C(Of T) + End Class + {accessibility} Class C(Of T) + End Class +End Namespace" + + Dim comp = CreateCompilation(source, {If(useMetadataReferences, referenceComp.ToMetadataReference(), referenceComp.EmitToImageReference())}) + comp.AssertTheseDiagnostics( + +BC30179: class 'C' and class 'C' conflict in namespace 'N'. + <%= accessibility %> Class C(Of T) + ~ +) + + Dim types = comp.GetTypesByMetadataName("N.C`1") + + Assert.Single(types) + AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) + Assert.Equal(2, types(0).Locations.Length) + End Sub + + + Public Sub GetTypesByMetadtaName_SingleInSourceSingleInReferences(useMetadataReference As Boolean, accessibility As String) + Dim source = +$"Namespace N + {accessibility} Class C(Of T) + End Class +End Namespace" + + Dim referenceComp = CreateCompilation(source) + Dim comp = CreateCompilation(source, {If(useMetadataReference, referenceComp.ToMetadataReference(), referenceComp.EmitToImageReference())}) + comp.AssertNoDiagnostics() + + Dim types = comp.GetTypesByMetadataName("N.C`1") + + Assert.Equal(2, types.Length) + AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) + Assert.Same(comp.Assembly, types(0).ContainingAssembly) + AssertEx.Equal("N.C(Of T)", types(1).ToTestDisplayString()) + If (useMetadataReference) Then + Assert.Same(referenceComp.Assembly, types(1).ContainingAssembly) + Else + Assert.False(types(1).IsInSource()) + End If + End Sub + + + Public Sub GetTypesByMetadtaName_NotInSourceSingleInReferences(useMetadataReference As Boolean, accessibility As String) + Dim source = +$"Namespace N + {accessibility} Class C(Of T) + End Class +End Namespace" + + Dim referenceComp = CreateCompilation(source) + Dim comp = CreateCompilation("", {GetReference(useMetadataReference, referenceComp)}) + comp.AssertNoDiagnostics() + + Dim types = comp.GetTypesByMetadataName("N.C`1") + + Assert.Single(types) + AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) + If (useMetadataReference) Then + Assert.Same(referenceComp.Assembly, types(0).ContainingAssembly) + Else + Assert.False(types(0).IsInSource()) + End If + End Sub + + Private Shared Function GetReference(useMetadataReference As Boolean, referenceComp As VisualBasicCompilation) As MetadataReference + Return If(useMetadataReference, referenceComp.ToMetadataReference(), referenceComp.EmitToImageReference()) + End Function + + + Public Sub GetTypesByMetadtaName_NotInSourceMultipleInReferences(useMetadataReference As Boolean, accessibility As String) + Dim source = +$"Namespace N + {accessibility} Class C(Of T) + End Class +End Namespace" + + Dim referenceComp1 = CreateCompilation(source) + Dim referenceComp2 = CreateCompilation(source) + Dim comp = CreateCompilation("", {GetReference(useMetadataReference, referenceComp1), GetReference(useMetadataReference, referenceComp2)}) + comp.AssertNoDiagnostics() + + Dim types = comp.GetTypesByMetadataName("N.C`1") + + Assert.Equal(2, types.Length) + AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) + AssertEx.Equal("N.C(Of T)", types(1).ToTestDisplayString()) + If (useMetadataReference) Then + Assert.Same(referenceComp1.Assembly, types(0).ContainingAssembly) + Assert.Same(referenceComp2.Assembly, types(1).ContainingAssembly) + Else + Assert.False(types(0).IsInSource()) + Assert.False(types(1).IsInSource()) + End If + End Sub + + + Public Sub GetTypesByMetadtaName_SingleInSourceMultipleInReferences(useMetadataReference As Boolean, accessibility As String) + Dim source = +$"Namespace N + {accessibility} Class C(Of T) + End Class +End Namespace" + + Dim referenceComp1 = CreateCompilation(source) + Dim referenceComp2 = CreateCompilation(source) + Dim comp = CreateCompilation(source, {GetReference(useMetadataReference, referenceComp1), GetReference(useMetadataReference, referenceComp2)}) + comp.AssertNoDiagnostics() + + Dim types = comp.GetTypesByMetadataName("N.C`1") + + Assert.Equal(3, types.Length) + AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) + Assert.Same(comp.Assembly, types(0).ContainingAssembly) + AssertEx.Equal("N.C(Of T)", types(1).ToTestDisplayString()) + AssertEx.Equal("N.C(Of T)", types(2).ToTestDisplayString()) + If (useMetadataReference) Then + Assert.Same(referenceComp1.Assembly, types(1).ContainingAssembly) + Assert.Same(referenceComp2.Assembly, types(2).ContainingAssembly) + Else + Assert.False(types(1).IsInSource()) + Assert.False(types(2).IsInSource()) + End If + End Sub + End Class +End Namespace diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/CompilationExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/CompilationExtensions.cs index 649d9f7bfa377..0384afee7e1fc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/CompilationExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/CompilationExtensions.cs @@ -2,6 +2,8 @@ // 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.Diagnostics; + namespace Microsoft.CodeAnalysis.Shared.Extensions { internal static class CompilationExtensions @@ -33,42 +35,33 @@ internal static class CompilationExtensions /// The symbol to use for code analysis; otherwise, . public static INamedTypeSymbol? GetBestTypeByMetadataName(this Compilation compilation, string fullyQualifiedMetadataName) { - // Try to get the unique type with this name, ignoring accessibility - var type = compilation.GetTypeByMetadataName(fullyQualifiedMetadataName); - - // Otherwise, try to get the unique type with this name originally defined in 'compilation' - type ??= compilation.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName); + INamedTypeSymbol? type = null; - // Otherwise, try to get the unique accessible type with this name from a reference - if (type is null) + foreach (var currentType in compilation.GetTypesByMetadataName(fullyQualifiedMetadataName)) { - foreach (var module in compilation.Assembly.Modules) + if (ReferenceEquals(currentType.ContainingAssembly, compilation.Assembly)) { - foreach (var referencedAssembly in module.ReferencedAssemblySymbols) - { - var currentType = referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName); - if (currentType is null) - continue; - - switch (currentType.GetResultantVisibility()) - { - case Utilities.SymbolVisibility.Public: - case Utilities.SymbolVisibility.Internal when referencedAssembly.GivesAccessTo(compilation.Assembly): - break; + Debug.Assert(type is null); + return currentType; + } - default: - continue; - } + switch (currentType.GetResultantVisibility()) + { + case Utilities.SymbolVisibility.Public: + case Utilities.SymbolVisibility.Internal when currentType.ContainingAssembly.GivesAccessTo(compilation.Assembly): + break; - if (type is object) - { - // Multiple visible types with the same metadata name are present - return null; - } + default: + continue; + } - type = currentType; - } + if (type is object) + { + // Multiple visible types with the same metadata name are present + return null; } + + type = currentType; } return type; From e8ca86835ac6eb0fbaf47f384244a37e5aa582e4 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Thu, 16 Dec 2021 11:15:23 -0800 Subject: [PATCH 2/8] Fix spelling Co-authored-by: Youssef Victor --- src/Compilers/Core/Portable/Compilation/Compilation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index fe95c6d0b40f9..4edfedc33c236 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1051,7 +1051,7 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) /// /// Gets all types with the compilation's assembly and all referenced assemblies that have the - /// given canonical CLR metadta name. + /// given canonical CLR metadata name. /// /// Empty array if no types match. Otherwise, all types that match the name, current assembly first if present. public ImmutableArray GetTypesByMetadataName(string fullyQualifiedMetadataName) From 24025f927a3b4938897e8b05691581c7de7dc7dd Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 6 Jan 2022 14:32:25 -0800 Subject: [PATCH 3/8] Add additional documentation for GetTypeByMetadataName, update filtering for GetTypesByMetadataName to match. --- .../Core/Portable/Compilation/Compilation.cs | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 4edfedc33c236..4062186fa1090 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1026,15 +1026,31 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) new ConcurrentCache(50, ReferenceEqualityComparer.Instance); private readonly ConcurrentCache> _getTypesCache = - new ConcurrentCache>(50); + new ConcurrentCache>(50, ReferenceEqualityComparer.Instance); /// /// Gets the type within the compilation's assembly and all referenced assemblies (other than /// those that can only be referenced via an extern alias) using its canonical CLR metadata name. - /// - /// Null if the type can't be found. + /// This lookup follows the following order: + /// + /// + /// + /// If the type is found in the compilation's assembly, that type is returned. + /// Next the core library (the library that defines System.Object is searched. If the type is found there, that type is returned. + /// + /// Finally, all remaining referenced assemblies are searched. If one and only one type matching the provided metadata name is found, that + /// single type is returned. Accessibility is ignored for this check. + /// + /// + /// + /// Null if the type can't be found or there was an ambiguity during lookup. /// /// Since VB does not have the concept of extern aliases, it considers all referenced assemblies. + ///
+ /// Because accessibility to the current assembly is ignored when searching for types that match the provided metadata name, this means if multiple referenced + /// assemblies define the same type symbol (as often happens when users copy well-known types from the BCL or other sources) then this API will return null, + /// even if all but one of those symbols would be otherwise inaccessible to user-written code in the current assembly. If it is not an error for a type to exist + /// in multiple referenced assemblies, consider using instead and filtering the results for the symbol required. ///
public INamedTypeSymbol? GetTypeByMetadataName(string fullyQualifiedMetadataName) { @@ -1051,7 +1067,8 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) /// /// Gets all types with the compilation's assembly and all referenced assemblies that have the - /// given canonical CLR metadata name. + /// given canonical CLR metadata name. Accessibility to the current assembly is ignored when + /// searching for matching type names. /// /// Empty array if no types match. Otherwise, all types that match the name, current assembly first if present. public ImmutableArray GetTypesByMetadataName(string fullyQualifiedMetadataName) @@ -1071,12 +1088,24 @@ ImmutableArray getTypesByMetadataNameImpl() { ArrayBuilder? typesByMetadataName = null; - // Start with the current assembly, then look through all references, to mimic GetTypeByMetadataName search order. + // Start with the current assembly, then corlib, then look through all references, to mimic GetTypeByMetadataName search order. addIfNotNull(Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName)); + var corLib = (IAssemblySymbol)((IAssemblySymbolInternal)Assembly).CorLibrary; + + if (!ReferenceEquals(corLib, Assembly)) + { + addIfNotNull(corLib.GetTypeByMetadataName(fullyQualifiedMetadataName)); + } + foreach (var referencedAssembly in SourceModule.ReferencedAssemblySymbols) { + if (ReferenceEquals(referencedAssembly, corLib)) + { + continue; + } + addIfNotNull(referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName)); } From 905ad80f877591a0e19af177263ac9465806fb03 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 6 Jan 2022 14:34:45 -0800 Subject: [PATCH 4/8] Remove duplicate list tag. --- src/Compilers/Core/Portable/Compilation/Compilation.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 4062186fa1090..f94ef05c9f86b 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1033,8 +1033,6 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) /// those that can only be referenced via an extern alias) using its canonical CLR metadata name. /// This lookup follows the following order: /// - /// - /// /// If the type is found in the compilation's assembly, that type is returned. /// Next the core library (the library that defines System.Object is searched. If the type is found there, that type is returned. /// From 295d86c1b40434a81379f6d3d7fcfebf0a908185 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 6 Jan 2022 14:41:20 -0800 Subject: [PATCH 5/8] Couple more doc clarity updates --- src/Compilers/Core/Portable/Compilation/Compilation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index f94ef05c9f86b..54f24e7f0df7f 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1034,7 +1034,7 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) /// This lookup follows the following order: /// /// If the type is found in the compilation's assembly, that type is returned. - /// Next the core library (the library that defines System.Object is searched. If the type is found there, that type is returned. + /// Next, the core library (the library that defines System.Object) is searched. If the type is found there, that type is returned. /// /// Finally, all remaining referenced assemblies are searched. If one and only one type matching the provided metadata name is found, that /// single type is returned. Accessibility is ignored for this check. @@ -1045,7 +1045,7 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) /// /// Since VB does not have the concept of extern aliases, it considers all referenced assemblies. ///
- /// Because accessibility to the current assembly is ignored when searching for types that match the provided metadata name, this means if multiple referenced + /// Because accessibility to the current assembly is ignored when searching for types that match the provided metadata name, if multiple referenced /// assemblies define the same type symbol (as often happens when users copy well-known types from the BCL or other sources) then this API will return null, /// even if all but one of those symbols would be otherwise inaccessible to user-written code in the current assembly. If it is not an error for a type to exist /// in multiple referenced assemblies, consider using instead and filtering the results for the symbol required. From baa38e26ac3d6ee85afdbaf2b290d1b3e03f5bfa Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 6 Jan 2022 16:59:41 -0800 Subject: [PATCH 6/8] Wording clarifications and test updates. --- .../Symbol/Compilation/CompilationAPITests.cs | 153 +++++++++++++----- .../Core/Portable/Compilation/Compilation.cs | 35 +++- .../Test/Symbol/CompilationAPITests.vb | 96 ++++++++--- 3 files changed, 212 insertions(+), 72 deletions(-) diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs index f6f1ba1d252b1..d0e18bdafc408 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs @@ -3108,7 +3108,8 @@ public void GetTypesByMetadataName_SingleInSourceSingleInReferences( referenceComp.VerifyDiagnostics(); - var comp = CreateCompilation(source, new[] { useMetadataReference ? referenceComp.ToMetadataReference() : referenceComp.EmitToImageReference() }); + MetadataReference reference = useMetadataReference ? referenceComp.ToMetadataReference() : referenceComp.EmitToImageReference(); + var comp = CreateCompilation(source, new[] { reference }); comp.VerifyDiagnostics(); var types = comp.GetTypesByMetadataName("N.C`1"); @@ -3117,14 +3118,9 @@ public void GetTypesByMetadataName_SingleInSourceSingleInReferences( AssertEx.Equal("N.C", types[0].ToTestDisplayString()); Assert.Same(comp.Assembly.GetPublicSymbol(), types[0].ContainingAssembly); AssertEx.Equal("N.C", types[1].ToTestDisplayString()); - if (useMetadataReference) - { - Assert.Same(referenceComp.Assembly.GetPublicSymbol(), types[1].ContainingAssembly); - } - else - { - Assert.False(types[1].IsInSource()); - } + + var assembly1 = comp.GetAssemblyOrModuleSymbol(reference).GetPublicSymbol(); + Assert.Same(types[1].ContainingAssembly, assembly1); } [Theory] @@ -3140,7 +3136,8 @@ public void GetTypesByMetadataName_NotInSourceSingleInReferences( referenceComp.VerifyDiagnostics(); - var comp = CreateCompilation("", new[] { useMetadataReference ? referenceComp.ToMetadataReference() : referenceComp.EmitToImageReference() }); + MetadataReference reference = useMetadataReference ? referenceComp.ToMetadataReference() : referenceComp.EmitToImageReference(); + var comp = CreateCompilation("", new[] { reference }); comp.VerifyDiagnostics(); var types = comp.GetTypesByMetadataName("N.C`1"); @@ -3148,14 +3145,9 @@ public void GetTypesByMetadataName_NotInSourceSingleInReferences( Assert.Single(types); AssertEx.Equal("N.C", types[0].ToTestDisplayString()); - if (useMetadataReference) - { - Assert.Same(referenceComp.Assembly.GetPublicSymbol(), types[0].ContainingAssembly); - } - else - { - Assert.False(types[0].IsInSource()); - } + + var assembly1 = comp.GetAssemblyOrModuleSymbol(reference).GetPublicSymbol(); + Assert.Same(types[0].ContainingAssembly, assembly1); } [Theory] @@ -3173,7 +3165,9 @@ public void GetTypesByMetadataName_NotInSourceMultipleInReferences( var referenceComp2 = CreateCompilation(source); referenceComp2.VerifyDiagnostics(); - var comp = CreateCompilation("", new[] { getReference(referenceComp1), getReference(referenceComp2) }); + MetadataReference reference1 = getReference(referenceComp1); + MetadataReference reference2 = getReference(referenceComp2); + var comp = CreateCompilation("", new[] { reference1, reference2 }); comp.VerifyDiagnostics(); var types = comp.GetTypesByMetadataName("N.C`1"); @@ -3181,17 +3175,12 @@ public void GetTypesByMetadataName_NotInSourceMultipleInReferences( Assert.Equal(2, types.Length); AssertEx.Equal("N.C", types[0].ToTestDisplayString()); AssertEx.Equal("N.C", types[1].ToTestDisplayString()); - if (useMetadataReference) - { - Assert.Same(referenceComp1.Assembly.GetPublicSymbol(), types[0].ContainingAssembly); - Assert.Same(referenceComp2.Assembly.GetPublicSymbol(), types[1].ContainingAssembly); - } - else - { - Assert.False(types[0].IsInSource()); - Assert.False(types[1].IsInSource()); - Assert.NotSame(types[0].ContainingAssembly, types[1].ContainingAssembly); - } + + var assembly1 = comp.GetAssemblyOrModuleSymbol(reference1).GetPublicSymbol(); + Assert.Same(types[0].ContainingAssembly, assembly1); + + var assembly2 = comp.GetAssemblyOrModuleSymbol(reference2).GetPublicSymbol(); + Assert.Same(types[1].ContainingAssembly, assembly2); MetadataReference getReference(CSharpCompilation referenceComp1) { @@ -3214,7 +3203,9 @@ public void GetTypesByMetadataName_SingleInSourceMultipleInReferences( var referenceComp2 = CreateCompilation(source); referenceComp2.VerifyDiagnostics(); - var comp = CreateCompilation(source, new[] { getReference(referenceComp1), getReference(referenceComp2) }); + MetadataReference reference1 = getReference(referenceComp1); + MetadataReference reference2 = getReference(referenceComp2); + var comp = CreateCompilation(source, new[] { reference1, reference2 }); comp.VerifyDiagnostics(); var types = comp.GetTypesByMetadataName("N.C`1"); @@ -3225,17 +3216,11 @@ public void GetTypesByMetadataName_SingleInSourceMultipleInReferences( AssertEx.Equal("N.C", types[1].ToTestDisplayString()); AssertEx.Equal("N.C", types[2].ToTestDisplayString()); - if (useMetadataReference) - { - Assert.Same(referenceComp1.Assembly.GetPublicSymbol(), types[1].ContainingAssembly); - Assert.Same(referenceComp2.Assembly.GetPublicSymbol(), types[2].ContainingAssembly); - } - else - { - Assert.False(types[1].IsInSource()); - Assert.False(types[2].IsInSource()); - Assert.NotSame(types[1].ContainingAssembly, types[2].ContainingAssembly); - } + var assembly1 = comp.GetAssemblyOrModuleSymbol(reference1).GetPublicSymbol(); + Assert.Same(types[1].ContainingAssembly, assembly1); + + var assembly2 = comp.GetAssemblyOrModuleSymbol(reference2).GetPublicSymbol(); + Assert.Same(types[2].ContainingAssembly, assembly2); MetadataReference getReference(CSharpCompilation referenceComp1) { @@ -3243,6 +3228,90 @@ MetadataReference getReference(CSharpCompilation referenceComp1) } } + [Fact] + public void GetTypesByMetadataName_Ordering() + { + var corlibSource = @" +namespace System +{ + public class Object {} + public class Void {} +} + +public class C {} +"; + + var corlib = CreateEmptyCompilation(corlibSource); + var corlibReference = corlib.EmitToImageReference(); + + var other = CreateEmptyCompilation(@"public class C {}", new[] { corlibReference }); + var otherReference = other.EmitToImageReference(); + + var current = CreateEmptyCompilation(@"public class C {}", new[] { otherReference, corlibReference }); + current.VerifyDiagnostics(); + + var types = current.GetTypesByMetadataName("C"); + + AssertEx.Equal(types.Select(t => t.ToTestDisplayString()), new[] { "C", "C", "C" }); + Assert.Same(types[0].ContainingAssembly, current.Assembly.GetPublicSymbol()); + + var corlibAssembly = current.GetAssemblyOrModuleSymbol(corlibReference).GetPublicSymbol(); + Assert.Same(types[1].ContainingAssembly, corlibAssembly); + + var otherAssembly = current.GetAssemblyOrModuleSymbol(otherReference).GetPublicSymbol(); + Assert.Same(types[2].ContainingAssembly, otherAssembly); + } + + [Fact] + public void GetTypeByMetadataName_CorLibViaExtern() + { + var corlibSource = @" +namespace System +{ + public class Object {} + public class Void {} +} + +public class C {} +"; + + var corlib = CreateEmptyCompilation(corlibSource); + var corlibReference = corlib.EmitToImageReference(aliases: ImmutableArray.Create("corlib")); + + var current = CreateEmptyCompilation(@"", new[] { corlibReference }); + current.VerifyDiagnostics(); + + var type = current.GetTypeByMetadataName("C"); + Assert.NotNull(type); + + var corlibAssembly = current.GetAssemblyOrModuleSymbol(corlibReference).GetPublicSymbol(); + Assert.Same(type.ContainingAssembly, corlibAssembly); + } + + [Fact] + public void GetTypeByMetadataName_OtherViaExtern() + { + var corlibSource = @" +namespace System +{ + public class Object {} + public class Void {} +} +"; + + var corlib = CreateEmptyCompilation(corlibSource); + var corlibReference = corlib.EmitToImageReference(); + + var other = CreateEmptyCompilation(@"public class C {}", new[] { corlibReference }); + var otherReference = other.EmitToImageReference(aliases: ImmutableArray.Create("other")); + + var current = CreateEmptyCompilation(@"", new[] { otherReference, corlibReference }); + current.VerifyDiagnostics(); + + var type = current.GetTypeByMetadataName("C"); + Assert.Null(type); + } + #endregion } } diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 54f24e7f0df7f..d48762238a837 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1034,21 +1034,35 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) /// This lookup follows the following order: /// /// If the type is found in the compilation's assembly, that type is returned. - /// Next, the core library (the library that defines System.Object) is searched. If the type is found there, that type is returned. /// - /// Finally, all remaining referenced assemblies are searched. If one and only one type matching the provided metadata name is found, that + /// Next, the core library (the library that defines System.Object and has no assembly references) is searched. + /// If the type is found there, that type is returned. + /// + /// + /// Finally, all remaining referenced non-extern assemblies are searched. If one and only one type matching the provided metadata name is found, that /// single type is returned. Accessibility is ignored for this check. /// /// ///
/// Null if the type can't be found or there was an ambiguity during lookup. /// + /// /// Since VB does not have the concept of extern aliases, it considers all referenced assemblies. - ///
+ ///
+ /// + /// In C#, if the core library is referenced as an extern assembly, it will be searched. All other extern-aliased assemblies will not be searched. + /// + /// /// Because accessibility to the current assembly is ignored when searching for types that match the provided metadata name, if multiple referenced /// assemblies define the same type symbol (as often happens when users copy well-known types from the BCL or other sources) then this API will return null, - /// even if all but one of those symbols would be otherwise inaccessible to user-written code in the current assembly. If it is not an error for a type to exist - /// in multiple referenced assemblies, consider using instead and filtering the results for the symbol required. + /// even if all but one of those symbols would be otherwise inaccessible to user-written code in the current assembly. For fine-grained control over ambiguity + /// resolution, consider using instead and filtering the results for the symbol required. + /// + /// + /// Assemblies can contain multiple modules. Within each assembly, the search is performed based on module's position in the module list of that assembly. When + /// a match is found in one module in an assembly, no further modules within that assembly are searched. + /// + /// Type forwarders are ignored, and not considered part of the assembly where the TypeForwardAttribute is written. ///
public INamedTypeSymbol? GetTypeByMetadataName(string fullyQualifiedMetadataName) { @@ -1069,6 +1083,13 @@ public INamedTypeSymbol CreateNativeIntegerTypeSymbol(bool signed) /// searching for matching type names. ///
/// Empty array if no types match. Otherwise, all types that match the name, current assembly first if present. + /// + /// + /// Assemblies can contain multiple modules. Within each assembly, the search is performed based on module's position in the module list of that assembly. When + /// a match is found in one module in an assembly, no further modules within that assembly are searched. + /// + /// Type forwarders are ignored, and not considered part of the assembly where the TypeForwardAttribute is written. + /// public ImmutableArray GetTypesByMetadataName(string fullyQualifiedMetadataName) { if (!_getTypesCache.TryGetValue(fullyQualifiedMetadataName, out ImmutableArray val)) @@ -1077,7 +1098,7 @@ public ImmutableArray GetTypesByMetadataName(string fullyQuali var result = _getTypesCache.TryAdd(fullyQualifiedMetadataName, val); Debug.Assert(result || (_getTypesCache.TryGetValue(fullyQualifiedMetadataName, out var addedArray) - && addedArray.Zip(val, (added, calculated) => (added, calculated)).All(el => ReferenceEquals(el.added, el.calculated)))); + && Enumerable.SequenceEqual(addedArray, val, ReferenceEqualityComparer.Instance))); } return val; @@ -1090,7 +1111,7 @@ ImmutableArray getTypesByMetadataNameImpl() addIfNotNull(Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName)); - var corLib = (IAssemblySymbol)((IAssemblySymbolInternal)Assembly).CorLibrary; + var corLib = ObjectType.ContainingAssembly; if (!ReferenceEquals(corLib, Assembly)) { diff --git a/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb index 1d8220b41ed34..5864207ef7d73 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb @@ -74,7 +74,8 @@ $"Namespace N End Namespace" Dim referenceComp = CreateCompilation(source) - Dim comp = CreateCompilation(source, {If(useMetadataReference, referenceComp.ToMetadataReference(), referenceComp.EmitToImageReference())}) + Dim reference As MetadataReference = If(useMetadataReference, referenceComp.ToMetadataReference(), referenceComp.EmitToImageReference()) + Dim comp = CreateCompilation(source, {reference}) comp.AssertNoDiagnostics() Dim types = comp.GetTypesByMetadataName("N.C`1") @@ -83,11 +84,9 @@ End Namespace" AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) Assert.Same(comp.Assembly, types(0).ContainingAssembly) AssertEx.Equal("N.C(Of T)", types(1).ToTestDisplayString()) - If (useMetadataReference) Then - Assert.Same(referenceComp.Assembly, types(1).ContainingAssembly) - Else - Assert.False(types(1).IsInSource()) - End If + + Dim referenceAssembly = comp.GetAssemblyOrModuleSymbol(reference) + Assert.Same(types(1).ContainingAssembly, referenceAssembly) End Sub @@ -99,18 +98,17 @@ $"Namespace N End Namespace" Dim referenceComp = CreateCompilation(source) - Dim comp = CreateCompilation("", {GetReference(useMetadataReference, referenceComp)}) + Dim reference As MetadataReference = GetReference(useMetadataReference, referenceComp) + Dim comp = CreateCompilation("", {reference}) comp.AssertNoDiagnostics() Dim types = comp.GetTypesByMetadataName("N.C`1") Assert.Single(types) AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) - If (useMetadataReference) Then - Assert.Same(referenceComp.Assembly, types(0).ContainingAssembly) - Else - Assert.False(types(0).IsInSource()) - End If + + Dim referenceAssembly = comp.GetAssemblyOrModuleSymbol(reference) + Assert.Same(types(0).ContainingAssembly, referenceAssembly) End Sub Private Shared Function GetReference(useMetadataReference As Boolean, referenceComp As VisualBasicCompilation) As MetadataReference @@ -127,7 +125,9 @@ End Namespace" Dim referenceComp1 = CreateCompilation(source) Dim referenceComp2 = CreateCompilation(source) - Dim comp = CreateCompilation("", {GetReference(useMetadataReference, referenceComp1), GetReference(useMetadataReference, referenceComp2)}) + Dim reference1 As MetadataReference = GetReference(useMetadataReference, referenceComp1) + Dim reference2 As MetadataReference = GetReference(useMetadataReference, referenceComp2) + Dim comp = CreateCompilation("", {reference1, reference2}) comp.AssertNoDiagnostics() Dim types = comp.GetTypesByMetadataName("N.C`1") @@ -135,9 +135,14 @@ End Namespace" Assert.Equal(2, types.Length) AssertEx.Equal("N.C(Of T)", types(0).ToTestDisplayString()) AssertEx.Equal("N.C(Of T)", types(1).ToTestDisplayString()) + + Dim referenceAssembly1 = comp.GetAssemblyOrModuleSymbol(reference1) + Assert.Same(types(0).ContainingAssembly, referenceAssembly1) + + Dim referenceAssembly2 = comp.GetAssemblyOrModuleSymbol(reference2) + Assert.Same(types(1).ContainingAssembly, referenceAssembly2) + If (useMetadataReference) Then - Assert.Same(referenceComp1.Assembly, types(0).ContainingAssembly) - Assert.Same(referenceComp2.Assembly, types(1).ContainingAssembly) Else Assert.False(types(0).IsInSource()) Assert.False(types(1).IsInSource()) @@ -154,7 +159,9 @@ End Namespace" Dim referenceComp1 = CreateCompilation(source) Dim referenceComp2 = CreateCompilation(source) - Dim comp = CreateCompilation(source, {GetReference(useMetadataReference, referenceComp1), GetReference(useMetadataReference, referenceComp2)}) + Dim reference1 As MetadataReference = GetReference(useMetadataReference, referenceComp1) + Dim reference2 As MetadataReference = GetReference(useMetadataReference, referenceComp2) + Dim comp = CreateCompilation(source, {reference1, reference2}) comp.AssertNoDiagnostics() Dim types = comp.GetTypesByMetadataName("N.C`1") @@ -164,13 +171,56 @@ End Namespace" Assert.Same(comp.Assembly, types(0).ContainingAssembly) AssertEx.Equal("N.C(Of T)", types(1).ToTestDisplayString()) AssertEx.Equal("N.C(Of T)", types(2).ToTestDisplayString()) - If (useMetadataReference) Then - Assert.Same(referenceComp1.Assembly, types(1).ContainingAssembly) - Assert.Same(referenceComp2.Assembly, types(2).ContainingAssembly) - Else - Assert.False(types(1).IsInSource()) - Assert.False(types(2).IsInSource()) - End If + + Dim referenceAssembly1 = comp.GetAssemblyOrModuleSymbol(reference1) + Assert.Same(types(1).ContainingAssembly, referenceAssembly1) + + Dim referenceAssembly2 = comp.GetAssemblyOrModuleSymbol(reference2) + Assert.Same(types(2).ContainingAssembly, referenceAssembly2) + End Sub + + + Public Sub GetTypesByMetadataName_Ordering() + Dim corlibSource = " +Namespace System + Public Class [Object] + End Class + Public Class [Void] + End Class +End Namespace +Public Class C +End Class +" + + Dim corlib = CreateEmptyCompilation(corlibSource) + Dim corlibReference = corlib.EmitToImageReference() + + Dim otherSource = " +Public Class C +End Class +" + + Dim other = CreateEmptyCompilation(otherSource, {corlibReference}) + Dim otherReference = other.EmitToImageReference() + + Dim currentSource = " +Public Class C +End Class +" + Dim current = CreateEmptyCompilation(currentSource, {otherReference, corlibReference}) + current.AssertNoDiagnostics() + + Dim types = current.GetTypesByMetadataName("C") + + AssertEx.Equal(types.Select(Function(t) t.ToTestDisplayString()), {"C", "C", "C"}) + + Assert.Same(current.Assembly, types(0).ContainingAssembly) + + Dim corlibAssembly = current.GetAssemblyOrModuleSymbol(corlibReference) + Assert.Same(types(1).ContainingAssembly, corlibAssembly) + + Dim otherAssembly = current.GetAssemblyOrModuleSymbol(otherReference) + Assert.Same(types(2).ContainingAssembly, otherAssembly) End Sub End Class End Namespace From 8d79a2b4d07d3a9e2206377229ce59b19fc9ef4e Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 6 Jan 2022 18:14:10 -0800 Subject: [PATCH 7/8] Call the correct GetTypeByMetadataName --- .../CSharp/Test/Symbol/Compilation/CompilationAPITests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs index d0e18bdafc408..6e608104926e4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs @@ -3281,7 +3281,7 @@ public class C {} var current = CreateEmptyCompilation(@"", new[] { corlibReference }); current.VerifyDiagnostics(); - var type = current.GetTypeByMetadataName("C"); + var type = ((Compilation)current).GetTypeByMetadataName("C"); Assert.NotNull(type); var corlibAssembly = current.GetAssemblyOrModuleSymbol(corlibReference).GetPublicSymbol(); @@ -3308,7 +3308,7 @@ public class Void {} var current = CreateEmptyCompilation(@"", new[] { otherReference, corlibReference }); current.VerifyDiagnostics(); - var type = current.GetTypeByMetadataName("C"); + var type = ((Compilation)current).GetTypeByMetadataName("C"); Assert.Null(type); } From 5aba7638d90a29dc525797a4482d8ba7088fdfbe Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 6 Jan 2022 18:18:08 -0800 Subject: [PATCH 8/8] Fix spelling --- .../VisualBasic/Test/Symbol/CompilationAPITests.vb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb index 5864207ef7d73..b0f3ed506cc9f 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/CompilationAPITests.vb @@ -11,7 +11,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Inherits BasicTestBase - Public Sub GetTypesByMetadtaName_NotInSourceNotInReferences() + Public Sub GetTypesByMetadataName_NotInSourceNotInReferences() Dim comp = CreateCompilation("") comp.AssertNoDiagnostics() @@ -20,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests End Sub - Public Sub GetTypesByMetadtaName_SingleInSourceNotInReferences(useMetadataReferences As Boolean, accessibility As String) + Public Sub GetTypesByMetadataName_SingleInSourceNotInReferences(useMetadataReferences As Boolean, accessibility As String) Dim referenceComp = CreateCompilation("") Dim source = @@ -39,7 +39,7 @@ End Namespace" End Sub - Public Sub GetTypesByMetadtaName_MultipleInSourceNotInReferences(useMetadataReferences As Boolean, accessibility As String) + Public Sub GetTypesByMetadataName_MultipleInSourceNotInReferences(useMetadataReferences As Boolean, accessibility As String) Dim referenceComp = CreateCompilation("") Dim source = @@ -66,7 +66,7 @@ BC30179: class 'C' and class 'C' conflict in namespace 'N'. End Sub - Public Sub GetTypesByMetadtaName_SingleInSourceSingleInReferences(useMetadataReference As Boolean, accessibility As String) + Public Sub GetTypesByMetadataName_SingleInSourceSingleInReferences(useMetadataReference As Boolean, accessibility As String) Dim source = $"Namespace N {accessibility} Class C(Of T) @@ -90,7 +90,7 @@ End Namespace" End Sub - Public Sub GetTypesByMetadtaName_NotInSourceSingleInReferences(useMetadataReference As Boolean, accessibility As String) + Public Sub GetTypesByMetadataName_NotInSourceSingleInReferences(useMetadataReference As Boolean, accessibility As String) Dim source = $"Namespace N {accessibility} Class C(Of T) @@ -116,7 +116,7 @@ End Namespace" End Function - Public Sub GetTypesByMetadtaName_NotInSourceMultipleInReferences(useMetadataReference As Boolean, accessibility As String) + Public Sub GetTypesByMetadataName_NotInSourceMultipleInReferences(useMetadataReference As Boolean, accessibility As String) Dim source = $"Namespace N {accessibility} Class C(Of T) @@ -150,7 +150,7 @@ End Namespace" End Sub - Public Sub GetTypesByMetadtaName_SingleInSourceMultipleInReferences(useMetadataReference As Boolean, accessibility As String) + Public Sub GetTypesByMetadataName_SingleInSourceMultipleInReferences(useMetadataReference As Boolean, accessibility As String) Dim source = $"Namespace N {accessibility} Class C(Of T)