diff --git a/mdoc/Makefile b/mdoc/Makefile index 602c96846..243760f08 100644 --- a/mdoc/Makefile +++ b/mdoc/Makefile @@ -134,6 +134,17 @@ Test/DocTest-enumerations.dll: Test/DocTest-embedded-type.dll: $(CSCOMPILE) $(TEST_CSCFLAGS) -unsafe -debug -optimize -target:library -out:$@ Test/DocTest-embedded-type.cs +Test/DocTest-typeForwards-First.dll: + rm -f $@ + $(CSCOMPILE) $(TEST_CSCFLAGS) -unsafe -debug -optimize -target:library -out:$@ Test/DocTest-typeForwards.cs /define:FIRST + +.PHONY: Test/DocTest-typeForwards-Second.dll +Test/DocTest-typeForwards-Second.dll: + rm -f $@ + rm -f Test/DocTest-typeForwards-Second-First.dll + $(CSCOMPILE) $(TEST_CSCFLAGS) -unsafe -debug -optimize -target:library -out:$@ Test/DocTest-typeForwards.cs /define:FIRST + $(CSCOMPILE) $(TEST_CSCFLAGS) -unsafe -debug -optimize -target:library -out:Test/DocTest-typeForwards-Second-First.dll /reference:$@ Test/DocTest-typeForwards.cs /define:SECOND + .PHONY: Test/FrameworkTestData Test/FrameworkTestData: Test/DocTest-addNonGeneric.dll Test/DocTest-DropNS-classic.dll Test/DocTest-DropNS-classic-secondary.dll rm -rf Test/FrameworkTestData @@ -155,6 +166,26 @@ Test/FrameworkTestData-fx-inheritance: Test/DocTest-framework-inheritance-one.dl cp Test/DocTest-framework-inheritance-two.dll Test/FrameworkTestData-fx-inheritance/Two/ $(MONO) $(PROGRAM) fx-bootstrap Test/FrameworkTestData-fx-inheritance +.PHONY: check-monodocer-typeForwards +check-monodocer-typeForwards : Test/DocTest-typeForwards-First.dll Test/DocTest-typeForwards-Second.dll + -rm -Rf Test/en.actual + + # set up the fx test data + -rm -Rf Test/FrameworkTestData-fx-typeForwards + mkdir Test/FrameworkTestData-fx-typeForwards + mkdir Test/FrameworkTestData-fx-typeForwards/One + mkdir Test/FrameworkTestData-fx-typeForwards/Two + mkdir Test/FrameworkTestData-fx-typeForwards/dependencies + mkdir Test/FrameworkTestData-fx-typeForwards/dependencies/Two + cp Test/DocTest-typeForwards-First.dll Test/FrameworkTestData-fx-typeForwards/One + cp Test/DocTest-typeForwards-Second-First.dll Test/FrameworkTestData-fx-typeForwards/Two + cp Test/DocTest-typeForwards-Second.dll Test/FrameworkTestData-fx-typeForwards/dependencies/Two + $(MONO) $(PROGRAM) fx-bootstrap Test/FrameworkTestData-fx-typeForwards + + # now run mdoc update + $(MONO) $(PROGRAM) update -o Test/en.actual -frameworks Test/FrameworkTestData-fx-typeForwards + $(DIFF) Test/en.expected.typeForwards Test/en.actual + check-monodocer-frameworks: Test/FrameworkTestData -rm -Rf Test/en.actual $(MONO) $(PROGRAM) update -o Test/en.actual -frameworks Test/FrameworkTestData @@ -708,7 +739,9 @@ run-test-local: check-doc-tools run-test-update : check-doc-tools-update -check-doc-tools: check-monodocer-Eii-importslashdoc \ +check-doc-tools: \ + check-monodocer-typeForwards \ + check-monodocer-Eii-importslashdoc \ check-monodocer-Eii-importecmadoc-oldNames \ check-monodocer-Eii \ check-monodocer-since \ diff --git a/mdoc/Mono.Documentation/MDocUpdater.cs b/mdoc/Mono.Documentation/MDocUpdater.cs index 0ecb99791..2304cda46 100644 --- a/mdoc/Mono.Documentation/MDocUpdater.cs +++ b/mdoc/Mono.Documentation/MDocUpdater.cs @@ -97,7 +97,12 @@ public static bool HasDroppedNamespace (AssemblyDefinition forAssembly) public static bool HasDroppedNamespace (ModuleDefinition forModule) { - return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any (da => da == forModule.Name); + return HasDroppedNamespace (forModule.Assembly.Name); + } + + public static bool HasDroppedNamespace (AssemblyNameReference assemblyRef) + { + return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any (da => Path.GetFileNameWithoutExtension(da) == assemblyRef.Name); } public static bool HasDroppedAnyNamespace () @@ -2137,14 +2142,27 @@ public void UpdateType (XmlElement root, TypeDefinition type, FrameworkTypeEntry /// Adds an AssemblyInfo with AssemblyName node to an XmlElement. /// The assembly that was either added, or was already present - XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type) + XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition forType) { - return AddAssemblyNameToNode (root, type.Module); + return AddAssemblyNameToNode (root, forType.Module, forType); + } + + XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module, TypeDefinition forType) + { + var list = this.assemblies.SelectMany (a => a.FullAssemblyChain (forType)); + + var names = list.Select(a => AddAssemblyNameToNodeCore (root, a, forType)); + return names.ToArray().First (); + } + + XmlElement AddAssemblyNameToNodeCore (XmlElement root, ModuleDefinition module, TypeDefinition forType) + { + return AddAssemblyNameToNodeCore (root, module.Assembly.Name, forType); } /// Adds an AssemblyInfo with AssemblyName node to an XmlElement. /// The assembly that was either added, or was already present - XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module) + XmlElement AddAssemblyNameToNodeCore (XmlElement root, AssemblyNameReference assembly, TypeDefinition forType) { Func assemblyFilter = x => { @@ -2152,27 +2170,27 @@ XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module) bool apiStyleMatches = true; string currentApiStyle = x.GetAttribute ("apistyle"); - if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") || + if ((HasDroppedNamespace (assembly) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") || (isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic"))) { apiStyleMatches = false; } - return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name)); + return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == assembly.Name)); }; return AddAssemblyXmlNode ( root.SelectNodes ("AssemblyInfo").Cast ().ToArray (), - assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name), + assemblyFilter, x => WriteElementText (x, "AssemblyName", assembly.Name), () => { XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true); - if (MDocUpdater.HasDroppedNamespace (module)) + if (MDocUpdater.HasDroppedNamespace (assembly)) ass.AddApiStyle (ApiStyle.Unified); if (isClassicRun) ass.AddApiStyle (ApiStyle.Classic); return ass; - }, module); + }, assembly); } static readonly string[] TypeNodeOrder = { @@ -2223,7 +2241,7 @@ private void UpdateMember (DocsNodeInfo info, FrameworkTypeEntry typeEntry, Dict UpdateAssemblyVersions (me, mi, true); else { - var node = AddAssemblyNameToNode (me, mi.Module); + var node = AddAssemblyNameToNode (me, mi.Module, mi.DeclaringType.Resolve()); UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true); } @@ -2477,7 +2495,12 @@ static void AddXmlNode (XmlElement[] relevant, Func valueMatch static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func valueMatches, Action setValue, Func makeNewNode, ModuleDefinition module) { - bool isUnified = MDocUpdater.HasDroppedNamespace (module); + return AddAssemblyXmlNode (relevant, valueMatches, setValue, makeNewNode, module.Assembly.Name); + } + + static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func valueMatches, Action setValue, Func makeNewNode, AssemblyNameReference assembly) + { + bool isUnified = MDocUpdater.HasDroppedNamespace (assembly); XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches); if (thisAssemblyNode == null) { diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs b/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs index 496d5624e..88a306432 100644 --- a/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs +++ b/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs @@ -40,12 +40,19 @@ public IDictionary AssemblyMapsPath get => assemblyPathsMap; } + private IDictionary> forwardedTypesTo = new Dictionary> (); + + public AssemblySet (IEnumerable paths) : this ("Default", paths, new string[0]) { } public AssemblySet (string name, IEnumerable paths, IEnumerable resolverSearchPaths, IEnumerable imports = null, string version = null, string id = null) { cachedResolver = cachedResolver ?? new CachedResolver (resolver); metadataResolver = metadataResolver ?? new Frameworks.MDocMetadataResolver (cachedResolver); + ((MDocResolver)resolver).TypeExported += (sender, e) => + { + TrackTypeExported (e); + }; Name = name; Version = version; @@ -83,6 +90,17 @@ public AssemblySet (string name, IEnumerable paths, IEnumerable this.Importers = new DocumentationImporter[0]; } + private void TrackTypeExported (MDocResolver.TypeForwardEventArgs e) + { + // keep track of types that have been exported for this assemblyset + if (!forwardedTypesTo.ContainsKey (e.ForType.FullName)) + { + forwardedTypesTo.Add (e.ForType.FullName, new List ()); + } + + forwardedTypesTo[e.ForType.FullName].Add (e); + } + public string Name { get; private set; } public string Version { get; private set; } public string Id { get; private set; } @@ -133,6 +151,40 @@ public bool ContainsForwardedType (string name) return forwardedTypes.Contains (name); } + /// + /// Forwardeds the assemblies. + /// + /// The assemblies. + /// Type. + public IEnumerable FullAssemblyChain(TypeDefinition type) + { + if (forwardedTypesTo.ContainsKey (type.FullName)) + { + var list = forwardedTypesTo[type.FullName]; + var assemblies = (new[] { type.Module.Assembly.Name }) + .Union (list.Select (f => f.To)) + .Union (list.Select (f => f.From)) + .Distinct (anc); + return assemblies; + } + else + return new[] { type.Module.Assembly.Name }; + } + + AssemblyNameComparer anc = new AssemblyNameComparer (); + class AssemblyNameComparer : IEqualityComparer + { + public bool Equals (AssemblyNameReference x, AssemblyNameReference y) + { + return x.FullName.Equals (y.FullName, StringComparison.Ordinal); + } + + public int GetHashCode (AssemblyNameReference obj) + { + return obj.FullName.GetHashCode (); + } + } + public void Dispose () { this.assemblies = null; @@ -149,8 +201,11 @@ IEnumerable LoadAllAssemblies () foreach (var path in this.assemblyPaths) { var assembly = MDocUpdater.Instance.LoadAssembly (path, metadataResolver, cachedResolver); if (assembly != null) { - foreach (var type in assembly.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName)) - forwardedTypes.Add (type); + foreach (var type in assembly.MainModule.ExportedTypes.Where (t => t.IsForwarder).Cast()) + { + forwardedTypes.Add (type.FullName); + TrackTypeExported (new MDocResolver.TypeForwardEventArgs (assembly.Name, (AssemblyNameReference)type.Scope, type.Resolve ())); + } } yield return assembly; } diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs b/mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs index 4c9636d1a..0785a265e 100644 --- a/mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs +++ b/mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs @@ -18,13 +18,29 @@ namespace Mono.Documentation.Updater.Frameworks /// Please note that you will need to provide a reference /// to the UWP framework directory, if you are trying to document /// a UWP library. - class MDocResolver : MDocBaseResolver + public class MDocResolver : MDocBaseResolver { + public class TypeForwardEventArgs : EventArgs + { + public AssemblyNameReference From { get; private set; } + public AssemblyNameReference To { get; private set; } + public TypeReference ForType { get; set; } + + public TypeForwardEventArgs(AssemblyNameReference from, AssemblyNameReference to, TypeReference forType) + { + From = from; + To = to; + ForType = forType; + } + } + public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters) { return Resolve (name, parameters, null, null); } + public event EventHandler TypeExported; + internal AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters r, TypeReference forType, List exportedFiles) { if (exportedFiles == null) @@ -39,6 +55,9 @@ internal AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameter string file = a.MainModule.FileName; AssemblyNameReference exportedTo = (AssemblyNameReference)etype.Scope; Console.WriteLine ($"resolving {forType.FullName} in {name.FullName}. Found {file}, but it's exported to {exportedTo.FullName}"); + if (forType != null) + TypeExported?.Invoke (this, new TypeForwardEventArgs(name, exportedTo, forType)); + exportedFiles.Add (file); return Resolve (exportedTo, r, forType, exportedFiles); } @@ -278,7 +297,7 @@ public void Dispose () /// /// There are two changes made from the original source /// - abstract class MDocBaseResolver : BaseAssemblyResolver + public abstract class MDocBaseResolver : BaseAssemblyResolver { static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null; diff --git a/mdoc/Mono.Documentation/Util/CecilExtensions.cs b/mdoc/Mono.Documentation/Util/CecilExtensions.cs index 15e939dcb..3d96cfc76 100644 --- a/mdoc/Mono.Documentation/Util/CecilExtensions.cs +++ b/mdoc/Mono.Documentation/Util/CecilExtensions.cs @@ -75,7 +75,8 @@ public static IEnumerable GetDefaultMembers (this TypeReference public static IEnumerable GetTypes (this AssemblyDefinition assembly) { - return assembly.Modules.SelectMany (md => md.GetAllTypes ()); + var types = assembly.Modules.SelectMany (md => md.GetAllTypes ()).Union(assembly.MainModule.ExportedTypes.Select(et => et.Resolve())); + return types; } public static TypeDefinition GetType (this AssemblyDefinition assembly, string type) diff --git a/mdoc/Test/DocTest-typeForwards.cs b/mdoc/Test/DocTest-typeForwards.cs new file mode 100644 index 000000000..c5cee7ba9 --- /dev/null +++ b/mdoc/Test/DocTest-typeForwards.cs @@ -0,0 +1,12 @@ +#if SECOND +using System.Runtime.CompilerServices; +[assembly:TypeForwardedToAttribute(typeof(TheNamespace.TheClass))] +#endif + +namespace TheNamespace +{ + #if FIRST + public class TheClass + {} + #endif +} \ No newline at end of file diff --git a/mdoc/Test/en.expected.typeForwards/FrameworksIndex/One.xml b/mdoc/Test/en.expected.typeForwards/FrameworksIndex/One.xml new file mode 100644 index 000000000..e111684ae --- /dev/null +++ b/mdoc/Test/en.expected.typeForwards/FrameworksIndex/One.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/mdoc/Test/en.expected.typeForwards/FrameworksIndex/Two.xml b/mdoc/Test/en.expected.typeForwards/FrameworksIndex/Two.xml new file mode 100644 index 000000000..cdce3ec80 --- /dev/null +++ b/mdoc/Test/en.expected.typeForwards/FrameworksIndex/Two.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/mdoc/Test/en.expected.typeForwards/TheNamespace/TheClass.xml b/mdoc/Test/en.expected.typeForwards/TheNamespace/TheClass.xml new file mode 100644 index 000000000..d84342a6c --- /dev/null +++ b/mdoc/Test/en.expected.typeForwards/TheNamespace/TheClass.xml @@ -0,0 +1,46 @@ + + + + + DocTest-typeForwards-First + 0.0.0.0 + + + DocTest-typeForwards-Second + 0.0.0.0 + + + DocTest-typeForwards-Second-First + + + System.Object + + + + To be added. + To be added. + + + + + + Constructor + + DocTest-typeForwards-First + 0.0.0.0 + + + DocTest-typeForwards-Second + 0.0.0.0 + + + DocTest-typeForwards-Second-First + + + + To be added. + To be added. + + + + diff --git a/mdoc/Test/en.expected.typeForwards/index.xml b/mdoc/Test/en.expected.typeForwards/index.xml new file mode 100644 index 000000000..a97888d79 --- /dev/null +++ b/mdoc/Test/en.expected.typeForwards/index.xml @@ -0,0 +1,32 @@ + + + + + + System.Diagnostics.Debuggable(System.Diagnostics.DebuggableAttribute+DebuggingModes.IgnoreSymbolStoreSequencePoints) + + + System.Runtime.CompilerServices.RuntimeCompatibility(WrapNonExceptionThrows=true) + + + + + + + System.Diagnostics.Debuggable(System.Diagnostics.DebuggableAttribute+DebuggingModes.IgnoreSymbolStoreSequencePoints) + + + System.Runtime.CompilerServices.RuntimeCompatibility(WrapNonExceptionThrows=true) + + + + + To be added. + To be added. + + + + + + Untitled + diff --git a/mdoc/Test/en.expected.typeForwards/ns-TheNamespace.xml b/mdoc/Test/en.expected.typeForwards/ns-TheNamespace.xml new file mode 100644 index 000000000..757c55bd0 --- /dev/null +++ b/mdoc/Test/en.expected.typeForwards/ns-TheNamespace.xml @@ -0,0 +1,6 @@ + + + To be added. + To be added. + +