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.
12 #if ! BOOTSTRAP_WITH_OLDLIB
14 using System
.Collections
;
15 using System
.Collections
.Specialized
;
17 using System
.Reflection
;
18 using System
.Reflection
.Emit
;
19 using System
.Runtime
.CompilerServices
;
20 using System
.Runtime
.InteropServices
;
21 using System
.Security
;
22 using System
.Security
.Permissions
;
26 using Mono
.CompilerServices
.SymbolWriter
;
28 namespace Mono
.CSharp
{
31 // Support class for XML documentation.
46 // Generates xml doc comments (if any), and if required,
47 // handle warning report.
49 internal static void GenerateTypeDocComment (TypeContainer t
,
52 GenerateDocComment (t
, ds
);
54 if (t
.DefaultStaticConstructor
!= null)
55 t
.DefaultStaticConstructor
.GenerateDocComment (t
);
57 if (t
.InstanceConstructors
!= null)
58 foreach (Constructor c
in t
.InstanceConstructors
)
59 c
.GenerateDocComment (t
);
62 foreach (TypeContainer tc
in t
.Types
)
63 tc
.GenerateDocComment (t
);
65 if (t
.Delegates
!= null)
66 foreach (Delegate de
in t
.Delegates
)
67 de
.GenerateDocComment (t
);
69 if (t
.Constants
!= null)
70 foreach (Const c
in t
.Constants
)
71 c
.GenerateDocComment (t
);
74 foreach (FieldBase f
in t
.Fields
)
75 f
.GenerateDocComment (t
);
78 foreach (Event e
in t
.Events
)
79 e
.GenerateDocComment (t
);
81 if (t
.Indexers
!= null)
82 foreach (Indexer ix
in t
.Indexers
)
83 ix
.GenerateDocComment (t
);
85 if (t
.Properties
!= null)
86 foreach (Property p
in t
.Properties
)
87 p
.GenerateDocComment (t
);
89 if (t
.Methods
!= null)
90 foreach (MethodOrOperator m
in t
.Methods
)
91 m
.GenerateDocComment (t
);
93 if (t
.Operators
!= null)
94 foreach (Operator o
in t
.Operators
)
95 o
.GenerateDocComment (t
);
99 private static readonly string line_head
=
100 Environment
.NewLine
+ " ";
102 private static XmlNode
GetDocCommentNode (MemberCore mc
,
105 // FIXME: It could be even optimizable as not
106 // to use XmlDocument. But anyways the nodes
107 // are not kept in memory.
108 XmlDocument doc
= RootContext
.Documentation
.XmlDocumentation
;
110 XmlElement el
= doc
.CreateElement ("member");
111 el
.SetAttribute ("name", name
);
112 string normalized
= mc
.DocComment
;
113 el
.InnerXml
= normalized
;
114 // csc keeps lines as written in the sources
115 // and inserts formatting indentation (which
116 // is different from XmlTextWriter.Formatting
117 // one), but when a start tag contains an
118 // endline, it joins the next line. We don't
119 // have to follow such a hacky behavior.
121 normalized
.Split ('\n');
123 for (int i
= 0; i
< split
.Length
; i
++) {
124 string s
= split
[i
].TrimEnd ();
128 el
.InnerXml
= line_head
+ String
.Join (
129 line_head
, split
, 0, j
);
131 } catch (Exception ex
) {
132 Report
.Warning (1570, 1, mc
.Location
, "XML comment on `{0}' has non-well-formed XML ({1})", name
, ex
.Message
);
133 XmlComment com
= doc
.CreateComment (String
.Format ("FIXME: Invalid documentation markup was found for member {0}", name
));
139 // Generates xml doc comments (if any), and if required,
140 // handle warning report.
142 internal static void GenerateDocComment (MemberCore mc
,
145 if (mc
.DocComment
!= null) {
146 string name
= mc
.GetDocCommentName (ds
);
148 XmlNode n
= GetDocCommentNode (mc
, name
);
150 XmlElement el
= n
as XmlElement
;
152 mc
.OnGenerateDocComment (el
);
154 // FIXME: it could be done with XmlReader
155 XmlNodeList nl
= n
.SelectNodes (".//include");
157 // It could result in current node removal, so prepare another list to iterate.
158 ArrayList al
= new ArrayList (nl
.Count
);
159 foreach (XmlNode inc
in nl
)
161 foreach (XmlElement inc
in al
)
162 if (!HandleInclude (mc
, inc
))
163 inc
.ParentNode
.RemoveChild (inc
);
166 // FIXME: it could be done with XmlReader
167 DeclSpace ds_target
= mc
as DeclSpace
;
168 if (ds_target
== null)
171 foreach (XmlElement see
in n
.SelectNodes (".//see"))
172 HandleSee (mc
, ds_target
, see
);
173 foreach (XmlElement seealso
in n
.SelectNodes (".//seealso"))
174 HandleSeeAlso (mc
, ds_target
, seealso
);
175 foreach (XmlElement see
in n
.SelectNodes (".//exception"))
176 HandleException (mc
, ds_target
, see
);
179 n
.WriteTo (RootContext
.Documentation
.XmlCommentOutput
);
181 else if (mc
.IsExposedFromAssembly ()) {
182 Constructor c
= mc
as Constructor
;
183 if (c
== null || !c
.IsDefault ())
184 Report
.Warning (1591, 4, mc
.Location
,
185 "Missing XML comment for publicly visible type or member `{0}'", mc
.GetSignatureForError ());
190 // Processes "include" element. Check included file and
191 // embed the document content inside this documentation node.
193 private static bool HandleInclude (MemberCore mc
, XmlElement el
)
195 bool keep_include_node
= false;
196 string file
= el
.GetAttribute ("file");
197 string path
= el
.GetAttribute ("path");
199 Report
.Warning (1590, 1, mc
.Location
, "Invalid XML `include' element. Missing `file' attribute");
200 el
.ParentNode
.InsertBefore (el
.OwnerDocument
.CreateComment (" Include tag is invalid "), el
);
201 keep_include_node
= true;
203 else if (path
.Length
== 0) {
204 Report
.Warning (1590, 1, mc
.Location
, "Invalid XML `include' element. Missing `path' attribute");
205 el
.ParentNode
.InsertBefore (el
.OwnerDocument
.CreateComment (" Include tag is invalid "), el
);
206 keep_include_node
= true;
209 XmlDocument doc
= RootContext
.Documentation
.StoredDocuments
[file
] as XmlDocument
;
212 doc
= new XmlDocument ();
214 RootContext
.Documentation
.StoredDocuments
.Add (file
, doc
);
215 } catch (Exception
) {
216 el
.ParentNode
.InsertBefore (el
.OwnerDocument
.CreateComment (String
.Format (" Badly formed XML in at comment file `{0}': cannot be included ", file
)), el
);
217 Report
.Warning (1592, 1, mc
.Location
, "Badly formed XML in included comments file -- `{0}'", file
);
222 XmlNodeList nl
= doc
.SelectNodes (path
);
224 el
.ParentNode
.InsertBefore (el
.OwnerDocument
.CreateComment (" No matching elements were found for the include tag embedded here. "), el
);
226 keep_include_node
= true;
228 foreach (XmlNode n
in nl
)
229 el
.ParentNode
.InsertBefore (el
.OwnerDocument
.ImportNode (n
, true), el
);
230 } catch (Exception ex
) {
231 el
.ParentNode
.InsertBefore (el
.OwnerDocument
.CreateComment (" Failed to insert some or all of included XML "), el
);
232 Report
.Warning (1589, 1, mc
.Location
, "Unable to include XML fragment `{0}' of file `{1}' ({2})", path
, file
, ex
.Message
);
236 return keep_include_node
;
240 // Handles <see> elements.
242 private static void HandleSee (MemberCore mc
,
243 DeclSpace ds
, XmlElement see
)
245 HandleXrefCommon (mc
, ds
, see
);
249 // Handles <seealso> elements.
251 private static void HandleSeeAlso (MemberCore mc
,
252 DeclSpace ds
, XmlElement seealso
)
254 HandleXrefCommon (mc
, ds
, seealso
);
258 // Handles <exception> elements.
260 private static void HandleException (MemberCore mc
,
261 DeclSpace ds
, XmlElement seealso
)
263 HandleXrefCommon (mc
, ds
, seealso
);
266 static readonly char [] wsChars
=
267 new char [] {' ', '\t', '\n', '\r'}
;
270 // returns a full runtime type name from a name which might
271 // be C# specific type name.
273 private static Type
FindDocumentedType (MemberCore mc
, string name
, DeclSpace ds
, string cref
)
275 bool is_array
= false;
276 string identifier
= name
;
277 if (name
[name
.Length
- 1] == ']') {
278 string tmp
= name
.Substring (0, name
.Length
- 1).Trim (wsChars
);
279 if (tmp
[tmp
.Length
- 1] == '[') {
280 identifier
= tmp
.Substring (0, tmp
.Length
- 1).Trim (wsChars
);
284 Type t
= FindDocumentedTypeNonArray (mc
, identifier
, ds
, cref
);
285 if (t
!= null && is_array
)
286 t
= Array
.CreateInstance (t
, 0).GetType ();
290 private static Type
FindDocumentedTypeNonArray (MemberCore mc
,
291 string identifier
, DeclSpace ds
, string cref
)
293 switch (identifier
) {
295 return TypeManager
.int32_type
;
297 return TypeManager
.uint32_type
;
299 return TypeManager
.short_type
;;
301 return TypeManager
.ushort_type
;
303 return TypeManager
.int64_type
;
305 return TypeManager
.uint64_type
;;
307 return TypeManager
.float_type
;;
309 return TypeManager
.double_type
;
311 return TypeManager
.char_type
;;
313 return TypeManager
.decimal_type
;;
315 return TypeManager
.byte_type
;;
317 return TypeManager
.sbyte_type
;;
319 return TypeManager
.object_type
;;
321 return TypeManager
.bool_type
;;
323 return TypeManager
.string_type
;;
325 return TypeManager
.void_type
;;
327 FullNamedExpression e
= ds
.LookupNamespaceOrType (identifier
, mc
.Location
, false);
329 if (!(e
is TypeExpr
))
333 int index
= identifier
.LastIndexOf ('.');
337 Type parent
= FindDocumentedType (mc
, identifier
.Substring (0, index
), ds
, cref
);
340 // no need to detect warning 419 here
341 return FindDocumentedMember (mc
, parent
,
342 identifier
.Substring (index
+ 1),
343 null, ds
, out warn
, cref
, false, null).Member
as Type
;
346 private static MemberInfo
[] empty_member_infos
=
349 private static MemberInfo
[] FindMethodBase (Type type
,
350 BindingFlags binding_flags
, MethodSignature signature
)
352 MemberList ml
= TypeManager
.FindMembers (
354 MemberTypes
.Constructor
| MemberTypes
.Method
| MemberTypes
.Property
| MemberTypes
.Custom
,
356 MethodSignature
.method_signature_filter
,
359 return empty_member_infos
;
361 return FilterOverridenMembersOut ((MemberInfo
[]) ml
);
364 static bool IsOverride (PropertyInfo deriv_prop
, PropertyInfo base_prop
)
366 if (!MethodGroupExpr
.IsAncestralType (base_prop
.DeclaringType
, deriv_prop
.DeclaringType
))
369 Type
[] deriv_pd
= TypeManager
.GetParameterData (deriv_prop
).Types
;
370 Type
[] base_pd
= TypeManager
.GetParameterData (base_prop
).Types
;
372 if (deriv_pd
.Length
!= base_pd
.Length
)
375 for (int j
= 0; j
< deriv_pd
.Length
; ++j
) {
376 if (deriv_pd
[j
] != base_pd
[j
])
378 Type ct
= TypeManager
.TypeToCoreType (deriv_pd
[j
]);
379 Type bt
= TypeManager
.TypeToCoreType (base_pd
[j
]);
388 private static MemberInfo
[] FilterOverridenMembersOut (
392 return empty_member_infos
;
394 ArrayList al
= new ArrayList (ml
.Length
);
395 for (int i
= 0; i
< ml
.Length
; i
++) {
396 MethodBase mx
= ml
[i
] as MethodBase
;
397 PropertyInfo px
= ml
[i
] as PropertyInfo
;
398 if (mx
!= null || px
!= null) {
399 bool overriden
= false;
400 for (int j
= 0; j
< ml
.Length
; j
++) {
403 MethodBase my
= ml
[j
] as MethodBase
;
404 if (mx
!= null && my
!= null &&
405 MethodGroupExpr
.IsOverride (my
, mx
)) {
411 PropertyInfo py
= ml
[j
] as PropertyInfo
;
412 if (px
!= null && py
!= null &&
413 IsOverride (py
, px
)) {
423 return al
.ToArray (typeof (MemberInfo
)) as MemberInfo
[];
428 public static FoundMember Empty
= new FoundMember (true);
431 public readonly MemberInfo Member
;
432 public readonly Type Type
;
434 public FoundMember (bool regardless_of_this_value_its_empty
)
441 public FoundMember (Type found_type
, MemberInfo member
)
450 // Returns a MemberInfo that is referenced in XML documentation
451 // (by "see" or "seealso" elements).
453 private static FoundMember
FindDocumentedMember (MemberCore mc
,
454 Type type
, string member_name
, Type
[] param_list
,
455 DeclSpace ds
, out int warning_type
, string cref
,
456 bool warn419
, string name_for_error
)
458 for (; type
!= null; type
= type
.DeclaringType
) {
459 MemberInfo mi
= FindDocumentedMemberNoNest (
460 mc
, type
, member_name
, param_list
, ds
,
461 out warning_type
, cref
, warn419
,
464 return new FoundMember (type
, mi
);
467 return FoundMember
.Empty
;
470 private static MemberInfo
FindDocumentedMemberNoNest (
471 MemberCore mc
, Type type
, string member_name
,
472 Type
[] param_list
, DeclSpace ds
, out int warning_type
,
473 string cref
, bool warn419
, string name_for_error
)
478 if (param_list
== null) {
479 // search for fields/events etc.
480 mis
= TypeManager
.MemberLookup (type
, null,
481 type
, MemberTypes
.All
,
482 BindingFlags
.Public
| BindingFlags
.NonPublic
| BindingFlags
.Static
| BindingFlags
.Instance
,
484 mis
= FilterOverridenMembersOut (mis
);
485 if (mis
== null || mis
.Length
== 0)
487 if (warn419
&& IsAmbiguous (mis
))
488 Report419 (mc
, name_for_error
, mis
);
492 MethodSignature msig
= new MethodSignature (member_name
, null, param_list
);
493 mis
= FindMethodBase (type
,
494 BindingFlags
.Public
| BindingFlags
.NonPublic
| BindingFlags
.Static
| BindingFlags
.Instance
,
497 if (warn419
&& mis
.Length
> 0) {
498 if (IsAmbiguous (mis
))
499 Report419 (mc
, name_for_error
, mis
);
503 // search for operators (whose parameters exactly
504 // matches with the list) and possibly report CS1581.
506 string return_type_name
= null;
507 if (member_name
.StartsWith ("implicit operator ")) {
508 Operator
.GetMetadataName (Operator
.OpType
.Implicit
);
509 return_type_name
= member_name
.Substring (18).Trim (wsChars
);
511 else if (member_name
.StartsWith ("explicit operator ")) {
512 oper
= Operator
.GetMetadataName (Operator
.OpType
.Explicit
);
513 return_type_name
= member_name
.Substring (18).Trim (wsChars
);
515 else if (member_name
.StartsWith ("operator ")) {
516 oper
= member_name
.Substring (9).Trim (wsChars
);
518 // either unary or binary
520 oper
= param_list
.Length
== 2 ?
521 Operator
.GetMetadataName (Operator
.OpType
.Addition
) :
522 Operator
.GetMetadataName (Operator
.OpType
.UnaryPlus
);
525 oper
= param_list
.Length
== 2 ?
526 Operator
.GetMetadataName (Operator
.OpType
.Subtraction
) :
527 Operator
.GetMetadataName (Operator
.OpType
.UnaryNegation
);
530 oper
= Operator
.GetMetadataName (oper
);
535 Report
.Warning (1020, 1, mc
.Location
, "Overloadable {0} operator is expected", param_list
.Length
== 2 ? "binary" : "unary");
536 Report
.Warning (1584, 1, mc
.Location
, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'",
537 mc
.GetSignatureForError (), cref
);
541 // here we still don't consider return type (to
542 // detect CS1581 or CS1002+CS1584).
543 msig
= new MethodSignature (oper
, null, param_list
);
545 mis
= FindMethodBase (type
,
546 BindingFlags
.Public
| BindingFlags
.NonPublic
| BindingFlags
.Static
| BindingFlags
.Instance
,
549 return null; // CS1574
550 MemberInfo mi
= mis
[0];
551 Type expected
= mi
is MethodInfo
?
552 ((MethodInfo
) mi
).ReturnType
:
554 ((PropertyInfo
) mi
).PropertyType
:
556 if (return_type_name
!= null) {
557 Type returnType
= FindDocumentedType (mc
, return_type_name
, ds
, cref
);
558 if (returnType
== null || returnType
!= expected
) {
560 Report
.Warning (1581, 1, mc
.Location
, "Invalid return type in XML comment cref attribute `{0}'", cref
);
567 private static bool IsAmbiguous (MemberInfo
[] members
)
569 if (members
.Length
< 2)
571 if (members
.Length
> 2)
573 if (members
[0] is EventInfo
&& members
[1] is FieldInfo
)
575 if (members
[1] is EventInfo
&& members
[0] is FieldInfo
)
581 // Processes "see" or "seealso" elements.
582 // Checks cref attribute.
584 private static void HandleXrefCommon (MemberCore mc
,
585 DeclSpace ds
, XmlElement xref
)
587 string cref
= xref
.GetAttribute ("cref").Trim (wsChars
);
588 // when, XmlReader, "if (cref == null)"
589 if (!xref
.HasAttribute ("cref"))
591 if (cref
.Length
== 0)
592 Report
.Warning (1001, 1, mc
.Location
, "Identifier expected");
593 // ... and continue until CS1584.
595 string signature
; // "x:" are stripped
596 string name
; // method invokation "(...)" are removed
597 string parameters
; // method parameter list
599 // When it found '?:' ('T:' 'M:' 'F:' 'P:' 'E:' etc.),
600 // MS ignores not only its member kind, but also
601 // the entire syntax correctness. Nor it also does
602 // type fullname resolution i.e. "T:List(int)" is kept
603 // as T:List(int), not
604 // T:System.Collections.Generic.List<System.Int32>
605 if (cref
.Length
> 2 && cref
[1] == ':')
610 // Also note that without "T:" any generic type
613 int parens_pos
= signature
.IndexOf ('(');
614 int brace_pos
= parens_pos
>= 0 ? -1 :
615 signature
.IndexOf ('[');
616 if (parens_pos
> 0 && signature
[signature
.Length
- 1] == ')') {
617 name
= signature
.Substring (0, parens_pos
).Trim (wsChars
);
618 parameters
= signature
.Substring (parens_pos
+ 1, signature
.Length
- parens_pos
- 2).Trim (wsChars
);
620 else if (brace_pos
> 0 && signature
[signature
.Length
- 1] == ']') {
621 name
= signature
.Substring (0, brace_pos
).Trim (wsChars
);
622 parameters
= signature
.Substring (brace_pos
+ 1, signature
.Length
- brace_pos
- 2).Trim (wsChars
);
628 Normalize (mc
, ref name
);
630 string identifier
= GetBodyIdentifierFromName (name
);
632 // Check if identifier is valid.
633 // This check is not necessary to mark as error, but
634 // csc specially reports CS1584 for wrong identifiers.
635 string [] name_elems
= identifier
.Split ('.');
636 for (int i
= 0; i
< name_elems
.Length
; i
++) {
637 string nameElem
= GetBodyIdentifierFromName (name_elems
[i
]);
639 Normalize (mc
, ref nameElem
);
640 if (!Tokenizer
.IsValidIdentifier (nameElem
)
641 && nameElem
.IndexOf ("operator") < 0) {
642 Report
.Warning (1584, 1, mc
.Location
, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'",
643 mc
.GetSignatureForError (), cref
);
644 xref
.SetAttribute ("cref", "!:" + signature
);
649 // check if parameters are valid
650 Type
[] parameter_types
;
651 if (parameters
== null)
652 parameter_types
= null;
653 else if (parameters
.Length
== 0)
654 parameter_types
= Type
.EmptyTypes
;
656 string [] param_list
= parameters
.Split (',');
657 ArrayList plist
= new ArrayList ();
658 for (int i
= 0; i
< param_list
.Length
; i
++) {
659 string param_type_name
= param_list
[i
].Trim (wsChars
);
660 Normalize (mc
, ref param_type_name
);
661 Type param_type
= FindDocumentedType (mc
, param_type_name
, ds
, cref
);
662 if (param_type
== null) {
663 Report
.Warning (1580, 1, mc
.Location
, "Invalid type for parameter `{0}' in XML comment cref attribute `{1}'",
664 (i
+ 1).ToString (), cref
);
667 plist
.Add (param_type
);
669 parameter_types
= plist
.ToArray (typeof (Type
)) as Type
[];
672 Type type
= FindDocumentedType (mc
, name
, ds
, cref
);
674 // delegate must not be referenced with args
675 && (!TypeManager
.IsDelegateType (type
)
676 || parameter_types
== null)) {
677 string result
= GetSignatureForDoc (type
)
678 + (brace_pos
< 0 ? String
.Empty
: signature
.Substring (brace_pos
));
679 xref
.SetAttribute ("cref", "T:" + result
);
683 int period
= name
.LastIndexOf ('.');
685 string typeName
= name
.Substring (0, period
);
686 string member_name
= name
.Substring (period
+ 1);
687 Normalize (mc
, ref member_name
);
688 type
= FindDocumentedType (mc
, typeName
, ds
, cref
);
691 FoundMember fm
= FindDocumentedMember (mc
, type
, member_name
, parameter_types
, ds
, out warn_result
, cref
, true, name
);
695 MemberInfo mi
= fm
.Member
;
696 // we cannot use 'type' directly
697 // to get its name, since mi
698 // could be from DeclaringType
700 xref
.SetAttribute ("cref", GetMemberDocHead (mi
.MemberType
) + GetSignatureForDoc (fm
.Type
) + "." + member_name
+ GetParametersFormatted (mi
));
701 return; // a member of a type
707 FoundMember fm
= FindDocumentedMember (mc
, ds
.TypeBuilder
, name
, parameter_types
, ds
, out warn_result
, cref
, true, name
);
711 MemberInfo mi
= fm
.Member
;
712 // we cannot use 'type' directly
713 // to get its name, since mi
714 // could be from DeclaringType
716 xref
.SetAttribute ("cref", GetMemberDocHead (mi
.MemberType
) + GetSignatureForDoc (fm
.Type
) + "." + name
+ GetParametersFormatted (mi
));
717 return; // local member name
721 // It still might be part of namespace name.
722 Namespace ns
= ds
.NamespaceEntry
.NS
.GetNamespace (name
, false);
724 xref
.SetAttribute ("cref", "N:" + ns
.GetSignatureForError ());
725 return; // a namespace
727 if (RootNamespace
.Global
.IsNamespace (name
)) {
728 xref
.SetAttribute ("cref", "N:" + name
);
729 return; // a namespace
732 Report
.Warning (1574, 1, mc
.Location
, "XML comment on `{0}' has cref attribute `{1}' that could not be resolved",
733 mc
.GetSignatureForError (), cref
);
735 xref
.SetAttribute ("cref", "!:" + name
);
738 static string GetParametersFormatted (MemberInfo mi
)
740 MethodBase mb
= mi
as MethodBase
;
741 bool is_setter
= false;
742 PropertyInfo pi
= mi
as PropertyInfo
;
744 mb
= pi
.GetGetMethod ();
747 mb
= pi
.GetSetMethod ();
753 AParametersCollection parameters
= TypeManager
.GetParameterData (mb
);
754 if (parameters
== null || parameters
.Count
== 0)
757 StringBuilder sb
= new StringBuilder ();
759 for (int i
= 0; i
< parameters
.Count
; i
++) {
760 if (is_setter
&& i
+ 1 == parameters
.Count
)
761 break; // skip "value".
764 Type t
= parameters
.Types
[i
];
765 sb
.Append (GetSignatureForDoc (t
));
768 return sb
.ToString ();
771 static string GetBodyIdentifierFromName (string name
)
773 string identifier
= name
;
775 if (name
.Length
> 0 && name
[name
.Length
- 1] == ']') {
776 string tmp
= name
.Substring (0, name
.Length
- 1).Trim (wsChars
);
777 int last
= tmp
.LastIndexOf ('[');
779 identifier
= tmp
.Substring (0, last
).Trim (wsChars
);
785 static void Report419 (MemberCore mc
, string member_name
, MemberInfo
[] mis
)
787 Report
.Warning (419, 3, mc
.Location
,
788 "Ambiguous reference in cref attribute `{0}'. Assuming `{1}' but other overloads including `{2}' have also matched",
790 TypeManager
.GetFullNameSignature (mis
[0]),
791 TypeManager
.GetFullNameSignature (mis
[1]));
795 // Get a prefix from member type for XML documentation (used
796 // to formalize cref target name).
798 static string GetMemberDocHead (MemberTypes type
)
801 case MemberTypes
.Constructor
:
802 case MemberTypes
.Method
:
804 case MemberTypes
.Event
:
806 case MemberTypes
.Field
:
808 case MemberTypes
.NestedType
:
809 case MemberTypes
.TypeInfo
:
811 case MemberTypes
.Property
:
820 // Returns a string that represents the signature for this
821 // member which should be used in XML documentation.
823 public static string GetMethodDocCommentName (MemberCore mc
, Parameters parameters
, DeclSpace ds
)
825 IParameterData
[] plist
= parameters
.FixedParameters
;
826 string paramSpec
= String
.Empty
;
828 StringBuilder psb
= new StringBuilder ();
830 foreach (Parameter p
in plist
) {
831 psb
.Append (psb
.Length
!= 0 ? "," : "(");
832 psb
.Append (GetSignatureForDoc (parameters
.Types
[i
++]));
833 if ((p
.ModFlags
& Parameter
.Modifier
.ISBYREF
) != 0)
836 paramSpec
= psb
.ToString ();
839 if (paramSpec
.Length
> 0)
842 string name
= mc
is Constructor
? "#ctor" : mc
.Name
;
844 if (mc
.MemberName
.IsGeneric
)
845 name
+= "``" + mc
.MemberName
.CountTypeArguments
;
847 string suffix
= String
.Empty
;
848 Operator op
= mc
as Operator
;
850 switch (op
.OperatorType
) {
851 case Operator
.OpType
.Implicit
:
852 case Operator
.OpType
.Explicit
:
853 suffix
= "~" + GetSignatureForDoc (op
.MethodBuilder
.ReturnType
);
857 return String
.Concat (mc
.DocCommentHeader
, ds
.Name
, ".", name
, paramSpec
, suffix
);
860 static string GetSignatureForDoc (Type type
)
863 if (TypeManager
.IsGenericParameter (type
))
864 return (type
.DeclaringMethod
!= null ? "``" : "`") + TypeManager
.GenericParameterPosition (type
);
866 if (TypeManager
.IsGenericType (type
)) {
867 string g
= type
.Namespace
;
868 if (g
!= null && g
.Length
> 0)
870 int idx
= type
.Name
.LastIndexOf ('`');
871 g
+= (idx
< 0 ? type
.Name
: type
.Name
.Substring (0, idx
)) + '{';
873 foreach (Type t
in type
.GetGenericArguments ())
874 g
+= (argpos
++ > 0 ? "," : String
.Empty
) + GetSignatureForDoc (t
);
880 string name
= type
.FullName
!= null ? type
.FullName
: type
.Name
;
881 return name
.Replace ("+", ".").Replace ('&', '@');
885 // Raised (and passed an XmlElement that contains the comment)
886 // when GenerateDocComment is writing documentation expectedly.
888 // FIXME: with a few effort, it could be done with XmlReader,
889 // that means removal of DOM use.
891 internal static void OnMethodGenerateDocComment (
892 MethodCore mc
, XmlElement el
)
894 Hashtable paramTags
= new Hashtable ();
895 foreach (XmlElement pelem
in el
.SelectNodes ("param")) {
896 string xname
= pelem
.GetAttribute ("name");
897 if (xname
.Length
== 0)
898 continue; // really? but MS looks doing so
899 if (xname
!= "" && mc
.Parameters
.GetParameterIndexByName (xname
) < 0)
900 Report
.Warning (1572, 2, mc
.Location
, "XML comment on `{0}' has a param tag for `{1}', but there is no parameter by that name",
901 mc
.GetSignatureForError (), xname
);
902 else if (paramTags
[xname
] != null)
903 Report
.Warning (1571, 2, mc
.Location
, "XML comment on `{0}' has a duplicate param tag for `{1}'",
904 mc
.GetSignatureForError (), xname
);
905 paramTags
[xname
] = xname
;
907 IParameterData
[] plist
= mc
.Parameters
.FixedParameters
;
908 foreach (Parameter p
in plist
) {
909 if (paramTags
.Count
> 0 && paramTags
[p
.Name
] == null)
910 Report
.Warning (1573, 4, mc
.Location
, "Parameter `{0}' has no matching param tag in the XML comment for `{1}'",
911 p
.Name
, mc
.GetSignatureForError ());
915 private static void Normalize (MemberCore mc
, ref string name
)
917 if (name
.Length
> 0 && name
[0] == '@')
918 name
= name
.Substring (1);
919 else if (name
== "this")
921 else if (Tokenizer
.IsKeyword (name
) && !IsTypeName (name
))
922 Report
.Warning (1041, 1, mc
.Location
, "Identifier expected. `{0}' is a keyword", name
);
925 private static bool IsTypeName (string name
)
951 // Implements XML documentation generation.
953 public class Documentation
955 public Documentation (string xml_output_filename
)
957 docfilename
= xml_output_filename
;
958 XmlDocumentation
= new XmlDocument ();
959 XmlDocumentation
.PreserveWhitespace
= false;
962 private string docfilename
;
965 // Used to create element which helps well-formedness checking.
967 public XmlDocument XmlDocumentation
;
970 // The output for XML documentation.
972 public XmlWriter XmlCommentOutput
;
975 // Stores XmlDocuments that are included in XML documentation.
976 // Keys are included filenames, values are XmlDocuments.
978 public Hashtable StoredDocuments
= new Hashtable ();
981 // Outputs XML documentation comment from tokenized comments.
983 public bool OutputDocComment (string asmfilename
)
985 XmlTextWriter w
= null;
987 w
= new XmlTextWriter (docfilename
, null);
989 w
.Formatting
= Formatting
.Indented
;
990 w
.WriteStartDocument ();
991 w
.WriteStartElement ("doc");
992 w
.WriteStartElement ("assembly");
993 w
.WriteStartElement ("name");
994 w
.WriteString (Path
.ChangeExtension (asmfilename
, null));
995 w
.WriteEndElement (); // name
996 w
.WriteEndElement (); // assembly
997 w
.WriteStartElement ("members");
998 XmlCommentOutput
= w
;
999 GenerateDocComment ();
1000 w
.WriteFullEndElement (); // members
1001 w
.WriteEndElement ();
1002 w
.WriteWhitespace (Environment
.NewLine
);
1003 w
.WriteEndDocument ();
1005 } catch (Exception ex
) {
1006 Report
.Error (1569, "Error generating XML documentation file `{0}' (`{1}')", docfilename
, ex
.Message
);
1015 // Fixes full type name of each documented types/members up.
1017 public void GenerateDocComment ()
1019 TypeContainer root
= RootContext
.ToplevelTypes
;
1021 if (root
.Types
!= null)
1022 foreach (TypeContainer tc
in root
.Types
)
1023 DocUtil
.GenerateTypeDocComment (tc
, null);
1025 if (root
.Delegates
!= null)
1026 foreach (Delegate d
in root
.Delegates
)
1027 DocUtil
.GenerateDocComment (d
, null);