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.
+
+