提交 94ad7286 编写于 作者: O Omar Tawfik

Fixed crashes when there are multiple type forwarders for the same type

上级 655fe90c
......@@ -1895,7 +1895,7 @@ private CSDiagnosticInfo NotFound(SyntaxNode where, string simpleName, int arity
// as a type forwarder. We'll look for type forwarders in the containing and
// referenced assemblies and report more specific diagnostics if they are found.
AssemblySymbol forwardedToAssembly;
bool encounteredForwardingCycle;
DiagnosticInfo encounteredForwardingError;
string fullName;
// for attributes, suggest both, but not for verbatim name
......@@ -1929,12 +1929,19 @@ private CSDiagnosticInfo NotFound(SyntaxNode where, string simpleName, int arity
fullName = qualifierOpt.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat) + "." + fullName;
}
forwardedToAssembly = GetForwardedToAssembly(fullName, arity, out encounteredForwardingCycle);
forwardedToAssembly = GetForwardedToAssembly(fullName, arity, out encounteredForwardingError);
if (encounteredForwardingCycle)
if (encounteredForwardingError != null)
{
Debug.Assert((object)forwardedToAssembly != null, "How did we find a cycle if there was no forwarding?");
diagnostics.Add(ErrorCode.ERR_CycleInTypeForwarder, location, fullName, forwardedToAssembly.Name);
if (encounteredForwardingError.Code == (int)ErrorCode.ERR_CycleInTypeForwarder)
{
Debug.Assert((object)forwardedToAssembly != null, "How did we find a cycle if there was no forwarding?");
diagnostics.Add(ErrorCode.ERR_CycleInTypeForwarder, location, fullName, forwardedToAssembly.Name);
}
else if (encounteredForwardingError.Code == (int)ErrorCode.ERR_TypeForwardedToMultipleAssemblies)
{
return diagnostics.Add(ErrorCode.ERR_TypeForwardedToMultipleAssemblies, location, encounteredForwardingError.Arguments);
}
}
if (qualifierIsCompilationGlobalNamespace)
......@@ -1974,12 +1981,19 @@ private CSDiagnosticInfo NotFound(SyntaxNode where, string simpleName, int arity
}
fullName = MetadataHelpers.ComposeAritySuffixedMetadataName(simpleName, arity);
forwardedToAssembly = GetForwardedToAssembly(fullName, arity, out encounteredForwardingCycle);
forwardedToAssembly = GetForwardedToAssembly(fullName, arity, out encounteredForwardingError);
if (encounteredForwardingCycle)
if (encounteredForwardingError != null)
{
Debug.Assert((object)forwardedToAssembly != null, "How did we find a cycle if there was no forwarding?");
diagnostics.Add(ErrorCode.ERR_CycleInTypeForwarder, location, fullName, forwardedToAssembly.Name);
if (encounteredForwardingError.Code == (int)ErrorCode.ERR_CycleInTypeForwarder)
{
Debug.Assert((object)forwardedToAssembly != null, "How did we find a cycle if there was no forwarding?");
diagnostics.Add(ErrorCode.ERR_CycleInTypeForwarder, location, fullName, forwardedToAssembly.Name);
}
else if (encounteredForwardingError.Code == (int)ErrorCode.ERR_TypeForwardedToMultipleAssemblies)
{
return diagnostics.Add(ErrorCode.ERR_TypeForwardedToMultipleAssemblies, location, encounteredForwardingError.Arguments);
}
}
return (object)forwardedToAssembly == null
......@@ -1993,17 +2007,17 @@ private CSDiagnosticInfo NotFound(SyntaxNode where, string simpleName, int arity
/// </summary>
/// <param name="fullName">The metadata name of the (potentially) forwarded type, including the arity (if non-zero).</param>
/// <param name="arity">The arity of the forwarded type.</param>
/// <param name="encounteredCycle">Set to true if a cycle was found in the type forwarders.</param>
/// <param name="encounteredForwardingError">Will not be null if an error was found during look up.</param>
/// <returns></returns>
/// <remarks>
/// Since this method is intended to be used for error reporting, it stops as soon as it finds
/// any type forwarder - it does not check other assemblies for consistency or better results.
/// any type forwarder (or an error to report). It does not check other assemblies for consistency or better results.
/// </remarks>
private AssemblySymbol GetForwardedToAssembly(string fullName, int arity, out bool encounteredCycle)
private AssemblySymbol GetForwardedToAssembly(string fullName, int arity, out DiagnosticInfo encounteredForwardingError)
{
Debug.Assert(arity == 0 || fullName.EndsWith("`" + arity, StringComparison.Ordinal));
encounteredCycle = false;
encounteredForwardingError = null;
// If we are in the process of binding assembly level attributes, we might get into an infinite cycle
// if any of the referenced assemblies forwards type to this assembly. Since forwarded types
......@@ -2054,11 +2068,7 @@ private AssemblySymbol GetForwardedToAssembly(string fullName, int arity, out bo
{
if (forwardedType.Kind == SymbolKind.ErrorType)
{
DiagnosticInfo diagInfo = ((ErrorTypeSymbol)forwardedType).ErrorInfo;
if (diagInfo.Code == (int)ErrorCode.ERR_CycleInTypeForwarder)
{
encounteredCycle = true;
}
encounteredForwardingError = ((ErrorTypeSymbol)forwardedType).ErrorInfo;
}
return forwardedType.ContainingAssembly;
......
......@@ -722,6 +722,7 @@
<Compile Include="Symbols\TypeUnification.cs" />
<Compile Include="Symbols\TypeWithModifiers.cs" />
<Compile Include="Symbols\UnboundGenericType.cs" />
<Compile Include="Symbols\MultipleForwardedTypeSymbol.cs" />
<Compile Include="Symbols\UnsupportedMetadataTypeSymbol.cs" />
<Compile Include="Symbols\VarianceSafety.cs" />
<Compile Include="Symbols\Wrapped\WrappedParameterSymbol.cs" />
......
......@@ -8980,6 +8980,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Type &apos;{0}&apos; is forwarded to multiple assemblies: &apos;{1}&apos; and &apos;{2}&apos;.
/// </summary>
internal static string ERR_TypeForwardedToMultipleAssemblies {
get {
return ResourceManager.GetString("ERR_TypeForwardedToMultipleAssemblies", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot infer the type of implicitly-typed deconstruction variable &apos;{0}&apos;..
/// </summary>
......
......@@ -5002,4 +5002,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_BadAssemblyName" xml:space="preserve">
<value>Invalid assembly name: {0}</value>
</data>
<data name="ERR_TypeForwardedToMultipleAssemblies" xml:space="preserve">
<value>Type '{0}' is forwarded to multiple assemblies: '{1}' and '{2}'</value>
</data>
</root>
\ No newline at end of file
......@@ -740,7 +740,7 @@ internal enum ErrorCode
WRN_BadXMLRef = 1574,
ERR_BadStackAllocExpr = 1575,
ERR_InvalidLineNumber = 1576,
//ERR_ALinkFailed = 1577, No alink usage in Roslyn
ERR_TypeForwardedToMultipleAssemblies = 1577,
ERR_MissingPPFile = 1578,
ERR_ForEachMissingMember = 1579,
WRN_BadXMLRefParamType = 1580,
......
......@@ -128,32 +128,40 @@ public override ImmutableArray<CSharpAttributeData> GetAttributes()
}
return _lazyCustomAttributes;
}
/// <summary>
/// Look up the assembly to which the given metadata type is forwarded.
/// </summary>
/// <param name="emittedName"></param>
/// <returns>
/// The assembly to which the given type is forwarded or null, if there isn't one.
/// </returns>
/// <remarks>
/// The returned assembly may also forward the type.
/// </remarks>
internal AssemblySymbol LookupAssemblyForForwardedMetadataType(ref MetadataTypeName emittedName)
{
// Look in the type forwarders of the primary module of this assembly, clr does not honor type forwarder
// in non-primary modules.
// Examine the type forwarders, but only from the primary module.
return this.PrimaryModule.GetAssemblyForForwardedType(ref emittedName);
/// <summary>
/// Look up the assemblies to which the given metadata type is forwarded.
/// </summary>
/// <param name="emittedName"></param>
/// <returns>
/// The assemblies to which the given type is forwarded.
/// </returns>
/// <remarks>
/// The returned assemblies may also forward the type.
/// </remarks>
internal ImmutableArray<AssemblySymbol> LookupAssembliesForForwardedMetadataType(ref MetadataTypeName emittedName)
{
// Look in the type forwarders of the primary module of this assembly, clr does not honor type forwarder
// in non-primary modules.
// Examine the type forwarders, but only from the primary module.
return this.PrimaryModule.GetAssembliesForForwardedType(ref emittedName);
}
internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList<AssemblySymbol> visitedAssemblies)
internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList<AssemblySymbol> visitedAssemblies)
{
// Check if it is a forwarded type.
var forwardedToAssembly = LookupAssemblyForForwardedMetadataType(ref emittedName);
if ((object)forwardedToAssembly != null)
var forwardedToAssemblies = LookupAssembliesForForwardedMetadataType(ref emittedName);
if (!forwardedToAssemblies.IsDefaultOrEmpty)
{
if (forwardedToAssemblies.Length > 1)
{
return new MultipleForwardedTypeSymbol(emittedName, forwardedToAssemblies[0], forwardedToAssemblies[1]);
}
AssemblySymbol forwardedToAssembly = forwardedToAssemblies.First();
// Don't bother to check the forwarded-to assembly if we've already seen it.
if (visitedAssemblies != null && visitedAssemblies.Contains(forwardedToAssembly))
{
......
......@@ -13,6 +13,7 @@
using Roslyn.Utilities;
using System.Reflection.PortableExecutable;
using System.Reflection;
using System.Linq;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
......@@ -641,32 +642,48 @@ internal NamedTypeSymbol LookupTopLevelMetadataType(ref MetadataTypeName emitted
}
/// <summary>
/// If this module forwards the given type to another assembly, return that assembly;
/// otherwise, return null.
/// Returns a list of the assemblies this module forwards the given type to.
/// </summary>
/// <param name="fullName">Type to look up.</param>
/// <returns>Assembly symbol or null.</returns>
/// <returns>An <see cref="ImmutableArray"/> of the forwarded to assemblies.</returns>
/// <remarks>
/// The returned assembly may also forward the type.
/// The returned assemblies may also forward the type.
/// </remarks>
internal AssemblySymbol GetAssemblyForForwardedType(ref MetadataTypeName fullName)
internal ImmutableArray<AssemblySymbol> GetAssembliesForForwardedType(ref MetadataTypeName fullName)
{
string matchedName;
AssemblyReferenceHandle assemblyRef = Module.GetAssemblyForForwardedType(fullName.FullName, ignoreCase: false, matchedName: out matchedName);
return assemblyRef.IsNil ? null : this.GetReferencedAssemblySymbol(assemblyRef);
HashSet<AssemblyReferenceHandle> assemblyRefs = Module.GetAssemblyRefsForForwardedType(fullName.FullName, ignoreCase: false, matchedName: out matchedName);
var assemblies = ArrayBuilder<AssemblySymbol>.GetInstance();
if (assemblyRefs != null)
{
foreach (var reference in assemblyRefs)
{
var assembly = this.GetReferencedAssemblySymbol(reference);
if (assembly != null)
{
assemblies.Add(assembly);
}
}
}
return assemblies.ToImmutableAndFree();
}
internal IEnumerable<NamedTypeSymbol> GetForwardedTypes()
{
foreach (KeyValuePair<string, AssemblyReferenceHandle> forwarder in Module.GetForwardedTypes())
foreach (KeyValuePair<string, HashSet<AssemblyReferenceHandle>> forwarder in Module.GetForwardedTypes())
{
var assemblySymbol = this.GetReferencedAssemblySymbol(forwarder.Value);
if ((object)assemblySymbol == null)
foreach (AssemblyReferenceHandle assemblyRef in forwarder.Value)
{
continue;
var assemblySymbol = this.GetReferencedAssemblySymbol(assemblyRef);
if ((object)assemblySymbol == null)
{
continue;
}
var name = MetadataTypeName.FromFullName(forwarder.Key);
yield return assemblySymbol.LookupTopLevelMetadataType(ref name, digThroughForwardedTypes: true);
}
var name = MetadataTypeName.FromFullName(forwarder.Key);
yield return assemblySymbol.LookupTopLevelMetadataType(ref name, digThroughForwardedTypes: true);
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class MultipleForwardedTypeSymbol : ErrorTypeSymbol
{
private readonly MetadataTypeName _metadataTypeName;
private readonly AssemblySymbol _assembly1;
private readonly AssemblySymbol _assembly2;
internal MultipleForwardedTypeSymbol(MetadataTypeName metadataTypeName, AssemblySymbol assembly1, AssemblySymbol assembly2)
{
this._metadataTypeName = metadataTypeName;
this._assembly1 = assembly1;
this._assembly2 = assembly2;
}
internal override DiagnosticInfo ErrorInfo
{
get
{
return new CSDiagnosticInfo(ErrorCode.ERR_TypeForwardedToMultipleAssemblies, this._metadataTypeName.FullName, this._assembly1.Name, this._assembly2.Name);
}
}
internal override bool MangleName
{
get
{
return false;
}
}
}
}
......@@ -2583,9 +2583,17 @@ internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetecti
{
var peModuleSymbol = (Metadata.PE.PEModuleSymbol)_modules[i];
var forwardedToAssembly = peModuleSymbol.GetAssemblyForForwardedType(ref emittedName);
if ((object)forwardedToAssembly != null)
var forwardedToAssemblies = peModuleSymbol.GetAssembliesForForwardedType(ref emittedName);
if (!forwardedToAssemblies.IsDefaultOrEmpty)
{
if (forwardedToAssemblies.Length > 1)
{
return new MultipleForwardedTypeSymbol(emittedName, forwardedToAssemblies[0], forwardedToAssemblies[1]);
}
AssemblySymbol forwardedToAssembly = forwardedToAssemblies.First();
// Don't bother to check the forwarded-to assembly if we've already seen it.
if (visitedAssemblies != null && visitedAssemblies.Contains(forwardedToAssembly))
{
......
......@@ -19412,6 +19412,142 @@ public void AbstractInSubmission()
// internal abstract event System.EventHandler E;
Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, "E").WithArguments("E", "Script").WithLocation(3, 45));
}
[Fact, WorkItem(16484, "https://github.com/dotnet/roslyn/issues/16484")]
public void MultipleForwardsToTheSameAssemblyShouldIgnoreDuplicate()
{
var forwardedToCode = @"
namespace Destination
{
public class TestClass
{
public const string Value = ""TEST VALUE"";
}
}";
var forwardedToReference = CreateCompilationWithMscorlib(forwardedToCode, assemblyName: "Destination").EmitToImageReference();
var forwardingCode = @"
namespace ForwardingNamespace
{
public class Program
{
public static void Main()
{
System.Console.WriteLine(Destination.TestClass.Value);
}
}
}";
var forwardingIL = @"
.assembly extern Destination { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination
}
.class extern forwarder Destination.TestClass
{
.assembly extern Destination
}";
var forwardingCompilation = CreateCompilationWithCustomILSource(
source: forwardingCode,
ilSource: forwardingIL,
references: new MetadataReference[] { forwardedToReference },
options: new CSharpCompilationOptions(OutputKind.ConsoleApplication));
CompileAndVerify(forwardingCompilation, expectedOutput: "TEST VALUE");
}
[Fact, WorkItem(16484, "https://github.com/dotnet/roslyn/issues/16484")]
public void MultipleForwardsToDifferentAssembliesWithoutReferencingThemShouldIgnoreIt()
{
var forwardingIL = @"
.assembly extern Destination1 { }
.assembly extern Destination2 { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination1
}
.class extern forwarder Destination.TestClass
{
.assembly extern Destination2
}";
var forwardingCompilation = CreateCompilationWithCustomILSource(string.Empty, forwardingIL);
forwardingCompilation.VerifyDiagnostics();
}
[Fact, WorkItem(16484, "https://github.com/dotnet/roslyn/issues/16484")]
public void MultipleForwardsToDifferentAssembliesWhileReferencingThemShouldErrorOut()
{
var forwardingCode = @"
namespace ForwardingNamespace
{
public class Program
{
public static void Main()
{
new Destination.TestClass();
}
}
}";
var forwardingIL = @"
.assembly extern Destination1 { }
.assembly extern Destination2 { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination1
}
.class extern forwarder Destination.TestClass
{
.assembly extern Destination2
}";
var forwardingCompilation = CreateCompilationWithCustomILSource(forwardingCode, forwardingIL);
forwardingCompilation.VerifyDiagnostics(
// (8,29): error CS1577: Type 'Destination.TestClass' is forwarded to multiple assemblies: 'Destination1' and 'Destination2'
// new Destination.TestClass();
Diagnostic(ErrorCode.ERR_TypeForwardedToMultipleAssemblies, "TestClass").WithArguments("Destination.TestClass", "Destination1", "Destination2").WithLocation(8, 29));
}
[Fact, WorkItem(16484, "https://github.com/dotnet/roslyn/issues/16484")]
public void MultipleForwardsThatResultInTheSameAssemblyDoNotErrorOut()
{
var forwardedToCode1 = @"
namespace Destination
{
public class TestClass {}
}";
var forwardedToReference1 = CreateCompilationWithMscorlib(forwardedToCode1, assemblyName: "Destination1").EmitToImageReference();
var forwardedToCode2 = @"[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Destination.TestClass))]";
var forwardedToCompilation2 = CreateCompilationWithMscorlib(forwardedToCode2, new MetadataReference[] { forwardedToReference1 }, assemblyName: "Destination2");
var forwardedToReference2 = forwardedToCompilation2.EmitToImageReference();
var forwardingCode = @"
namespace ForwardingNamespace
{
public class Program
{
public static void Main()
{
new Destination.TestClass();
}
}
}";
var forwardingIL = @"
.assembly extern Destination1 { }
.assembly extern Destination2 { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination1
}
.class extern forwarder Destination.TestClass
{
.assembly extern Destination2
}";
var forwardingCompilation = CreateCompilationWithCustomILSource(forwardingCode, forwardingIL, new MetadataReference[] { forwardedToReference1, forwardedToReference2 });
forwardingCompilation.VerifyDiagnostics();
}
}
}
......@@ -38,7 +38,7 @@ internal sealed class PEModule : IDisposable
private MetadataReader _lazyMetadataReader;
private ImmutableArray<AssemblyIdentity> _lazyAssemblyReferences;
private Dictionary<string, AssemblyReferenceHandle> _lazyForwardedTypesToAssemblyMap;
private Dictionary<string, HashSet<AssemblyReferenceHandle>> _lazyForwardedTypesToAssemblyMap;
private readonly Lazy<IdentifierCollection> _lazyTypeNameCollection;
private readonly Lazy<IdentifierCollection> _lazyNamespaceNameCollection;
......@@ -2822,7 +2822,7 @@ private ConstantValue GetConstantValueOrThrow(ConstantHandle handle)
return ConstantValue.Bad;
}
internal AssemblyReferenceHandle GetAssemblyForForwardedType(string fullName, bool ignoreCase, out string matchedName)
internal HashSet<AssemblyReferenceHandle> GetAssemblyRefsForForwardedType(string fullName, bool ignoreCase, out string matchedName)
{
EnsureForwardTypeToAssemblyMap();
......@@ -2843,19 +2843,19 @@ internal AssemblyReferenceHandle GetAssemblyForForwardedType(string fullName, bo
}
else
{
AssemblyReferenceHandle assemblyRef;
if (_lazyForwardedTypesToAssemblyMap.TryGetValue(fullName, out assemblyRef))
HashSet<AssemblyReferenceHandle> assemblyRefs;
if (_lazyForwardedTypesToAssemblyMap.TryGetValue(fullName, out assemblyRefs))
{
matchedName = fullName;
return assemblyRef;
return assemblyRefs;
}
}
matchedName = null;
return default(AssemblyReferenceHandle);
return default(HashSet<AssemblyReferenceHandle>);
}
internal IEnumerable<KeyValuePair<string, AssemblyReferenceHandle>> GetForwardedTypes()
internal IEnumerable<KeyValuePair<string, HashSet<AssemblyReferenceHandle>>> GetForwardedTypes()
{
EnsureForwardTypeToAssemblyMap();
return _lazyForwardedTypesToAssemblyMap;
......@@ -2865,7 +2865,7 @@ private void EnsureForwardTypeToAssemblyMap()
{
if (_lazyForwardedTypesToAssemblyMap == null)
{
var typesToAssemblyMap = new Dictionary<string, AssemblyReferenceHandle>();
var typesToAssemblyMap = new Dictionary<string, HashSet<AssemblyReferenceHandle>>();
try
{
......@@ -2873,23 +2873,41 @@ private void EnsureForwardTypeToAssemblyMap()
foreach (var handle in forwarders)
{
ExportedType exportedType = MetadataReader.GetExportedType(handle);
if (!exportedType.IsForwarder)
{
continue;
}
AssemblyReferenceHandle newReference = (AssemblyReferenceHandle)exportedType.Implementation;
if (newReference.IsNil)
{
continue;
}
string name = MetadataReader.GetString(exportedType.Name);
StringHandle ns = exportedType.Namespace;
if (!ns.IsNil)
if (!exportedType.Namespace.IsNil)
{
string namespaceString = MetadataReader.GetString(ns);
string namespaceString = MetadataReader.GetString(exportedType.Namespace);
if (namespaceString.Length > 0)
{
name = namespaceString + "." + name;
}
}
typesToAssemblyMap.Add(name, (AssemblyReferenceHandle)exportedType.Implementation);
HashSet<AssemblyReferenceHandle> references;
if (typesToAssemblyMap.TryGetValue(name, out references))
{
references.Add(newReference);
}
else
{
references = new HashSet<AssemblyReferenceHandle>();
references.Add(newReference);
typesToAssemblyMap.Add(name, references);
}
}
}
catch (BadImageFormatException)
......
......@@ -137,8 +137,9 @@ Friend Module CompilationUtils
Public Function CreateCompilationWithMscorlibAndReferences(sources As XElement,
references As IEnumerable(Of MetadataReference),
Optional options As VisualBasicCompilationOptions = Nothing,
Optional parseOptions As VisualBasicParseOptions = Nothing) As VisualBasicCompilation
Return CreateCompilationWithReferences(sources, {MscorlibRef}.Concat(references), options, parseOptions:=parseOptions)
Optional parseOptions As VisualBasicParseOptions = Nothing,
Optional assemblyName As String = Nothing) As VisualBasicCompilation
Return CreateCompilationWithReferences(sources, {MscorlibRef}.Concat(references), options, parseOptions:=parseOptions, assemblyName:=assemblyName)
End Function
''' <summary>
......@@ -244,8 +245,8 @@ Friend Module CompilationUtils
Public Function CreateCompilationWithReferences(sources As XElement,
references As IEnumerable(Of MetadataReference),
Optional options As VisualBasicCompilationOptions = Nothing,
Optional parseOptions As VisualBasicParseOptions = Nothing) As VisualBasicCompilation
Dim assemblyName As String = Nothing
Optional parseOptions As VisualBasicParseOptions = Nothing,
Optional assemblyName As String = Nothing) As VisualBasicCompilation
Dim sourceTrees = ParseSouceXml(sources, parseOptions, assemblyName)
Return CreateCompilationWithReferences(sourceTrees, references, options, assemblyName)
End Function
......@@ -385,7 +386,8 @@ Friend Module CompilationUtils
Optional parseOptions As VisualBasicParseOptions = Nothing,
Optional additionalReferences As IEnumerable(Of MetadataReference) = Nothing,
<Out> Optional ByRef ilReference As MetadataReference = Nothing,
<Out> Optional ByRef ilImage As ImmutableArray(Of Byte) = Nothing
<Out> Optional ByRef ilImage As ImmutableArray(Of Byte) = Nothing,
Optional assemblyName As String = Nothing
) As VisualBasicCompilation
Dim references = If(additionalReferences IsNot Nothing, New List(Of MetadataReference)(additionalReferences), New List(Of MetadataReference))
If includeVbRuntime Then
......@@ -396,13 +398,13 @@ Friend Module CompilationUtils
End If
If ilSource Is Nothing Then
Return CreateCompilationWithMscorlibAndReferences(sources, references, options, parseOptions)
Return CreateCompilationWithMscorlibAndReferences(sources, references, options, parseOptions, assemblyName)
End If
ilReference = CreateReferenceFromIlCode(ilSource, appendDefaultHeader, ilImage)
references.Add(ilReference)
Return CreateCompilationWithMscorlibAndReferences(sources, references, options, parseOptions)
Return CreateCompilationWithMscorlibAndReferences(sources, references, options, parseOptions, assemblyName)
End Function
Public Function CreateReferenceFromIlCode(ilSource As String, Optional appendDefaultHeader As Boolean = True, <Out> Optional ByRef ilImage As ImmutableArray(Of Byte) = Nothing) As MetadataReference
......
......@@ -668,6 +668,7 @@
<Compile Include="Symbols\MissingModuleSymbol.vb" />
<Compile Include="Symbols\MissingNamespaceSymbol.vb" />
<Compile Include="Symbols\ModuleSymbol.vb" />
<Compile Include="Symbols\MultipleForwardedTypeSymbol.vb" />
<Compile Include="Symbols\NamedTypeSymbol.vb" />
<Compile Include="Symbols\NamedTypeSymbolExtensions.vb" />
<Compile Include="Symbols\NamespaceExtent.vb" />
......
......@@ -402,6 +402,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Private Shared Function NotFound(typeSyntax As TypeSyntax, diagName As String, binder As Binder, diagBag As DiagnosticBag, reportedAnError As Boolean) As DiagnosticInfo
Dim diagInfo As DiagnosticInfo
If diagName = "Any" AndAlso IsParameterTypeOfDeclareMethod(typeSyntax) Then
diagInfo = ErrorFactory.ErrorInfo(ERRID.ERR_ObsoleteAsAny, diagName)
......@@ -413,15 +414,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End If
Dim forwardedToAssembly As AssemblySymbol = Nothing
Dim encounteredForwardingCycle As Boolean = False
Dim encounteredForwardingError As DiagnosticInfo = Nothing
If diagName.Length > 0 Then
CheckForForwardedType(binder.Compilation.Assembly, typeSyntax, diagName, forwardedToAssembly, encounteredForwardingCycle)
CheckForForwardedType(binder.Compilation.Assembly, typeSyntax, diagName, forwardedToAssembly, encounteredForwardingError)
Else
Debug.Assert(typeSyntax.IsMissing OrElse typeSyntax.HasErrors)
Return Nothing
End If
If Not reportedAnError Then
If Not encounteredForwardingError Is Nothing Then
If encounteredForwardingError.Code = ERRID.ERR_TypeFwdCycle2 Then
Debug.Assert(forwardedToAssembly IsNot Nothing, "How did we find a cycle if there is no forwarding?")
Binder.ReportDiagnostic(diagBag, typeSyntax, ERRID.ERR_TypeFwdCycle2, diagName, forwardedToAssembly)
ElseIf encounteredForwardingError.Code = ERRID.ERR_TypeForwardedToMultipleAssemblies Then
Binder.ReportDiagnostic(diagBag, typeSyntax, ERRID.ERR_TypeForwardedToMultipleAssemblies, encounteredForwardingError.Arguments)
Return encounteredForwardingError
End If
End If
End If
If forwardedToAssembly Is Nothing Then
diagInfo = ErrorFactory.ErrorInfo(ERRID.ERR_UndefinedType1, diagName)
Else
......@@ -429,12 +443,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End If
If Not reportedAnError Then
Binder.ReportDiagnostic(diagBag, typeSyntax, diagInfo)
If encounteredForwardingCycle Then
Debug.Assert(forwardedToAssembly IsNot Nothing, "How did we find a cycle if there is no forwarding?")
Binder.ReportDiagnostic(diagBag, typeSyntax, ERRID.ERR_TypeFwdCycle2, diagName, forwardedToAssembly)
End If
binder.ReportDiagnostic(diagBag, typeSyntax, diagInfo)
End If
Return diagInfo
......@@ -448,8 +457,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
''' <param name="typeSyntax">Full name of type that failed lookup. Shortened as different prefixes are checked.</param>
''' <param name="diagName">GetBaseNamesForDiagnostic(typeSyntax) (basically dot-delimited list of names). Shortened as different prefixes are checked.</param>
''' <param name="forwardedToAssembly">Set if some prefix matches a forwarded type.</param>
''' <param name="encounteredForwardingCycle">True if forwardedToAssembly is non-null and the type indicated by typeSyntax/diagName is in a forwarder cycle.</param>
Private Shared Sub CheckForForwardedType(containingAssembly As AssemblySymbol, ByRef typeSyntax As TypeSyntax, ByRef diagName As String, ByRef forwardedToAssembly As AssemblySymbol, ByRef encounteredForwardingCycle As Boolean)
''' <param name="encounteredForwardingError">Will not be null if an error was found during look up.</param>
Private Shared Sub CheckForForwardedType(containingAssembly As AssemblySymbol, ByRef typeSyntax As TypeSyntax, ByRef diagName As String, ByRef forwardedToAssembly As AssemblySymbol, ByRef encounteredForwardingError As DiagnosticInfo)
Dim currTypeSyntax As TypeSyntax = typeSyntax
Dim currDiagName As String = diagName
......@@ -467,7 +476,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
fullName = MetadataHelpers.ComposeAritySuffixedMetadataName(currDiagName, arity)
End If
forwardedToAssembly = GetForwardedToAssembly(containingAssembly, fullName, arity, encounteredForwardingCycle)
forwardedToAssembly = GetForwardedToAssembly(containingAssembly, fullName, arity, encounteredForwardingError)
If forwardedToAssembly IsNot Nothing Then
typeSyntax = currTypeSyntax
......@@ -489,19 +498,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
''' <param name="containingAssembly">The assembly in which to look for the type forwarder.</param>
''' <param name="fullName">The metadata name of the (potentially) forwarded type, including the arity (if non-zero).</param>
''' <param name="arity">The arity of the forwarded type.</param>
''' <param name="encounteredCycle">Set to true if a cycle was found in the type forwarders.</param>
''' <param name="encounteredForwardingError">Will Not be null if an error was found during look up.</param>
''' <returns></returns>
''' <remarks>
''' Since this method is intended to be used for error reporting, it stops as soon as it finds
''' any type forwarder - it does not check other assemblies for consistency or better results.
''' any type forwarder (or an error to report). It does not check other assemblies for consistency or better results.
'''
''' NOTE: unlike in C#, this method searches for type forwarders case-insensitively.
''' </remarks>
Private Shared Function GetForwardedToAssembly(containingAssembly As AssemblySymbol, fullName As String, arity As Integer, ByRef encounteredCycle As Boolean) As AssemblySymbol
Private Shared Function GetForwardedToAssembly(containingAssembly As AssemblySymbol, fullName As String, arity As Integer, ByRef encounteredForwardingError As DiagnosticInfo) As AssemblySymbol
Debug.Assert(arity = 0 OrElse fullName.EndsWith("`" & arity, StringComparison.Ordinal))
encounteredCycle = False
' NOTE: This won't work if the type isn't using CLS-style generic naming (i.e. `arity), but this code is
' only intended to improve diagnostic messages, so false negatives in corner cases aren't a big deal.
Dim metadataName = MetadataTypeName.FromFullName(fullName, useCLSCompliantNameArityEncoding:=True, forcedArity:=arity)
......@@ -517,10 +524,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
If forwardedType IsNot Nothing Then
If forwardedType.IsErrorType Then
Dim diagInfo As DiagnosticInfo = DirectCast(forwardedType, ErrorTypeSymbol).ErrorInfo
If diagInfo.Code = ERRID.ERR_TypeFwdCycle2 Then
encounteredCycle = True
End If
encounteredForwardingError = DirectCast(forwardedType, ErrorTypeSymbol).ErrorInfo
End If
Return forwardedType.ContainingAssembly
......
......@@ -1629,7 +1629,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
ERR_NetModuleNameMismatch = 37205
ERR_BadModuleName = 37206
ERR_CmdOptionConflictsSource = 37207
' unused 37208
ERR_TypeForwardedToMultipleAssemblies = 37208
ERR_InvalidSignaturePublicKey = 37209
ERR_CollisionWithPublicTypeInModule = 37210
ERR_ExportedTypeConflictsWithDeclaration = 37211
......
......@@ -141,32 +141,39 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
End Get
End Property
''' <summary>
''' Look up the assembly to which the given metadata type Is forwarded.
''' </summary>
''' <param name="emittedName"></param>
''' <param name="ignoreCase">Pass true to look up fullName case-insensitively. WARNING: more expensive.</param>
''' <param name="matchedName">Returns the actual casing of the matching name.</param>
''' <returns>
''' The assembly to which the given type Is forwarded Or null, if there isn't one.
''' </returns>
''' <remarks>
''' The returned assembly may also forward the type.
''' </remarks>
Friend Function LookupAssemblyForForwardedMetadataType(ByRef emittedName As MetadataTypeName, ignoreCase As Boolean, <Out> ByRef matchedName As String) As AssemblySymbol
' Look in the type forwarders of the primary module of this assembly, clr does not honor type forwarder
' in non-primary modules.
' Examine the type forwarders, but only from the primary module.
Return PrimaryModule.GetAssemblyForForwardedType(emittedName, ignoreCase, matchedName)
''' <summary>
''' Look up the assemblies to which the given metadata type Is forwarded.
''' </summary>
''' <param name="emittedName"></param>
''' <param name="ignoreCase">Pass true to look up fullName case-insensitively. WARNING: more expensive.</param>
''' <param name="matchedName">Returns the actual casing of the matching name.</param>
''' <returns>
''' The assemblies to which the given type Is forwarded Or null, if there isn't one.
''' </returns>
''' <remarks>
''' The returned assemblies may also forward the type.
''' </remarks>
Friend Function LookupAssembliesForForwardedMetadataType(ByRef emittedName As MetadataTypeName, ignoreCase As Boolean, <Out> ByRef matchedName As String) As ImmutableArray(Of AssemblySymbol)
' Look in the type forwarders of the primary module of this assembly, clr does not honor type forwarder
' in non-primary modules.
' Examine the type forwarders, but only from the primary module.
Return PrimaryModule.GetAssembliesForForwardedType(emittedName, ignoreCase, matchedName)
End Function
Friend Overrides Function TryLookupForwardedMetadataTypeWithCycleDetection(ByRef emittedName As MetadataTypeName, visitedAssemblies As ConsList(Of AssemblySymbol), ignoreCase As Boolean) As NamedTypeSymbol
' Check if it is a forwarded type.
Dim matchedName As String = Nothing
Dim forwardedToAssembly = LookupAssemblyForForwardedMetadataType(emittedName, ignoreCase, matchedName)
' Don't bother to check the forwarded-to assembly if we've already seen it.
If forwardedToAssembly IsNot Nothing Then
Dim forwardedToAssemblies = LookupAssembliesForForwardedMetadataType(emittedName, ignoreCase, matchedName)
If (Not forwardedToAssemblies.IsDefaultOrEmpty) Then
If (forwardedToAssemblies.Length > 1) Then
Return New MultipleForwardedTypeSymbol(emittedName, forwardedToAssemblies(0), forwardedToAssemblies(1))
End If
Dim forwardedToAssembly = forwardedToAssemblies.First()
' Don't bother to check the forwarded-to assembly if we've already seen it.
If visitedAssemblies IsNot Nothing AndAlso visitedAssemblies.Contains(forwardedToAssembly) Then
Return CreateCycleInTypeForwarderErrorTypeSymbol(emittedName)
Else
......
......@@ -423,30 +423,42 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
End Function
''' <summary>
''' If this module forwards the given type to another assembly, return that assembly;
''' otherwise, return Nothing.
''' Returns a list of the assemblies this module forwards the given type to.
''' </summary>
''' <param name="fullName">Type to look up.</param>
''' <param name="ignoreCase">Pass true to look up fullName case-insensitively. WARNING: more expensive.</param>
''' <param name="matchedName">Returns the actual casing of the matching name.</param>
''' <returns>Assembly symbol or Nothing.</returns>
''' <returns>An <see cref="ImmutableArray"/> of the forwarded to assemblies.</returns>
''' <remarks>
''' The returned assembly may also forward the type.
''' The returned assemblies may also forward the type.
''' </remarks>
Friend Function GetAssemblyForForwardedType(ByRef fullName As MetadataTypeName, ignoreCase As Boolean, <Out> ByRef matchedName As String) As AssemblySymbol
Dim assemblyRef As AssemblyReferenceHandle = Me.Module.GetAssemblyForForwardedType(fullName.FullName, ignoreCase, matchedName)
Return If(assemblyRef.IsNil, Nothing, GetReferencedAssemblySymbol(assemblyRef))
Friend Function GetAssembliesForForwardedType(ByRef fullName As MetadataTypeName, ignoreCase As Boolean, <Out> ByRef matchedName As String) As ImmutableArray(Of AssemblySymbol)
Dim assemblyRefs As IEnumerable(Of AssemblyReferenceHandle) = Me.Module.GetAssemblyRefsForForwardedType(fullName.FullName, ignoreCase, matchedName)
Dim assemblies = ArrayBuilder(Of AssemblySymbol).GetInstance()
If Not assemblyRefs Is Nothing Then
For Each reference As AssemblyReferenceHandle In assemblyRefs
Dim Assembly = GetReferencedAssemblySymbol(reference)
If Not Assembly Is Nothing Then
assemblies.Add(Assembly)
End If
Next
End If
Return assemblies.ToImmutableAndFree()
End Function
Friend Iterator Function GetForwardedTypes() As IEnumerable(Of NamedTypeSymbol)
For Each forwarder As KeyValuePair(Of String, AssemblyReferenceHandle) In Me.Module.GetForwardedTypes()
Dim assembly As AssemblySymbol = GetReferencedAssemblySymbol(forwarder.Value)
If assembly Is Nothing Then
Continue For
End If
For Each forwarder As KeyValuePair(Of String, HashSet(Of AssemblyReferenceHandle)) In Me.Module.GetForwardedTypes()
For Each assemblyRef As AssemblyReferenceHandle In forwarder.Value
Dim assembly As AssemblySymbol = GetReferencedAssemblySymbol(assemblyRef)
If assembly Is Nothing Then
Continue For
End If
Dim name = MetadataTypeName.FromFullName(forwarder.Key)
Yield assembly.LookupTopLevelMetadataType(name, digThroughForwardedTypes:=True)
Dim name = MetadataTypeName.FromFullName(forwarder.Key)
Yield assembly.LookupTopLevelMetadataType(name, digThroughForwardedTypes:=True)
Next
Next
End Function
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Friend NotInheritable Class MultipleForwardedTypeSymbol
Inherits ErrorTypeSymbol
Private ReadOnly _metadataTypeName As MetadataTypeName
Private ReadOnly _assembly1 As AssemblySymbol
Private ReadOnly _assembly2 As AssemblySymbol
Public Sub New(metadataTypeName As MetadataTypeName, assembly1 As AssemblySymbol, assembly2 As AssemblySymbol)
_metadataTypeName = metadataTypeName
_assembly1 = assembly1
_assembly2 = assembly2
End Sub
Friend Overrides ReadOnly Property ErrorInfo As DiagnosticInfo
Get
Return ErrorFactory.ErrorInfo(ERRID.ERR_TypeForwardedToMultipleAssemblies, _metadataTypeName.FullName, _assembly1.Name, _assembly2.Name)
End Get
End Property
End Class
End Namespace
......@@ -1693,9 +1693,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
' Similar to attributes, type forwarders from the second added module should override type forwarders from the first added module, etc.
For i As Integer = _modules.Length - 1 To 1 Step -1
Dim peModuleSymbol = DirectCast(_modules(i), PEModuleSymbol)
Dim forwardedToAssemblies = peModuleSymbol.GetAssembliesForForwardedType(emittedName, ignoreCase, matchedName)
If (Not forwardedToAssemblies.IsDefaultOrEmpty) Then
If (forwardedToAssemblies.Length > 1) Then
Return New MultipleForwardedTypeSymbol(emittedName, forwardedToAssemblies(0), forwardedToAssemblies(1))
End If
Dim forwardedToAssembly = forwardedToAssemblies.First()
Dim forwardedToAssembly = peModuleSymbol.GetAssemblyForForwardedType(emittedName, ignoreCase, matchedName)
If forwardedToAssembly IsNot Nothing Then
' Don't bother to check the forwarded-to assembly if we've already seen it.
If visitedAssemblies IsNot Nothing AndAlso visitedAssemblies.Contains(forwardedToAssembly) Then
Return CreateCycleInTypeForwarderErrorTypeSymbol(emittedName)
......
......@@ -10685,6 +10685,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Type &apos;{0}&apos; is forwarded to multiple assemblies: &apos;{1}&apos; and &apos;{2}&apos;.
'''</summary>
Friend ReadOnly Property ERR_TypeForwardedToMultipleAssemblies() As String
Get
Return ResourceManager.GetString("ERR_TypeForwardedToMultipleAssemblies", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to &apos;{0}&apos; in assembly &apos;{1}&apos; has been forwarded to itself and so is an unsupported type..
'''</summary>
......
......@@ -5461,4 +5461,7 @@
<data name="ERR_BadAssemblyName" xml:space="preserve">
<value>Invalid assembly name: {0}</value>
</data>
</root>
<data name="ERR_TypeForwardedToMultipleAssemblies" xml:space="preserve">
<value>Type '{0}' is forwarded to multiple assemblies: '{1}' and '{2}'</value>
</data>
</root>
\ No newline at end of file
......@@ -23746,5 +23746,156 @@ Friend MustOverride ReadOnly Property P
</expected>)
End Sub
<Fact>
<WorkItem(16484, "https://github.com/dotnet/roslyn/issues/16484")>
Public Sub MultipleForwardsToTheSameAssemblyShouldIgnoreDuplicate()
Dim forwardedToCode = "
Namespace Destination
Public Class TestClass
Public Const Value = ""TEST VALUE""
End Class
End Namespace
"
Dim forwardedToCompilation = CreateCompilationWithMscorlib(
source:=forwardedToCode,
assemblyName:="Destination",
options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
Dim forwardedToReference = forwardedToCompilation.EmitToImageReference()
Dim forwardingCode =
<compilation>
<file name="a.vb"><![CDATA[
Public Module Program
Sub Main()
System.Console.WriteLine(Destination.TestClass.Value)
End Sub
End Module
]]></file>
</compilation>
Dim forwardingIL = "
.assembly extern Destination { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination
}
.class extern forwarder Destination.TestClass
{
.assembly extern Destination
}"
Dim forwardingCompilation = CreateCompilationWithCustomILSource(
sources:=forwardingCode,
ilSource:=forwardingIL,
additionalReferences:={forwardedToReference, MsvbRef},
options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication))
CompileAndVerify(forwardingCompilation, expectedOutput:="TEST VALUE")
End Sub
<Fact>
<WorkItem(16484, "https://github.com/dotnet/roslyn/issues/16484")>
Public Sub MultipleForwardsToDifferentAssembliesWithoutReferencingThemShouldIgnoreIt()
Dim forwardingCode = <compilation></compilation>
Dim forwardingIL = <![CDATA[
.assembly extern Destination1 { }
.assembly extern Destination2 { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination1
}
.class extern forwarder Destination.TestClass
{
.assembly extern Destination2
}]]>
Dim forwardingCompilation = CreateCompilationWithCustomILSource(forwardingCode, forwardingIL)
CompilationUtils.AssertTheseDiagnostics(forwardingCompilation)
End Sub
<Fact>
<WorkItem(16484, "https://github.com/dotnet/roslyn/issues/16484")>
Public Sub MultipleForwardsToDifferentAssembliesWhileReferencingThemShouldErrorOut()
Dim forwardingCode =
<compilation>
<file name="a.vb"><![CDATA[
Public Module Program
Public Sub Main()
Dim obj = New Destination.TestClass()
End Sub
End Module
]]></file>
</compilation>
Dim forwardingIL = "
.assembly extern Destination1 { }
.assembly extern Destination2 { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination1
}
.class extern forwarder Destination.TestClass
{
.assembly extern Destination2
}"
Dim forwardingCompilation = CreateCompilationWithCustomILSource(forwardingCode, forwardingIL, additionalReferences:={MsvbRef})
CompilationUtils.AssertTheseDiagnostics(forwardingCompilation,
<errors><![CDATA[
BC37208: Type 'Destination.TestClass' is forwarded to multiple assemblies: 'Destination1' and 'Destination2'
Dim obj = New Destination.TestClass()
~~~~~~~~~~~~~~~~~~~~~
]]></errors>)
End Sub
<Fact>
<WorkItem(16484, "https://github.com/dotnet/roslyn/issues/16484")>
Public Sub MultipleForwardsThatResultInTheSameAssemblyDoNotErrorOut()
Dim forwardedToCode1 = "
Namespace Destination
Public Class TestClass
End Class
End Namespace
"
Dim forwardedToCompilation1 = CreateCompilationWithMscorlib(
source:=forwardedToCode1,
assemblyName:="Destination1",
options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
Dim forwardedToReference1 = forwardedToCompilation1.EmitToImageReference()
Dim forwardedToCode2 = <compilation></compilation>
Dim forwardedToIL2 = "
.assembly extern Destination1 { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination1
}"
Dim forwardedToCompilation2 = CreateCompilationWithCustomILSource(forwardedToCode2, forwardedToIL2, additionalReferences:={forwardedToReference1}, assemblyName:="Destination2")
Dim forwardedToReference2 = forwardedToCompilation2.EmitToImageReference()
Dim forwardingCode =
<compilation>
<file name="a.vb"><![CDATA[
Public Module Program
Public Sub Main()
Dim obj = New Destination.TestClass()
End Sub
End Module
]]></file>
</compilation>
Dim forwardingIL = "
.assembly extern Destination1 { }
.assembly extern Destination2 { }
.class extern forwarder Destination.TestClass
{
.assembly extern Destination1
}
.class extern forwarder Destination.TestClass
{
.assembly extern Destination2
}"
Dim forwardingCompilation = CreateCompilationWithCustomILSource(forwardingCode, forwardingIL, additionalReferences:={forwardedToReference1, forwardedToReference2, MsvbRef})
CompilationUtils.AssertTheseDiagnostics(forwardingCompilation)
End Sub
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册