[bcl] Reflect cecil breaking changes
[mono.git] / mcs / tools / mdoc / Mono.Documentation / monodocer.cs
index 0a031767cce7e042b9df3374caacd271d33391f8..8b5b5ab08e4d440f58d3dd88d1b732cbd5ccc449 100644 (file)
@@ -133,7 +133,9 @@ class MDocUpdater : MDocCommand
        List<AssemblyDefinition> assemblies;
        readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
        
-       bool multiassembly;
+       string apistyle = string.Empty;
+       bool isClassicRun;
+       
        bool delete;
        bool show_exceptions;
        bool no_assembly_versions, ignore_missing_types;
@@ -192,7 +194,14 @@ class MDocUpdater : MDocCommand
        {
                return !string.IsNullOrWhiteSpace (droppedNamespace);
        }
-
+       
+       /// <summary>Logic flag to signify that we should list assemblies at the method level, since there are multiple
+       /// assemblies for a given type/method.</summary>
+       public bool IsMultiAssembly {
+               get { 
+                       return apistyle == "classic" || apistyle == "unified";
+               }
+       }
        
        static List<string> droppedAssemblies = new List<string>();
 
@@ -279,9 +288,9 @@ class MDocUpdater : MDocCommand
                        { "preserve",
                                "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
                                v => PreserveTag = "true" },
