1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
15 using System.Xml.XPath;
20 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
21 using StringList = System.Collections.Generic.List<string>;
22 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
23 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
25 namespace Mono.Documentation {
27 class MDocUpdater : MDocCommand
30 List<AssemblyDefinition> assemblies;
31 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
35 bool no_assembly_versions, ignore_missing_types;
36 ExceptionLocations? exceptions;
38 internal int additions = 0, deletions = 0;
40 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
42 DocumentationEnumerator docEnum;
46 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
47 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
49 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
50 new CSharpMemberFormatter (),
51 new ILMemberFormatter (),
54 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
55 new CSharpFullMemberFormatter (),
56 new ILFullMemberFormatter (),
59 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
61 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
63 HashSet<string> forwardedTypes = new HashSet<string> ();
65 public override void Run (IEnumerable<string> args)
67 show_exceptions = DebugOutput;
68 var types = new List<string> ();
69 var p = new OptionSet () {
71 "Delete removed members from the XML files.",
72 v => delete = v != null },
74 "Document potential exceptions that members can generate. {SOURCES} " +
75 "is a comma-separated list of:\n" +
76 " asm Method calls in same assembly\n" +
77 " depasm Method calls in dependent assemblies\n" +
78 " all Record all possible exceptions\n" +
79 " added Modifier; only create <exception/>s\n" +
80 " for NEW types/members\n" +
81 "If nothing is specified, then only exceptions from the member will " +
83 v => exceptions = ParseExceptionLocations (v) },
85 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
88 case "ignore-missing-types":
89 ignore_missing_types = true;
91 case "no-assembly-versions":
92 no_assembly_versions = true;
95 throw new Exception ("Unsupported flag `" + v + "'.");
98 { "fignore-missing-types",
99 "Do not report an error if a --type=TYPE type\nwas not found.",
100 v => ignore_missing_types = v != null },
101 { "fno-assembly-versions",
102 "Do not generate //AssemblyVersion elements.",
103 v => no_assembly_versions = v != null },
105 "Import documentation from {FILE}.",
106 v => AddImporter (v) },
108 "Check for assembly references in {DIRECTORY}.",
109 v => assemblyResolver.AddSearchDirectory (v) },
111 "Ignored for compatibility with update-ecma-xml.",
114 "Root {DIRECTORY} to generate/update documentation.",
117 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
118 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
119 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
121 "Manually specify the assembly {VERSION} that new members were added in.",
124 "Only update documentation for {TYPE}.",
125 v => types.Add (v) },
127 var assemblies = Parse (p, args, "update",
128 "[OPTIONS]+ ASSEMBLIES",
129 "Create or update documentation from ASSEMBLIES.");
130 if (assemblies == null)
132 if (assemblies.Count == 0)
133 Error ("No assemblies specified.");
135 foreach (var dir in assemblies
136 .Where (a => a.Contains (Path.DirectorySeparatorChar))
137 .Select (a => Path.GetDirectoryName (a)))
138 assemblyResolver.AddSearchDirectory (dir);
140 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
143 throw new InvalidOperationException("The --out option is required.");
145 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
147 // Store types that have been forwarded to avoid duplicate generation
148 GatherForwardedTypes ();
150 docEnum = docEnum ?? new DocumentationEnumerator ();
152 // PERFORM THE UPDATES
154 if (types.Count > 0) {
156 DoUpdateTypes (srcPath, types, srcPath);
159 else if (opts.@namespace != null)
160 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
161 Path.Combine (dest_dir, opts.@namespace));
164 DoUpdateAssemblies (srcPath, srcPath);
166 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
169 void AddImporter (string path)
172 XmlReader r = new XmlTextReader (path);
174 while (r.NodeType != XmlNodeType.Element) {
176 Error ("Unable to read XML file: {0}.", path);
178 if (r.LocalName == "doc") {
179 importers.Add (new MsxdocDocumentationImporter (path));
181 else if (r.LocalName == "Libraries") {
182 var ecmadocs = new XmlTextReader (path);
183 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
184 importers.Add (new EcmaDocumentationImporter (ecmadocs));
187 Error ("Unsupported XML format within {0}.", path);
190 } catch (Exception e) {
191 Environment.ExitCode = 1;
192 Error ("Could not load XML file: {0}.", e.Message);
196 void GatherForwardedTypes ()
198 foreach (var asm in assemblies)
199 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
200 forwardedTypes.Add (type);
203 static ExceptionLocations ParseExceptionLocations (string s)
205 ExceptionLocations loc = ExceptionLocations.Member;
208 foreach (var type in s.Split (',')) {
210 case "added": loc |= ExceptionLocations.AddedMembers; break;
211 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
212 case "asm": loc |= ExceptionLocations.Assembly; break;
213 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
214 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
220 internal void Warning (string format, params object[] args)
222 Message (TraceLevel.Warning, "mdoc: " + format, args);
225 private AssemblyDefinition LoadAssembly (string name)
227 AssemblyDefinition assembly = null;
229 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
230 } catch (System.IO.FileNotFoundException) { }
232 if (assembly == null)
233 throw new InvalidOperationException("Assembly " + name + " not found.");
238 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
239 OrderTypeAttributes (element);
240 XmlTextWriter writer = new XmlTextWriter(output);
241 writer.Formatting = Formatting.Indented;
242 writer.Indentation = 2;
243 writer.IndentChar = ' ';
244 element.WriteTo(writer);
248 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
250 Action<string> creator = file => {
251 using (var writer = OpenWrite (file, mode))
255 MdocFile.UpdateFile (filename, creator);
258 private static void OrderTypeAttributes (XmlElement e)
260 foreach (XmlElement type in e.SelectNodes ("//Type")) {
261 OrderTypeAttributes (type.Attributes);
265 static readonly string[] TypeAttributeOrder = {
266 "Name", "FullName", "FullNameSP", "Maintainer"
269 private static void OrderTypeAttributes (XmlAttributeCollection c)
271 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
272 for (int i = 0; i < c.Count; ++i) {
273 XmlAttribute a = c [i];
274 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
275 if (a.Name == TypeAttributeOrder [j]) {
281 for (int i = attrs.Length-1; i >= 0; --i) {
282 XmlAttribute n = attrs [i];
285 XmlAttribute r = null;
286 for (int j = i+1; j < attrs.Length; ++j) {
287 if (attrs [j] != null) {
295 c.InsertBefore (n, r);
299 private XmlDocument CreateIndexStub()
301 XmlDocument index = new XmlDocument();
303 XmlElement index_root = index.CreateElement("Overview");
304 index.AppendChild(index_root);
306 if (assemblies.Count == 0)
307 throw new Exception ("No assembly");
309 XmlElement index_assemblies = index.CreateElement("Assemblies");
310 index_root.AppendChild(index_assemblies);
312 XmlElement index_remarks = index.CreateElement("Remarks");
313 index_remarks.InnerText = "To be added.";
314 index_root.AppendChild(index_remarks);
316 XmlElement index_copyright = index.CreateElement("Copyright");
317 index_copyright.InnerText = "To be added.";
318 index_root.AppendChild(index_copyright);
320 XmlElement index_types = index.CreateElement("Types");
321 index_root.AppendChild(index_types);
326 private static void WriteNamespaceStub(string ns, string outdir) {
327 XmlDocument index = new XmlDocument();
329 XmlElement index_root = index.CreateElement("Namespace");
330 index.AppendChild(index_root);
332 index_root.SetAttribute("Name", ns);
334 XmlElement index_docs = index.CreateElement("Docs");
335 index_root.AppendChild(index_docs);
337 XmlElement index_summary = index.CreateElement("summary");
338 index_summary.InnerText = "To be added.";
339 index_docs.AppendChild(index_summary);
341 XmlElement index_remarks = index.CreateElement("remarks");
342 index_remarks.InnerText = "To be added.";
343 index_docs.AppendChild(index_remarks);
345 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
346 writer => WriteXml (index.DocumentElement, writer));
349 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
351 var index = CreateIndexForTypes (dest);
353 var found = new HashSet<string> ();
354 foreach (AssemblyDefinition assembly in assemblies) {
355 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
356 string relpath = DoUpdateType (type, basepath, dest);
360 found.Add (type.FullName);
365 index.Add (assembly);
373 if (ignore_missing_types)
376 var notFound = from n in typenames where !found.Contains (n) select n;
378 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
381 class IndexForTypes {
387 XmlElement index_types;
388 XmlElement index_assemblies;
390 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
393 this.indexFile = indexFile;
396 index_types = WriteElement (index.DocumentElement, "Types");
397 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
400 public void Add (AssemblyDefinition assembly)
402 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
405 app.AddIndexAssembly (assembly, index_assemblies);
408 public void Add (TypeDefinition type)
410 app.AddIndexType (type, index_types);
415 SortIndexEntries (index_types);
416 WriteFile (indexFile, FileMode.Create,
417 writer => WriteXml (index.DocumentElement, writer));
421 IndexForTypes CreateIndexForTypes (string dest)
423 string indexFile = Path.Combine (dest, "index.xml");
424 if (File.Exists (indexFile))
426 return new IndexForTypes (this, indexFile, CreateIndexStub ());
429 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
431 if (type.Namespace == null)
432 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
434 if (!IsPublic (type))
437 // Must get the A+B form of the type name.
438 string typename = GetTypeFileName(type);
440 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
441 string typefile = Path.Combine (basepath, reltypefile);
442 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
444 string output = null;
447 } else if (dest == "-") {
450 output = Path.Combine (dest, reltypefile);
455 XmlDocument basefile = new XmlDocument();
457 basefile.Load(typefile);
458 } catch (Exception e) {
459 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
462 DoUpdateType2("Updating", basefile, type, output, false);
465 XmlElement td = StubType(type, output);
469 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
472 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
478 public void DoUpdateNS (string ns, string nspath, string outpath)
480 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
481 AssemblyDefinition assembly = assemblies [0];
483 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
484 XmlDocument basefile = new XmlDocument();
485 string typefile = Path.Combine(nspath, file.Name);
487 basefile.Load(typefile);
488 } catch (Exception e) {
489 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
493 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
494 TypeDefinition type = assembly.GetType(typename);
496 Warning ("Type no longer in assembly: " + typename);
500 seenTypes[type] = seenTypes;
501 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
504 // Stub types not in the directory
505 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
506 if (type.Namespace != ns || seenTypes.ContainsKey(type))
509 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
510 if (td == null) continue;
514 private static string GetTypeFileName (TypeReference type)
516 return filenameFormatter.GetName (type);
519 public static string GetTypeFileName (string typename)
521 StringBuilder filename = new StringBuilder (typename.Length);
525 for (int i = 0; i < typename.Length; ++i) {
526 char c = typename [i];
535 filename.Append ('`').Append ((numArgs+1).ToString());
550 return filename.ToString ();
553 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
555 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
556 index_assembly.SetAttribute ("Name", assembly.Name.Name);
557 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
559 AssemblyNameDefinition name = assembly.Name;
560 if (name.HasPublicKey) {
561 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
562 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
564 foreach (byte b in name.PublicKey)
565 key.AppendFormat ("{0,2:x2} ", b);
567 pubkey.InnerText = key.ToString ();
568 index_assembly.AppendChild (pubkey);
571 if (!string.IsNullOrEmpty (name.Culture)) {
572 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
573 culture.InnerText = name.Culture;
574 index_assembly.AppendChild (culture);
577 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
578 parent.AppendChild(index_assembly);
581 private void AddIndexType (TypeDefinition type, XmlElement index_types)
583 string typename = GetTypeFileName(type);
585 // Add namespace and type nodes into the index file as needed
586 string ns = DocUtils.GetNamespace (type);
587 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
588 if (nsnode == null) {
589 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
590 nsnode.SetAttribute ("Name", ns);
591 index_types.AppendChild (nsnode);
593 string doc_typename = GetDocTypeName (type);
594 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
595 if (typenode == null) {
596 typenode = index_types.OwnerDocument.CreateElement ("Type");
597 typenode.SetAttribute ("Name", typename);
598 nsnode.AppendChild (typenode);
600 if (typename != doc_typename)
601 typenode.SetAttribute("DisplayName", doc_typename);
603 typenode.RemoveAttribute("DisplayName");
605 typenode.SetAttribute ("Kind", GetTypeKind (type));
608 private void DoUpdateAssemblies (string source, string dest)
610 string indexfile = dest + "/index.xml";
612 if (System.IO.File.Exists(indexfile)) {
613 index = new XmlDocument();
614 index.Load(indexfile);
617 ClearElement(index.DocumentElement, "Assembly");
618 ClearElement(index.DocumentElement, "Attributes");
620 index = CreateIndexStub();
623 string defaultTitle = "Untitled";
624 if (assemblies.Count == 1)
625 defaultTitle = assemblies[0].Name.Name;
626 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
628 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
629 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
630 index_assemblies.RemoveAll ();
633 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
635 foreach (AssemblyDefinition assm in assemblies) {
636 AddIndexAssembly (assm, index_assemblies);
637 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
640 SortIndexEntries (index_types);
642 CleanupFiles (dest, goodfiles);
643 CleanupIndexTypes (index_types, goodfiles);
644 CleanupExtensions (index_types);
646 WriteFile (indexfile, FileMode.Create,
647 writer => WriteXml(index.DocumentElement, writer));
650 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
652 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
654 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
655 string typename = GetTypeFileName(type);
656 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
659 string reltypepath = DoUpdateType (type, source, dest);
660 if (reltypepath == null)
663 // Add namespace and type nodes into the index file as needed
664 AddIndexType (type, index_types);
666 // Ensure the namespace index file exists
667 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
668 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
669 if (File.Exists (onsdoc)) {
670 File.Move (onsdoc, nsdoc);
673 if (!File.Exists (nsdoc)) {
674 Console.WriteLine("New Namespace File: " + type.Namespace);
675 WriteNamespaceStub(type.Namespace, dest);
678 goodfiles.Add (reltypepath);
682 private static void SortIndexEntries (XmlElement indexTypes)
684 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
685 XmlNodeComparer c = new AttributeNameComparer ();
686 SortXmlNodes (indexTypes, namespaces, c);
688 for (int i = 0; i < namespaces.Count; ++i)
689 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
692 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
694 MyXmlNodeList l = new MyXmlNodeList (children.Count);
695 for (int i = 0; i < children.Count; ++i)
696 l.Add (children [i]);
698 for (int i = l.Count - 1; i > 0; --i) {
699 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
703 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
705 public abstract int Compare (XmlNode x, XmlNode y);
707 public int Compare (object x, object y)
709 return Compare ((XmlNode) x, (XmlNode) y);
713 class AttributeNameComparer : XmlNodeComparer {
716 public AttributeNameComparer ()
721 public AttributeNameComparer (string attribute)
723 this.attribute = attribute;
726 public override int Compare (XmlNode x, XmlNode y)
728 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
732 class VersionComparer : XmlNodeComparer {
733 public override int Compare (XmlNode x, XmlNode y)
735 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
736 string a = GetVersion (x.InnerText);
737 string b = GetVersion (y.InnerText);
738 return new Version (a).CompareTo (new Version (b));
741 static string GetVersion (string v)
743 int n = v.IndexOf ("x");
746 return v.Substring (0, n-1);
750 private static string GetTypeKind (TypeDefinition type)
753 return "Enumeration";
754 if (type.IsValueType)
756 if (type.IsInterface)
758 if (DocUtils.IsDelegate (type))
760 if (type.IsClass || type.FullName == "System.Enum") // FIXME
762 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
765 private static bool IsPublic (TypeDefinition type)
767 TypeDefinition decl = type;
768 while (decl != null) {
769 if (!(decl.IsPublic || decl.IsNestedPublic ||
770 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
773 decl = (TypeDefinition) decl.DeclaringType;
778 private void CleanupFiles (string dest, HashSet<string> goodfiles)
780 // Look for files that no longer correspond to types
781 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
782 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
783 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
784 if (!goodfiles.Contains (relTypeFile)) {
785 XmlDocument doc = new XmlDocument ();
786 doc.Load (typefile.FullName);
787 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
788 if (e != null && !no_assembly_versions && UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
789 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
790 WriteXml(doc.DocumentElement, writer);
791 goodfiles.Add (relTypeFile);
794 string newname = typefile.FullName + ".remove";
795 try { System.IO.File.Delete(newname); } catch (Exception) { }
796 try { typefile.MoveTo(newname); } catch (Exception) { }
797 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
803 private static TextWriter OpenWrite (string path, FileMode mode)
805 var w = new StreamWriter (
806 new FileStream (path, mode),
807 new UTF8Encoding (false)
813 private string[] GetAssemblyVersions ()
815 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
818 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
820 // Look for type nodes that no longer correspond to types
821 MyXmlNodeList remove = new MyXmlNodeList ();
822 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
823 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
824 if (!goodfiles.Contains (fulltypename)) {
825 remove.Add (typenode);
828 foreach (XmlNode n in remove)
829 n.ParentNode.RemoveChild (n);
832 private void CleanupExtensions (XmlElement index_types)
834 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
835 if (extensionMethods.Count == 0) {
838 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
842 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
843 index_types.SelectSingleNode ("/Overview").AppendChild (e);
847 extensionMethods.Sort (DefaultExtensionMethodComparer);
848 foreach (XmlNode m in extensionMethods) {
849 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
853 class ExtensionMethodComparer : XmlNodeComparer {
854 public override int Compare (XmlNode x, XmlNode y)
856 XmlNode xLink = x.SelectSingleNode ("Member/Link");
857 XmlNode yLink = y.SelectSingleNode ("Member/Link");
859 int n = xLink.Attributes ["Type"].Value.CompareTo (
860 yLink.Attributes ["Type"].Value);
863 n = xLink.Attributes ["Member"].Value.CompareTo (
864 yLink.Attributes ["Member"].Value);
865 if (n == 0 && !object.ReferenceEquals (x, y))
866 throw new InvalidOperationException ("Duplicate extension method found!");
871 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
873 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
875 Console.WriteLine(message + ": " + type.FullName);
877 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
879 // Update type metadata
880 UpdateType(basefile.DocumentElement, type);
882 // Update existing members. Delete member nodes that no longer should be there,
883 // and remember what members are already documented so we don't add them again.
885 MyXmlNodeList todelete = new MyXmlNodeList ();
886 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
887 XmlElement oldmember = info.Node;
888 MemberReference oldmember2 = info.Member;
889 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
891 // Interface implementations and overrides are deleted from the docs
892 // unless the overrides option is given.
893 if (oldmember2 != null && sig == null)
896 // Deleted (or signature changed)
897 if (oldmember2 == null) {
898 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
900 DeleteMember ("Member Removed", output, oldmember, todelete);
905 if (seenmembers.ContainsKey (sig)) {
906 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
907 // ignore, already seen
909 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
910 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
912 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
916 // Update signature information
919 seenmembers.Add (sig, oldmember);
921 foreach (XmlElement oldmember in todelete)
922 oldmember.ParentNode.RemoveChild (oldmember);
925 if (!DocUtils.IsDelegate (type)) {
926 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
927 foreach (MemberReference m in type.GetMembers()) {
928 if (m is TypeDefinition) continue;
930 string sig = memberFormatters [0].GetDeclaration (m);
931 if (sig == null) continue;
932 if (seenmembers.ContainsKey(sig)) continue;
934 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
935 if (mm == null) continue;
936 members.AppendChild( mm );
938 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
943 // Import code snippets from files
944 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
945 if (!(code is XmlElement)) continue;
946 string file = ((XmlElement)code).GetAttribute("src");
947 string lang = ((XmlElement)code).GetAttribute("lang");
949 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
951 code.InnerText = src;
955 if (insertSince && since != null) {
956 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
957 docs.AppendChild (CreateSinceNode (basefile));
961 XmlElement d = basefile.DocumentElement ["Docs"];
962 XmlElement m = basefile.DocumentElement ["Members"];
963 if (d != null && m != null)
964 basefile.DocumentElement.InsertBefore (
965 basefile.DocumentElement.RemoveChild (d), m);
970 WriteXml(basefile.DocumentElement, Console.Out);
972 FileInfo file = new FileInfo (output);
973 if (!file.Directory.Exists) {
974 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
975 file.Directory.Create ();
977 WriteFile (output, FileMode.Create,
978 writer => WriteXml(basefile.DocumentElement, writer));
982 private string GetCodeSource (string lang, string file)
985 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
986 // Grab the specified region
987 string region = "#region " + file.Substring (anchorStart + 4);
988 file = file.Substring (0, anchorStart + 3);
990 using (StreamReader reader = new StreamReader (file)) {
992 StringBuilder src = new StringBuilder ();
994 while ((line = reader.ReadLine ()) != null) {
995 if (line.Trim() == region) {
996 indent = line.IndexOf (region);
999 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1004 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1007 return src.ToString ();
1009 } catch (Exception e) {
1010 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1011 file, region, show_exceptions ? e.ToString () : e.Message);
1016 using (StreamReader reader = new StreamReader (file))
1017 return reader.ReadToEnd ();
1018 } catch (Exception e) {
1019 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1024 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1026 string format = output != null
1027 ? "{0}: File='{1}'; Signature='{4}'"
1028 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1032 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1033 member.Attributes ["MemberName"].Value,
1034 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1035 if (!delete && MemberDocsHaveUserContent (member)) {
1036 Warning ("Member deletions must be enabled with the --delete option.");
1038 todelete.Add (member);
1043 class MemberComparer : XmlNodeComparer {
1044 public override int Compare (XmlNode x, XmlNode y)
1047 string xMemberName = x.Attributes ["MemberName"].Value;
1048 string yMemberName = y.Attributes ["MemberName"].Value;
1050 // generic methods *end* with '>'
1051 // it's possible for explicitly implemented generic interfaces to
1052 // contain <...> without being a generic method
1053 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1054 (r = xMemberName.CompareTo (yMemberName)) != 0)
1058 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1059 xMemberName = xMemberName.Substring (0, lt);
1060 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1061 yMemberName = yMemberName.Substring (0, lt);
1062 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1065 // if @MemberName matches, then it's either two different types of
1066 // members sharing the same name, e.g. field & property, or it's an
1067 // overloaded method.
1068 // for different type, sort based on MemberType value.
1069 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1070 y.SelectSingleNode ("MemberType").InnerText);
1074 // same type -- must be an overloaded method. Sort based on type
1075 // parameter count, then parameter count, then by the parameter
1077 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1078 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1079 if (xTypeParams.Count != yTypeParams.Count)
1080 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1081 for (int i = 0; i < xTypeParams.Count; ++i) {
1082 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1083 yTypeParams [i].Attributes ["Name"].Value);
1088 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1089 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1090 if (xParams.Count != yParams.Count)
1091 return xParams.Count <= yParams.Count ? -1 : 1;
1092 for (int i = 0; i < xParams.Count; ++i) {
1093 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1094 yParams [i].Attributes ["Type"].Value);
1098 // all parameters match, but return value might not match if it was
1099 // changed between one version and another.
1100 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1101 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1102 if (xReturn != null && yReturn != null) {
1103 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1112 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1114 private static void SortTypeMembers (XmlNode members)
1116 if (members == null)
1118 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1121 private static bool MemberDocsHaveUserContent (XmlNode e)
1123 e = (XmlElement)e.SelectSingleNode("Docs");
1124 if (e == null) return false;
1125 foreach (XmlElement d in e.SelectNodes("*"))
1126 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1131 // UPDATE HELPER FUNCTIONS
1133 // CREATE A STUB DOCUMENTATION FILE
1135 public XmlElement StubType (TypeDefinition type, string output)
1137 string typesig = typeFormatters [0].GetDeclaration (type);
1138 if (typesig == null) return null; // not publicly visible
1140 XmlDocument doc = new XmlDocument();
1141 XmlElement root = doc.CreateElement("Type");
1142 doc.AppendChild (root);
1144 DoUpdateType2 ("New Type", doc, type, output, true);
1149 private XmlElement CreateSinceNode (XmlDocument doc)
1151 XmlElement s = doc.CreateElement ("since");
1152 s.SetAttribute ("version", since);
1156 // STUBBING/UPDATING FUNCTIONS
1158 public void UpdateType (XmlElement root, TypeDefinition type)
1160 root.SetAttribute("Name", GetDocTypeName (type));
1161 root.SetAttribute("FullName", GetDocTypeFullName (type));
1163 foreach (MemberFormatter f in typeFormatters) {
1164 string element = "TypeSignature[@Language='" + f.Language + "']";
1165 WriteElementAttribute (root, element, "Language", f.Language);
1166 WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
1169 XmlElement ass = WriteElement(root, "AssemblyInfo");
1170 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1171 if (!no_assembly_versions) {
1172 UpdateAssemblyVersions (root, type, true);
1175 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1176 foreach (var version in versions)
1177 ass.RemoveChild (version);
1179 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1180 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1182 ClearElement(ass, "AssemblyCulture");
1184 // Why-oh-why do we put assembly attributes in each type file?
1185 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1186 // since they're outdated in current docs, and a waste of space.
1187 //MakeAttributes(ass, type.Assembly, true);
1188 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1189 if (assattrs != null)
1190 ass.RemoveChild(assattrs);
1192 NormalizeWhitespace(ass);
1194 if (type.IsGenericType ()) {
1195 MakeTypeParameters (root, type.GenericParameters);
1197 ClearElement(root, "TypeParameters");
1200 if (type.BaseType != null) {
1201 XmlElement basenode = WriteElement(root, "Base");
1203 string basetypename = GetDocTypeFullName (type.BaseType);
1204 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1205 WriteElementText(root, "Base/BaseTypeName", basetypename);
1207 // Document how this type instantiates the generic parameters of its base type
1208 TypeReference origBase = type.BaseType.GetElementType ();
1209 if (origBase.IsGenericType ()) {
1210 ClearElement(basenode, "BaseTypeArguments");
1211 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1212 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1213 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1214 if (baseGenArgs.Count != baseGenParams.Count)
1215 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1216 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1217 GenericParameter param = baseGenParams [i];
1218 TypeReference value = baseGenArgs [i];
1220 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1221 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1222 bta.AppendChild(arg);
1223 arg.SetAttribute ("TypeParamName", param.Name);
1224 arg.InnerText = GetDocTypeFullName (value);
1228 ClearElement(root, "Base");
1231 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1232 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1233 List<string> interface_names = userInterfaces
1234 .Select (iface => GetDocTypeFullName (iface))
1238 XmlElement interfaces = WriteElement(root, "Interfaces");
1239 interfaces.RemoveAll();
1240 foreach (string iname in interface_names) {
1241 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1242 interfaces.AppendChild(iface);
1243 WriteElementText(iface, "InterfaceName", iname);
1246 ClearElement(root, "Interfaces");
1249 MakeAttributes (root, GetCustomAttributes (type));
1251 if (DocUtils.IsDelegate (type)) {
1252 MakeTypeParameters (root, type.GenericParameters);
1253 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1254 MakeReturnValue(root, type.GetMethod("Invoke"));
1257 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1258 MakeDocNode (typeInfo);
1260 if (!DocUtils.IsDelegate (type))
1261 WriteElement (root, "Members");
1263 OrderTypeNodes (root, root.ChildNodes);
1264 NormalizeWhitespace(root);
1267 static readonly string[] TypeNodeOrder = {
1271 "ThreadingSafetyStatement",
1272 "ThreadSafetyStatement",
1284 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1286 ReorderNodes (member, children, TypeNodeOrder);
1289 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1291 List<T> l = new List<T> (list);
1296 private void UpdateMember (DocsNodeInfo info)
1298 XmlElement me = (XmlElement) info.Node;
1299 MemberReference mi = info.Member;
1301 foreach (MemberFormatter f in memberFormatters) {
1302 string element = "MemberSignature[@Language='" + f.Language + "']";
1303 WriteElementAttribute (me, element, "Language", f.Language);
1304 WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
1307 WriteElementText(me, "MemberType", GetMemberType(mi));
1309 if (!no_assembly_versions) {
1310 UpdateAssemblyVersions (me, mi, true);
1313 ClearElement (me, "AssemblyInfo");
1316 MakeAttributes (me, GetCustomAttributes (mi));
1318 MakeReturnValue(me, mi);
1319 if (mi is MethodReference) {
1320 MethodReference mb = (MethodReference) mi;
1321 if (mb.IsGenericMethod ())
1322 MakeTypeParameters (me, mb.GenericParameters);
1324 MakeParameters(me, mi);
1327 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1328 WriteElementText(me, "MemberValue", fieldValue);
1330 info.Node = WriteElement (me, "Docs");
1332 OrderMemberNodes (me, me.ChildNodes);
1333 UpdateExtensionMethods (me, info);
1336 static readonly string[] MemberNodeOrder = {
1351 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1353 ReorderNodes (member, children, MemberNodeOrder);
1356 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1358 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1359 for (int i = 0; i < ordering.Length; ++i) {
1360 for (int j = 0; j < children.Count; ++j) {
1361 XmlNode c = children [j];
1362 if (c.Name == ordering [i]) {
1363 newChildren.Add (c);
1367 if (newChildren.Count >= 0)
1368 node.PrependChild ((XmlNode) newChildren [0]);
1369 for (int i = 1; i < newChildren.Count; ++i) {
1370 XmlNode prev = (XmlNode) newChildren [i-1];
1371 XmlNode cur = (XmlNode) newChildren [i];
1372 node.RemoveChild (cur);
1373 node.InsertAfter (cur, prev);
1377 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1379 IEnumerable<string> attrs = Enumerable.Empty<string>();
1381 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1383 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1385 PropertyDefinition pd = mi as PropertyDefinition;
1387 if (pd.GetMethod != null)
1388 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1389 if (pd.SetMethod != null)
1390 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1393 EventDefinition ed = mi as EventDefinition;
1395 if (ed.AddMethod != null)
1396 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1397 if (ed.RemoveMethod != null)
1398 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1404 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1406 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1408 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1409 if (attrType != null && !IsPublic (attrType))
1411 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1414 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1417 StringList fields = new StringList ();
1419 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1420 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1421 fields.Add (MakeAttributesValueString (
1426 (from namedArg in attribute.Fields
1427 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1429 (from namedArg in attribute.Properties
1430 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1431 .OrderBy (v => v.Name);
1432 foreach (var d in namedArgs)
1433 fields.Add (string.Format ("{0}={1}", d.Name,
1434 MakeAttributesValueString (d.Value, d.Type)));
1436 string a2 = String.Join(", ", fields.ToArray ());
1437 if (a2 != "") a2 = "(" + a2 + ")";
1439 string name = attribute.GetDeclaringType();
1440 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1441 yield return prefix + name + a2;
1445 static readonly string[] ValidExtensionMembers = {
1454 static readonly string[] ValidExtensionDocMembers = {
1460 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1462 MethodDefinition me = info.Member as MethodDefinition;
1465 if (info.Parameters.Count < 1)
1467 if (!DocUtils.IsExtensionMethod (me))
1470 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1471 XmlNode member = e.CloneNode (true);
1472 em.AppendChild (member);
1473 RemoveExcept (member, ValidExtensionMembers);
1474 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1475 WriteElementText (member, "MemberType", "ExtensionMethod");
1476 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1477 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1478 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1479 member.AppendChild (link);
1480 AddTargets (em, info);
1482 extensionMethods.Add (em);
1485 private static void RemoveExcept (XmlNode node, string[] except)
1489 MyXmlNodeList remove = null;
1490 foreach (XmlNode n in node.ChildNodes) {
1491 if (Array.BinarySearch (except, n.Name) < 0) {
1493 remove = new MyXmlNodeList ();
1498 foreach (XmlNode n in remove)
1499 node.RemoveChild (n);
1502 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1504 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1505 member.PrependChild (targets);
1506 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1507 AppendElementAttributeText (targets, "Target", "Type",
1508 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1511 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1512 IList<TypeReference> constraints = gp.Constraints;
1513 if (constraints.Count == 0)
1514 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1516 foreach (TypeReference c in constraints)
1517 AppendElementAttributeText(targets, "Target", "Type",
1518 slashdocFormatter.GetDeclaration (c));
1522 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1525 TypeDefinition type = field.DeclaringType.Resolve ();
1526 if (type != null && type.IsEnum) return false;
1528 if (type != null && type.IsGenericType ()) return false;
1529 if (!field.HasConstant)
1531 if (field.IsLiteral) {
1532 object val = field.Constant;
1533 if (val == null) value = "null";
1534 else if (val is Enum) value = val.ToString();
1535 else if (val is IFormattable) {
1536 value = ((IFormattable)val).ToString();
1538 value = "\"" + value + "\"";
1540 if (value != null && value != "")
1546 // XML HELPER FUNCTIONS
1548 internal static XmlElement WriteElement(XmlNode parent, string element) {
1549 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1551 string[] path = element.Split('/');
1552 foreach (string p in path) {
1553 ret = (XmlElement)parent.SelectSingleNode(p);
1556 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1557 ename = ename.Substring(0, ename.IndexOf('['));
1558 ret = parent.OwnerDocument.CreateElement(ename);
1559 parent.AppendChild(ret);
1568 private static void WriteElementText(XmlNode parent, string element, string value) {
1569 XmlElement node = WriteElement(parent, element);
1570 node.InnerText = value;
1573 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1575 XmlElement n = parent.OwnerDocument.CreateElement (element);
1576 parent.AppendChild (n);
1577 n.InnerText = value;
1581 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1583 XmlElement n = parent.OwnerDocument.CreateElement (element);
1584 parent.AppendChild (n);
1585 n.SetAttribute (attribute, value);
1589 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1591 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1592 dest.AppendChild (copy);
1596 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1597 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1600 node = WriteElement(parent, element);
1601 node.InnerText = value;
1603 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1604 XmlElement node = WriteElement(parent, element);
1605 if (node.GetAttribute(attribute) == value) return;
1606 node.SetAttribute(attribute, value);
1608 internal static void ClearElement(XmlElement parent, string name) {
1609 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1611 parent.RemoveChild(node);
1614 // DOCUMENTATION HELPER FUNCTIONS
1616 private void MakeDocNode (DocsNodeInfo info)
1618 List<GenericParameter> genericParams = info.GenericParameters;
1619 IList<ParameterDefinition> parameters = info.Parameters;
1620 TypeReference returntype = info.ReturnType;
1621 bool returnisreturn = info.ReturnIsReturn;
1622 XmlElement e = info.Node;
1623 bool addremarks = info.AddRemarks;
1625 WriteElementInitialText(e, "summary", "To be added.");
1627 if (parameters != null) {
1628 string[] values = new string [parameters.Count];
1629 for (int i = 0; i < values.Length; ++i)
1630 values [i] = parameters [i].Name;
1631 UpdateParameters (e, "param", values);
1634 if (genericParams != null) {
1635 string[] values = new string [genericParams.Count];
1636 for (int i = 0; i < values.Length; ++i)
1637 values [i] = genericParams [i].Name;
1638 UpdateParameters (e, "typeparam", values);
1641 string retnodename = null;
1642 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1643 retnodename = returnisreturn ? "returns" : "value";
1644 string retnodename_other = !returnisreturn ? "returns" : "value";
1646 // If it has a returns node instead of a value node, change its name.
1647 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1648 if (retother != null) {
1649 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1650 foreach (XmlNode node in retother)
1651 retnode.AppendChild(node.CloneNode(true));
1652 e.ReplaceChild(retnode, retother);
1654 WriteElementInitialText(e, retnodename, "To be added.");
1657 ClearElement(e, "returns");
1658 ClearElement(e, "value");
1662 WriteElementInitialText(e, "remarks", "To be added.");
1664 if (exceptions.HasValue && info.Member != null &&
1665 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1666 UpdateExceptions (e, info.Member);
1669 foreach (DocumentationImporter importer in importers)
1670 importer.ImportDocumentation (info);
1672 OrderDocsNodes (e, e.ChildNodes);
1673 NormalizeWhitespace(e);
1676 static readonly string[] DocsNodeOrder = {
1677 "typeparam", "param", "summary", "returns", "value", "remarks",
1680 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1682 ReorderNodes (docs, children, DocsNodeOrder);
1686 private void UpdateParameters (XmlElement e, string element, string[] values)
1688 if (values != null) {
1689 XmlNode[] paramnodes = new XmlNode[values.Length];
1691 // Some documentation had param nodes with leading spaces.
1692 foreach (XmlElement paramnode in e.SelectNodes(element)){
1693 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1696 // If a member has only one parameter, we can track changes to
1697 // the name of the parameter easily.
1698 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1699 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1702 bool reinsert = false;
1704 // Pick out existing and still-valid param nodes, and
1705 // create nodes for parameters not in the file.
1706 Hashtable seenParams = new Hashtable();
1707 for (int pi = 0; pi < values.Length; pi++) {
1708 string p = values [pi];
1711 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1712 if (paramnodes[pi] != null) continue;
1714 XmlElement pe = e.OwnerDocument.CreateElement(element);
1715 pe.SetAttribute("name", p);
1716 pe.InnerText = "To be added.";
1717 paramnodes[pi] = pe;
1721 // Remove parameters that no longer exist and check all params are in the right order.
1723 MyXmlNodeList todelete = new MyXmlNodeList ();
1724 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1725 string name = paramnode.GetAttribute("name");
1726 if (!seenParams.ContainsKey(name)) {
1727 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1728 Warning ("The following param node can only be deleted if the --delete option is given: ");
1729 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1731 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1732 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1736 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1737 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1738 e.ParentNode.Attributes ["MemberName"].Value,
1741 Warning ("\tValue={0}", paramnode.OuterXml);
1743 todelete.Add (paramnode);
1748 if ((int)seenParams[name] != idx)
1754 foreach (XmlNode n in todelete) {
1755 n.ParentNode.RemoveChild (n);
1758 // Re-insert the parameter nodes at the top of the doc section.
1760 for (int pi = values.Length-1; pi >= 0; pi--)
1761 e.PrependChild(paramnodes[pi]);
1763 // Clear all existing param nodes
1764 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1765 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1766 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1767 Console.WriteLine(paramnode.OuterXml);
1769 paramnode.ParentNode.RemoveChild(paramnode);
1775 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1777 string existingName = pe.GetAttribute ("name");
1778 pe.SetAttribute ("name", newName);
1779 if (existingName == newName)
1781 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1782 if (paramref.GetAttribute ("name").Trim () == existingName)
1783 paramref.SetAttribute ("name", newName);
1786 class CrefComparer : XmlNodeComparer {
1788 public CrefComparer ()
1792 public override int Compare (XmlNode x, XmlNode y)
1794 string xType = x.Attributes ["cref"].Value;
1795 string yType = y.Attributes ["cref"].Value;
1796 string xNamespace = GetNamespace (xType);
1797 string yNamespace = GetNamespace (yType);
1799 int c = xNamespace.CompareTo (yNamespace);
1802 return xType.CompareTo (yType);
1805 static string GetNamespace (string type)
1807 int n = type.LastIndexOf ('.');
1809 return type.Substring (0, n);
1810 return string.Empty;
1814 private void UpdateExceptions (XmlNode docs, MemberReference member)
1816 string indent = new string (' ', 10);
1817 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1818 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1819 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1822 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1823 e.SetAttribute ("cref", cref);
1824 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
1825 string.Join ("\" />,\n" + indent + "<see cref=\"",
1826 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1827 .OrderBy (s => s)) +
1829 docs.AppendChild (e);
1831 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1832 new CrefComparer ());
1835 private static void NormalizeWhitespace(XmlElement e) {
1836 // Remove all text and whitespace nodes from the element so it
1837 // is outputted with nice indentation and no blank lines.
1838 ArrayList deleteNodes = new ArrayList();
1839 foreach (XmlNode n in e)
1840 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1842 foreach (XmlNode n in deleteNodes)
1843 n.ParentNode.RemoveChild(n);
1846 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1848 TypeDefinition type = member as TypeDefinition;
1850 type = member.DeclaringType as TypeDefinition;
1851 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1854 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1856 return assembly.Name.Version.ToString();
1859 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1861 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
1863 // AssemblyVersions is not part of the spec
1864 root.RemoveChild (av);
1867 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1869 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1870 root.AppendChild(e);
1872 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1873 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1875 // matches.Count > 0 && add: ignore -- already present
1876 if (matches.Count > 0 && !add) {
1877 foreach (XmlNode c in matches)
1880 else if (matches.Count == 0 && add) {
1881 foreach (string sv in assemblyVersions) {
1882 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1887 // matches.Count == 0 && !add: ignore -- already not present
1889 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1890 SortXmlNodes (e, avs, new VersionComparer ());
1892 return avs.Count != 0;
1895 // FIXME: get TypeReferences instead of string comparison?
1896 private static string[] IgnorableAttributes = {
1897 // Security related attributes
1898 "System.Reflection.AssemblyKeyFileAttribute",
1899 "System.Reflection.AssemblyDelaySignAttribute",
1900 // Present in @RefType
1901 "System.Runtime.InteropServices.OutAttribute",
1902 // For naming the indexer to use when not using indexers
1903 "System.Reflection.DefaultMemberAttribute",
1904 // for decimal constants
1905 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1906 // compiler generated code
1907 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1908 // more compiler generated code, e.g. iterator methods
1909 "System.Diagnostics.DebuggerHiddenAttribute",
1910 "System.Runtime.CompilerServices.FixedBufferAttribute",
1911 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1912 // extension methods
1913 "System.Runtime.CompilerServices.ExtensionAttribute",
1914 // Used to differentiate 'object' from C#4 'dynamic'
1915 "System.Runtime.CompilerServices.DynamicAttribute",
1918 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1920 if (!attributes.Any ()) {
1921 ClearElement (root, "Attributes");
1925 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1929 e = root.OwnerDocument.CreateElement("Attributes");
1931 foreach (string attribute in attributes) {
1932 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1935 WriteElementText(ae, "AttributeName", attribute);
1938 if (e.ParentNode == null)
1939 root.AppendChild(e);
1941 NormalizeWhitespace(e);
1944 public static string MakeAttributesValueString (object v, TypeReference valueType)
1948 if (valueType.FullName == "System.Type")
1949 return "typeof(" + v.ToString () + ")";
1950 if (valueType.FullName == "System.String")
1951 return "\"" + v.ToString () + "\"";
1952 if (valueType.FullName == "System.Char")
1953 return "'" + v.ToString () + "'";
1955 return (bool)v ? "true" : "false";
1956 TypeDefinition valueDef = valueType.Resolve ();
1957 if (valueDef == null || !valueDef.IsEnum)
1958 return v.ToString ();
1959 string typename = GetDocTypeFullName (valueType);
1960 var values = GetEnumerationValues (valueDef);
1961 long c = ToInt64 (v);
1962 if (values.ContainsKey (c))
1963 return typename + "." + values [c];
1964 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1965 return string.Join (" | ",
1966 (from i in values.Keys
1968 select typename + "." + values [i])
1969 .DefaultIfEmpty (v.ToString ()).ToArray ());
1971 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1974 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1976 var values = new Dictionary<long, string> ();
1978 (from f in type.Fields
1979 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1981 values [ToInt64 (f.Constant)] = f.Name;
1986 static long ToInt64 (object value)
1989 return (long) (ulong) value;
1990 return Convert.ToInt64 (value);
1993 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1995 XmlElement e = WriteElement(root, "Parameters");
1997 foreach (ParameterDefinition p in parameters) {
1998 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2000 pe.SetAttribute("Name", p.Name);
2001 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2002 if (p.ParameterType is ByReferenceType) {
2003 if (p.IsOut) pe.SetAttribute("RefType", "out");
2004 else pe.SetAttribute("RefType", "ref");
2006 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2010 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
2012 if (typeParams == null || typeParams.Count == 0) {
2013 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2015 root.RemoveChild (f);
2018 XmlElement e = WriteElement(root, "TypeParameters");
2020 foreach (GenericParameter t in typeParams) {
2021 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2023 pe.SetAttribute("Name", t.Name);
2024 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2025 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2026 IList<TypeReference> constraints = t.Constraints;
2027 GenericParameterAttributes attrs = t.Attributes;
2028 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2036 ce = root.OwnerDocument.CreateElement ("Constraints");
2038 pe.AppendChild (ce);
2039 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2040 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2041 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2042 AppendElementText (ce, "ParameterAttribute", "Covariant");
2043 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2044 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2045 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2046 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2047 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2048 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2049 foreach (TypeReference c in constraints) {
2050 TypeDefinition cd = c.Resolve ();
2051 AppendElementText (ce,
2052 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2053 GetDocTypeFullName (c));
2058 private void MakeParameters (XmlElement root, MemberReference mi)
2060 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2061 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2062 else if (mi is MethodDefinition) {
2063 MethodDefinition mb = (MethodDefinition) mi;
2064 IList<ParameterDefinition> parameters = mb.Parameters;
2065 MakeParameters(root, parameters);
2066 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2067 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2068 p.SetAttribute ("RefType", "this");
2071 else if (mi is PropertyDefinition) {
2072 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2073 if (parameters.Count > 0)
2074 MakeParameters(root, parameters);
2078 else if (mi is FieldDefinition) return;
2079 else if (mi is EventDefinition) return;
2080 else throw new ArgumentException();
2083 internal static string GetDocParameterType (TypeReference type)
2085 return GetDocTypeFullName (type).Replace ("@", "&");
2088 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
2090 XmlElement e = WriteElement(root, "ReturnValue");
2092 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2093 if (attributes != null)
2094 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2097 private void MakeReturnValue (XmlElement root, MemberReference mi)
2099 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2101 else if (mi is MethodDefinition)
2102 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2103 else if (mi is PropertyDefinition)
2104 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2105 else if (mi is FieldDefinition)
2106 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2107 else if (mi is EventDefinition)
2108 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2110 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2113 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2115 MemberReference mi = info.Member;
2116 if (mi is TypeDefinition) return null;
2118 string sigs = memberFormatters [0].GetDeclaration (mi);
2119 if (sigs == null) return null; // not publicly visible
2121 // no documentation for property/event accessors. Is there a better way of doing this?
2122 if (mi.Name.StartsWith("get_")) return null;
2123 if (mi.Name.StartsWith("set_")) return null;
2124 if (mi.Name.StartsWith("add_")) return null;
2125 if (mi.Name.StartsWith("remove_")) return null;
2126 if (mi.Name.StartsWith("raise_")) return null;
2128 XmlElement me = doc.CreateElement("Member");
2129 me.SetAttribute("MemberName", GetMemberName (mi));
2133 if (exceptions.HasValue &&
2134 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2135 UpdateExceptions (info.Node, info.Member);
2137 if (since != null) {
2138 XmlNode docs = me.SelectSingleNode("Docs");
2139 docs.AppendChild (CreateSinceNode (doc));
2145 internal static string GetMemberName (MemberReference mi)
2147 MethodDefinition mb = mi as MethodDefinition;
2149 PropertyDefinition pi = mi as PropertyDefinition;
2152 return DocUtils.GetPropertyName (pi);
2154 StringBuilder sb = new StringBuilder (mi.Name.Length);
2155 if (!DocUtils.IsExplicitlyImplemented (mb))
2156 sb.Append (mi.Name);
2158 TypeReference iface;
2159 MethodReference ifaceMethod;
2160 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2161 sb.Append (GetDocTypeFullName (iface));
2163 sb.Append (ifaceMethod.Name);
2165 if (mb.IsGenericMethod ()) {
2166 IList<GenericParameter> typeParams = mb.GenericParameters;
2167 if (typeParams.Count > 0) {
2169 sb.Append (typeParams [0].Name);
2170 for (int i = 1; i < typeParams.Count; ++i)
2171 sb.Append (",").Append (typeParams [i].Name);
2175 return sb.ToString ();
2178 /// SIGNATURE GENERATION FUNCTIONS
2179 internal static bool IsPrivate (MemberReference mi)
2181 return memberFormatters [0].GetDeclaration (mi) == null;
2184 internal static string GetMemberType (MemberReference mi)
2186 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2187 return "Constructor";
2188 if (mi is MethodDefinition)
2190 if (mi is PropertyDefinition)
2192 if (mi is FieldDefinition)
2194 if (mi is EventDefinition)
2196 throw new ArgumentException();
2199 private static string GetDocTypeName (TypeReference type)
2201 return docTypeFormatter.GetName (type);
2204 internal static string GetDocTypeFullName (TypeReference type)
2206 return DocTypeFullMemberFormatter.Default.GetName (type);
2209 internal static string GetXPathForMember (DocumentationMember member)
2211 StringBuilder xpath = new StringBuilder ();
2212 xpath.Append ("//Members/Member[@MemberName=\"")
2213 .Append (member.MemberName)
2215 if (member.Parameters != null && member.Parameters.Count > 0) {
2216 xpath.Append ("/Parameters[count(Parameter) = ")
2217 .Append (member.Parameters.Count);
2218 for (int i = 0; i < member.Parameters.Count; ++i) {
2219 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2220 xpath.Append (member.Parameters [i]);
2221 xpath.Append ("\"");
2223 xpath.Append ("]/..");
2225 return xpath.ToString ();
2228 public static string GetXPathForMember (XPathNavigator member)
2230 StringBuilder xpath = new StringBuilder ();
2231 xpath.Append ("//Type[@FullName=\"")
2232 .Append (member.SelectSingleNode ("../../@FullName").Value)
2234 xpath.Append ("Members/Member[@MemberName=\"")
2235 .Append (member.SelectSingleNode ("@MemberName").Value)
2237 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2238 if (parameters.Count > 0) {
2239 xpath.Append ("/Parameters[count(Parameter) = ")
2240 .Append (parameters.Count);
2242 while (parameters.MoveNext ()) {
2244 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2245 xpath.Append (parameters.Current.Value);
2246 xpath.Append ("\"");
2248 xpath.Append ("]/..");
2250 return xpath.ToString ();
2253 public static string GetXPathForMember (MemberReference member)
2255 StringBuilder xpath = new StringBuilder ();
2256 xpath.Append ("//Type[@FullName=\"")
2257 .Append (member.DeclaringType.FullName)
2259 xpath.Append ("Members/Member[@MemberName=\"")
2260 .Append (GetMemberName (member))
2263 IList<ParameterDefinition> parameters = null;
2264 if (member is MethodDefinition)
2265 parameters = ((MethodDefinition) member).Parameters;
2266 else if (member is PropertyDefinition) {
2267 parameters = ((PropertyDefinition) member).Parameters;
2269 if (parameters != null && parameters.Count > 0) {
2270 xpath.Append ("/Parameters[count(Parameter) = ")
2271 .Append (parameters.Count);
2272 for (int i = 0; i < parameters.Count; ++i) {
2273 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2274 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2275 xpath.Append ("\"");
2277 xpath.Append ("]/..");
2279 return xpath.ToString ();
2283 static class CecilExtensions {
2284 public static string GetDeclaringType(this CustomAttribute attribute)
2286 return attribute.Constructor.DeclaringType.FullName;
2289 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2291 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2292 yield return (MemberReference) c;
2293 foreach (var e in type.Events)
2294 yield return (MemberReference) e;
2295 foreach (var f in type.Fields)
2296 yield return (MemberReference) f;
2297 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2298 yield return (MemberReference) m;
2299 foreach (var t in type.NestedTypes)
2300 yield return (MemberReference) t;
2301 foreach (var p in type.Properties)
2302 yield return (MemberReference) p;
2305 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2307 return GetMembers (type).Where (m => m.Name == member);
2310 public static MemberReference GetMember (this TypeDefinition type, string member)
2312 return GetMembers (type, member).EnsureZeroOrOne ();
2315 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2317 if (source.Count () > 1)
2318 throw new InvalidOperationException ("too many matches");
2319 return source.FirstOrDefault ();
2322 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2325 .Where (m => m.Name == method)
2326 .EnsureZeroOrOne ();
2329 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2331 TypeDefinition def = type as TypeDefinition;
2333 return new MemberReference [0];
2334 CustomAttribute defMemberAttr = def.CustomAttributes
2335 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2336 if (defMemberAttr == null)
2337 return new MemberReference [0];
2338 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2339 return def.Properties
2340 .Where (p => p.Name == name)
2341 .Select (p => (MemberReference) p);
2344 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2346 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2349 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2351 return GetTypes (assembly)
2352 .Where (td => td.FullName == type)
2353 .EnsureZeroOrOne ();
2356 public static bool IsGenericType (this TypeReference type)
2358 return type.GenericParameters.Count > 0;
2361 public static bool IsGenericMethod (this MethodReference method)
2363 return method.GenericParameters.Count > 0;
2366 public static MemberReference Resolve (this MemberReference member)
2368 FieldReference fr = member as FieldReference;
2370 return fr.Resolve ();
2371 MethodReference mr = member as MethodReference;
2373 return mr.Resolve ();
2374 TypeReference tr = member as TypeReference;
2376 return tr.Resolve ();
2377 PropertyReference pr = member as PropertyReference;
2380 EventReference er = member as EventReference;
2383 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2386 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2390 return type.Fields.First (f => f.Name == "value__").FieldType;
2393 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2395 return self.Types.SelectMany (t => t.GetAllTypes ());
2398 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2402 if (!self.HasNestedTypes)
2405 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2410 static class DocUtils {
2411 public static bool IsExplicitlyImplemented (MethodDefinition method)
2413 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2416 public static string GetTypeDotMember (string name)
2418 int startType, startMethod;
2419 startType = startMethod = -1;
2420 for (int i = 0; i < name.Length; ++i) {
2421 if (name [i] == '.') {
2422 startType = startMethod;
2426 return name.Substring (startType+1);
2429 public static string GetMember (string name)
2431 int i = name.LastIndexOf ('.');
2434 return name.Substring (i+1);
2437 public static void GetInfoForExplicitlyImplementedMethod (
2438 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2442 if (method.Overrides.Count != 1)
2443 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2444 iface = method.Overrides [0].DeclaringType;
2445 ifaceMethod = method.Overrides [0];
2448 public static string GetPropertyName (PropertyDefinition pi)
2450 // Issue: (g)mcs-generated assemblies that explicitly implement
2451 // properties don't specify the full namespace, just the
2452 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2453 MethodDefinition method = pi.GetMethod;
2455 method = pi.SetMethod;
2456 if (!IsExplicitlyImplemented (method))
2459 // Need to determine appropriate namespace for this member.
2460 TypeReference iface;
2461 MethodReference ifaceMethod;
2462 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2463 return string.Join (".", new string[]{
2464 DocTypeFullMemberFormatter.Default.GetName (iface),
2465 GetMember (pi.Name)});
2468 public static string GetNamespace (TypeReference type)
2470 if (type.GetElementType ().IsNested)
2471 type = type.GetElementType ();
2472 while (type != null && type.IsNested)
2473 type = type.DeclaringType;
2475 return string.Empty;
2476 return type.Namespace;
2479 public static string PathCombine (string dir, string path)
2485 return Path.Combine (dir, path);
2488 public static bool IsExtensionMethod (MethodDefinition method)
2491 method.CustomAttributes
2492 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2493 && method.DeclaringType.CustomAttributes
2494 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2497 public static bool IsDelegate (TypeDefinition type)
2499 TypeReference baseRef = type.BaseType;
2500 if (baseRef == null)
2502 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2503 baseRef.FullName == "System.MulticastDelegate";
2506 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2508 List<TypeReference> decls = new List<TypeReference> ();
2510 while (type.DeclaringType != null) {
2511 decls.Add (type.DeclaringType);
2512 type = type.DeclaringType;
2518 public static int GetGenericArgumentCount (TypeReference type)
2520 GenericInstanceType inst = type as GenericInstanceType;
2522 ? inst.GenericArguments.Count
2523 : type.GenericParameters.Count;
2526 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2528 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2529 List<TypeReference> userInterfaces = new List<TypeReference> ();
2530 foreach (TypeReference iface in type.Interfaces) {
2531 TypeReference lookup = iface.Resolve () ?? iface;
2532 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2533 userInterfaces.Add (iface);
2535 return userInterfaces;
2538 private static string GetQualifiedTypeName (TypeReference type)
2540 return "[" + type.Scope.Name + "]" + type.FullName;
2543 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2545 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2546 Action<TypeDefinition> a = null;
2548 if (t == null) return;
2549 foreach (TypeReference r in t.Interfaces) {
2550 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2554 TypeReference baseRef = type.BaseType;
2555 while (baseRef != null) {
2556 TypeDefinition baseDef = baseRef.Resolve ();
2557 if (baseDef != null) {
2559 baseRef = baseDef.BaseType;
2564 foreach (TypeReference r in type.Interfaces)
2566 return inheritedInterfaces;
2570 class DocsNodeInfo {
2571 public DocsNodeInfo (XmlElement node)
2576 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2582 public DocsNodeInfo (XmlElement node, MemberReference member)
2585 SetMemberInfo (member);
2588 void SetType (TypeDefinition type)
2591 throw new ArgumentNullException ("type");
2593 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2594 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2595 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2596 for (int i = 0; i < declTypes.Count - 1; ++i) {
2597 int remove = System.Math.Min (maxGenArgs,
2598 DocUtils.GetGenericArgumentCount (declTypes [i]));
2599 maxGenArgs -= remove;
2600 while (remove-- > 0)
2601 GenericParameters.RemoveAt (0);
2603 if (DocUtils.IsDelegate (type)) {
2604 Parameters = type.GetMethod("Invoke").Parameters;
2605 ReturnType = type.GetMethod("Invoke").ReturnType;
2606 ReturnIsReturn = true;
2610 void SetMemberInfo (MemberReference member)
2613 throw new ArgumentNullException ("member");
2614 ReturnIsReturn = true;
2618 if (member is MethodReference ) {
2619 MethodReference mr = (MethodReference) member;
2620 Parameters = mr.Parameters;
2621 if (mr.IsGenericMethod ()) {
2622 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2625 else if (member is PropertyDefinition) {
2626 Parameters = ((PropertyDefinition) member).Parameters;
2629 if (member is MethodDefinition) {
2630 ReturnType = ((MethodDefinition) member).ReturnType;
2631 } else if (member is PropertyDefinition) {
2632 ReturnType = ((PropertyDefinition) member).PropertyType;
2633 ReturnIsReturn = false;
2636 // no remarks section for enum members
2637 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2641 public TypeReference ReturnType;
2642 public List<GenericParameter> GenericParameters;
2643 public IList<ParameterDefinition> Parameters;
2644 public bool ReturnIsReturn;
2645 public XmlElement Node;
2646 public bool AddRemarks = true;
2647 public MemberReference Member;
2648 public TypeDefinition Type;
2651 class DocumentationEnumerator {
2653 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2655 return GetDocumentationTypes (assembly, forTypes, null);
2658 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2660 foreach (TypeDefinition type in assembly.GetTypes()) {
2661 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2663 if (seen != null && seen.Contains (type.FullName))
2666 foreach (TypeDefinition nested in type.NestedTypes)
2667 yield return nested;
2671 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2673 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2674 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2675 oldmember.RemoveAttribute ("__monodocer-seen__");
2678 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2680 yield return new DocsNodeInfo (oldmember);
2683 yield return new DocsNodeInfo (oldmember, m);
2688 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2690 string membertype = member.MemberType;
2692 string returntype = member.ReturnType;
2694 string docName = member.MemberName;
2695 string[] docTypeParams = GetTypeParameters (docName);
2697 // Loop through all members in this type with the same name
2698 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2699 if (mi is TypeDefinition) continue;
2700 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2702 if (MDocUpdater.IsPrivate (mi))
2705 IList<ParameterDefinition> pis = null;
2706 string[] typeParams = null;
2707 if (mi is MethodDefinition) {
2708 MethodDefinition mb = (MethodDefinition) mi;
2709 pis = mb.Parameters;
2710 if (docTypeParams != null && mb.IsGenericMethod ()) {
2711 IList<GenericParameter> args = mb.GenericParameters;
2712 if (args.Count == docTypeParams.Length) {
2713 typeParams = args.Select (p => p.Name).ToArray ();
2717 else if (mi is PropertyDefinition)
2718 pis = ((PropertyDefinition)mi).Parameters;
2720 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2721 int pcount = pis == null ? 0 : pis.Count;
2722 if (mcount != pcount)
2725 MethodDefinition mDef = mi as MethodDefinition;
2726 if (mDef != null && !mDef.IsConstructor) {
2727 // Casting operators can overload based on return type.
2728 if (returntype != GetReplacedString (
2729 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
2730 typeParams, docTypeParams)) {
2738 for (int i = 0; i < pis.Count; i++) {
2739 string paramType = GetReplacedString (
2740 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2741 typeParams, docTypeParams);
2742 if (paramType != (string) member.Parameters [i]) {
2747 if (!good) continue;
2755 static string[] GetTypeParameters (string docName)
2757 if (docName [docName.Length-1] != '>')
2759 StringList types = new StringList ();
2760 int endToken = docName.Length-2;
2761 int i = docName.Length-2;
2763 if (docName [i] == ',' || docName [i] == '<') {
2764 types.Add (docName.Substring (i + 1, endToken - i));
2767 if (docName [i] == '<')
2772 return types.ToArray ();
2775 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2777 // need to worry about 4 forms of //@MemberName values:
2778 // 1. "Normal" (non-generic) member names: GetEnumerator
2780 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2781 // - try as-is, and try type.member (due to "kludge" for property
2783 // 3. "Normal" Generic member names: Sort<T> (CSC)
2784 // - need to remove generic parameters --> "Sort"
2785 // 4. Explicitly-implemented interface members for generic interfaces:
2786 // -- System.Collections.Generic.IEnumerable<T>.Current
2787 // - Try as-is, and try type.member, *keeping* the generic parameters.
2788 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2789 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2790 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2791 // this as (1) or (2).
2792 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2794 foreach (MemberReference mi in type.GetMembers (docName))
2796 if (CountChars (docName, '.') > 0)
2797 // might be a property; try only type.member instead of
2798 // namespace.type.member.
2799 foreach (MemberReference mi in
2800 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2807 int startLt, startType, startMethod;
2808 startLt = startType = startMethod = -1;
2809 for (int i = 0; i < docName.Length; ++i) {
2810 switch (docName [i]) {
2819 if (numLt == 0 && (i + 1) < docName.Length)
2820 // there's another character in docName, so this <...> sequence is
2821 // probably part of a generic type -- case 4.
2825 startType = startMethod;
2831 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2833 foreach (MemberReference mi in type.GetMembers (refName))
2837 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2840 // If we _still_ haven't found it, we've hit another generic naming issue:
2841 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2842 // explicitly-implemented METHOD names (not properties), e.g.
2843 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2844 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2845 // which the XML docs will contain.
2847 // Alas, we can't derive the Mono name from docName, so we need to iterate
2848 // over all member names, convert them into CSC format, and compare... :-(
2851 foreach (MemberReference mi in type.GetMembers ()) {
2852 if (MDocUpdater.GetMemberName (mi) == docName)
2857 static string GetReplacedString (string typeName, string[] from, string[] to)
2861 for (int i = 0; i < from.Length; ++i)
2862 typeName = typeName.Replace (from [i], to [i]);
2866 private static int CountChars (string s, char c)
2869 for (int i = 0; i < s.Length; ++i) {
2877 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2882 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2885 this.ecmadocs = ecmaDocs;
2888 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2890 HashSet<string> seen = new HashSet<string> ();
2891 return GetDocumentationTypes (assembly, forTypes, seen)
2892 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2895 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2898 while (ecmadocs.Read ()) {
2899 switch (ecmadocs.Name) {
2901 if (typeDepth == -1)
2902 typeDepth = ecmadocs.Depth;
2903 if (ecmadocs.NodeType != XmlNodeType.Element)
2905 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2907 string typename = ecmadocs.GetAttribute ("FullName");
2908 string typename2 = MDocUpdater.GetTypeFileName (typename);
2909 if (forTypes != null &&
2910 forTypes.BinarySearch (typename) < 0 &&
2911 typename != typename2 &&
2912 forTypes.BinarySearch (typename2) < 0)
2915 if ((t = assembly.GetType (typename)) == null &&
2916 (t = assembly.GetType (typename2)) == null)
2918 seen.Add (typename);
2919 if (typename != typename2)
2920 seen.Add (typename2);
2921 Console.WriteLine (" Import: {0}", t.FullName);
2922 if (ecmadocs.Name != "Docs") {
2923 int depth = ecmadocs.Depth;
2924 while (ecmadocs.Read ()) {
2925 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2929 if (!ecmadocs.IsStartElement ("Docs"))
2930 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2940 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2942 return GetMembers (basefile, type)
2943 .Concat (base.GetDocumentationMembers (basefile, type));
2946 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2948 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2951 if (ecmadocs.IsEmptyElement)
2954 int membersDepth = ecmadocs.Depth;
2956 while (go && ecmadocs.Read ()) {
2957 switch (ecmadocs.Name) {
2959 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2961 DocumentationMember dm = new DocumentationMember (ecmadocs);
2962 string xp = MDocUpdater.GetXPathForMember (dm);
2963 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2965 if (oldmember == null) {
2966 m = GetMember (type, dm);
2968 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2969 type.FullName, dm.MemberSignatures ["C#"]);
2970 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2973 // oldmember lookup may have failed due to type parameter renames.
2975 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2976 if (oldmember == null) {
2977 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2978 oldmember = basefile.CreateElement ("Member");
2979 oldmember.SetAttribute ("MemberName", dm.MemberName);
2980 members.AppendChild (oldmember);
2981 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2982 XmlElement ms = basefile.CreateElement ("MemberSignature");
2983 ms.SetAttribute ("Language", key);
2984 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2985 oldmember.AppendChild (ms);
2987 oldmember.SetAttribute ("__monodocer-seen__", "true");
2988 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2993 m = GetMember (type, new DocumentationMember (oldmember));
2995 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2996 type.FullName, dm.MemberSignatures ["C#"]);
2999 oldmember.SetAttribute ("__monodocer-seen__", "true");
3001 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3002 if (ecmadocs.Name != "Docs")
3003 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3008 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3017 abstract class DocumentationImporter {
3019 public abstract void ImportDocumentation (DocsNodeInfo info);
3022 class MsxdocDocumentationImporter : DocumentationImporter {
3024 XmlDocument slashdocs;
3026 public MsxdocDocumentationImporter (string file)
3028 var xml = File.ReadAllText (file);
3030 // Ensure Unix line endings
3031 xml = xml.Replace ("\r", "");
3033 slashdocs = new XmlDocument();
3034 slashdocs.LoadXml (xml);
3037 public override void ImportDocumentation (DocsNodeInfo info)
3039 XmlNode elem = GetDocs (info.Member ?? info.Type);
3044 XmlElement e = info.Node;
3046 if (elem.SelectSingleNode("summary") != null)
3047 MDocUpdater.ClearElement(e, "summary");
3048 if (elem.SelectSingleNode("remarks") != null)
3049 MDocUpdater.ClearElement(e, "remarks");
3050 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3051 MDocUpdater.ClearElement(e, "value");
3052 MDocUpdater.ClearElement(e, "returns");
3055 foreach (XmlNode child in elem.ChildNodes) {
3056 switch (child.Name) {
3059 XmlAttribute name = child.Attributes ["name"];
3062 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3064 p2.InnerXml = child.InnerXml;
3067 // Occasionally XML documentation will use <returns/> on
3068 // properties, so let's try to normalize things.
3071 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3072 v.InnerXml = child.InnerXml;
3078 case "permission": {
3079 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3082 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3084 a = e.OwnerDocument.CreateElement (child.Name);
3085 a.SetAttribute ("cref", cref.Value);
3088 a.InnerXml = child.InnerXml;
3092 XmlAttribute cref = child.Attributes ["cref"];
3095 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3097 a = e.OwnerDocument.CreateElement ("altmember");
3098 a.SetAttribute ("cref", cref.Value);
3105 if (child.NodeType == XmlNodeType.Element &&
3106 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3109 MDocUpdater.CopyNode (child, e);
3116 private XmlNode GetDocs (MemberReference member)
3118 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3119 if (slashdocsig != null)
3120 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3125 class EcmaDocumentationImporter : DocumentationImporter {
3129 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3131 this.ecmadocs = ecmaDocs;
3134 public override void ImportDocumentation (DocsNodeInfo info)
3136 if (!ecmadocs.IsStartElement ("Docs")) {
3140 XmlElement e = info.Node;
3142 int depth = ecmadocs.Depth;
3143 ecmadocs.ReadStartElement ("Docs");
3144 while (ecmadocs.Read ()) {
3145 if (ecmadocs.Name == "Docs") {
3146 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3149 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3151 if (!ecmadocs.IsStartElement ())
3153 switch (ecmadocs.Name) {
3156 string name = ecmadocs.GetAttribute ("name");
3159 XmlNode doc = e.SelectSingleNode (
3160 ecmadocs.Name + "[@name='" + name + "']");
3161 string value = ecmadocs.ReadInnerXml ();
3163 doc.InnerXml = value.Replace ("\r", "");
3170 string name = ecmadocs.Name;
3171 string cref = ecmadocs.GetAttribute ("cref");
3174 XmlNode doc = e.SelectSingleNode (
3175 ecmadocs.Name + "[@cref='" + cref + "']");
3176 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3178 doc.InnerXml = value;
3180 XmlElement n = e.OwnerDocument.CreateElement (name);
3181 n.SetAttribute ("cref", cref);
3188 string name = ecmadocs.Name;
3189 string xpath = ecmadocs.Name;
3190 StringList attributes = new StringList (ecmadocs.AttributeCount);
3191 if (ecmadocs.MoveToFirstAttribute ()) {
3193 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3194 } while (ecmadocs.MoveToNextAttribute ());
3195 ecmadocs.MoveToContent ();
3197 if (attributes.Count > 0) {
3198 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3200 XmlNode doc = e.SelectSingleNode (xpath);
3201 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3203 doc.InnerXml = value;
3206 XmlElement n = e.OwnerDocument.CreateElement (name);
3208 foreach (string a in attributes) {
3209 int eq = a.IndexOf ('=');
3210 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3221 class DocumentationMember {
3222 public StringToStringMap MemberSignatures = new StringToStringMap ();
3223 public string ReturnType;
3224 public StringList Parameters;
3225 public string MemberName;
3226 public string MemberType;
3228 public DocumentationMember (XmlReader reader)
3230 MemberName = reader.GetAttribute ("MemberName");
3231 int depth = reader.Depth;
3233 StringList p = new StringList ();
3235 if (reader.NodeType != XmlNodeType.Element)
3237 switch (reader.Name) {
3238 case "MemberSignature":
3239 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3242 MemberType = reader.ReadElementString ();
3245 if (reader.Depth == depth + 2)
3246 ReturnType = reader.ReadElementString ();
3249 if (reader.Depth == depth + 2)
3250 p.Add (reader.GetAttribute ("Type"));
3253 if (reader.Depth == depth + 1)
3257 } while (go && reader.Read () && reader.Depth >= depth);
3263 public DocumentationMember (XmlNode node)
3265 MemberName = node.Attributes ["MemberName"].Value;
3266 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3267 XmlAttribute l = n.Attributes ["Language"];
3268 XmlAttribute v = n.Attributes ["Value"];
3269 if (l != null && v != null)
3270 MemberSignatures [l.Value] = v.Value;
3272 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3273 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3275 ReturnType = rt.InnerText;
3276 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3278 Parameters = new StringList (p.Count);
3279 for (int i = 0; i < p.Count; ++i)
3280 Parameters.Add (p [i].Attributes ["Type"].Value);
3285 public class DynamicParserContext {
3286 public ReadOnlyCollection<bool> TransformFlags;
3287 public int TransformIndex;
3289 public DynamicParserContext (ICustomAttributeProvider provider)
3292 if (provider.HasCustomAttributes &&
3293 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3294 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3295 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3296 ? new CustomAttributeArgument [0]
3297 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3299 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3304 public enum MemberFormatterState {
3306 WithinGenericTypeParameters,
3309 public abstract class MemberFormatter {
3311 public virtual string Language {
3315 public string GetName (MemberReference member)
3317 return GetName (member, null);
3320 public virtual string GetName (MemberReference member, DynamicParserContext context)
3322 TypeReference type = member as TypeReference;
3324 return GetTypeName (type, context);
3325 MethodReference method = member as MethodReference;
3326 if (method != null && method.Name == ".ctor") // method.IsConstructor
3327 return GetConstructorName (method);
3329 return GetMethodName (method);
3330 PropertyReference prop = member as PropertyReference;
3332 return GetPropertyName (prop);
3333 FieldReference field = member as FieldReference;
3335 return GetFieldName (field);
3336 EventReference e = member as EventReference;
3338 return GetEventName (e);
3339 throw new NotSupportedException ("Can't handle: " +
3340 (member == null ? "null" : member.GetType().ToString()));
3343 protected virtual string GetTypeName (TypeReference type)
3345 return GetTypeName (type, null);
3348 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3351 throw new ArgumentNullException ("type");
3352 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3355 protected virtual char[] ArrayDelimeters {
3356 get {return new char[]{'[', ']'};}
3359 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3361 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3363 if (type is ArrayType) {
3364 TypeSpecification spec = type as TypeSpecification;
3365 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3366 return AppendArrayModifiers (buf, (ArrayType) type);
3368 if (type is ByReferenceType) {
3369 return AppendRefTypeName (buf, type, context);
3371 if (type is PointerType) {
3372 return AppendPointerTypeName (buf, type, context);
3374 if (type is GenericParameter) {
3375 return AppendTypeName (buf, type, context);
3377 AppendNamespace (buf, type);
3378 GenericInstanceType genInst = type as GenericInstanceType;
3379 if (type.GenericParameters.Count == 0 &&
3380 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3381 return AppendFullTypeName (buf, type, context);
3383 return AppendGenericType (buf, type, context);
3386 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3388 string ns = DocUtils.GetNamespace (type);
3389 if (ns != null && ns.Length > 0)
3390 buf.Append (ns).Append ('.');
3394 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3396 if (type.DeclaringType != null)
3397 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3398 return AppendTypeName (buf, type, context);
3401 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3403 if (context != null)
3404 context.TransformIndex++;
3405 return AppendTypeName (buf, type.Name);
3408 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3410 int n = typename.IndexOf ("`");
3412 return buf.Append (typename.Substring (0, n));
3413 return buf.Append (typename);
3416 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3418 buf.Append (ArrayDelimeters [0]);
3419 int rank = array.Rank;
3421 buf.Append (new string (',', rank-1));
3422 return buf.Append (ArrayDelimeters [1]);
3425 protected virtual string RefTypeModifier {
3429 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3431 TypeSpecification spec = type as TypeSpecification;
3432 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3433 .Append (RefTypeModifier);
3436 protected virtual string PointerModifier {
3440 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3442 TypeSpecification spec = type as TypeSpecification;
3443 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3444 .Append (PointerModifier);
3447 protected virtual char[] GenericTypeContainer {
3448 get {return new char[]{'<', '>'};}
3451 protected virtual char NestedTypeSeparator {
3455 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3457 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3458 type is GenericInstanceType ? type.GetElementType () : type);
3459 List<TypeReference> genArgs = GetGenericArguments (type);
3462 bool insertNested = false;
3463 foreach (var decl in decls) {
3464 TypeReference declDef = decl.Resolve () ?? decl;
3466 buf.Append (NestedTypeSeparator);
3468 insertNested = true;
3469 AppendTypeName (buf, declDef, context);
3470 int ac = DocUtils.GetGenericArgumentCount (declDef);
3474 buf.Append (GenericTypeContainer [0]);
3475 var origState = MemberFormatterState;
3476 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3477 _AppendTypeName (buf, genArgs [argIdx++], context);
3478 for (int i = 1; i < c; ++i) {
3479 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
3481 MemberFormatterState = origState;
3482 buf.Append (GenericTypeContainer [1]);
3488 protected List<TypeReference> GetGenericArguments (TypeReference type)
3490 var args = new List<TypeReference> ();
3491 GenericInstanceType inst = type as GenericInstanceType;
3493 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3495 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3499 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3504 protected virtual string GetConstructorName (MethodReference constructor)
3506 return constructor.Name;
3509 protected virtual string GetMethodName (MethodReference method)
3514 protected virtual string GetPropertyName (PropertyReference property)
3516 return property.Name;
3519 protected virtual string GetFieldName (FieldReference field)
3524 protected virtual string GetEventName (EventReference e)
3529 public virtual string GetDeclaration (MemberReference member)
3532 throw new ArgumentNullException ("member");
3533 TypeDefinition type = member as TypeDefinition;
3535 return GetTypeDeclaration (type);
3536 MethodDefinition method = member as MethodDefinition;
3537 if (method != null && method.IsConstructor)
3538 return GetConstructorDeclaration (method);
3540 return GetMethodDeclaration (method);
3541 PropertyDefinition prop = member as PropertyDefinition;
3543 return GetPropertyDeclaration (prop);
3544 FieldDefinition field = member as FieldDefinition;
3546 return GetFieldDeclaration (field);
3547 EventDefinition e = member as EventDefinition;
3549 return GetEventDeclaration (e);
3550 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3553 protected virtual string GetTypeDeclaration (TypeDefinition type)
3556 throw new ArgumentNullException ("type");
3557 StringBuilder buf = new StringBuilder (type.Name.Length);
3558 _AppendTypeName (buf, type, null);
3559 AppendGenericTypeConstraints (buf, type);
3560 return buf.ToString ();
3563 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3565 return GetConstructorName (constructor);
3568 protected virtual string GetMethodDeclaration (MethodDefinition method)
3570 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
3571 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
3574 // Special signature for destructors.
3575 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3576 return GetFinalizerName (method);
3578 StringBuilder buf = new StringBuilder ();
3580 AppendVisibility (buf, method);
3581 if (buf.Length == 0 &&
3582 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3585 AppendModifiers (buf, method);
3587 if (buf.Length != 0)
3589 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
3591 AppendMethodName (buf, method);
3592 AppendGenericMethod (buf, method).Append (" ");
3593 AppendParameters (buf, method, method.Parameters);
3594 AppendGenericMethodConstraints (buf, method);
3595 return buf.ToString ();
3598 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3600 return buf.Append (method.Name);
3603 protected virtual string GetFinalizerName (MethodDefinition method)
3608 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3613 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3618 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3623 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3628 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3633 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3635 return GetPropertyName (property);
3638 protected virtual string GetFieldDeclaration (FieldDefinition field)
3640 return GetFieldName (field);
3643 protected virtual string GetEventDeclaration (EventDefinition e)
3645 return GetEventName (e);
3649 class ILFullMemberFormatter : MemberFormatter {
3651 public override string Language {
3652 get {return "ILAsm";}
3655 protected override char NestedTypeSeparator {
3661 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3663 if (GetBuiltinType (type.FullName) != null)
3665 string ns = DocUtils.GetNamespace (type);
3666 if (ns != null && ns.Length > 0) {
3667 if (type.IsValueType)
3668 buf.Append ("valuetype ");
3670 buf.Append ("class ");
3671 buf.Append (ns).Append ('.');
3676 private static string GetBuiltinType (string t)
3679 case "System.Byte": return "unsigned int8";
3680 case "System.SByte": return "int8";
3681 case "System.Int16": return "int16";
3682 case "System.Int32": return "int32";
3683 case "System.Int64": return "int64";
3684 case "System.IntPtr": return "native int";
3686 case "System.UInt16": return "unsigned int16";
3687 case "System.UInt32": return "unsigned int32";
3688 case "System.UInt64": return "unsigned int64";
3689 case "System.UIntPtr": return "native unsigned int";
3691 case "System.Single": return "float32";
3692 case "System.Double": return "float64";
3693 case "System.Boolean": return "bool";
3694 case "System.Char": return "char";
3695 case "System.Void": return "void";
3696 case "System.String": return "string";
3697 case "System.Object": return "object";
3702 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3704 return buf.Append (typename);
3707 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3709 if (type is GenericParameter) {
3710 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3714 string s = GetBuiltinType (type.FullName);
3716 return buf.Append (s);
3718 return base.AppendTypeName (buf, type, context);
3721 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3723 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3724 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3726 GenericParameterAttributes attrs = type.Attributes;
3727 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3728 buf.Append ("class ");
3729 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3730 buf.Append ("struct ");
3731 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3732 buf.Append (".ctor ");
3733 IList<TypeReference> constraints = type.Constraints;
3734 MemberFormatterState = 0;
3735 if (constraints.Count > 0) {
3736 var full = new ILFullMemberFormatter ();
3737 buf.Append ("(").Append (full.GetName (constraints [0]));
3738 for (int i = 1; i < constraints.Count; ++i) {
3739 buf.Append (", ").Append (full.GetName (constraints [i]));
3743 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3745 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3747 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3752 protected override string GetTypeDeclaration (TypeDefinition type)
3754 string visibility = GetTypeVisibility (type.Attributes);
3755 if (visibility == null)
3758 StringBuilder buf = new StringBuilder ();
3760 buf.Append (".class ");
3762 buf.Append ("nested ");
3763 buf.Append (visibility).Append (" ");
3764 if (type.IsInterface)
3765 buf.Append ("interface ");
3766 if (type.IsSequentialLayout)
3767 buf.Append ("sequential ");
3768 if (type.IsAutoLayout)
3769 buf.Append ("auto ");
3770 if (type.IsAnsiClass)
3771 buf.Append ("ansi ");
3772 if (type.IsAbstract)
3773 buf.Append ("abstract ");
3774 if (type.IsSerializable)
3775 buf.Append ("serializable ");
3777 buf.Append ("sealed ");
3778 if (type.IsBeforeFieldInit)
3779 buf.Append ("beforefieldinit ");
3780 var state = MemberFormatterState;
3781 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3782 buf.Append (GetName (type));
3783 MemberFormatterState = state;
3784 var full = new ILFullMemberFormatter ();
3785 if (type.BaseType != null) {
3786 buf.Append (" extends ");
3787 if (type.BaseType.FullName == "System.Object")
3788 buf.Append ("System.Object");
3790 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3793 foreach (var name in type.Interfaces
3794 .Select (i => full.GetName (i))
3795 .OrderBy (n => n)) {
3797 buf.Append (" implements ");
3806 return buf.ToString ();
3809 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3811 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3812 type is GenericInstanceType ? type.GetElementType () : type);
3814 foreach (var decl in decls) {
3815 TypeReference declDef = decl.Resolve () ?? decl;
3817 buf.Append (NestedTypeSeparator);
3820 AppendTypeName (buf, declDef, context);
3824 foreach (TypeReference arg in GetGenericArguments (type)) {
3828 _AppendTypeName (buf, arg, context);
3834 static string GetTypeVisibility (TypeAttributes ta)
3836 switch (ta & TypeAttributes.VisibilityMask) {
3837 case TypeAttributes.Public:
3838 case TypeAttributes.NestedPublic:
3841 case TypeAttributes.NestedFamily:
3842 case TypeAttributes.NestedFamORAssem:
3850 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3852 return GetMethodDeclaration (constructor);
3855 protected override string GetMethodDeclaration (MethodDefinition method)
3857 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3860 var buf = new StringBuilder ();
3861 buf.Append (".method ");
3862 AppendVisibility (buf, method);
3863 if (method.IsStatic)
3864 buf.Append ("static ");
3865 if (method.IsHideBySig)
3866 buf.Append ("hidebysig ");
3867 if (method.IsPInvokeImpl) {
3868 var info = method.PInvokeInfo;
3869 buf.Append ("pinvokeimpl (\"")
3870 .Append (info.Module.Name)
3871 .Append ("\" as \"")
3872 .Append (info.EntryPoint)
3874 if (info.IsCharSetAuto)
3875 buf.Append (" auto");
3876 if (info.IsCharSetUnicode)
3877 buf.Append (" unicode");
3878 if (info.IsCharSetAnsi)
3879 buf.Append (" ansi");
3880 if (info.IsCallConvCdecl)
3881 buf.Append (" cdecl");
3882 if (info.IsCallConvStdCall)
3883 buf.Append (" stdcall");
3884 if (info.IsCallConvWinapi)
3885 buf.Append (" winapi");
3886 if (info.IsCallConvThiscall)
3887 buf.Append (" thiscall");
3888 if (info.SupportsLastError)
3889 buf.Append (" lasterr");
3892 if (method.IsSpecialName)
3893 buf.Append ("specialname ");
3894 if (method.IsRuntimeSpecialName)
3895 buf.Append ("rtspecialname ");
3896 if (method.IsNewSlot)
3897 buf.Append ("newslot ");
3898 if (method.IsVirtual)
3899 buf.Append ("virtual ");
3900 if (!method.IsStatic)
3901 buf.Append ("instance ");
3902 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
3904 .Append (method.Name);
3905 if (method.IsGenericMethod ()) {
3906 var state = MemberFormatterState;
3907 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3908 IList<GenericParameter> args = method.GenericParameters;
3909 if (args.Count > 0) {
3911 _AppendTypeName (buf, args [0], null);
3912 for (int i = 1; i < args.Count; ++i)
3913 _AppendTypeName (buf.Append (", "), args [i], null);
3916 MemberFormatterState = state;
3921 for (int i = 0; i < method.Parameters.Count; ++i) {
3925 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
3927 buf.Append (method.Parameters [i].Name);
3931 buf.Append (" cil");
3932 if (method.IsRuntime)
3933 buf.Append (" runtime");
3934 if (method.IsManaged)
3935 buf.Append (" managed");
3937 return buf.ToString ();
3940 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3942 if (DocUtils.IsExplicitlyImplemented (method)) {
3943 TypeReference iface;
3944 MethodReference ifaceMethod;
3945 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3946 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3948 .Append (ifaceMethod.Name);
3950 return base.AppendMethodName (buf, method);
3953 protected override string RefTypeModifier {
3957 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3959 if (method.IsPublic)
3960 return buf.Append ("public ");
3961 if (method.IsFamilyAndAssembly)
3962 return buf.Append ("familyandassembly");
3963 if (method.IsFamilyOrAssembly)
3964 return buf.Append ("familyorassembly");
3965 if (method.IsFamily)
3966 return buf.Append ("family");
3970 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3972 string modifiers = String.Empty;
3973 if (method.IsStatic) modifiers += " static";
3974 if (method.IsVirtual && !method.IsAbstract) {
3975 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3976 else modifiers += " override";
3978 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3979 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3980 if (method.IsFinal) modifiers += " sealed";
3981 if (modifiers == " virtual sealed") modifiers = "";
3983 return buf.Append (modifiers);
3986 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3988 if (method.IsGenericMethod ()) {
3989 IList<GenericParameter> args = method.GenericParameters;
3990 if (args.Count > 0) {
3992 buf.Append (args [0].Name);
3993 for (int i = 1; i < args.Count; ++i)
3994 buf.Append (",").Append (args [i].Name);
4001 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4003 return AppendParameters (buf, method, parameters, '(', ')');
4006 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4010 if (parameters.Count > 0) {
4011 if (DocUtils.IsExtensionMethod (method))
4012 buf.Append ("this ");
4013 AppendParameter (buf, parameters [0]);
4014 for (int i = 1; i < parameters.Count; ++i) {
4016 AppendParameter (buf, parameters [i]);
4020 return buf.Append (end);
4023 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4025 if (parameter.ParameterType is ByReferenceType) {
4026 if (parameter.IsOut)
4027 buf.Append ("out ");
4029 buf.Append ("ref ");
4031 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4032 return buf.Append (parameter.Name);
4035 protected override string GetPropertyDeclaration (PropertyDefinition property)
4037 MethodDefinition gm = null, sm = null;
4039 string get_visible = null;
4040 if ((gm = property.GetMethod) != null &&
4041 (DocUtils.IsExplicitlyImplemented (gm) ||
4042 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4043 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4044 string set_visible = null;
4045 if ((sm = property.SetMethod) != null &&
4046 (DocUtils.IsExplicitlyImplemented (sm) ||
4047 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4048 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4050 if ((set_visible == null) && (get_visible == null))
4053 StringBuilder buf = new StringBuilder ()
4054 .Append (".property ");
4055 if (!(gm ?? sm).IsStatic)
4056 buf.Append ("instance ");
4057 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4058 buf.Append (' ').Append (property.Name);
4059 if (!property.HasParameters || property.Parameters.Count == 0)
4060 return buf.ToString ();
4064 foreach (ParameterDefinition p in property.Parameters) {
4068 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4072 return buf.ToString ();
4075 protected override string GetFieldDeclaration (FieldDefinition field)
4077 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4078 if (declType.IsEnum && field.Name == "value__")
4079 return null; // This member of enums aren't documented.
4081 StringBuilder buf = new StringBuilder ();
4082 AppendFieldVisibility (buf, field);
4083 if (buf.Length == 0)
4086 buf.Insert (0, ".field ");
4089 buf.Append ("static ");
4090 if (field.IsInitOnly)
4091 buf.Append ("initonly ");
4092 if (field.IsLiteral)
4093 buf.Append ("literal ");
4094 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4095 buf.Append (' ').Append (field.Name);
4096 AppendFieldValue (buf, field);
4098 return buf.ToString ();
4101 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4104 return buf.Append ("public ");
4105 if (field.IsFamilyAndAssembly)
4106 return buf.Append ("familyandassembly ");
4107 if (field.IsFamilyOrAssembly)
4108 return buf.Append ("familyorassembly ");
4110 return buf.Append ("family ");
4114 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4116 // enums have a value__ field, which we ignore
4117 if (field.DeclaringType.IsGenericType ())
4119 if (field.HasConstant && field.IsLiteral) {
4122 val = field.Constant;
4127 buf.Append (" = ").Append ("null");
4128 else if (val is Enum)
4130 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4132 .Append (val.ToString ())
4134 else if (val is IFormattable) {
4135 string value = ((IFormattable)val).ToString();
4138 buf.Append ("\"" + value + "\"");
4140 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4149 protected override string GetEventDeclaration (EventDefinition e)
4151 StringBuilder buf = new StringBuilder ();
4152 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4157 buf.Append (".event ")
4158 .Append (GetName (e.EventType))
4162 return buf.ToString ();
4166 class ILMemberFormatter : ILFullMemberFormatter {
4167 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4173 class CSharpFullMemberFormatter : MemberFormatter {
4175 public override string Language {
4179 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4181 string ns = DocUtils.GetNamespace (type);
4182 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4183 buf.Append (ns).Append ('.');
4187 private string GetCSharpType (string t)
4190 case "System.Byte": return "byte";
4191 case "System.SByte": return "sbyte";
4192 case "System.Int16": return "short";
4193 case "System.Int32": return "int";
4194 case "System.Int64": return "long";
4196 case "System.UInt16": return "ushort";
4197 case "System.UInt32": return "uint";
4198 case "System.UInt64": return "ulong";
4200 case "System.Single": return "float";
4201 case "System.Double": return "double";
4202 case "System.Decimal": return "decimal";
4203 case "System.Boolean": return "bool";
4204 case "System.Char": return "char";
4205 case "System.Void": return "void";
4206 case "System.String": return "string";
4207 case "System.Object": return "object";
4212 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4214 if (context != null && context.TransformFlags != null &&
4215 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4216 context.TransformIndex++;
4217 return buf.Append ("dynamic");
4220 if (type is GenericParameter)
4221 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4222 string t = type.FullName;
4223 if (!t.StartsWith ("System.")) {
4224 return base.AppendTypeName (buf, type, context);
4227 string s = GetCSharpType (t);
4229 if (context != null)
4230 context.TransformIndex++;
4231 return buf.Append (s);
4234 return base.AppendTypeName (buf, type, context);
4237 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4239 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4241 GenericParameterAttributes attrs = type.Attributes;
4242 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4243 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4247 buf.Append ("out ");
4251 protected override string GetTypeDeclaration (TypeDefinition type)
4253 string visibility = GetTypeVisibility (type.Attributes);
4254 if (visibility == null)
4257 StringBuilder buf = new StringBuilder ();
4259 buf.Append (visibility);
4262 MemberFormatter full = new CSharpFullMemberFormatter ();
4264 if (DocUtils.IsDelegate (type)) {
4265 buf.Append("delegate ");
4266 MethodDefinition invoke = type.GetMethod ("Invoke");
4267 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4268 buf.Append (GetName (type));
4269 AppendParameters (buf, invoke, invoke.Parameters);
4270 AppendGenericTypeConstraints (buf, type);
4273 return buf.ToString();
4276 if (type.IsAbstract && !type.IsInterface)
4277 buf.Append("abstract ");
4278 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4279 buf.Append("sealed ");
4280 buf.Replace ("abstract sealed", "static");
4282 buf.Append (GetTypeKind (type));
4284 buf.Append (GetCSharpType (type.FullName) == null
4289 TypeReference basetype = type.BaseType;
4290 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4293 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4294 .Select (iface => full.GetName (iface))
4298 if (basetype != null || interface_names.Count > 0)
4301 if (basetype != null) {
4302 buf.Append (full.GetName (basetype));
4303 if (interface_names.Count > 0)
4307 for (int i = 0; i < interface_names.Count; i++){
4310 buf.Append (interface_names [i]);
4312 AppendGenericTypeConstraints (buf, type);
4315 return buf.ToString ();
4318 static string GetTypeKind (TypeDefinition t)
4324 if (t.IsClass || t.FullName == "System.Enum")
4328 throw new ArgumentException(t.FullName);
4331 static string GetTypeVisibility (TypeAttributes ta)
4333 switch (ta & TypeAttributes.VisibilityMask) {
4334 case TypeAttributes.Public:
4335 case TypeAttributes.NestedPublic:
4338 case TypeAttributes.NestedFamily:
4339 case TypeAttributes.NestedFamORAssem:
4347 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4349 if (type.GenericParameters.Count == 0)
4351 return AppendConstraints (buf, type.GenericParameters);
4354 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4356 foreach (GenericParameter genArg in genArgs) {
4357 GenericParameterAttributes attrs = genArg.Attributes;
4358 IList<TypeReference> constraints = genArg.Constraints;
4359 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4362 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4363 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4364 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4367 if (!isref && !isvt && !isnew && constraints.Count == 0)
4369 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4371 buf.Append ("class");
4375 buf.Append ("struct");
4378 if (constraints.Count > 0 && !isvt) {
4381 buf.Append (GetTypeName (constraints [0]));
4382 for (int i = 1; i < constraints.Count; ++i)
4383 buf.Append (", ").Append (GetTypeName (constraints [i]));
4385 if (isnew && !isvt) {
4388 buf.Append ("new()");
4394 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4396 StringBuilder buf = new StringBuilder ();
4397 AppendVisibility (buf, constructor);
4398 if (buf.Length == 0)
4402 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4403 AppendParameters (buf, constructor, constructor.Parameters);
4406 return buf.ToString ();
4409 protected override string GetMethodDeclaration (MethodDefinition method)
4411 string decl = base.GetMethodDeclaration (method);
4417 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4419 if (DocUtils.IsExplicitlyImplemented (method)) {
4420 TypeReference iface;
4421 MethodReference ifaceMethod;
4422 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4423 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4425 .Append (ifaceMethod.Name);
4427 return base.AppendMethodName (buf, method);
4430 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4432 if (method.GenericParameters.Count == 0)
4434 return AppendConstraints (buf, method.GenericParameters);
4437 protected override string RefTypeModifier {
4441 protected override string GetFinalizerName (MethodDefinition method)
4443 return "~" + method.DeclaringType.Name + " ()";
4446 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4450 if (method.IsPublic)
4451 return buf.Append ("public");
4452 if (method.IsFamily || method.IsFamilyOrAssembly)
4453 return buf.Append ("protected");
4457 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4459 string modifiers = String.Empty;
4460 if (method.IsStatic) modifiers += " static";
4461 if (method.IsVirtual && !method.IsAbstract) {
4462 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4463 else modifiers += " override";
4465 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4466 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4467 if (method.IsFinal) modifiers += " sealed";
4468 if (modifiers == " virtual sealed") modifiers = "";
4470 return buf.Append (modifiers);
4473 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4475 if (method.IsGenericMethod ()) {
4476 IList<GenericParameter> args = method.GenericParameters;
4477 if (args.Count > 0) {
4479 buf.Append (args [0].Name);
4480 for (int i = 1; i < args.Count; ++i)
4481 buf.Append (",").Append (args [i].Name);
4488 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4490 return AppendParameters (buf, method, parameters, '(', ')');
4493 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4497 if (parameters.Count > 0) {
4498 if (DocUtils.IsExtensionMethod (method))
4499 buf.Append ("this ");
4500 AppendParameter (buf, parameters [0]);
4501 for (int i = 1; i < parameters.Count; ++i) {
4503 AppendParameter (buf, parameters [i]);
4507 return buf.Append (end);
4510 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4512 if (parameter.ParameterType is ByReferenceType) {
4513 if (parameter.IsOut)
4514 buf.Append ("out ");
4516 buf.Append ("ref ");
4518 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
4519 buf.Append (parameter.Name);
4520 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
4521 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
4526 protected override string GetPropertyDeclaration (PropertyDefinition property)
4528 MethodDefinition method;
4530 string get_visible = null;
4531 if ((method = property.GetMethod) != null &&
4532 (DocUtils.IsExplicitlyImplemented (method) ||
4533 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4534 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4535 string set_visible = null;
4536 if ((method = property.SetMethod) != null &&
4537 (DocUtils.IsExplicitlyImplemented (method) ||
4538 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4539 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4541 if ((set_visible == null) && (get_visible == null))
4545 StringBuilder buf = new StringBuilder ();
4546 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4547 buf.Append (visibility = get_visible);
4548 else if (set_visible != null && get_visible == null)
4549 buf.Append (visibility = set_visible);
4551 buf.Append (visibility = "public");
4553 // Pick an accessor to use for static/virtual/override/etc. checks.
4554 method = property.SetMethod;
4556 method = property.GetMethod;
4558 string modifiers = String.Empty;
4559 if (method.IsStatic) modifiers += " static";
4560 if (method.IsVirtual && !method.IsAbstract) {
4561 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4562 modifiers += " virtual";
4564 modifiers += " override";
4566 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4567 if (method.IsAbstract && !declDef.IsInterface)
4568 modifiers += " abstract";
4570 modifiers += " sealed";
4571 if (modifiers == " virtual sealed")
4573 buf.Append (modifiers).Append (' ');
4575 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
4577 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4578 string name = property.Name;
4579 foreach (MemberReference mi in defs) {
4580 if (mi == property) {
4585 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4587 if (property.Parameters.Count != 0) {
4588 AppendParameters (buf, method, property.Parameters, '[', ']');
4592 if (get_visible != null) {
4593 if (get_visible != visibility)
4594 buf.Append (' ').Append (get_visible);
4595 buf.Append (" get;");
4597 if (set_visible != null) {
4598 if (set_visible != visibility)
4599 buf.Append (' ').Append (set_visible);
4600 buf.Append (" set;");
4604 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4607 protected override string GetFieldDeclaration (FieldDefinition field)
4609 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4610 if (declType.IsEnum && field.Name == "value__")
4611 return null; // This member of enums aren't documented.
4613 StringBuilder buf = new StringBuilder ();
4614 AppendFieldVisibility (buf, field);
4615 if (buf.Length == 0)
4618 if (declType.IsEnum)
4621 if (field.IsStatic && !field.IsLiteral)
4622 buf.Append (" static");
4623 if (field.IsInitOnly)
4624 buf.Append (" readonly");
4625 if (field.IsLiteral)
4626 buf.Append (" const");
4628 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
4629 buf.Append (field.Name);
4630 AppendFieldValue (buf, field);
4633 return buf.ToString ();
4636 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4639 return buf.Append ("public");
4640 if (field.IsFamily || field.IsFamilyOrAssembly)
4641 return buf.Append ("protected");
4645 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4647 // enums have a value__ field, which we ignore
4648 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4649 field.DeclaringType.IsGenericType ())
4651 if (field.HasConstant && field.IsLiteral) {
4654 val = field.Constant;
4659 buf.Append (" = ").Append ("null");
4660 else if (val is Enum)
4661 buf.Append (" = ").Append (val.ToString ());
4662 else if (val is IFormattable) {
4663 string value = ((IFormattable)val).ToString();
4665 value = "\"" + value + "\"";
4666 buf.Append (" = ").Append (value);
4672 protected override string GetEventDeclaration (EventDefinition e)
4674 StringBuilder buf = new StringBuilder ();
4675 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4679 AppendModifiers (buf, e.AddMethod);
4681 buf.Append (" event ");
4682 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
4683 buf.Append (e.Name).Append (';');
4685 return buf.ToString ();
4689 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4690 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4696 class DocTypeFullMemberFormatter : MemberFormatter {
4697 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4699 protected override char NestedTypeSeparator {
4704 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4705 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4711 class SlashDocMemberFormatter : MemberFormatter {
4713 protected override char[] GenericTypeContainer {
4714 get {return new char[]{'{', '}'};}
4717 private bool AddTypeCount = true;
4719 private TypeReference genDeclType;
4720 private MethodReference genDeclMethod;
4722 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4724 if (type is GenericParameter) {
4726 if (genDeclType != null) {
4727 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4728 for (int i = 0; i < genArgs.Count; ++i) {
4729 if (genArgs [i].Name == type.Name) {
4730 buf.Append ('`').Append (i);
4735 if (genDeclMethod != null) {
4736 IList<GenericParameter> genArgs = null;
4737 if (genDeclMethod.IsGenericMethod ()) {
4738 genArgs = genDeclMethod.GenericParameters;
4739 for (int i = 0; i < genArgs.Count; ++i) {
4740 if (genArgs [i].Name == type.Name) {
4741 buf.Append ("``").Append (i);
4747 if (genDeclType == null && genDeclMethod == null) {
4748 // Probably from within an explicitly implemented interface member,
4749 // where CSC uses parameter names instead of indices (why?), e.g.
4750 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4751 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4752 buf.Append (type.Name);
4754 if (buf.Length == l) {
4755 throw new Exception (string.Format (
4756 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4757 type.Name, genDeclType, genDeclMethod));
4761 base.AppendTypeName (buf, type, context);
4763 int numArgs = type.GenericParameters.Count;
4764 if (type.DeclaringType != null)
4765 numArgs -= type.GenericParameters.Count;
4767 buf.Append ('`').Append (numArgs);
4774 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4776 buf.Append (ArrayDelimeters [0]);
4777 int rank = array.Rank;
4780 for (int i = 1; i < rank; ++i) {
4784 return buf.Append (ArrayDelimeters [1]);
4787 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4790 base.AppendGenericType (buf, type, context);
4792 AppendType (buf, type, context);
4796 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4798 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4799 bool insertNested = false;
4800 int prevParamCount = 0;
4801 foreach (var decl in decls) {
4803 buf.Append (NestedTypeSeparator);
4804 insertNested = true;
4805 base.AppendTypeName (buf, decl, context);
4806 int argCount = DocUtils.GetGenericArgumentCount (decl);
4807 int numArgs = argCount - prevParamCount;
4808 prevParamCount = argCount;
4810 buf.Append ('`').Append (numArgs);
4815 public override string GetDeclaration (MemberReference member)
4817 TypeReference r = member as TypeReference;
4819 return "T:" + GetTypeName (r);
4821 return base.GetDeclaration (member);
4824 protected override string GetConstructorName (MethodReference constructor)
4826 return GetMethodDefinitionName (constructor, "#ctor");
4829 protected override string GetMethodName (MethodReference method)
4832 MethodDefinition methodDef = method as MethodDefinition;
4833 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4836 TypeReference iface;
4837 MethodReference ifaceMethod;
4838 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4839 AddTypeCount = false;
4840 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4841 AddTypeCount = true;
4843 return GetMethodDefinitionName (method, name);
4846 private string GetMethodDefinitionName (MethodReference method, string name)
4848 StringBuilder buf = new StringBuilder ();
4849 buf.Append (GetTypeName (method.DeclaringType));
4851 buf.Append (name.Replace (".", "#"));
4852 if (method.IsGenericMethod ()) {
4853 IList<GenericParameter> genArgs = method.GenericParameters;
4854 if (genArgs.Count > 0)
4855 buf.Append ("``").Append (genArgs.Count);
4857 IList<ParameterDefinition> parameters = method.Parameters;
4859 genDeclType = method.DeclaringType;
4860 genDeclMethod = method;
4861 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4865 genDeclMethod = null;
4867 return buf.ToString ();
4870 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4872 if (parameters.Count == 0)
4877 AppendParameter (buf, genArgs, parameters [0]);
4878 for (int i = 1; i < parameters.Count; ++i) {
4880 AppendParameter (buf, genArgs, parameters [i]);
4883 return buf.Append (')');
4886 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4888 AddTypeCount = false;
4889 buf.Append (GetTypeName (parameter.ParameterType));
4890 AddTypeCount = true;
4894 protected override string GetPropertyName (PropertyReference property)
4898 PropertyDefinition propertyDef = property as PropertyDefinition;
4899 MethodDefinition method = null;
4900 if (propertyDef != null)
4901 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4902 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4903 name = property.Name;
4905 TypeReference iface;
4906 MethodReference ifaceMethod;
4907 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4908 AddTypeCount = false;
4909 name = string.Join ("#", new string[]{
4910 GetTypeName (iface).Replace (".", "#"),
4911 DocUtils.GetMember (property.Name)
4913 AddTypeCount = true;
4916 StringBuilder buf = new StringBuilder ();
4917 buf.Append (GetName (property.DeclaringType));
4920 IList<ParameterDefinition> parameters = property.Parameters;
4921 if (parameters.Count > 0) {
4922 genDeclType = property.DeclaringType;
4924 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4925 AppendParameter (buf, genArgs, parameters [0]);
4926 for (int i = 1; i < parameters.Count; ++i) {
4928 AppendParameter (buf, genArgs, parameters [i]);
4933 return buf.ToString ();
4936 protected override string GetFieldName (FieldReference field)
4938 return string.Format ("{0}.{1}",
4939 GetName (field.DeclaringType), field.Name);
4942 protected override string GetEventName (EventReference e)
4944 return string.Format ("{0}.{1}",
4945 GetName (e.DeclaringType), e.Name);
4948 protected override string GetTypeDeclaration (TypeDefinition type)
4950 string name = GetName (type);
4956 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4958 string name = GetName (constructor);
4964 protected override string GetMethodDeclaration (MethodDefinition method)
4966 string name = GetName (method);
4969 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4970 genDeclType = method.DeclaringType;
4971 genDeclMethod = method;
4972 name += "~" + GetName (method.ReturnType);
4974 genDeclMethod = null;
4979 protected override string GetPropertyDeclaration (PropertyDefinition property)
4981 string name = GetName (property);
4987 protected override string GetFieldDeclaration (FieldDefinition field)
4989 string name = GetName (field);
4995 protected override string GetEventDeclaration (EventDefinition e)
4997 string name = GetName (e);
5004 class FileNameMemberFormatter : SlashDocMemberFormatter {
5005 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5010 protected override char NestedTypeSeparator {