2 // doc.cs: Support for XML documentation comment.
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2004 Novell, Inc.
14 using System.Collections.Generic;
21 namespace Mono.CSharp {
24 // Support class for XML documentation.
31 // Generates xml doc comments (if any), and if required,
32 // handle warning report.
34 internal static void GenerateTypeDocComment (TypeContainer t,
35 DeclSpace ds, Report Report)
37 GenerateDocComment (t, ds, Report);
39 if (t.DefaultStaticConstructor != null)
40 t.DefaultStaticConstructor.GenerateDocComment (t);
42 if (t.InstanceConstructors != null)
43 foreach (Constructor c in t.InstanceConstructors)
44 c.GenerateDocComment (t);
47 foreach (TypeContainer tc in t.Types)
48 tc.GenerateDocComment (t);
50 if (t.Constants != null)
51 foreach (Const c in t.Constants)
52 c.GenerateDocComment (t);
55 foreach (FieldBase f in t.Fields)
56 f.GenerateDocComment (t);
59 foreach (Event e in t.Events)
60 e.GenerateDocComment (t);
62 if (t.Indexers != null)
63 foreach (Indexer ix in t.Indexers)
64 ix.GenerateDocComment (t);
66 if (t.Properties != null)
67 foreach (Property p in t.Properties)
68 p.GenerateDocComment (t);
70 if (t.Methods != null)
71 foreach (MethodOrOperator m in t.Methods)
72 m.GenerateDocComment (t);
74 if (t.Operators != null)
75 foreach (Operator o in t.Operators)
76 o.GenerateDocComment (t);
80 private static readonly string line_head =
81 Environment.NewLine + " ";
83 private static XmlNode GetDocCommentNode (MemberCore mc,
84 string name, Report Report)
86 // FIXME: It could be even optimizable as not
87 // to use XmlDocument. But anyways the nodes
88 // are not kept in memory.
89 XmlDocument doc = mc.Compiler.Settings.Documentation.XmlDocumentation;
91 XmlElement el = doc.CreateElement ("member");
92 el.SetAttribute ("name", name);
93 string normalized = mc.DocComment;
94 el.InnerXml = normalized;
95 // csc keeps lines as written in the sources
96 // and inserts formatting indentation (which
97 // is different from XmlTextWriter.Formatting
98 // one), but when a start tag contains an
99 // endline, it joins the next line. We don't
100 // have to follow such a hacky behavior.
102 normalized.Split ('\n');
104 for (int i = 0; i < split.Length; i++) {
105 string s = split [i].TrimEnd ();
109 el.InnerXml = line_head + String.Join (
110 line_head, split, 0, j);
112 } catch (Exception ex) {
113 Report.Warning (1570, 1, mc.Location, "XML comment on `{0}' has non-well-formed XML ({1})", name, ex.Message);
114 XmlComment com = doc.CreateComment (String.Format ("FIXME: Invalid documentation markup was found for member {0}", name));
120 // Generates xml doc comments (if any), and if required,
121 // handle warning report.
123 internal static void GenerateDocComment (MemberCore mc,
124 DeclSpace ds, Report Report)
126 if (mc.DocComment != null) {
127 string name = mc.GetDocCommentName (ds);
129 XmlNode n = GetDocCommentNode (mc, name, Report);
131 XmlElement el = n as XmlElement;
133 mc.OnGenerateDocComment (el);
135 // FIXME: it could be done with XmlReader
136 XmlNodeList nl = n.SelectNodes (".//include");
138 // It could result in current node removal, so prepare another list to iterate.
139 var al = new List<XmlNode> (nl.Count);
140 foreach (XmlNode inc in nl)
142 foreach (XmlElement inc in al)
143 if (!HandleInclude (mc, inc, Report))
144 inc.ParentNode.RemoveChild (inc);
147 // FIXME: it could be done with XmlReader
148 DeclSpace ds_target = mc as DeclSpace;
149 if (ds_target == null)
152 foreach (XmlElement see in n.SelectNodes (".//see"))
153 HandleSee (mc, ds_target, see, Report);
154 foreach (XmlElement seealso in n.SelectNodes (".//seealso"))
155 HandleSeeAlso (mc, ds_target, seealso ,Report);
156 foreach (XmlElement see in n.SelectNodes (".//exception"))
157 HandleException (mc, ds_target, see, Report);
160 n.WriteTo (mc.Compiler.Settings.Documentation.XmlCommentOutput);
162 else if (mc.IsExposedFromAssembly ()) {
163 Constructor c = mc as Constructor;
164 if (c == null || !c.IsDefault ())
165 Report.Warning (1591, 4, mc.Location,
166 "Missing XML comment for publicly visible type or member `{0}'", mc.GetSignatureForError ());
171 // Processes "include" element. Check included file and
172 // embed the document content inside this documentation node.
174 private static bool HandleInclude (MemberCore mc, XmlElement el, Report Report)
176 bool keep_include_node = false;
177 string file = el.GetAttribute ("file");
178 string path = el.GetAttribute ("path");
180 Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `file' attribute");
181 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el);
182 keep_include_node = true;
184 else if (path.Length == 0) {
185 Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `path' attribute");
186 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el);
187 keep_include_node = true;
191 if (!mc.Compiler.Settings.Documentation.StoredDocuments.TryGetValue (file, out doc)) {
193 doc = new XmlDocument ();
195 mc.Compiler.Settings.Documentation.StoredDocuments.Add (file, doc);
196 } catch (Exception) {
197 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (String.Format (" Badly formed XML in at comment file `{0}': cannot be included ", file)), el);
198 Report.Warning (1592, 1, mc.Location, "Badly formed XML in included comments file -- `{0}'", file);
203 XmlNodeList nl = doc.SelectNodes (path);
205 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" No matching elements were found for the include tag embedded here. "), el);
207 keep_include_node = true;
209 foreach (XmlNode n in nl)
210 el.ParentNode.InsertBefore (el.OwnerDocument.ImportNode (n, true), el);
211 } catch (Exception ex) {
212 el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Failed to insert some or all of included XML "), el);
213 Report.Warning (1589, 1, mc.Location, "Unable to include XML fragment `{0}' of file `{1}' ({2})", path, file, ex.Message);
217 return keep_include_node;
221 // Handles <see> elements.
223 private static void HandleSee (MemberCore mc,
224 DeclSpace ds, XmlElement see, Report r)
226 HandleXrefCommon (mc, ds, see, r);
230 // Handles <seealso> elements.
232 private static void HandleSeeAlso (MemberCore mc,
233 DeclSpace ds, XmlElement seealso, Report r)
235 HandleXrefCommon (mc, ds, seealso, r);
239 // Handles <exception> elements.
241 private static void HandleException (MemberCore mc,
242 DeclSpace ds, XmlElement seealso, Report r)
244 HandleXrefCommon (mc, ds, seealso, r);
247 static readonly char [] wsChars =
248 new char [] {' ', '\t', '\n', '\r'};
251 // returns a full runtime type name from a name which might
252 // be C# specific type name.
254 private static TypeSpec FindDocumentedType (MemberCore mc, string name, DeclSpace ds, string cref, Report r)
256 bool is_array = false;
257 string identifier = name;
258 if (name [name.Length - 1] == ']') {
259 string tmp = name.Substring (0, name.Length - 1).Trim (wsChars);
260 if (tmp [tmp.Length - 1] == '[') {
261 identifier = tmp.Substring (0, tmp.Length - 1).Trim (wsChars);
265 TypeSpec t = FindDocumentedTypeNonArray (mc, identifier, ds, cref, r);
266 if (t != null && is_array)
267 t = ArrayContainer.MakeType (mc.Module, t);
271 private static TypeSpec FindDocumentedTypeNonArray (MemberCore mc,
272 string identifier, DeclSpace ds, string cref, Report r)
274 var types = mc.Module.Compiler.BuiltinTypes;
275 switch (identifier) {
295 return types.Decimal;
309 FullNamedExpression e = ds.LookupNamespaceOrType (identifier, 0, mc.Location, false);
311 if (!(e is TypeExpr))
315 int index = identifier.LastIndexOf ('.');
319 var nsName = identifier.Substring (0, index);
320 var typeName = identifier.Substring (index + 1);
321 Namespace ns = ds.NamespaceEntry.NS.GetNamespace (nsName, false);
322 ns = ns ?? mc.Module.GlobalRootNamespace.GetNamespace(nsName, false);
324 var te = ns.LookupType(mc, typeName, 0, true, mc.Location);
330 TypeSpec parent = FindDocumentedType (mc, identifier.Substring (0, index), ds, cref, r);
333 // no need to detect warning 419 here
334 var ts = FindDocumentedMember (mc, parent,
335 identifier.Substring (index + 1),
336 null, ds, out warn, cref, false, null, r) as TypeSpec;
343 // Returns a MemberInfo that is referenced in XML documentation
344 // (by "see" or "seealso" elements).
346 private static MemberSpec FindDocumentedMember (MemberCore mc,
347 TypeSpec type, string member_name, AParametersCollection param_list,
348 DeclSpace ds, out int warning_type, string cref,
349 bool warn419, string name_for_error, Report r)
351 // for (; type != null; type = type.DeclaringType) {
352 var mi = FindDocumentedMemberNoNest (
353 mc, type, member_name, param_list, ds,
354 out warning_type, cref, warn419,
357 return mi; // new FoundMember (type, mi);
363 private static MemberSpec FindDocumentedMemberNoNest (
364 MemberCore mc, TypeSpec type, string member_name,
365 AParametersCollection param_list, DeclSpace ds, out int warning_type,
366 string cref, bool warn419, string name_for_error, Report Report)
369 // var filter = new MemberFilter (member_name, 0, MemberKind.All, param_list, null);
370 IList<MemberSpec> found = null;
371 while (type != null && found == null) {
372 found = MemberCache.FindMembers (type, member_name, false);
373 type = type.DeclaringType;
379 if (warn419 && found.Count > 1) {
380 Report419 (mc, name_for_error, found.ToArray (), Report);
386 if (param_list == null) {
387 // search for fields/events etc.
388 mis = TypeManager.MemberLookup (type, null,
389 type, MemberKind.All,
390 BindingRestriction.None,
392 mis = FilterOverridenMembersOut (mis);
393 if (mis == null || mis.Length == 0)
395 if (warn419 && IsAmbiguous (mis))
396 Report419 (mc, name_for_error, mis, Report);
400 MethodSignature msig = new MethodSignature (member_name, null, param_list);
401 mis = FindMethodBase (type,
402 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance,
405 if (warn419 && mis.Length > 0) {
406 if (IsAmbiguous (mis))
407 Report419 (mc, name_for_error, mis, Report);
411 // search for operators (whose parameters exactly
412 // matches with the list) and possibly report CS1581.
414 string return_type_name = null;
415 if (member_name.StartsWith ("implicit operator ")) {
416 Operator.GetMetadataName (Operator.OpType.Implicit);
417 return_type_name = member_name.Substring (18).Trim (wsChars);
419 else if (member_name.StartsWith ("explicit operator ")) {
420 oper = Operator.GetMetadataName (Operator.OpType.Explicit);
421 return_type_name = member_name.Substring (18).Trim (wsChars);
423 else if (member_name.StartsWith ("operator ")) {
424 oper = member_name.Substring (9).Trim (wsChars);
426 // either unary or binary
428 oper = param_list.Length == 2 ?
429 Operator.GetMetadataName (Operator.OpType.Addition) :
430 Operator.GetMetadataName (Operator.OpType.UnaryPlus);
433 oper = param_list.Length == 2 ?
434 Operator.GetMetadataName (Operator.OpType.Subtraction) :
435 Operator.GetMetadataName (Operator.OpType.UnaryNegation);
438 oper = Operator.GetMetadataName (oper);
443 Report.Warning (1020, 1, mc.Location, "Overloadable {0} operator is expected", param_list.Length == 2 ? "binary" : "unary");
444 Report.Warning (1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'",
445 mc.GetSignatureForError (), cref);
449 // here we still don't consider return type (to
450 // detect CS1581 or CS1002+CS1584).
451 msig = new MethodSignature (oper, null, param_list);
453 mis = FindMethodBase (type,
454 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance,
457 return null; // CS1574
459 TypeSpec expected = mi is MethodSpec ?
460 ((MethodSpec) mi).ReturnType :
462 ((PropertySpec) mi).PropertyType :
464 if (return_type_name != null) {
465 TypeSpec returnType = FindDocumentedType (mc, return_type_name, ds, cref, Report);
466 if (returnType == null || returnType != expected) {
468 Report.Warning (1581, 1, mc.Location, "Invalid return type in XML comment cref attribute `{0}'", cref);
477 // Processes "see" or "seealso" elements.
478 // Checks cref attribute.
480 private static void HandleXrefCommon (MemberCore mc,
481 DeclSpace ds, XmlElement xref, Report Report)
483 string cref = xref.GetAttribute ("cref").Trim (wsChars);
484 // when, XmlReader, "if (cref == null)"
485 if (!xref.HasAttribute ("cref"))
487 if (cref.Length == 0)
488 Report.Warning (1001, 1, mc.Location, "Identifier expected");
489 // ... and continue until CS1584.
491 string signature; // "x:" are stripped
492 string name; // method invokation "(...)" are removed
493 string parameters; // method parameter list
495 // When it found '?:' ('T:' 'M:' 'F:' 'P:' 'E:' etc.),
496 // MS ignores not only its member kind, but also
497 // the entire syntax correctness. Nor it also does
498 // type fullname resolution i.e. "T:List(int)" is kept
499 // as T:List(int), not
500 // T:System.Collections.Generic.List<System.Int32>
501 if (cref.Length > 2 && cref [1] == ':')
506 // Also note that without "T:" any generic type
509 int parens_pos = signature.IndexOf ('(');
510 int brace_pos = parens_pos >= 0 ? -1 :
511 signature.IndexOf ('[');
512 if (parens_pos > 0 && signature [signature.Length - 1] == ')') {
513 name = signature.Substring (0, parens_pos).Trim (wsChars);
514 parameters = signature.Substring (parens_pos + 1, signature.Length - parens_pos - 2).Trim (wsChars);
516 else if (brace_pos > 0 && signature [signature.Length - 1] == ']') {
517 name = signature.Substring (0, brace_pos).Trim (wsChars);
518 parameters = signature.Substring (brace_pos + 1, signature.Length - brace_pos - 2).Trim (wsChars);
524 Normalize (mc, ref name, Report);
526 string identifier = GetBodyIdentifierFromName (name);
528 // Check if identifier is valid.
529 // This check is not necessary to mark as error, but
530 // csc specially reports CS1584 for wrong identifiers.
531 string [] name_elems = identifier.Split ('.');
532 for (int i = 0; i < name_elems.Length; i++) {
533 string nameElem = GetBodyIdentifierFromName (name_elems [i]);
535 Normalize (mc, ref nameElem, Report);
536 if (!Tokenizer.IsValidIdentifier (nameElem)
537 && nameElem.IndexOf ("operator") < 0) {
538 Report.Warning (1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'",
539 mc.GetSignatureForError (), cref);
540 xref.SetAttribute ("cref", "!:" + signature);
545 // check if parameters are valid
546 AParametersCollection parameter_types;
547 if (parameters == null)
548 parameter_types = null;
549 else if (parameters.Length == 0)
550 parameter_types = ParametersCompiled.EmptyReadOnlyParameters;
552 string [] param_list = parameters.Split (',');
553 var plist = new List<TypeSpec> ();
554 for (int i = 0; i < param_list.Length; i++) {
555 string param_type_name = param_list [i].Trim (wsChars);
556 Normalize (mc, ref param_type_name, Report);
557 TypeSpec param_type = FindDocumentedType (mc, param_type_name, ds, cref, Report);
558 if (param_type == null) {
559 Report.Warning (1580, 1, mc.Location, "Invalid type for parameter `{0}' in XML comment cref attribute `{1}'",
560 (i + 1).ToString (), cref);
563 plist.Add (param_type);
566 parameter_types = ParametersCompiled.CreateFullyResolved (plist.ToArray ());
569 TypeSpec type = FindDocumentedType (mc, name, ds, cref, Report);
571 // delegate must not be referenced with args
573 || parameter_types == null)) {
574 string result = GetSignatureForDoc (type)
575 + (brace_pos < 0 ? String.Empty : signature.Substring (brace_pos));
576 xref.SetAttribute ("cref", "T:" + result);
580 int period = name.LastIndexOf ('.');
582 string typeName = name.Substring (0, period);
583 string member_name = name.Substring (period + 1);
584 string lookup_name = member_name == "this" ? MemberCache.IndexerNameAlias : member_name;
585 Normalize (mc, ref lookup_name, Report);
586 Normalize (mc, ref member_name, Report);
587 type = FindDocumentedType (mc, typeName, ds, cref, Report);
590 var mi = FindDocumentedMember (mc, type, lookup_name, parameter_types, ds, out warn_result, cref, true, name, Report);
594 // we cannot use 'type' directly
595 // to get its name, since mi
596 // could be from DeclaringType
598 xref.SetAttribute ("cref", GetMemberDocHead (mi) + GetSignatureForDoc (mi.DeclaringType) + "." + member_name + GetParametersFormatted (mi));
599 return; // a member of a type
604 var mi = FindDocumentedMember (mc, ds.PartialContainer.Definition, name, parameter_types, ds, out warn_result, cref, true, name, Report);
609 // we cannot use 'type' directly
610 // to get its name, since mi
611 // could be from DeclaringType
613 xref.SetAttribute ("cref", GetMemberDocHead (mi) + GetSignatureForDoc (mi.DeclaringType) + "." + name + GetParametersFormatted (mi));
614 return; // local member name
618 // It still might be part of namespace name.
619 Namespace ns = ds.NamespaceEntry.NS.GetNamespace (name, false);
621 xref.SetAttribute ("cref", "N:" + ns.GetSignatureForError ());
622 return; // a namespace
624 if (mc.Module.GlobalRootNamespace.IsNamespace (name)) {
625 xref.SetAttribute ("cref", "N:" + name);
626 return; // a namespace
629 Report.Warning (1574, 1, mc.Location, "XML comment on `{0}' has cref attribute `{1}' that could not be resolved",
630 mc.GetSignatureForError (), cref);
632 xref.SetAttribute ("cref", "!:" + name);
635 static string GetParametersFormatted (MemberSpec mi)
637 var pm = mi as IParametersMember;
638 if (pm == null || pm.Parameters.IsEmpty)
641 AParametersCollection parameters = pm.Parameters;
643 if (parameters == null || parameters.Count == 0)
646 StringBuilder sb = new StringBuilder ();
648 for (int i = 0; i < parameters.Count; i++) {
649 // if (is_setter && i + 1 == parameters.Count)
650 // break; // skip "value".
653 TypeSpec t = parameters.Types [i];
654 sb.Append (GetSignatureForDoc (t));
657 return sb.ToString ();
660 static string GetBodyIdentifierFromName (string name)
662 string identifier = name;
664 if (name.Length > 0 && name [name.Length - 1] == ']') {
665 string tmp = name.Substring (0, name.Length - 1).Trim (wsChars);
666 int last = tmp.LastIndexOf ('[');
668 identifier = tmp.Substring (0, last).Trim (wsChars);
674 static void Report419 (MemberCore mc, string member_name, MemberSpec [] mis, Report Report)
676 Report.Warning (419, 3, mc.Location,
677 "Ambiguous reference in cref attribute `{0}'. Assuming `{1}' but other overloads including `{2}' have also matched",
679 TypeManager.GetFullNameSignature (mis [0]),
680 TypeManager.GetFullNameSignature (mis [1]));
684 // Get a prefix from member type for XML documentation (used
685 // to formalize cref target name).
687 static string GetMemberDocHead (MemberSpec type)
689 if (type is FieldSpec)
691 if (type is MethodSpec)
693 if (type is EventSpec)
695 if (type is PropertySpec)
697 if (type is TypeSpec)
706 // Returns a string that represents the signature for this
707 // member which should be used in XML documentation.
709 public static string GetMethodDocCommentName (MemberCore mc, ParametersCompiled parameters, DeclSpace ds)
711 IParameterData [] plist = parameters.FixedParameters;
712 string paramSpec = String.Empty;
714 StringBuilder psb = new StringBuilder ();
716 foreach (Parameter p in plist) {
717 psb.Append (psb.Length != 0 ? "," : "(");
718 psb.Append (GetSignatureForDoc (parameters.Types [i++]));
719 if ((p.ModFlags & Parameter.Modifier.ISBYREF) != 0)
722 paramSpec = psb.ToString ();
725 if (paramSpec.Length > 0)
728 string name = mc.Name;
729 if (mc is Constructor)
731 else if (mc is InterfaceMemberBase) {
732 var imb = (InterfaceMemberBase) mc;
733 name = imb.GetFullName (imb.ShortName);
735 name = name.Replace ('.', '#');
737 if (mc.MemberName.TypeArguments != null && mc.MemberName.TypeArguments.Count > 0)
738 name += "``" + mc.MemberName.CountTypeArguments;
740 string suffix = String.Empty;
741 Operator op = mc as Operator;
743 switch (op.OperatorType) {
744 case Operator.OpType.Implicit:
745 case Operator.OpType.Explicit:
746 suffix = "~" + GetSignatureForDoc (op.ReturnType);
750 return String.Concat (mc.DocCommentHeader, ds.Name, ".", name, paramSpec, suffix);
753 static string GetSignatureForDoc (TypeSpec type)
755 var tp = type as TypeParameterSpec;
758 type = type.DeclaringType;
759 while (type != null && type.DeclaringType != null) {
760 type = type.DeclaringType;
761 c += type.MemberDefinition.TypeParametersCount;
763 var prefix = tp.IsMethodOwned ? "``" : "`";
764 return prefix + (c + tp.DeclaredPosition);
767 var pp = type as PointerContainer;
769 return GetSignatureForDoc (pp.Element) + "*";
771 ArrayContainer ap = type as ArrayContainer;
773 return GetSignatureForDoc (ap.Element) +
774 ArrayContainer.GetPostfixSignature (ap.Rank);
776 if (TypeManager.IsGenericType (type)) {
777 string g = type.MemberDefinition.Namespace;
778 if (g != null && g.Length > 0)
780 int idx = type.Name.LastIndexOf ('`');
781 g += (idx < 0 ? type.Name : type.Name.Substring (0, idx)) + '{';
783 foreach (TypeSpec t in TypeManager.GetTypeArguments (type))
784 g += (argpos++ > 0 ? "," : String.Empty) + GetSignatureForDoc (t);
789 string name = type.GetMetaInfo ().FullName != null ? type.GetMetaInfo ().FullName : type.Name;
790 return name.Replace ("+", ".").Replace ('&', '@');
794 // Raised (and passed an XmlElement that contains the comment)
795 // when GenerateDocComment is writing documentation expectedly.
797 // FIXME: with a few effort, it could be done with XmlReader,
798 // that means removal of DOM use.
800 internal static void OnMethodGenerateDocComment (
801 MethodCore mc, XmlElement el, Report Report)
803 var paramTags = new Dictionary<string, string> ();
804 foreach (XmlElement pelem in el.SelectNodes ("param")) {
805 string xname = pelem.GetAttribute ("name");
806 if (xname.Length == 0)
807 continue; // really? but MS looks doing so
808 if (xname != "" && mc.ParameterInfo.GetParameterIndexByName (xname) < 0)
809 Report.Warning (1572, 2, mc.Location, "XML comment on `{0}' has a param tag for `{1}', but there is no parameter by that name",
810 mc.GetSignatureForError (), xname);
811 else if (paramTags.ContainsKey (xname))
812 Report.Warning (1571, 2, mc.Location, "XML comment on `{0}' has a duplicate param tag for `{1}'",
813 mc.GetSignatureForError (), xname);
814 paramTags [xname] = xname;
816 IParameterData [] plist = mc.ParameterInfo.FixedParameters;
817 foreach (Parameter p in plist) {
818 if (paramTags.Count > 0 && !paramTags.ContainsKey (p.Name))
819 Report.Warning (1573, 4, mc.Location, "Parameter `{0}' has no matching param tag in the XML comment for `{1}'",
820 p.Name, mc.GetSignatureForError ());
824 private static void Normalize (MemberCore mc, ref string name, Report Report)
826 if (name.Length > 0 && name [0] == '@')
827 name = name.Substring (1);
828 else if (name == "this")
830 else if (Tokenizer.IsKeyword (name) && !IsTypeName (name))
831 Report.Warning (1041, 1, mc.Location, "Identifier expected. `{0}' is a keyword", name);
834 private static bool IsTypeName (string name)
860 // Implements XML documentation generation.
862 public class Documentation
864 public Documentation (string xml_output_filename)
866 docfilename = xml_output_filename;
867 XmlDocumentation = new XmlDocument ();
868 XmlDocumentation.PreserveWhitespace = false;
871 private string docfilename;
874 // Used to create element which helps well-formedness checking.
876 public XmlDocument XmlDocumentation;
879 // The output for XML documentation.
881 public XmlWriter XmlCommentOutput;
884 // Stores XmlDocuments that are included in XML documentation.
885 // Keys are included filenames, values are XmlDocuments.
887 public Dictionary<string, XmlDocument> StoredDocuments = new Dictionary<string, XmlDocument> ();
890 // Outputs XML documentation comment from tokenized comments.
892 public bool OutputDocComment (string asmfilename, Report Report)
894 XmlTextWriter w = null;
896 w = new XmlTextWriter (docfilename, null);
898 w.Formatting = Formatting.Indented;
899 w.WriteStartDocument ();
900 w.WriteStartElement ("doc");
901 w.WriteStartElement ("assembly");
902 w.WriteStartElement ("name");
903 w.WriteString (Path.ChangeExtension (asmfilename, null));
904 w.WriteEndElement (); // name
905 w.WriteEndElement (); // assembly
906 w.WriteStartElement ("members");
907 XmlCommentOutput = w;
908 GenerateDocComment (Report);
909 w.WriteFullEndElement (); // members
910 w.WriteEndElement ();
911 w.WriteWhitespace (Environment.NewLine);
912 w.WriteEndDocument ();
914 } catch (Exception ex) {
915 Report.Error (1569, "Error generating XML documentation file `{0}' (`{1}')", docfilename, ex.Message);
924 // Fixes full type name of each documented types/members up.
926 public void GenerateDocComment (Report r)
928 TypeContainer root = RootContext.ToplevelTypes;
930 if (root.Types != null)
931 foreach (TypeContainer tc in root.Types)
932 DocUtil.GenerateTypeDocComment (tc, null, r);