diff --git a/src/GenFacades/Program.cs b/src/GenFacades/Program.cs index c445e9e6a4..90a48044ee 100644 --- a/src/GenFacades/Program.cs +++ b/src/GenFacades/Program.cs @@ -312,33 +312,62 @@ private static IEnumerable EnumerateDocIdsToForward(IAssembly contractAs var typesToForward = contractAssembly.GetAllTypes().Where(t => TypeHelper.IsVisibleOutsideAssembly(t)) .OfType(); + List result = typeForwardsToForward.Concat(typesToForward) + .Select(type => TypeHelper.GetTypeName(type, NameFormattingOptions.DocumentationId)).ToList(); + + foreach(var type in typesToForward) + { + AddNestedTypeDocIds(result, type); + } + + return result; + } - return typeForwardsToForward.Concat(typesToForward) - .Select(type => TypeHelper.GetTypeName(type, NameFormattingOptions.DocumentationId)); + private static void AddNestedTypeDocIds(List docIds, INamedTypeDefinition type) + { + foreach (var nestedType in type.NestedTypes) + { + if (TypeHelper.IsVisibleOutsideAssembly(nestedType)) + docIds.Add(TypeHelper.GetTypeName(nestedType, NameFormattingOptions.DocumentationId)); + AddNestedTypeDocIds(docIds, nestedType); + } } - private static IReadOnlyDictionary> GenerateTypeTable(IEnumerable seedAssemblies) + private static IReadOnlyDictionary> GenerateTypeTable(IEnumerable seedAssemblies) { - var typeTable = new Dictionary>(); + var typeTable = new Dictionary>(); foreach (var assembly in seedAssemblies) { - foreach (var type in assembly.GetAllTypes().OfType()) + foreach (var type in assembly.GetAllTypes().OfType()) { if (!TypeHelper.IsVisibleOutsideAssembly(type)) continue; + AddTypeAndNestedTypesToTable(typeTable, type); + } + } + return typeTable; + } - IReadOnlyList seedTypes; - string docId = TypeHelper.GetTypeName(type, NameFormattingOptions.DocumentationId); - if (!typeTable.TryGetValue(docId, out seedTypes)) - { - seedTypes = new List(1); - typeTable.Add(docId, seedTypes); - } + private static void AddTypeAndNestedTypesToTable(Dictionary> typeTable, INamedTypeDefinition type) + { + if (type != null) + { + IReadOnlyList seedTypes; + string docId = TypeHelper.GetTypeName(type, NameFormattingOptions.DocumentationId); + if (!typeTable.TryGetValue(docId, out seedTypes)) + { + seedTypes = new List(1); + typeTable.Add(docId, seedTypes); + } + if (!seedTypes.Contains(type)) + ((List)seedTypes).Add(type); - ((List)seedTypes).Add(type); + foreach (INestedTypeDefinition nestedType in type.NestedTypes) + { + if (TypeHelper.IsVisibleOutsideAssembly(nestedType)) + AddTypeAndNestedTypesToTable(typeTable, nestedType); } } - return typeTable; } private class FacadeGenerator @@ -346,7 +375,7 @@ private class FacadeGenerator private readonly IMetadataHost _seedHost; private readonly IMetadataHost _contractHost; private readonly IReadOnlyDictionary> _docIdTable; - private readonly IReadOnlyDictionary> _typeTable; + private readonly IReadOnlyDictionary> _typeTable; private readonly IReadOnlyDictionary _seedTypePreferences; private readonly bool _clearBuildAndRevision; private readonly bool _buildDesignTimeFacades; @@ -356,7 +385,7 @@ public FacadeGenerator( IMetadataHost seedHost, IMetadataHost contractHost, IReadOnlyDictionary> docIdTable, - IReadOnlyDictionary> typeTable, + IReadOnlyDictionary> typeTable, IReadOnlyDictionary seedTypePreferences, bool clearBuildAndRevision, bool buildDesignTimeFacades, @@ -398,7 +427,7 @@ public Assembly GenerateFacade(IAssembly contractAssembly, IAssemblyReference se IEnumerable missingDocIds = docIds.Where(id => !existingDocIds.Contains(id)); foreach (string docId in missingDocIds) { - IReadOnlyList seedTypes; + IReadOnlyList seedTypes; if (!_typeTable.TryGetValue(docId, out seedTypes)) { if (!ignoreMissingTypes) @@ -409,7 +438,7 @@ public Assembly GenerateFacade(IAssembly contractAssembly, IAssemblyReference se continue; } - INamespaceTypeDefinition seedType = GetSeedType(docId, seedTypes); + INamedTypeDefinition seedType = GetSeedType(docId, seedTypes); if (seedType == null) { TraceDuplicateSeedTypeError(docId, seedTypes); @@ -447,7 +476,7 @@ public Assembly GenerateFacade(IAssembly contractAssembly, IAssemblyReference se return assembly; } - private INamespaceTypeDefinition GetSeedType(string docId, IReadOnlyList seedTypes) + private INamedTypeDefinition GetSeedType(string docId, IReadOnlyList seedTypes) { Debug.Assert(seedTypes.Count != 0); // we should already have checked for non-existent types. @@ -459,22 +488,30 @@ private INamespaceTypeDefinition GetSeedType(string docId, IReadOnlyList String.Equals(t.ContainingUnitNamespace.Unit.Name.Value, preferredSeedAssembly, StringComparison.OrdinalIgnoreCase)); + return seedTypes.SingleOrDefault(t => String.Equals(GetContainingUnitNamespaceFromType(t), preferredSeedAssembly, StringComparison.OrdinalIgnoreCase)); } return null; } - private static void TraceDuplicateSeedTypeError(string docId, IReadOnlyList seedTypes) + private static void TraceDuplicateSeedTypeError(string docId, IReadOnlyList seedTypes) { Trace.TraceError("The type '{0}' is defined in multiple seed assemblies. If this is intentional, specify one of the following arguments to choose the preferred seed type:", docId); - foreach (INamespaceTypeDefinition type in seedTypes) + foreach (INamedTypeDefinition type in seedTypes) { - Trace.TraceError(" /preferSeedType:{0}={1}", docId.Substring("T:".Length), type.ContainingUnitNamespace.Unit.Name.Value); + Trace.TraceError(" /preferSeedType:{0}={1}", docId.Substring("T:".Length), GetContainingUnitNamespaceFromType(type)); } } + private static string GetContainingUnitNamespaceFromType(INamedTypeDefinition type) + { + INamespaceTypeDefinition namespaceTypeDefinition = type as INamespaceTypeDefinition; + if (namespaceTypeDefinition != null) + return namespaceTypeDefinition.ContainingUnitNamespace.Unit.Name.Value; + return type.GetAssembly().Name.Value; + } + private void AddTypeForward(Assembly assembly, INamedTypeDefinition seedType, IAssembly contractAssembly) { var alias = new NamespaceAliasForType(); @@ -484,25 +521,6 @@ private void AddTypeForward(Assembly assembly, INamedTypeDefinition seedType, IA if (assembly.ExportedTypes == null) assembly.ExportedTypes = new List(); assembly.ExportedTypes.Add(alias); - - // Recursively add forwarders for nested types. - // NOTE: Some design-time tools can resolve forwarded nested types with only the top-level forwarder, - // but the runtime currently throws a TypeLoadException without explicit forwarders for the nested - // types. - INamedTypeDefinition contractType = contractAssembly.GetAllTypes().Where(t => t.FullName() == seedType.FullName()).FirstOrDefault(); - if (contractType != null) // Only add forwarders for the nested types on the contract - { - foreach (var nestedType in contractType.NestedTypes.OrderBy(t => t.Name.Value)) - { - var seedNestedType = seedType.NestedTypes.Where(nt => nt.Name.Value == nestedType.Name.Value).FirstOrDefault(); - if (seedNestedType == null) - throw new Exception("Nested type found in the contract, but not on the seedType."); - AddTypeForward(assembly, seedNestedType, contractAssembly); - } - } - else // Add all the nested types for ExportedTypes. - foreach (var nestedType in seedType.NestedTypes.OrderBy(t => t.Name.Value)) - AddTypeForward(assembly, nestedType, contractAssembly); } private void AddWin32VersionResource(string contractLocation, Assembly facade)