-                       { "multiassembly",
-                               "Allow types to be in multiple assemblies.",
-                               v => multiassembly = true },
+                       { "api-style=",
+                               "Denotes the apistyle. Currently, only `classic` and `unified` are supported. `classic` set of assemblies should be run first, immediately followed by 'unified' assemblies with the `dropns` parameter.",
+                               v => { apistyle = v.ToLowerInvariant (); }},
                };
                var assemblies = Parse (p, args, "update", 
                                "[OPTIONS]+ ASSEMBLIES",
@@ -290,7 +299,17 @@ class MDocUpdater : MDocCommand
                        return;
                if (assemblies.Count == 0)
                        Error ("No assemblies specified.");
-
+               
+               // validation for the api-style parameter
+               if (apistyle == "classic") 
+                       isClassicRun = true;
+               else if (apistyle == "unified") {
+                       if (!droppedAssemblies.Any ())
+                               Error ("api-style 'unified' must also supply the 'dropns' parameter with at least one assembly and dropped namespace.");
+               } else if (!string.IsNullOrWhiteSpace (apistyle)) 
+                       Error ("api-style '{0}' is not currently supported", apistyle);
+                       
+                               
                foreach (var dir in assemblies
                                .Where (a => a.Contains (Path.DirectorySeparatorChar))
                                .Select (a => Path.GetDirectoryName (a)))
@@ -772,7 +791,7 @@ class MDocUpdater : MDocCommand
        private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
        {
                XmlElement index_assembly = null;
-               if (multiassembly) 
+               if (IsMultiAssembly) 
                        index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
                
                if (index_assembly == null) 
@@ -852,7 +871,7 @@ class MDocUpdater : MDocCommand
                
                XmlElement index_types = WriteElement(index.DocumentElement, "Types");
                XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
-               if (!multiassembly) 
+               if (!IsMultiAssembly) 
                        index_assemblies.RemoveAll ();
 
 
@@ -1045,7 +1064,7 @@ class MDocUpdater : MDocCommand
                                                saveDoc ();
 
                                                var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
-                                               var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']");
+                                               var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[not(@apistyle) or @apistyle='classic']");
                                                var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
                                                var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
                                                bool isUnifiedRun = HasDroppedAnyNamespace ();
@@ -1265,10 +1284,10 @@ class MDocUpdater : MDocCommand
                                XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
                                if (mm == null) continue;
 
-                               if (MDocUpdater.SwitchingToMagicTypes) {
+                               if (MDocUpdater.SwitchingToMagicTypes || MDocUpdater.HasDroppedNamespace (m)) {
                                        // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
                                        // it with apistyle="unified", so that it's not displayed for classic style APIs
-                                       mm.SetAttribute ("apistyle", "unified");
+                                       mm.AddApiStyle (ApiStyle.Unified);
                                }
 
                                members.AppendChild( mm );
@@ -1379,7 +1398,7 @@ class MDocUpdater : MDocCommand
 
                bool unifiedRun = HasDroppedNamespace (type);
 
-               var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']");
+               var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
                bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
                var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
                bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
@@ -1398,12 +1417,13 @@ class MDocUpdater : MDocCommand
                } else if (unifiedRun && nodeIsClassic) {
                        // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
                        member.RemoveApiStyle (ApiStyle.Unified);
+                       member.AddApiStyle (ApiStyle.Classic);
                        Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
                } else if (unifiedRun && !nodeIsClassic) {
                        // unified run, and the node is not classic, which means it doesn't exist anywhere.
                        actuallyDelete ();
                } else { 
-                       if (!nodeIsClassic && !nodeIsUnified) { // regular codepath (ie. not classic/unified)
+                       if (!isClassicRun || (isClassicRun && !nodeIsClassic && !nodeIsUnified)) { // regular codepath (ie. not classic/unified)
                                actuallyDelete ();
                        } else { // this is a classic run
                                Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
@@ -1659,18 +1679,25 @@ class MDocUpdater : MDocCommand
 
        /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
        /// <returns>The assembly that was either added, or was already present</returns>
-       static XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
+       XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
        {
                return AddAssemblyNameToNode (root, type.Module);
        }
 
        /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
        /// <returns>The assembly that was either added, or was already present</returns>
-       static XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
+       XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
        {
                Func<XmlElement, bool> assemblyFilter = x => {
                        var existingName = x.SelectSingleNode ("AssemblyName");
-                       return existingName != null && existingName.InnerText == module.Assembly.Name.Name;
+
+                       bool apiStyleMatches = true;
+                       string currentApiStyle = x.GetAttribute ("apistyle");
+                       if ((HasDroppedNamespace (module) && !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 AddAssemblyXmlNode (
@@ -1678,8 +1705,11 @@ class MDocUpdater : MDocCommand
                        assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name), 
                        () =>  {
                                XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
+                               
                                if (MDocUpdater.HasDroppedNamespace (module))
-                                       ass.SetAttribute ("apistyle", "unified");
+                                       ass.AddApiStyle (ApiStyle.Unified);
+                               if (isClassicRun) 
+                                       ass.AddApiStyle (ApiStyle.Classic);
                                return ass;
                        }, module);
        }
@@ -1739,7 +1769,7 @@ class MDocUpdater : MDocCommand
                WriteElementText(me, "MemberType", GetMemberType(mi));
 
                if (!no_assembly_versions) {
-                       if (!multiassembly)
+                       if (!IsMultiAssembly)
                                UpdateAssemblyVersions (me, mi, true);
                        else {
                                var node = AddAssemblyNameToNode (me, mi.Module);
@@ -1785,8 +1815,8 @@ class MDocUpdater : MDocCommand
                XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
                if (thisAssemblyNode == null) {
                        thisAssemblyNode = makeNewNode ();
-                       setValue (thisAssemblyNode);
                }
+               setValue (thisAssemblyNode);
 
                if (isUnified) {
                        thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
@@ -1987,7 +2017,10 @@ class MDocUpdater : MDocCommand
                member.AppendChild (link);
                AddTargets (em, info);
 
-               extensionMethods.Add (em);
+               var sig = em.SelectSingleNode ("Member/MemberSignature[@Language='C#']/@Value");
+               if (!IsMultiAssembly || (IsMultiAssembly && sig != null && !extensionMethods.Any (ex => ex.SelectSingleNode ("Member/MemberSignature[@Language='C#']/@Value").Value == sig.Value))) {
+                       extensionMethods.Add (em);
+               }
        }
 
        private static void RemoveExcept (XmlNode node, string[] except)
@@ -2041,7 +2074,7 @@ class MDocUpdater : MDocCommand
                        if (val == null) value = "null";
                        else if (val is Enum) value = val.ToString();
                        else if (val is IFormattable) {
-                               value = ((IFormattable)val).ToString();
+                               value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
                                if (val is string)
                                        value = "\"" + value + "\"";
                        }
@@ -2357,7 +2390,7 @@ class MDocUpdater : MDocCommand
                                n.ParentNode.RemoveChild(n);
        }
        
-       private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
+       private bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
        {
                TypeDefinition type = member as TypeDefinition;
                if (type == null)
@@ -2376,8 +2409,11 @@ class MDocUpdater : MDocCommand
                return assembly.Name.Version.ToString();
        }
        
-       private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
+       private bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
        {
+               if (IsMultiAssembly)
+                       return false;
+                       
                XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
                if (av != null) {
                                // AssemblyVersions is not part of the spec
@@ -2394,7 +2430,7 @@ class MDocUpdater : MDocCommand
                        e = root.OwnerDocument.CreateElement("AssemblyInfo");
 
                        if (MDocUpdater.HasDroppedNamespace (assembly)) {
-                               e.SetAttribute ("apistyle", "unified");
+                               e.AddApiStyle (ApiStyle.Unified);
                        }
 
                        root.AppendChild(e);
@@ -2403,8 +2439,8 @@ class MDocUpdater : MDocCommand
                var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
                if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
                        // there's a classic node, we should add apistyles
-                       e.SetAttribute ("apistyle", "unified");
-                       thatNode.SetAttribute ("apistyle", "classic");
+                       e.AddApiStyle (ApiStyle.Unified);
+                       thatNode.AddApiStyle (ApiStyle.Classic);
                }
 
                return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
@@ -2971,26 +3007,6 @@ static class CecilExtensions {
                return method.GenericParameters.Count > 0;
        }
 
-       public static MemberReference Resolve (this MemberReference member)
-       {
-               FieldReference fr = member as FieldReference;
-               if (fr != null)
-                       return fr.Resolve ();
-               MethodReference mr = member as MethodReference;
-               if (mr != null)
-                       return mr.Resolve ();
-               TypeReference tr = member as TypeReference;
-               if (tr != null)
-                       return tr.Resolve ();
-               PropertyReference pr = member as PropertyReference;
-               if (pr != null)
-                       return pr;
-               EventReference er = member as EventReference;
-               if (er != null)
-                       return er;
-               throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
-       }
-
        public static TypeReference GetUnderlyingType (this TypeDefinition type)
        {
                if (!type.IsEnum)
@@ -3042,6 +3058,39 @@ static class DocUtils {
                if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
                        element.SetAttribute ("apistyle", styleString);
                }
+               
+               // Propagate the API style up to the membernode if necessary
+               if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") {
+                       var member = element.ParentNode;
+                       var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
+                       var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
+
+                       var parentAttribute = element.ParentNode.Attributes ["apistyle"];
+                       Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
+                       Action propagateStyle = () => {
+                               if (parentAttribute == null) {
+                                       // if it doesn't have the attribute, then add it
+                                       parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
+                                       parentAttribute.Value = styleString;
+                                       element.ParentNode.Attributes.Append (parentAttribute);
+                               } 
+                       };
+
+                       if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null)) 
+                               removeStyle ();
+                       else
+                               propagateStyle ();
+               }
+       }
+       public static void AddApiStyle (this XmlNode node, ApiStyle style) 
+       {
+               string styleString = style.ToString ().ToLowerInvariant ();
+               var existingAttribute = node.Attributes ["apistyle"];
+               if (existingAttribute == null) {
+                       existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
+                       node.Attributes.Append (existingAttribute);
+               }
+               existingAttribute.Value = styleString;
        }
        public static void RemoveApiStyle (this XmlElement element, ApiStyle style) 
        {
@@ -3187,7 +3236,8 @@ static class DocUtils {
        {
                HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
                List<TypeReference> userInterfaces = new List<TypeReference> ();
-               foreach (TypeReference iface in type.Interfaces) {
+               foreach (var ii in type.Interfaces) {
+                       var iface = ii.InterfaceType;
                        TypeReference lookup = iface.Resolve () ?? iface;
                        if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
                                userInterfaces.Add (iface);
@@ -3206,9 +3256,9 @@ static class DocUtils {
                Action<TypeDefinition> a = null;
                a = t => {
                        if (t == null) return;
-                       foreach (TypeReference r in t.Interfaces) {
-                               inheritedInterfaces.Add (GetQualifiedTypeName (r));
-                               a (r.Resolve ());
+                       foreach (var r in t.Interfaces) {
+                               inheritedInterfaces.Add (GetQualifiedTypeName (r.InterfaceType));
+                               a (r.InterfaceType.Resolve ());
                        }
                };
                TypeReference baseRef = type.BaseType;
@@ -3221,8 +3271,8 @@ static class DocUtils {
                        else
                                baseRef = null;
                }
-               foreach (TypeReference r in type.Interfaces)
-                       a (r.Resolve ());
+               foreach (var r in type.Interfaces)
+                       a (r.InterfaceType.Resolve ());
                return inheritedInterfaces;
        }
 }
@@ -4297,8 +4347,13 @@ public abstract class MemberFormatter {
        {
                return e.Name;
        }
+       
+       public string GetDeclaration (MemberReference mreference)
+       {
+               return GetDeclaration (mreference.Resolve ());
+       }
 
-       public virtual string GetDeclaration (MemberReference member)
+       string GetDeclaration (IMemberDefinition member)
        {
                if (member == null)
                        throw new ArgumentNullException ("member");
@@ -4562,8 +4617,8 @@ class ILFullMemberFormatter : MemberFormatter {
                                buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
                }
                bool first = true;
-               foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
-                               .Select (i => full.GetName (i))
+               foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.InterfaceType.Resolve ()))
+                               .Select (i => full.GetName (i.InterfaceType))
                                .OrderBy (n => n)) {
                        if (first) {
                                buf.Append (" implements ");
@@ -4904,7 +4959,7 @@ class ILFullMemberFormatter : MemberFormatter {
                                        .Append (val.ToString ())
                                        .Append (')');
                        else if (val is IFormattable) {
-                               string value = ((IFormattable)val).ToString();
+                               string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
                                buf.Append (" = ");
                                if (val is string)
                                        buf.Append ("\"" + value + "\"");
@@ -5463,7 +5518,7 @@ class CSharpFullMemberFormatter : MemberFormatter {
                        else if (val is Enum)
                                buf.Append (" = ").Append (val.ToString ());
                        else if (val is IFormattable) {
-                               string value = ((IFormattable)val).ToString();
+                               string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
                                if (val is string)
                                        value = "\"" + value + "\"";
                                buf.Append (" = ").Append (value);
@@ -5615,15 +5670,6 @@ class SlashDocMemberFormatter : MemberFormatter {
                return buf;
        }
 
-       public override string GetDeclaration (MemberReference member)
-       {
-               TypeReference r = member as TypeReference;
-               if (r != null) {
-                       return "T:" + GetTypeName (r);
-               }
-               return base.GetDeclaration (member);
-       }
-
        protected override string GetConstructorName (MethodReference constructor)
        {
                return GetMethodDefinitionName (constructor, "#ctor");