📄 doc.cs
字号:
//// doc.cs: Support for XML documentation comment.//// Author:// Atsushi Enomoto <atsushi@ximian.com>//// Licensed under the terms of the GNU GPL//// (C) 2004 Novell, Inc.////#if ! BOOTSTRAP_WITH_OLDLIBusing System;using System.Collections;using System.Collections.Specialized;using System.IO;using System.Reflection;using System.Reflection.Emit;using System.Runtime.CompilerServices;using System.Runtime.InteropServices;using System.Security;using System.Security.Permissions;using System.Text;using System.Xml;using Mono.CompilerServices.SymbolWriter;namespace Mono.CSharp { // // Support class for XML documentation. // public class DocUtil { // TypeContainer // // Generates xml doc comments (if any), and if required, // handle warning report. // internal static void GenerateTypeDocComment (TypeContainer t, DeclSpace ds) { GenerateDocComment (t, ds); if (t.DefaultStaticConstructor != null) t.DefaultStaticConstructor.GenerateDocComment (t); if (t.InstanceConstructors != null) foreach (Constructor c in t.InstanceConstructors) c.GenerateDocComment (t); if (t.Types != null) foreach (TypeContainer tc in t.Types) tc.GenerateDocComment (t); if (t.Parts != null) { IDictionary comments = RootContext.Documentation.PartialComments; foreach (ClassPart cp in t.Parts) { if (cp.DocComment == null) continue; comments [cp] = cp; } } if (t.Enums != null) foreach (Enum en in t.Enums) en.GenerateDocComment (t); if (t.Constants != null) foreach (Const c in t.Constants) c.GenerateDocComment (t); if (t.Fields != null) foreach (Field f in t.Fields) f.GenerateDocComment (t); if (t.Events != null) foreach (Event e in t.Events) e.GenerateDocComment (t); if (t.Indexers != null) foreach (Indexer ix in t.Indexers) ix.GenerateDocComment (t); if (t.Properties != null) foreach (Property p in t.Properties) p.GenerateDocComment (t); if (t.Methods != null) foreach (Method m in t.Methods) m.GenerateDocComment (t); if (t.Operators != null) foreach (Operator o in t.Operators) o.GenerateDocComment (t); } // MemberCore private static readonly string lineHead = Environment.NewLine + " "; private static XmlNode GetDocCommentNode (MemberCore mc, string name) { // FIXME: It could be even optimizable as not // to use XmlDocument. But anyways the nodes // are not kept in memory. XmlDocument doc = RootContext.Documentation.XmlDocumentation; try { XmlElement el = doc.CreateElement ("member"); el.SetAttribute ("name", name); string normalized = mc.DocComment; el.InnerXml = normalized; // csc keeps lines as written in the sources // and inserts formatting indentation (which // is different from XmlTextWriter.Formatting // one), but when a start tag contains an // endline, it joins the next line. We don't // have to follow such a hacky behavior. string [] split = normalized.Split ('\n'); int j = 0; for (int i = 0; i < split.Length; i++) { string s = split [i].TrimEnd (); if (s.Length > 0) split [j++] = s; } el.InnerXml = lineHead + String.Join ( lineHead, split, 0, j); return el; } catch (XmlException ex) { Report.Warning (1570, 1, mc.Location, "XML comment on `{0}' has non-well-formed XML ({1})", name, ex.Message); XmlComment com = doc.CreateComment (String.Format ("FIXME: Invalid documentation markup was found for member {0}", name)); return com; } } // // Generates xml doc comments (if any), and if required, // handle warning report. // internal static void GenerateDocComment (MemberCore mc, DeclSpace ds) { if (mc.DocComment != null) { string name = mc.GetDocCommentName (ds); XmlNode n = GetDocCommentNode (mc, name); XmlElement el = n as XmlElement; if (el != null) { mc.OnGenerateDocComment (ds, el); // FIXME: it could be done with XmlReader foreach (XmlElement inc in n.SelectNodes (".//include")) HandleInclude (mc, inc); // FIXME: it could be done with XmlReader DeclSpace dsTarget = mc as DeclSpace; if (dsTarget == null) dsTarget = ds; foreach (XmlElement see in n.SelectNodes (".//see")) HandleSee (mc, dsTarget, see); foreach (XmlElement seealso in n.SelectNodes (".//seealso")) HandleSeeAlso (mc, dsTarget, seealso); foreach (XmlElement see in n.SelectNodes (".//exception")) HandleException (mc, dsTarget, see); } n.WriteTo (RootContext.Documentation.XmlCommentOutput); } else if (mc.IsExposedFromAssembly (ds)) { Constructor c = mc as Constructor; if (c == null || !c.IsDefault ()) Report.Warning (1591, 4, mc.Location, "Missing XML comment for publicly visible type or member `{0}'", mc.GetSignatureForError ()); } } // // Processes "include" element. Check included file and // embed the document content inside this documentation node. // private static void HandleInclude (MemberCore mc, XmlElement el) { string file = el.GetAttribute ("file"); string path = el.GetAttribute ("path"); if (file == "") { Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `file' attribute"); el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el); } else if (path == "") { Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `path' attribute"); el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el); } else { XmlDocument doc = RootContext.Documentation.StoredDocuments [file] as XmlDocument; if (doc == null) { try { doc = new XmlDocument (); doc.Load (file); RootContext.Documentation.StoredDocuments.Add (file, doc); } catch (Exception) { el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (String.Format (" Badly formed XML in at comment file `{0}': cannot be included ", file)), el); Report.Warning (1592, 1, mc.Location, "Badly formed XML in included comments file -- `{0}'", file); } } bool keepIncludeNode = false; if (doc != null) { try { XmlNodeList nl = doc.SelectNodes (path); if (nl.Count == 0) { el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" No matching elements were found for the include tag embedded here. "), el); keepIncludeNode = true; } foreach (XmlNode n in nl) el.ParentNode.InsertBefore (el.OwnerDocument.ImportNode (n, true), el); } catch (Exception ex) { el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Failed to insert some or all of included XML "), el); Report.Warning (1589, 1, mc.Location, "Unable to include XML fragment `{0}' of file `{1}' ({2})", path, file, ex.Message); } } if (!keepIncludeNode) el.ParentNode.RemoveChild (el); } } // // Handles <see> elements. // private static void HandleSee (MemberCore mc, DeclSpace ds, XmlElement see) { HandleXrefCommon (mc, ds, see); } // // Handles <seealso> elements. // private static void HandleSeeAlso (MemberCore mc, DeclSpace ds, XmlElement seealso) { HandleXrefCommon (mc, ds, seealso); } // // Handles <exception> elements. // private static void HandleException (MemberCore mc, DeclSpace ds, XmlElement seealso) { HandleXrefCommon (mc, ds, seealso); } static readonly char [] wsChars = new char [] {' ', '\t', '\n', '\r'}; // // returns a full runtime type name from a name which might // be C# specific type name. // private static Type FindDocumentedType (MemberCore mc, string name, DeclSpace ds, string cref) { bool isArray = false; string identifier = name; if (name [name.Length - 1] == ']') { string tmp = name.Substring (0, name.Length - 1).Trim (wsChars); if (tmp [tmp.Length - 1] == '[') { identifier = tmp.Substring (0, tmp.Length - 1).Trim (wsChars); isArray = true; } } Type t = FindDocumentedTypeNonArray (mc, identifier, ds, cref); if (t != null && isArray) t = Array.CreateInstance (t, 0).GetType (); return t; } private static Type FindDocumentedTypeNonArray (MemberCore mc, string identifier, DeclSpace ds, string cref) { switch (identifier) { case "int": return typeof (int); case "uint": return typeof (uint); case "short": return typeof (short); case "ushort": return typeof (ushort); case "long": return typeof (long); case "ulong": return typeof (ulong); case "float": return typeof (float); case "double": return typeof (double); case "char": return typeof (char); case "decimal": return typeof (decimal); case "byte": return typeof (byte); case "sbyte": return typeof (sbyte); case "object": return typeof (object); case "bool": return typeof (bool); case "string": return typeof (string); case "void": return typeof (void); } FullNamedExpression e = ds.LookupType (identifier, mc.Location, false); if (e != null) { if (!(e is TypeExpr)) return null; return ((TypeExpr) e).Type; } int index = identifier.LastIndexOf ('.'); if (index < 0) return null; int warn; Type parent = FindDocumentedType (mc, identifier.Substring (0, index), ds, cref); if (parent == null) return null; // no need to detect warning 419 here return FindDocumentedMember (mc, parent, identifier.Substring (index + 1), emptyParamList, ds, out warn, cref) as Type; } // // Returns a MemberInfo that is referenced in XML documentation // (by "see" or "seealso" elements). // private static MemberInfo FindDocumentedMember (MemberCore mc, Type type, string memberName, Type [] paramList, DeclSpace ds, out int warningType, string cref) { warningType = 0; MethodSignature msig = new MethodSignature (memberName, null, paramList); MemberInfo [] mis = type.FindMembers ( MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, MethodSignature.method_signature_filter, msig); if (mis.Length > 0) { if (IsAmbiguous (mis)) warningType = 419; return mis [0]; } if (paramList.Length == 0) { // search for fields/events etc. mis = type.FindMembers ( MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, Type.FilterName, memberName); if (mis.Length == 0) return null; if (IsAmbiguous (mis)) warningType = 419; return mis [0]; } // search for operators (whose parameters exactly // matches with the list) and possibly report CS1581. string oper = null; string returnTypeName = null; if (memberName.StartsWith ("implicit operator ")) { oper = "op_Implicit"; returnTypeName = memberName.Substring (18).Trim (wsChars); } else if (memberName.StartsWith ("explicit operator ")) { oper = "op_Explicit"; returnTypeName = memberName.Substring (18).Trim (wsChars); } else if (memberName.StartsWith ("operator ")) { oper = memberName.Substring (9).Trim (wsChars); switch (oper) { // either unary or binary case "+": oper = paramList.Length == 2 ? Binary.oper_names [(int) Binary.Operator.Addition] : Unary.oper_names [(int) Unary.Operator.UnaryPlus]; break; case "-": oper = paramList.Length == 2 ? Binary.oper_names [(int) Binary.Operator.Subtraction] : Unary.oper_names [(int) Unary.Operator.UnaryNegation]; break; // unary case "!": oper = Unary.oper_names [(int) Unary.Operator.LogicalNot]; break; case "~": oper = Unary.oper_names [(int) Unary.Operator.OnesComplement]; break; case "++": oper = "op_Increment"; break; case "--": oper = "op_Decrement"; break; case "true": oper = "op_True"; break; case "false": oper = "op_False"; break; // binary case "*": oper = Binary.oper_names [(int) Binary.Operator.Multiply]; break; case "/": oper = Binary.oper_names [(int) Binary.Operator.Division]; break; case "%": oper = Binary.oper_names [(int) Binary.Operator.Modulus]; break; case "&": oper = Binary.oper_names [(int) Binary.Operator.BitwiseAnd]; break; case "|": oper = Binary.oper_names [(int) Binary.Operator.BitwiseOr]; break; case "^": oper = Binary.oper_names [(int) Binary.Operator.ExclusiveOr]; break; case "<<": oper = Binary.oper_names [(int) Binary.Operator.LeftShift]; break; case ">>": oper = Binary.oper_names [(int) Binary.Operator.RightShift]; break; case "==": oper = Binary.oper_names [(int) Binary.Operator.Equality]; break; case "!=": oper = Binary.oper_names [(int) Binary.Operator.Inequality]; break; case "<": oper = Binary.oper_names [(int) Binary.Operator.LessThan]; break; case ">": oper = Binary.oper_names [(int) Binary.Operator.GreaterThan]; break; case "<=": oper = Binary.oper_names [(int) Binary.Operator.LessThanOrEqual]; break; case ">=": oper = Binary.oper_names [(int) Binary.Operator.GreaterThanOrEqual]; break; default: warningType = 1584; Report.Warning (1020, 1, mc.Location, "Overloadable {0} operator is expected", paramList.Length == 2 ? "binary" : "unary"); Report.Warning (1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'", mc.GetSignatureForError (), cref);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -